464 lines
26 KiB
PHP
464 lines
26 KiB
PHP
<?php
|
||
/**
|
||
* EB rack rate config — content only
|
||
* app/Views/docs/eb-rack-rate-config.php
|
||
*/
|
||
?>
|
||
|
||
<h2 id="purpose">Purpose</h2>
|
||
|
||
<p>
|
||
<strong>EB rack rate config</strong> is how Nhance authors <strong>premium rack rates</strong> for a client policy:
|
||
rate tables (SI, age, grade, relationship, etc.) plus two <strong>policy-wide behaviours</strong> that apply to
|
||
<strong>every</strong> grid type (1–13): <a href="#premium-calculation-modes">Premium calculation</a> and
|
||
<a href="#applicable-family-members">Applicable family members</a>. GMC policies may also define multiple
|
||
named rack rates (Primary + additional tabs). Technical save/load paths reference
|
||
<code>ClientController</code>, <code>policy_grid.php</code>, and <code>policy_grid_excel.php</code>.
|
||
For the <strong>Excel employee onboarding</strong> pipeline that consumes this configuration and runs
|
||
<code>calculate_premium_new</code>, see
|
||
<a href="<?= base_url('docs/eb-rack-rate-calculation') ?>">EB rack rate calculation</a>.
|
||
</p>
|
||
|
||
<h2 id="premium-calculation-modes">Premium calculation (all grid types)</h2>
|
||
|
||
<p>
|
||
The modal exposes <strong>Premium calculation</strong> as three radios: <strong>Individual</strong>,
|
||
<strong>Family floater</strong>, and <strong>Family floater cum Individual</strong>. This choice is stored
|
||
with the rack (e.g. <code>premium_type</code> on premium rows where used) and interpreted <strong>when
|
||
premiums are calculated in downstream flows</strong> (enrollment, endorsements, etc.) — not re-derived from
|
||
the grid layout alone.
|
||
</p>
|
||
|
||
<table>
|
||
<thead>
|
||
<tr><th>Mode</th><th>Meaning (at calculation time)</th></tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><strong>Individual</strong></td>
|
||
<td>
|
||
Sum insured is covered <strong>per individual</strong> family member, and <strong>premium is calculated
|
||
(and applied) per member</strong> according to the rack rules and member attributes.
|
||
</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Family floater</strong></td>
|
||
<td>
|
||
Sum insured applies to the <strong>whole family</strong> as one floater cover. Premium is calculated for
|
||
the family unit, but the amount is <strong>stored / represented against the self member only</strong>
|
||
(single premium bucket for the floater).
|
||
</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Family floater cum Individual</strong></td>
|
||
<td>
|
||
<strong>Combines both</strong> behaviours where the product rules require floater cover together with
|
||
individually rated members (exact split depends on policy / insurer rules in the calculation engine).
|
||
</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
<div class="callout info">
|
||
<span>i</span>
|
||
<div>
|
||
For developers: the rack modal captures the <strong>mode</strong> and the <strong>rate table</strong>; always
|
||
trace how <code>premium_type</code> (and policy terms) are read in the <strong>premium calculation</strong> path
|
||
you are debugging — not only in <code>createClientPolicyPremium</code>.
|
||
</div>
|
||
</div>
|
||
|
||
<h2 id="applicable-family-members">Applicable family members (all grid types)</h2>
|
||
|
||
<p>
|
||
The section <strong>“Choose applicable family members”</strong> is driven by what the <strong>policy
|
||
terms</strong> allow for that client policy (who can exist on the cover). The user then selects which of
|
||
those relationships are <strong>in scope for this specific rack rate</strong> (Self / Spouse / Children /
|
||
Parents / Parents in law) using the radio options below.
|
||
</p>
|
||
|
||
<h3 id="applicable-members-radio-meanings">Radio option meanings (per relationship row)</h3>
|
||
|
||
<p>
|
||
Each relationship (Self, Spouse, Children, Parents, Parents in law) uses the same vocabulary of choices.
|
||
These define <strong>eligibility rules for this rack rate</strong>, not the member list itself.
|
||
</p>
|
||
|
||
<table>
|
||
<thead>
|
||
<tr><th>Choice</th><th>Meaning for this rack rate</th></tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><strong>Yes</strong></td>
|
||
<td>That relationship <strong>must</strong> be present in the incoming family for this rack rate to apply.</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>No</strong></td>
|
||
<td>That relationship <strong>must not</strong> be present — if it is, this rack rate does not match.</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Maybe</strong></td>
|
||
<td>If that relationship is <strong>available</strong> on the family, it is <strong>included in</strong> this rack rate’s match; if not present, the rack can still match without it.</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>NA</strong></td>
|
||
<td>Even if members of that relationship exist on the policy, they are <strong>not applicable</strong> to this rack rate (this rack never prices or targets them).</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Any</strong></td>
|
||
<td><strong>Any number</strong> of members with that same relationship is allowed for a match (no fixed count).</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>1, 2, 3, 4, …</strong> (numeric)</td>
|
||
<td>An <strong>exact count</strong> of members with that relationship must be present for this rack rate to match (e.g. exactly two parents).</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
<h3 id="applicable-members-matching">How rack rates are picked at premium time</h3>
|
||
|
||
<p>
|
||
At calculation time (enrollment / endorsement / etc.), the engine compares the <strong>incoming family’s
|
||
members and relationships</strong> (who is on the cover and in what roles) against <strong>each configured rack
|
||
rate’s applicable members</strong> (the rules saved in <code>additional_relationship</code> for that tab’s rack).
|
||
Racks whose rules <strong>match</strong> the family pattern are candidates for premium.
|
||
</p>
|
||
|
||
<p>
|
||
<strong>More than one rack rate can match</strong> the same family (e.g. a base employee rack and a separate
|
||
parents top-up). In those cases <strong>multiple rack rates may apply</strong> and <strong>premium is calculated
|
||
accordingly</strong> (combined according to product rules in the calculation path — not in the modal UI alone).
|
||
</p>
|
||
|
||
<p>
|
||
The diagram below is <strong>conceptual</strong>: the exact class or function name lives in your premium /
|
||
onboarding pipeline, but the decision order is what new developers should internalize.
|
||
</p>
|
||
|
||
<div class="mermaid-wrapper" id="rack-rate-family-matching-flowchart">
|
||
<div class="mermaid">
|
||
flowchart TD
|
||
Start([Start premium run]) --> Family["Incoming family snapshot:<br/>members, relationship, counts"]
|
||
Family --> Load["Load active rack config for policy<br/>policy_premium_1 / 2, is_active = 1<br/>each rack_rate_name + rules + grid rows"]
|
||
Load --> Compare["For each rack rate:<br/>compare family vs applicable members<br/>Yes / No / Maybe / NA / Any / count"]
|
||
Compare --> Matched["Build matched rack list<br/>0, 1, or many racks"]
|
||
Matched --> Calc["Premium engine:<br/>use premium_type + rate table per matched rack"]
|
||
Calc --> Multi{"Several racks matched?"}
|
||
Multi -->|Yes| Combine["Combine premium<br/>per product rules"]
|
||
Multi -->|No| Single["Single-rack premium"]
|
||
Combine --> Done([Allocate to members / floater])
|
||
Single --> Done
|
||
</div>
|
||
</div>
|
||
|
||
<ul>
|
||
<li><strong>Why multiple tabs exist:</strong> A single policy may define <strong>several rack rates</strong> (GMC
|
||
Primary + “Add Rack Rate” tabs). Each tab encodes a different applicable-member pattern and/or rate table.</li>
|
||
<li><strong>Persisted as:</strong> On save, the selections are stored in <code>additional_relationship</code>
|
||
JSON on premium rows (keys such as <code>self</code>, <code>spouse</code>, <code>childrens</code>,
|
||
<code>parents</code>, <code>parents-in-law</code>). Grid ids <strong>1</strong> and <strong>2</strong> force a
|
||
simplified relation map in the controller (self-only path).</li>
|
||
<li><strong>Reload in UI:</strong> For GMC, <code>getpolicyGridData</code> returns <code>jsonArray</code> keyed by
|
||
<code>rack_rate_name</code> so the modal can restore checkbox state per tab.</li>
|
||
</ul>
|
||
|
||
<h2 id="how-to-configure-rack-rate">How to configure a rack rate (checklist)</h2>
|
||
|
||
<ol>
|
||
<li><strong>Policy terms first.</strong> Ensure <code>client_policy.policy_terms</code> reflects allowed members,
|
||
family floater flags, SI ladders, etc. The rack UI inherits what is allowed.</li>
|
||
<li><strong>Open the Rack Rate modal</strong> (<code>.btnPolicyModel</code>) for the target <code>client_policy_id</code>.</li>
|
||
<li><strong>Set Premium calculation</strong> — pick Individual, Family floater, or Family floater cum Individual
|
||
per product rules (see <a href="#premium-calculation-modes">Premium calculation</a>).</li>
|
||
<li><strong>Set applicable family members</strong> for <em>this</em> rack rate tab — narrow from policy-allowed
|
||
members to who this table applies to (see <a href="#applicable-family-members">Applicable family members</a>).</li>
|
||
<li><strong>Choose Policy premium type</strong> — maps to <code>policy_grid_id</code> 1–13 from
|
||
<code>policy_grid_master</code> (see <a href="#grid-types-1-13">Grid types</a>).</li>
|
||
<li><strong>Fill the grid</strong> — manual rows, “+” rows, and/or <strong>Copy from excel</strong> using headers
|
||
from <code>policy_grid_excel.php</code>.</li>
|
||
<li><strong>Save</strong> — POST to <code>client/premimum/create</code>; confirm no validation errors (Parsley,
|
||
SI vs policy terms, duplicate family composition across tabs where enforced).</li>
|
||
<li><strong>Repeat for additional GMC tabs</strong> if the product uses more than one named rack rate.</li>
|
||
</ol>
|
||
|
||
<h2 id="ui-entry">Where it appears in the UI</h2>
|
||
|
||
<ul>
|
||
<li><strong>View files:</strong> <code>app/Views/policy_grid.php</code> (modal shell, tabs, forms) and
|
||
<code>app/Views/policy_grid_excel.php</code> (Excel header maps, paste helpers, <code>copyHeaders</code> / <code>generateTable</code>).</li>
|
||
<li><strong>Open modal:</strong> A control with class <code>.btnPolicyModel</code> passes <code>data-id</code> (client policy id) and policy type context; the script loads grid definitions and any saved premiums (see AJAX below).</li>
|
||
<li><strong>GMC vs GPA in the modal:</strong> For GPA (<code>policy_type_string == 'GPA'</code> or policy type ids 6 / 7), “Add Rack Rate” and the primary tab chrome are hidden and the submit button label is “Submit”. For GMC, multiple rack-rate tabs are supported (<code>appendNewTab</code>, rename).</li>
|
||
<li><strong>Forms:</strong> Each tab uses a form whose id starts with <code>GridForm_</code> (primary <code>GridForm_</code>, additional tabs <code>GridForm_{tabId}</code>). Hidden fields carry <code>client_id</code>, <code>client_policy_id</code>, <code>rack_rate_name</code> (e.g. Primary), and the selected <code>policy_grid_id</code> from the “Policy Premium Type” dropdown.</li>
|
||
</ul>
|
||
|
||
<h2 id="getpolicy-grid-data">Load grid data — <code>ClientController::getpolicyGridData</code></h2>
|
||
|
||
<p><strong>HTTP:</strong> <code>GET</code> <code>util/policy-premium</code> with query <code>client_policy_id</code> (see <code>app/Config/Routes.php</code> under the <code>/util</code> group). The front end calls <code>base_url("util/policy-premium")</code> with that query parameter.</p>
|
||
|
||
<p><strong>What it does:</strong></p>
|
||
<ol>
|
||
<li>Loads <code>client_policy</code> joined to <code>policy_type</code> and reads <code>policy_terms</code> JSON (family floater flags, <code>family_floaters</code>, etc.).</li>
|
||
<li>Resolves <code>terms_si_amount_array</code> via <code>getPolicyTerms($client_policy_id)</code> and branch units via <code>getBranchUnitsByBranchId</code>.</li>
|
||
<li>Counts active employees on the policy for UI gating (<code>hideShowSubmitButton</code>).</li>
|
||
<li>Derives a search token from policy type name / id: <strong>GMC</strong> (regex on type name, or type id 72) vs <strong>GPA</strong> (regex or type ids 6 / 7).</li>
|
||
<li>Fetches grid <strong>templates</strong> from <code>policy_grid_master</code> via <code>PolicyGridModel::like('policy_type', $search_term)</code> — these rows drive which “Policy Premium Type” options exist and what columns each grid id expects.</li>
|
||
<li><strong>GPA:</strong> Loads saved rows from <code>policy_premium_1</code> for this client + client_policy where <code>is_active = 1</code>.</li>
|
||
<li><strong>GMC:</strong> Loads saved rows from <code>policy_premium_2</code> for the same scope. Also builds <code>jsonArray</code>: grouped by <code>rack_rate_name</code>, each value is <code>json_decode(additional_relationship)</code> (family-composition flags used when re-rendering checkboxes).</li>
|
||
<li><strong>GMC family floater filter:</strong> If <code>family_floater == 0</code>, when existing data exists it keeps premiums whose <code>policy_grid_id</code> is in 3–9; if <code>family_floater == 1</code>, it prefers grid ids 10–11 when data exists. Empty data passes through unchanged.</li>
|
||
</ol>
|
||
|
||
<p><strong>Response shape (success):</strong> <code>data</code> (grid master rows), <code>premiumData</code> (JSON string of premium rows for the UI), <code>jsonArray</code>, <code>branch_units</code>, <code>family_floater</code>, <code>self</code>, <code>count</code>, <code>terms_si_amount_array</code>, <code>client_policy_id</code>. GPA and GMC branches return the same keys; non-GPA/GMC types still return grid templates but premium payload may be empty.</p>
|
||
|
||
<h2 id="create-premium">Save rack rate — <code>ClientController::createClientPolicyPremium</code></h2>
|
||
|
||
<p><strong>HTTP:</strong> <code>POST</code> <code>client/premimum/create</code> (spelling <code>premimum</code> matches routes). Body is multipart form data from the modal (<code>FormData</code> in JS).</p>
|
||
|
||
<p><strong>What it does:</strong></p>
|
||
<ol>
|
||
<li>Sanitizes POST via <code>sanitizeInputArrayAdvanced</code>; requires a valid <code>client_policy_id</code> and loads <code>client_policy</code> for <code>client_id</code> / branch.</li>
|
||
<li>Builds <code>additional_relationship</code> JSON from checkboxes: <code>self</code>, <code>spouse</code>, <code>childrens</code>, <code>parents</code>, <code>parents-in-law</code>. For grid ids <strong>1</strong> or <strong>2</strong> it forces a fixed relation map (self only).</li>
|
||
<li><strong>Deactivate old rows (soft replace):</strong> For <code>policy_grid_id</code> 1 or 2, sets <code>is_active = 0</code> on <strong>all</strong> <code>policy_premium_1</code> rows for that client policy. Otherwise sets <code>is_active = 0</code> on <code>policy_premium_2</code> rows matching the same <code>rack_rate_name</code> (so additional GMC tabs do not wipe other rack rates).</li>
|
||
<li><strong>Insert new active rows:</strong> One insert per SI/premium row (arrays in POST). Units default from branch if a slot is empty or <code>undefined</code>.</li>
|
||
</ol>
|
||
|
||
<p><strong>Branching by <code>policy_grid_id</code> (high level):</strong></p>
|
||
<ul>
|
||
<li><strong>1 (GPA-style primary):</strong> Uses <code>si_or_bp</code>: <code>1</code> = sum insured + premium rows (<code>gpa_sum_si[]</code>, <code>gpa_sum_premium[]</code>, …); <code>2</code> = basic pay ladder; <code>3</code> = band/grade + SI + premium. Writes to <code>policy_premium_1</code>.</li>
|
||
<li><strong>2 or 9:</strong> Simple SI + premium columns (<code>gpa_si29[]</code>, <code>gpa_premium29[]</code>). Grid <strong>2</strong> uses <code>policy_premium_1</code>; grid <strong>9</strong> uses <code>policy_premium_2</code>.</li>
|
||
<li><strong>3–8, 10–13:</strong> Prefix <code>{id}_</code> on POST keys (e.g. <code>3_premium[]</code>, <code>3_si[]</code>, age from/to, grade, relationship, <code>max_si</code>). All go to <code>policy_premium_2</code>.</li>
|
||
</ul>
|
||
|
||
<p><strong>Success response:</strong> <code>status: true</code>, <code>rack_rate_json</code> — a small JSON map used by the UI to prevent duplicate family-floater combinations across tabs (<code>rarc_rate_json_array</code> in <code>policy_grid.php</code>).</p>
|
||
|
||
<h2 id="frontend-flow">Frontend flow (<code>policy_grid.php</code>)</h2>
|
||
|
||
<ol>
|
||
<li><strong>Open:</strong> Click <code>.btnPolicyModel</code> → GET <code>util/policy-premium?client_policy_id=…</code>.</li>
|
||
<li><strong>Populate dropdown:</strong> <code>appendGridData(res.data, …)</code> fills “Policy Premium Type” from grid master rows.</li>
|
||
<li><strong>Build inputs:</strong> Changing the dropdown calls <code>addGridHTML</code>, which injects large HTML templates for grid ids 1–13 (SI/basic/grade layouts, GMC age bands, etc.). Existing <code>premiumData</code> pre-fills values when editing.</li>
|
||
<li><strong>Family composition:</strong> <code>createCheckboxes</code> uses <code>res.self</code> / policy terms and, for GMC, saved <code>additional_relationship</code> from <code>jsonArray</code>.</li>
|
||
<li><strong>Submit:</strong> Delegated submit handler on <code>form[id^="GridForm_"]</code> — Parsley validation, duplicate SI checks against <code>terms_si_amount_array</code> (<code>checkPolicyTermsSI</code>), unit checks, then <code>POST client/premimum/create</code> with <code>FormData</code>. On success, appends <code>res.rack_rate_json</code> for duplicate-tab prevention; for grid 1 or 2 the modal may auto-close.</li>
|
||
<li><strong>Excel:</strong> “Copy from excel” toggles a textarea; <code>policy_grid_excel.php</code> defines per-grid header order and parsing to fill the grid.</li>
|
||
</ol>
|
||
|
||
<h2 id="grid-ids-storage">Grid IDs and database tables</h2>
|
||
|
||
<table>
|
||
<thead>
|
||
<tr><th>Artifact</th><th>Role</th></tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><code>policy_grid_master</code></td>
|
||
<td>Catalog of available premium grid layouts filtered by policy type (GPA vs GMC).</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>policy_premium_1</code></td>
|
||
<td>Stores GPA primary grid (id 1), GPA-style grid 2, and other rows where the controller routes to <code>PolicyPremium1Model</code>.</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>policy_premium_2</code></td>
|
||
<td>Stores most GMC grids (3+), grid 9, and additional rack rates distinguished by <code>rack_rate_name</code>.</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>client_policy.policy_terms</code></td>
|
||
<td>JSON: drives family floater behaviour in <code>getpolicyGridData</code> and which checkbox defaults appear.</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
<h2 id="grid-types-1-13">Grid types (1–13) — <code>policy_grid_master</code></h2>
|
||
|
||
<p>
|
||
Every grid below uses the same two layers documented above:
|
||
<a href="#premium-calculation-modes">Premium calculation</a> and
|
||
<a href="#applicable-family-members">Applicable family members</a>.
|
||
The <strong>only</strong> difference between ids 1–13 is the <strong>shape of the rate table</strong> (which columns
|
||
appear and how POST fields are named). Downstream premium logic must combine <code>premium_type</code>,
|
||
<code>additional_relationship</code>, and these rows.
|
||
</p>
|
||
|
||
<h3 id="grid-4-vs-5-ui">UI pattern: grid 4 vs grid 5 (age vs age + SI per row)</h3>
|
||
|
||
<p>
|
||
These two GMC layouts are easy to confuse; the modal layout differs as follows (reviewed UI):
|
||
</p>
|
||
|
||
<ul>
|
||
<li><strong>Grid 4 — Employees Age band:</strong> One <strong>Sum insured</strong> field applies to the whole table
|
||
block; each row is only <strong>From age</strong>, <strong>To age</strong>, and <strong>Premium</strong>. You are
|
||
building age bands under a single SI.</li>
|
||
<li><strong>Grid 5 — Employees Age + SI:</strong> Each row includes <strong>Sum insured</strong>,
|
||
<strong>From age</strong>, <strong>To age</strong>, and <strong>Premium</strong>. SI can change per row together
|
||
with the age band.</li>
|
||
</ul>
|
||
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Grid ID</th>
|
||
<th>Line</th>
|
||
<th>Master label</th>
|
||
<th>Rate table (what differs)</th>
|
||
<th>Persisted in</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><code>1</code></td>
|
||
<td>GPA</td>
|
||
<td>Sum Insured (SI) * Multiplier</td>
|
||
<td>
|
||
GPA primary: sub-mode <code>si_or_bp</code> — <strong>1</strong> SI × multiplier rows, <strong>2</strong> basic pay ladder with multipliers,
|
||
<strong>3</strong> band/grade + SI + premium. Multiple unit/SI/premium lines. Controller forces <code>additional_relationship</code> to self-only for grids 1–2.
|
||
Same <a href="#premium-calculation-modes">premium calculation</a> radios apply when shown.
|
||
</td>
|
||
<td><code>policy_premium_1</code></td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>2</code></td>
|
||
<td>GPA</td>
|
||
<td>Flat Rate for all SI</td>
|
||
<td>
|
||
Simple ladder: <code>gpa_unit29[]</code>, <code>gpa_si29[]</code>, <code>gpa_premium29[]</code> per row — no age/grade columns.
|
||
</td>
|
||
<td><code>policy_premium_1</code></td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>3</code></td>
|
||
<td>GMC</td>
|
||
<td>SI</td>
|
||
<td>
|
||
Unit + SI + premium per row (no age/relationship in the standard template). Prefix <code>3_</code> on POST keys.
|
||
</td>
|
||
<td><code>policy_premium_2</code></td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>4</code></td>
|
||
<td>GMC</td>
|
||
<td>Employees Age band</td>
|
||
<td>
|
||
<strong>One SI</strong> for the table; rows = age band + premium only (see <a href="#grid-4-vs-5-ui">Grid 4 vs 5</a>).
|
||
Backend still stores <code>si</code>, <code>age_from</code>, <code>age_to</code>, <code>premium</code> per insert; UI collects one SI context then many age rows.
|
||
</td>
|
||
<td><code>policy_premium_2</code></td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>5</code></td>
|
||
<td>GMC</td>
|
||
<td>Employees Age + SI</td>
|
||
<td>
|
||
<strong>Each row</strong>: SI + from age + to age + premium (see <a href="#grid-4-vs-5-ui">Grid 4 vs 5</a>). Prefix <code>5_</code>.
|
||
</td>
|
||
<td><code>policy_premium_2</code></td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>6</code></td>
|
||
<td>GMC</td>
|
||
<td>Employees + Dependent Age band</td>
|
||
<td>
|
||
Age-band table where dependents are in product scope (<code>policy_grid_master</code> dependent flags). Same POST pattern as other GMC age grids with prefix <code>6_</code>.
|
||
</td>
|
||
<td><code>policy_premium_2</code></td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>7</code></td>
|
||
<td>GMC</td>
|
||
<td>Employees + Dependent Age + SI</td>
|
||
<td>
|
||
Dependent-aware age bands <strong>and</strong> SI on each row (prefix <code>7_</code>) for combined pricing dimensions.
|
||
</td>
|
||
<td><code>policy_premium_2</code></td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>8</code></td>
|
||
<td>GMC</td>
|
||
<td>SI as per Grade or Band</td>
|
||
<td>
|
||
Adds <strong>grade/band</strong> per row with SI and premium (<code>8_grade[]</code>, etc.). For corporate grade–based insurer tables.
|
||
</td>
|
||
<td><code>policy_premium_2</code></td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>9</code></td>
|
||
<td>GMC</td>
|
||
<td>Flat Rate for all</td>
|
||
<td>
|
||
Same row shape as grid <strong>2</strong> (<code>gpa_si29[]</code> / <code>gpa_premium29[]</code>) but saved to <code>policy_premium_2</code> for GMC.
|
||
</td>
|
||
<td><code>policy_premium_2</code></td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>10</code></td>
|
||
<td>GMC</td>
|
||
<td>Maximum age of Dependents</td>
|
||
<td>
|
||
Floater-oriented table (age + SI + premium); <code>getpolicyGridData</code> prefers ids <strong>10–11</strong> when <code>family_floater = 1</code> and premium data exists. Prefix <code>10_</code>.
|
||
</td>
|
||
<td><code>policy_premium_2</code></td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>11</code></td>
|
||
<td>GMC</td>
|
||
<td>Maximum count per Family</td>
|
||
<td>
|
||
Similar family/floater use case as 10; includes <strong>max SI</strong> column (<code>11_max_si[]</code>) for family-count / cap rules. Prefix <code>11_</code>.
|
||
</td>
|
||
<td><code>policy_premium_2</code></td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>12</code></td>
|
||
<td>GMC</td>
|
||
<td>Employees + relationship</td>
|
||
<td>
|
||
Each row carries a <strong>relationship</strong> value plus SI/premium (and unit) so rates differ by member type. Prefix <code>12_</code>.
|
||
</td>
|
||
<td><code>policy_premium_2</code></td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>13</code></td>
|
||
<td>GMC</td>
|
||
<td>Employees age + relationship age</td>
|
||
<td>
|
||
Full row: relationship + age from/to + SI + premium. Prefix <code>13_</code>.
|
||
</td>
|
||
<td><code>policy_premium_2</code></td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
<div class="callout info">
|
||
<span>i</span>
|
||
<div>
|
||
<strong>POST naming for GMC grids 3–13:</strong> fields use the <code>{gridId}_</code> prefix, e.g.
|
||
<code>5_age_from[]</code>, <code>5_age_to[]</code>, <code>5_si[]</code>, <code>5_premium[]</code>, <code>5_unit[]</code>.
|
||
See <code>policy_grid_excel.php</code> → <code>excel_headers</code> for paste column order per id.
|
||
</div>
|
||
</div>
|
||
|
||
<h2 id="excel-paste">Excel paste path</h2>
|
||
|
||
<p>
|
||
<code>policy_grid_excel.php</code> defines <code>excel_headers</code> keyed by grid id (and nested keys for GPA grid 1 variants).
|
||
Users paste tab-separated data; helpers build a preview table and map columns into the same field names the manual grid uses.
|
||
Use this when bulk-entering many SI/premium lines instead of row-by-row “+” buttons.
|
||
</p>
|
||
|
||
<h2 id="related-routes">Related routes and follow-ups</h2>
|
||
|
||
<ul>
|
||
<li><code>POST client/premimum/edit</code> — <code>ClientController::editClientPolicyPremium</code> (edit path; not detailed on this page).</li>
|
||
<li><code>GET util/delete-additional-rack-rate/(:any)</code> — referenced elsewhere for removing extra GMC rack-rate tabs / data.</li>
|
||
<li><strong>Premium calculation at enrollment:</strong> Employee flows (e.g. onboarding) read slab / rack configuration through existing policy services — this page documents <strong>where rack rows are authored</strong>, not every consumer.</li>
|
||
</ul>
|
||
|
||
<div class="callout warning">
|
||
<span>!</span>
|
||
<div>
|
||
<strong>Production caution:</strong> Saving grid <strong>1</strong> or <strong>2</strong> deactivates <strong>all</strong> rows in
|
||
<code>policy_premium_1</code> for the policy before insert. Other grids deactivate only rows sharing the same
|
||
<code>rack_rate_name</code> in <code>policy_premium_2</code>. Test on a copy of client policy data first.
|
||
</div>
|
||
</div>
|