Plan Facts Parsing Analysis · v1
Fixed 2-network parse with exam, lens, frame, and contact copays / allowances, frequencies, and four-tier composite rates.
Smallest parser, but several small fragility items.
The parser hardcodes exactly two networks ("In-Network" and "Out-of-Network"), has a dual-key mapping that may drop information, and is missing out-of-network entries for two text fields (Conventional Contacts Discount Beyond Allowance, Frames Discount Beyond Allowance).
Hard-coded 2 networks named exactly "In-Network" and "Out-of-Network" at line 2321. No detection or sort logic. No employee-class concept.
| Plan Facts field | Quote field | Notes |
|---|---|---|
| Plan Name | name | Direct (line 2317) |
| Exams.Exam Copay (in-network) | inNetwork1EyeExams + Type | parseCopay (line 2332) |
| Exams.Exam Allowance (out-of-network) | outNetworkEyeExams + Type | parseCopay |
| Lenses.Single / Bifocal / Trifocal / Standard Progressive | inNetwork1SingleVisionLenses, …BifocalLenses, etc. | "Standard Progressive" → BasicProvisionLenses (line 67) |
| Frames.Frames Allowance | inNetwork1FramesCovered, outNetworkFramesCovered | parseCopay; pay=Allowance flag |
| Lenses.Frames + Lenses Materials | inNetwork1Materials | ⚠ Dual-key with SingleVisionLenses (line 64) |
| Exams.Exam Frequency | inNetwork1ExamFrequency, outNetworkExamFrequency | Direct text |
| Lenses.Lenses Frequency | inNetwork1LensesFrequency, outNetworkLensesFrequency | Direct text |
| Lenses.Premium Progressive Lenses Copay/Allowance | inNetwork1PremiumProgressiveLenses, outNetworkPremiumProgressiveLenses | Text only (line 93) |
| Contacts.Contacts Frequency | inNetwork1ContactsFrequency, outNetworkContactsFrequency | Direct text |
| Contacts.Conventional Contacts Allowance | inNetwork1ContactsElective, outNetworkContactsElective | Direct text |
| Contacts.Conventional Contacts Discount Beyond Allowance | inNetwork1ContactsAboveAllowance | ⚠ Out-of-network missing |
| Contacts.Medically Necessary Contacts Allowance | inNetwork1ContactsNecessary, outNetworkContactsNecessary | Direct text |
| Frames.Frames Discount Beyond Allowance | inNetwork1FramesAdditional | ⚠ Out-of-network missing |
| Contacts.Contacts-In-Lieu of Glasses | inNetwork1ContactsInLieuOfGlasses, outNetworkContactsInLieuOfGlasses | Yes / No (case-insensitive, line 2398) |
| Lasik coverage description | inNetwork1LasikDiscount, outNetworkLasikDiscount | Direct text (line 2415) |
| Rates.[Coverage Tier] | rateEmployeeOnly, rateEmployeeSpouse, rateEmployeeChildren, rateEmployeeChild, rateEmployeeFamily | Numeric (line 2423) |
The copayMap entry maps 'Lenses.Single Vision Lenses Copay' to both 'SingleVisionLenses' and 'Materials'. The parse loop iterates both keys and writes the same value to both Quote fields. If Plan Facts intends to distinguish lens cost from material upcharge, the dual-key mapping drops that distinction.
In app/Services/PlanfactService.php at line 64 (the copayMap definition for Vision), the entry is: 'Lenses.Single Vision Lenses Copay' => ['SingleVisionLenses', 'Materials'] The parse loop (around lines 2338–2370 in parseQuoteVision()) iterates over both keys and writes the SAME copay value to both inNetwork1SingleVisionLenses and inNetwork1Materials. Please: 1. Show me the copayMap entry and the loop. 2. Confirm whether the dual-write is intentional (both fields are meant to hold the same value, perhaps for two different form labels). If yes, add a comment. 3. If not intentional, find the correct Plan Facts source for "Materials" specifically (likely a different key) and split the mapping. 4. Don't change yet — show the analysis.
The textMap for Vision Out-of-Network (around lines 101–108) doesn't include entries for "Conventional Contacts Discount Beyond Allowance" or "Frames Discount Beyond Allowance". If Plan Facts returns out-of-network values for those, they're dropped.
In app/Services/PlanfactService.php inside the textMap['vision'] definition (around lines 96–108), the In-Network section has entries for: - "Contacts.Conventional Contacts Discount Beyond Allowance" → inNetwork1ContactsAboveAllowance - "Frames.Frames Discount Beyond Allowance" → inNetwork1FramesAdditional But the Out-of-Network section is missing the corresponding entries. Please: 1. Show me the In-Network and Out-of-Network sections of textMap['vision']. 2. Confirm whether those two fields should have out-of-network mappings (e.g., outNetworkContactsAboveAllowance, outNetworkFramesAdditional). If the Quote model has those fields, add the entries. 3. If the Quote model doesn't have those fields, document why out-of-network is intentionally not captured. 4. Don't change yet — show the analysis.
The Contacts-In-Lieu / Lasik fields use strtolower() matching for "yes" / "no". If Plan Facts returns "Y", "N", "true", "false", the value silently drops.
In app/Services/PlanfactService.php at lines 2398–2411, inside parseQuoteVision(), the Contacts-In-Lieu and Lasik switches match strtolower("yes") / strtolower("no").
If Plan Facts ever returns "Y", "N", "true", "false", or "included" / "not included", they fail silently.
Please:
1. Show me the switch.
2. Broaden the matcher to handle the common variants: "yes"/"y"/"true"/"included" → Yes; "no"/"n"/"false"/"not included" → No.
3. Add a default branch that logs unexpected values rather than silently dropping.
4. Show me the proposed change.
Vision always assumes exactly two networks named "In-Network" and "Out-of-Network". If a carrier ever uses different names ("Provider Network" / "Non-Participating", etc.), the copayMap keys won't match and data is silently dropped.
In app/Services/PlanfactService.php at line 2321, inside parseQuoteVision(), the parser hardcodes two network names: $inNetwork = "In-Network"; $outNetwork = "Out-of-Network"; If a carrier uses different network names (e.g., "Provider Network", "Non-Participating"), every subsequent lookup silently fails and the Quote ends up empty. Please: 1. Show me the assignment. 2. Replace it with detection: read the actual network names from the response (e.g., $data['Vision']['Networks'] or wherever Plan Facts lists them) and use those. 3. If the carrier really does have non-standard names, the parse should still work — or fall back to the hardcoded names with a log warning. 4. Show me the proposed change.
If Plan Facts omits "Plan Name", the Quote's name field is empty. Could fall back to the carrier name to avoid blank labels in the UI.
In app/Services/PlanfactService.php at line 2317, inside parseQuoteVision(), the Quote name field is set from Plan Facts' "Plan Name" with no fallback. If Plan Facts omits "Plan Name" (or it's empty), the Quote ends up nameless. Please: 1. Show me the current assignment. 2. Add a fallback: if "Plan Name" is missing or empty, use the carrier name + "Vision Plan" or similar. 3. Show me the proposed change.
Either document the intentional dual-write or split into two distinct sources.
Match the in-network coverage so Plan Facts data isn't dropped.
Handle common variants; log unexpected values.
Or document the assumption and add a fallback / warning.
| File | Lines | Purpose |
|---|---|---|
| app/Services/PlanfactService.php | 162–172 | POST /vision endpoint config |
| app/Services/PlanfactService.php | 451 | Dispatch to parseQuoteVision |
| app/Services/PlanfactService.php | 2313–2443 | Full parser |
| app/Services/PlanfactService.php | 2321 | Hardcoded network names |
| app/Services/PlanfactService.php | 2332–2374 | copayMap iteration / parseCopay |
| app/Services/PlanfactService.php | 2398–2411 | Yes/No matchers |
| app/Services/PlanfactService.php | 61–78 | copayMap['vision'] + textMap['vision'] |