250 lines
9.2 KiB
PHP
250 lines
9.2 KiB
PHP
<?php
|
|
/**
|
|
* Visit offboard - content only
|
|
* app/Views/docs/visit-offboard.php
|
|
*
|
|
* Based on:
|
|
* - app/Controllers/EmployeeController.php (visitOffBoard, updateVisitoffboardStatus)
|
|
* - app/Controllers/EmpDataServiceController.php (deletion / endorsement job enqueue)
|
|
* - app/Controllers/JobWorker.php
|
|
* - app/Config/Routes.php, app/Config/Acl.php
|
|
*/
|
|
?>
|
|
|
|
<p>
|
|
Visit offboard removes members from the Visit wellness side after they are
|
|
deleted or exited in Nhance. A background job calls the Visit delete-policy
|
|
API; on HTTP <code>200</code> and a JSON body where <code>message</code> is
|
|
<code>success</code>, Nhance appends <code>_DEL</code> to
|
|
<code>employee_polices.wellness_onboard</code> for the affected rows.
|
|
</p>
|
|
|
|
<div class="callout info">
|
|
<span>i</span>
|
|
<div>
|
|
<strong>Core files</strong>
|
|
API and DB update logic live in
|
|
<code>app/Controllers/EmployeeController.php</code>
|
|
(<code>visitOffBoard()</code>, <code>updateVisitoffboardStatus()</code>).
|
|
The insurer deletion flow <code>importDeletionUpdateEndorsementID()</code>
|
|
enqueues the job from
|
|
<code>app/Controllers/EmpDataServiceController.php</code>. The worker maps
|
|
the job name in <code>app/Controllers/JobWorker.php</code>.
|
|
</div>
|
|
</div>
|
|
|
|
<h2 id="overview">Overview</h2>
|
|
|
|
<div class="mermaid-wrapper">
|
|
<div class="mermaid">
|
|
flowchart TD
|
|
A[Deletion batch completes for insurer file] --> B[Jobs::addJob visitOffBoard]
|
|
B --> C[JobWorker runs EmployeeController::visitOffBoard]
|
|
C --> D[POST delete-policy-with-dependents]
|
|
D --> E{HTTP 200 and JSON message success}
|
|
E -->|Yes| F[updateVisitoffboardStatus]
|
|
F --> G[CONCAT wellness_onboard with _DEL]
|
|
E -->|No| H[Log failure, no DB marker]
|
|
</div>
|
|
</div>
|
|
|
|
<h2 id="when-it-is-queued">When it is queued</h2>
|
|
|
|
<p>
|
|
After <code>importDeletionUpdateEndorsementID()</code> applies policy and
|
|
endorsement updates from the deletion Excel, when
|
|
<code>$file['insurer_or_tpa'] == 'insurer'</code>, the controller enqueues
|
|
<code>visitOffBoard</code> alongside cash-deposit and BDS jobs. The payload
|
|
carries the list of <code>employee_polices.id</code> values processed in that
|
|
batch, the client policy number, and a fixed source string.
|
|
</p>
|
|
|
|
<pre><code class="language-php">$r = Jobs::addJob(['job_name' => 'visitOffBoard', 'payload' => [
|
|
'memberIds' => $employee_policy_table_primaryKey ?? [],
|
|
'policyNumber' => $policy_name['policy_no'] ?? null,
|
|
'source' => 'NHANCE',
|
|
]]);</code></pre>
|
|
|
|
<table>
|
|
<thead>
|
|
<tr><th>Payload key</th><th>Meaning</th></tr>
|
|
</thead>
|
|
<tbody>
|
|
<tr>
|
|
<td><code>memberIds</code></td>
|
|
<td>Array of <code>employee_polices.id</code> primary keys collected from the deletion Excel flow (<code>emp_policy_primarykey</code> per row).</td>
|
|
</tr>
|
|
<tr>
|
|
<td><code>policyNumber</code></td>
|
|
<td>Policy number from <code>getPolicyNameUsingClientPolicyId()</code> for the batch client policy.</td>
|
|
</tr>
|
|
<tr>
|
|
<td><code>source</code></td>
|
|
<td>Always <code>NHANCE</code> in the enqueue; <code>visitOffBoard()</code> also forces <code>source</code> to <code>NHANCE</code> before the HTTP call.</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
|
|
<pre><code class="language-php">'visitOffBoard' => [
|
|
'type' => 'CC',
|
|
'handler' => 'App\Controllers\EmployeeController',
|
|
],</code></pre>
|
|
|
|
<h2 id="visit-off-board-api">visitOffBoard()</h2>
|
|
|
|
<p>
|
|
The handler builds the Visit URL from environment configuration, appends the
|
|
path <code>delete-policy-with-dependents</code>, and POSTs JSON with
|
|
<code>memberIds</code>, <code>policyNumber</code>, and <code>source</code>.
|
|
Authorization uses a <code>JWT</code> prefix (this differs from the Visit
|
|
onboard upload path, which uses Basic auth in
|
|
<code>sendFamiliesToWellnessApi()</code>).
|
|
</p>
|
|
|
|
<pre><code class="language-php">$apiUrl = env('WELLNESS_ONBOARD_ENDPOINT_URL') . 'delete-policy-with-dependents';
|
|
$apiToken = env('WELLNESS_ONBOARD_AUTHORIZATION');
|
|
|
|
$response = $client->request('POST', '', [
|
|
'headers' => [
|
|
'Authorization' => 'JWT ' . $apiToken,
|
|
'Content-Type' => 'application/json',
|
|
'Accept' => 'application/json',
|
|
],
|
|
'json' => [
|
|
'memberIds' => $params['memberIds'] ?? [],
|
|
'policyNumber' => $params['policyNumber'] ?? '',
|
|
'source' => $params['source'] ?? '',
|
|
],
|
|
'http_errors' => false,
|
|
]);</code></pre>
|
|
|
|
<table>
|
|
<thead>
|
|
<tr><th>Outcome</th><th>Behavior</th></tr>
|
|
</thead>
|
|
<tbody>
|
|
<tr>
|
|
<td>HTTP status <code>200</code></td>
|
|
<td>Decodes JSON, calls <code>updateVisitoffboardStatus()</code> with <code>memberIds</code> and <code>api_result</code>, returns that array shape to the worker.</td>
|
|
</tr>
|
|
<tr>
|
|
<td>Other HTTP status</td>
|
|
<td>Logs failure; returns a structured error array; DB is not updated here.</td>
|
|
</tr>
|
|
<tr>
|
|
<td>Network or client exception</td>
|
|
<td>Logs exception; returns error array with <code>error</code> message.</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
|
|
<h2 id="update-status">updateVisitoffboardStatus()</h2>
|
|
|
|
<p>
|
|
This method is only invoked from <code>visitOffBoard()</code> after a
|
|
<code>200</code> response. It validates the payload, requires
|
|
<code>$data['api_result']['message'] === 'success'</code>, then updates all
|
|
listed employee policy rows in one statement.
|
|
</p>
|
|
|
|
<ol class="steps">
|
|
<li>
|
|
<strong>Validate structure</strong>
|
|
<p>Empty or non-array input logs and returns <code>false</code>.</p>
|
|
</li>
|
|
<li>
|
|
<strong>Require API result</strong>
|
|
<p>Missing <code>api_result</code> logs and returns <code>false</code>.</p>
|
|
</li>
|
|
<li>
|
|
<strong>Require success message</strong>
|
|
<p>If <code>message</code> is not exactly <code>success</code>, logs and returns <code>false</code> (no DB update).</p>
|
|
</li>
|
|
<li>
|
|
<strong>Optional warning for missingMemberIds</strong>
|
|
<p>If the API returns <code>missingMemberIds</code>, it is logged but processing continues.</p>
|
|
</li>
|
|
<li>
|
|
<strong>Append offboard marker</strong>
|
|
<p>Runs <code>whereIn('id', $memberIds)</code> and sets <code>wellness_onboard</code> to <code>CONCAT(wellness_onboard, '_DEL')</code> so existing reference ids stay traceable.</p>
|
|
</li>
|
|
</ol>
|
|
|
|
<pre><code class="language-php">$this->employeePolicyModel
|
|
->whereIn('id', $memberIds)
|
|
->set('wellness_onboard', "CONCAT(wellness_onboard, '_DEL')", false)
|
|
->update();</code></pre>
|
|
|
|
<div class="callout warning">
|
|
<span>!</span>
|
|
<div>
|
|
<strong>HTTP 200 alone is not enough</strong>
|
|
The DB update runs only when the decoded body has
|
|
<code>message === 'success'</code>. A 200 with a failed business payload will
|
|
not append <code>_DEL</code>.
|
|
</div>
|
|
</div>
|
|
|
|
<h2 id="manual-test-route">Manual test route</h2>
|
|
|
|
<p>
|
|
<code>visitOffBoardCheck()</code> is a thin admin helper that builds a hardcoded
|
|
sample <code>$params</code> array and prints <code>visitOffBoard($params)</code>.
|
|
It is not part of the production deletion pipeline; use it only for targeted
|
|
debugging in non-production environments.
|
|
</p>
|
|
|
|
<pre><code class="language-php">$routes->get('/visitOffBoardCheck', 'EmployeeController::visitOffBoardCheck');</code></pre>
|
|
|
|
<p>
|
|
ACL restricts this path to admin role in <code>app/Config/Acl.php</code>
|
|
(<code>#^/visitOffBoardCheck#</code>).
|
|
</p>
|
|
|
|
<h2 id="developer-steps">Developer steps</h2>
|
|
|
|
<ol class="steps">
|
|
<li>
|
|
<strong>Keep payload keys aligned with the Visit API contract</strong>
|
|
<p>The job must supply <code>memberIds</code>, <code>policyNumber</code>, and <code>source</code> in the shape the delete endpoint expects.</p>
|
|
</li>
|
|
<li>
|
|
<strong>Confirm environment variables</strong>
|
|
<p><code>WELLNESS_ONBOARD_ENDPOINT_URL</code> must include the correct base (trailing slash behavior matters when concatenating <code>delete-policy-with-dependents</code>). <code>WELLNESS_ONBOARD_AUTHORIZATION</code> must be the token value expected after <code>JWT </code>.</p>
|
|
</li>
|
|
<li>
|
|
<strong>Do not call <code>updateVisitoffboardStatus()</code> directly for partial failures</strong>
|
|
<p>It trusts <code>api_result['message']</code>; wire new callers through <code>visitOffBoard()</code> or replicate its guards.</p>
|
|
</li>
|
|
<li>
|
|
<strong>Queue dependency</strong>
|
|
<p>As with other jobs, the worker must be running; otherwise the offboard job stays queued after deletion processing.</p>
|
|
</li>
|
|
</ol>
|
|
|
|
<h2 id="common-pitfalls">Common pitfalls</h2>
|
|
|
|
<table>
|
|
<thead>
|
|
<tr><th>Pitfall</th><th>Why it happens</th></tr>
|
|
</thead>
|
|
<tbody>
|
|
<tr>
|
|
<td><code>wellness_onboard</code> never gets <code>_DEL</code></td>
|
|
<td>HTTP not 200, or JSON <code>message</code> is not <code>success</code>, or <code>memberIds</code> empty / wrong type.</td>
|
|
</tr>
|
|
<tr>
|
|
<td>Visit receives wrong identifiers</td>
|
|
<td><code>memberIds</code> must match what the delete API expects (same identifiers family as onboard where applicable).</td>
|
|
</tr>
|
|
<tr>
|
|
<td>Auth works for onboard but not offboard</td>
|
|
<td>Offboard uses <code>JWT</code> header construction; onboard upload uses <code>Basic</code> in a different helper.</td>
|
|
</tr>
|
|
<tr>
|
|
<td>Job never enqueues</td>
|
|
<td>The enqueue block runs only when <code>insurer_or_tpa == 'insurer'</code> on that deletion file path.</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|