Plan Facts Parsing Analysis · v1
Single-class parse covering ~20 covered conditions (cancer, heart attack, stroke, organ failure, neurological diseases), benefit amount + guarantee issue, age-based reductions (up to 3 tiers), spouse / child rider %, separation periods, pre-existing conditions, and age-banded rates.
One semantic mapping question, plus three smaller items.
The "Angioplasty" Plan Facts field maps to a Quote field named criticalIllnessCoronaryArteriosclerosis — the field name doesn't match the source. The other items are fragility around enum casing and age-band collapsing.
Single-class. $class = $data['Critical Illness']['Classes'][0] at line 1930. Age reductions are stored in up to 3 separate tiers (criticalIllnessTier1Age, etc., line 2025).
| Plan Facts field | Quote field | Notes |
|---|---|---|
| Plan Name | name | Direct |
| Participation.[Class].Min Participation % | criticalIllnessParticipationRequired | Strip % |
| Participation.[Class].Min Enrolled Lives | criticalIllnessMinimumLives | Numeric |
| Employee Benefit Schedule.[Class].Benefit Minimum | criticalIllnessIncrementsMinMaxMin | Currency stripped |
| Employee Benefit Schedule.[Class].Benefit Maximum | criticalIllnessIncrementsMinMaxMax | Currency stripped |
| Employee Benefit Schedule.[Class].Benefit Increment | criticalIllnessHowDoBenefitsPay | Appended with "Benefit Increment: " prefix |
| Employee Benefit Schedule.[Class].Guarantee Issue Amount | criticalIllnessGuaranteedIssue | Currency stripped |
| Employee Benefit Schedule.[Class].Lifetime Maximum | criticalIllnessHowDoBenefitsPay | Appended with "Lifetime Maximum: " prefix |
| Spouse Benefit Schedule.[Class].% of Employee Amount | criticalIllnessSpouseCoverage | Strip % |
| Child Benefit Schedule.[Class].% of Employee Amount | criticalIllnessDependentCoverage | Strip % |
| Employee Age Reductions.[Class].Ages (up to 3) | criticalIllnessTier{1..3}Age + criticalIllnessTier{1..3}Reduction + criticalIllnessNumberOfTiers + criticalIllnessBenefitReduction | "By" type inverts (100 - amount); line 2025 |
| Pre-existing Conditions.[Class].Look-back / Exclusion | criticalIllnessPreExistingConditionsMin / criticalIllnessPreExistingConditionsMax | Direct |
| Separation Period.[Class].Additional Occurrence | criticalIllnessDifferentConditionDaysMonths + …Type | Type hardcoded to "Months" |
| Separation Period.[Class].Recurrence | criticalIllnessSameConditionDaysMonths + …Type | Type hardcoded to "Months" |
| Wellness Benefit.[Class].Employee | criticalIllnessWellnessBenefit | Direct |
| Heart Attack / Heart Failure / Stroke / Major Organ Failure / End-stage Renal / Coronary Artery Bypass / Coma / Benign Brain Tumor / Invasive Cancer / Non-invasive Cancer / Occupational HIV / Blind / ALS / Alzheimer's / Addison's / Parkinson's / Huntington's / Multiple Sclerosis / Paralysis.[Class].Initial | criticalIllness[ConditionName] + …Type | "n/a" → "Not Covered"; else strip % + Type="%" |
| Angioplasty.[Class].Initial | criticalIllnessCoronaryArteriosclerosis | ⚠ Field name doesn't match source — see issue (line 2108) |
| Skin Cancer.[Class].Initial | criticalIllnessSkinCancer + Type | Type inferred from "$" prefix (line 2147) |
| Childhood conditions.[Class].Initial | criticalIllnessChildhoodConditions | Boolean from presence (line 2161) |
| Portability.[Class].Portability | criticalIllnessPortability | Included / Not Included |
| Rates.[Class].Employee Unismoker Rates (age-banded, 11+ bands) | age-band fields, criticalIllnessRateType, criticalIllnessBenefitEmployee | Per-$100 benefit extracted via regex (line 2189) |
| Rates.[Class].Employee Unismoker Rates.Age Basis | criticalIllnessPremiums | "Attained Age" / "Issue Age" |
| Rates.[Class].Spouse Unismoker Rates | spouse age-band fields (line 2259) | Per-$100 benefit extracted |
The Plan Facts field 'Angioplasty' is assigned to criticalIllnessCoronaryArteriosclerosis. These are clinically related but not the same: angioplasty is a procedure; coronary arteriosclerosis is the underlying disease. Either the field name is misleading or the source key is wrong.
In app/Services/PlanfactService.php at line 2108, inside parseQuoteCriticalIllness(), the Plan Facts field "Angioplasty" is being assigned to a Quote field named criticalIllnessCoronaryArteriosclerosis. These are clinically related but distinct: angioplasty is a procedure, coronary arteriosclerosis is the underlying condition. Please: 1. Show me the line. 2. Look at the form template — what label is shown for criticalIllnessCoronaryArteriosclerosis? 3. Check whether Plan Facts also returns a separate "Coronary Arteriosclerosis" key (or similar) that's currently unmapped. 4. Recommend either: (a) renaming criticalIllnessCoronaryArteriosclerosis → criticalIllnessAngioplasty end-to-end, or (b) confirming the mapping is intentional and adding a code comment explaining why. 5. Don't change anything yet — show the analysis.
The Childhood Conditions field is set to "Yes" if the Plan Facts source is present, "No" if absent. If Plan Facts ever returns an explicit "No" or "Not Included" value, the parse will incorrectly set "Yes" because the field is technically present.
In app/Services/PlanfactService.php at lines 2161–2168, inside parseQuoteCriticalIllness(), the Childhood Conditions Quote field (criticalIllnessChildhoodConditions) is set to "Yes" or "No" based on presence of the source field, not its value.
This is fragile: if Plan Facts returns { "Initial": "No" } or { "Initial": "Not Included" }, the field will be present and the parser will incorrectly set criticalIllnessChildhoodConditions = "Yes".
Please:
1. Show me the current logic.
2. Update it to check the actual value: "Yes" only if the value is truthy AND not "No" / "Not Included" / "Not Covered" (case-insensitive); otherwise "No".
3. Show me the proposed change.
Skin Cancer Type is determined by checking if the value starts with "$" (then "%"). If a carrier returns "25% of benefit" or anything not starting with "$" or a bare percentage, the Type field may be wrong or unset.
In app/Services/PlanfactService.php at lines 2147–2157, inside parseQuoteCriticalIllness(), the Skin Cancer Type is inferred from whether the value starts with "$": - starts with "$" → Type = "$" - otherwise → Type = "%" This is fragile if a carrier returns "25% of benefit" or any value with mixed format. Please: 1. Show me the current logic. 2. Strengthen it: check for "$" anywhere in the value, OR check for "%" suffix. Default to a sensible value if neither matches. 3. Show me the proposed change.
Both "<20" and "20-24" age bands map to criticalIllnessLess25Employee. If a carrier returns separate rates for those bands, one overwrites the other. Same in the Spouse section (line 2263–2276).
In app/Services/PlanfactService.php at lines 2195–2208 (Employee) and 2263–2276 (Spouse), inside parseQuoteCriticalIllness(), the age bands "<20" and "20-24" both map to the same Quote field (criticalIllnessLess25Employee / criticalIllnessLess25FamilySpouse). If Plan Facts returns separate rates for those two bands, one overwrites the other. Please: 1. Show me the current age-band mapping. 2. Confirm whether Plan Facts ever returns separate rates for <20 and 20-24 in practice (a quick grep of any test fixtures may help). 3. If yes, recommend splitting into two Quote fields. If no, add a code comment confirming the collapse is intentional and the values are equivalent in practice.
Either rename the Quote field, or confirm intentional and add a comment.
Check the value, not just presence.
Match "%" or "$" anywhere in the string, not just at the start.
Quick code comment, or split into two fields if rates do differ.
| File | Lines | Purpose |
|---|---|---|
| app/Services/PlanfactService.php | 246 | Critical Illness endpoint config |
| app/Services/PlanfactService.php | 463 | Dispatch to parseQuoteCriticalIllness |
| app/Services/PlanfactService.php | 1923–2310 | Full parser |
| app/Services/PlanfactService.php | 2025–2054 | Age reduction tiers |
| app/Services/PlanfactService.php | 2108 | Angioplasty → CoronaryArteriosclerosis |
| app/Services/PlanfactService.php | 2147–2157 | Skin Cancer Type inference |
| app/Services/PlanfactService.php | 2161–2168 | Childhood Conditions boolean |
| app/Services/PlanfactService.php | 2189–2310 | Employee + Spouse age-band rates |