πŸ“‹ Migration Summary

Source Tables: 8
Target Models: 10
Decisions: 12

Migrating insurance coverage records (GL, Auto, WC, Umbrella, Liquor, Other, User-Defined) and linking them to certificates and requirements.

🎯 Key Actions

⚠️ Key Challenges

DTX 1.0 β†’ DTX 2.0 Migration Plan: Coverage & Insurance

Overview

Migration of insurance coverage data from DTX 1.0 to DTX 2.0. This is Phase 3 β€” depends on Accounts (Area 2) and Certificates (Area 4). This area also performs the Carrier A-F backfill on Certificate records deferred from Area 4 (Decision #16).

DTX 2.0 coverage models were designed with migration in mind β€” most fields map 1:1 with identical ACORD field names (lowercased). This is the most straightforward area since Area 2.

Important: Full production data is not available. Migration scripts must be built defensively with dry-run mode, self-validation, and graceful error handling.

Source Tables (8)

Table Columns Purpose
general_liability 47 GL coverage β€” carrier, 6 limits, applies-to flags, deductible
automobile_liability 32 Auto coverage β€” carrier, auto type flags, 4 limits
workers_comp 20 WC coverage β€” carrier, statutory flag, 3 disease limits
umbrella_liability 28 Umbrella/excess β€” carrier, retention, 2 limits, applies-to
liquor_liability 19 Liquor liability β€” carrier, 2 limits, not-applicable flag
other_coverage 19 Misc types (Crime, E&O, Property, etc.) β€” type desc, 3 desc/limit pairs
user_defined_policy 18 Custom policy types β€” UDPType, loss_payee, mortgagee
requirement_coverage 10 Requirement-to-coverage tracking β€” vendor, email, source

Target Models (10)

Model Purpose
GeneralLiability GL coverage (certificate FK or coi_risk_profile FK)
AutoLiability Auto coverage
WorkersCompensation WC coverage
UmbrellaLiability Umbrella/excess coverage
LiquorLiability Liquor liability coverage
PropertyCoverage Property coverage (type FK to PropertyCoverageType)
OtherCoverage Misc coverage (type FK to OtherCoverageType)
PropertyCoverageType Type definitions for property coverages
OtherCoverageType Type definitions for other coverages
CarrierRating AM Best rating requirement (avii_or_above flag)

Estimated Volume

Item Estimate
GL records 50K-200K
Auto records 40K-150K
WC records 30K-120K
UMB records 20K-80K
LL records 2K-10K
Other/Property records 10K-50K
UDP (cert data) 0-10K (sample has 0 β€” may be sparse)
CarrierRating records 10K-50K
Certificate carrier backfills 50K-200K
Total records created 160K-660K

Pre-Migration Actions Required

These must be resolved before running the migration.

Action 1: Fix llsamepot Typo in DTX 2.0 Model

Priority: HIGH β€” do this first

The LiquorLiability model has field llsamepot β€” should be llsamepol (same policy). This is a typo baked into the Django model. Fixing it after data migration requires a data migration too. Fix now via a Django schema migration while the table is empty.

# Current (wrong):
llsamepot = models.TextField(null=True, blank=True)
# Should be:
llsamepol = models.TextField(null=True, blank=True)

Action 2: Investigate DropdownOptions NULL Safety

Priority: HIGH β€” potential UI breakage

All coverage models have *_options FKs to DropdownOptions (e.g., general_aggregate_options, each_occurrence_options). Migration leaves all these NULL β€” there's no DTX 1.0 equivalent.

Risk: If DTX 2.0 frontend/API accesses coverage.general_aggregate_options.options without null-checking, every migrated coverage record will crash the UI.

Action: Review DTX 2.0 frontend code for null-safe access on all *_options FKs. If not null-safe, either: - Fix the frontend to handle NULL (preferred) - Create stub DropdownOptions records with empty option arrays

Action 3: Verify Evaluation Coverage Type Strings

Priority: HIGH β€” silent data disconnect

Area 4 created EvaluationCoverage records with coverage_type strings like "GeneralLiability", "AutoLiability", etc. (from certificate_status.field_name prefix mapping). DTX 2.0's evaluation engine presumably uses these strings to match coverage records to evaluations.

Action: Verify that DTX 2.0's evaluation engine expects exactly these strings. If it uses different casing, display names, or model-derived names, the evaluations created in Area 4 will disconnect from the coverage records created here.

Area 4 sets:     EvaluationCoverage.coverage_type = "GeneralLiability"
DTX 2.0 expects: ??? (must match)

Action 4: Confirm CarrierRating Model Intent

Priority: MEDIUM

Decision #5 creates CarrierRating records from certificate.AMBestRatingOK with NULL carrier info (name/naic/code). The model has per-carrier fields, suggesting it's designed for per-carrier rating tracking β€” not per-certificate flags.

Action: Confirm with DTX 2.0 team: - One CarrierRating per certificate (flag approach β€” current plan)? - Or one per unique carrier on the certificate, each with avii_or_above=True (per-carrier approach)?


Known Issues & Risks

Critical

# Issue Impact Mitigation
1 CarrierCode NULL in 4 largest tables β€” GL, Auto, WC, UMB samples all have CarrierCode=NULL. Only LL and Other have codes ("A", "C", "F"). The Carrier A-F backfill for the majority of records has no direct slot assignment. Carrier A-F slots may not match original ACORD form layout Use NAIC cross-referencing: if LL/Other record says CarrierCode="A", NAIC=14377, propagate slot A to any GL/Auto/WC/UMB record sharing NAIC 14377. See Decision #2.
2 Carrier name variants waste A-F slots β€” Same carrier appears as "Continental Casualty Co.", "Continental Casualty Company", "Continental Casualty Company " (trailing space). All share NAIC 20443. Name-based dedup treats these as 3 different carriers. Wrong carrier count, wasted A-F slots Deduplicate by NAIC first (when populated). Fall back to normalized name only when NAIC is NULL. See Decision #2.
3 Coverage timestamps lost β€” DTX 1.0 coverage tables have no create_time/update_time columns. DTX 2.0's TimestampedModel auto-sets created/modified to migration run time. All 160K-660K records will share the same created date. Cannot answer "when was this coverage recorded?" for migrated data Use parent certificate's create_time as proxy. See Decision #12.

High

# Issue Impact Mitigation
4 NULL DropdownOptions may break UI β€” All *_options FKs left NULL for migrated records. If frontend doesn't null-check, every coverage view crashes. Broken UI for all migrated coverage Pre-migration action #2: investigate frontend null safety.
5 Evaluation↔Coverage type string mismatch β€” No FK between coverage records and evaluations. Linkage is by coverage_type string convention. If strings don't match, evaluations disconnect. Evaluations appear to have no coverage data Pre-migration action #3: verify type strings match.
6 Wrong-table coverage types β€” Sample shows "Commercial Umbrella" and "HNOA" (Hired/Non-Owned Auto) in other_coverage. If DTX 2.0's evaluation engine only checks UmbrellaLiability and AutoLiability models, these records are invisible to compliance. Missed compliance data Flag other_coverage types matching standard coverage names. See Decision #3.

Medium

# Issue Impact Mitigation
7 user_defined_policy cert path untestable β€” 100% of sample records are templates (certificate_id=NULL). The certificate migration path has zero test coverage. Production may hit unexpected patterns Extra dry-run validation for this subset. See Decision #4.
8 Cascade from Area 4 skips β€” If Area 4 skipped certificates (missing customer/vendor), all their coverage records are also skipped. One bad customer could mean hundreds of orphaned coverage records. Silent data loss Dry-run must quantify cascade: "N coverage records orphaned by M Area 4 certificate skips."
9 Suspicious limit values β€” LL sample shows "1000000000" ($1B) β€” almost certainly a data entry error ($1M = "1000000"). Plan stores as-is (correct), but doesn't flag. Bad data migrated silently Dry-run flags limits exceeding thresholds (GL >$100M, Auto >$50M, etc.). See Decision #10.
10 Coverage type dedup is case-sensitive β€” "Equipment Floater" vs "Equipment floater" creates two separate types. Duplicate OtherCoverageType/PropertyCoverageType records Case-insensitive, whitespace-normalized dedup. See Decision #3.

Low

# Issue Impact Mitigation
11 GLLimMisc has no DTX 2.0 target β€” No gllimmisc field exists. NULL in all samples but may be populated in full data. Minor data loss Append to gldescmisc with label. See Decision #11.
12 Other1Desc1 has no DTX 2.0 target β€” No other1desc1 field exists. Contains labels like "Per Limit", "Each Claim/ Agg." Minor data loss Log all unique values. Store raw Other1Lim1 in both limit and other1lim1. See Decision #11.

Decisions

Decision #1: Certificate FK Linking

Risk Level: Low

All coverage records use certificate FK (actual data). coi_risk_profile FK stays NULL (templates, Area 6). All *_options FKs stay NULL (UI config, not data β€” pending Pre-Migration Action #2).

For each coverage record:
  1. Look up certificate_id in id_mapping_certificates.csv
  2. If found β†’ set certificate FK to mapped DTX 2.0 Certificate ID
  3. If not found β†’ log to migration_issues.csv, skip record

Decision #2: Carrier A-F Backfill on Certificate

Risk Level: HIGH

Area 4 (Decision #16) left Certificate carrier_a_name through carrier_f_naic NULL. This area backfills them from coverage data.

CarrierCode availability from sample data

Table CarrierCode populated? Sample values
general_liability Rarely β€” NULL in all samples β€”
automobile_liability Rarely β€” NULL in all samples β€”
workers_comp Rarely β€” NULL in all samples β€”
umbrella_liability Rarely β€” NULL in all samples β€”
liquor_liability Yes "A", "C"
other_coverage Sometimes "A", "C", "F"

CarrierCode values "A" through "F" are ACORD insurer letter designations that directly map to Certificate slots.

Migration approach

For each migrated Certificate:
  1. Collect all coverage records linked to this certificate
     (across all 6 tables + user_defined_policy)

  2. Build NAIC β†’ Slot mapping from records WITH CarrierCode:
     - LL record with CarrierCode="A", NAIC=14377 β†’ slot A owns NAIC 14377
     - Other record with CarrierCode="C", NAIC=31194 β†’ slot C owns NAIC 31194

  3. Assign carriers to slots:
     Phase A β€” Direct code assignment:
       Records with CarrierCode populated β†’ assign to that slot
     Phase B β€” NAIC cross-reference:
       Records without CarrierCode but with NAIC matching a Phase A carrier
       β†’ assign to the same slot
     Phase C β€” Remaining unassigned:
       Deduplicate by NAIC (when populated), then by normalized name
       β†’ assign to next available slot A-F

  4. Normalize carrier names for dedup:
     - Strip leading/trailing whitespace
     - Collapse multiple spaces

  5. If >6 unique carriers β†’ fill A-F, log overflow to migration_issues.csv

  6. Update Certificate record with populated carrier slots

Key insight: NAIC is more reliable than carrier name for dedup. Same carrier appears as "Continental Casualty Co." / "Continental Casualty Company" / "Continental Casualty Company " β€” all NAIC 20443.

Decision #3: other_coverage β†’ OtherCoverage vs PropertyCoverage

Risk Level: Medium

DTX 1.0 has a single other_coverage table. DTX 2.0 splits into PropertyCoverage (with loss_payee, mortgagee) and OtherCoverage. The other_coverage table has no loss_payee/mortgagee fields, so routing is based on the Other1 type description.

Sample coverage types from Other1

Property-like:    "Extra Expense", "Equipment Floater", "Business Pers(onal Property)",
                  "Scheduled Equipment", "Contents", "Leased/Rented Equipment",
                  "Installation Floater", "Business Income", "Inland Marine"

Liability/Other:  "Professional Liability", "Professional-Claims Made",
                  "TECHNOLOGY E & O", "Crime Policy", "Crime", "EPLI",
                  "Fire Legal Liability", "Poll Legal Liab", "HNOA"

Pollution:        "Transportation Pollution-Occ", "Contractor Pollution-Occ",
                  "Professional/ Pollution Liab.", "Pollution-worksite"

Wrong-table:      "Commercial Umbrella" (should be umbrella_liability)

Data quality issues observed

Migration approach

1. Extract all unique Other1 values from full dataset
2. Normalize: strip whitespace, title case
3. Case-insensitive dedup (so "Equipment Floater" and "Equipment floater"
   become one type)
4. Classify each into "property" or "other" using keyword matching:
   Property keywords: property, equipment, floater, inland marine,
     business personal, building, contents, real property, business income,
     extra expense, boiler, machinery, scheduled, installation
   Everything else β†’ OtherCoverage
5. Flag wrong-table types for post-migration review:
   Wrong-table keywords: umbrella, hired auto, non-owned auto, HNOA,
     workers comp, general liability
   These types are in other_coverage but logically belong in standard tables.
   DTX 2.0's evaluation engine may not find them.
6. Create PropertyCoverageType / OtherCoverageType (case-insensitive deduped)
7. Route each record to the appropriate model
8. Log all classifications to migration report for manual review

Fallback: Uncertain β†’ OtherCoverage. Safer than misclassifying as PropertyCoverage.

Decision #4: user_defined_policy β€” Template vs Certificate Data

Risk Level: Medium

Dual-purpose table: - Template records: certificate_id=NULL, requirements_id set β†’ Area 6 - Certificate records: certificate_id set β†’ this area

Sample data observation

All 100 sample records are templates. Single customer (815), 3 UDPType values repeating: "Professional Liability", "Cyber Liability", "Pollution Liability". All have UDPLimits=-1, all other fields NULL. The certificate data path cannot be validated from sample data.

Migration approach

1. Split:
   a. Template records (certificate_id=NULL) β†’ defer to Area 6, log count
   b. Certificate records (certificate_id set) β†’ migrate here

2. For each certificate record:
   a. Route to target model:
      - If UDPLossPayeeCB or UDPMortgageeCB is truthy β†’ PropertyCoverage
      - Else if UDPType matches property keywords (Decision #3 list) β†’ PropertyCoverage
      - Otherwise β†’ OtherCoverage
   b. Find or create type record (case-insensitive dedup)
   c. Create coverage record with certificate FK
   d. UDPLimits: -1 or NULL β†’ set limit = NULL

3. Dry-run MUST log (since sample is 100% templates):
   - Count of template vs certificate records
   - Full value distribution for EVERY field in the certificate subset
   - All unique UDPType values with routing decisions

Decision #5: CarrierRating from certificate.AMBestRatingOK

Risk Level: Low (pending Pre-Migration Action #4)

Area 4 noted certificate.AMBestRatingOK maps here. Sample: cert 5307 has AMBestRatingOK="yes".

Open question: Is CarrierRating per-certificate or per-carrier? (See Pre-Migration Action #4)

Default approach (per-certificate, pending confirmation)

For each certificate where AMBestRatingOK is truthy ("yes"):
  1. Look up mapped Certificate ID
  2. Create CarrierRating:
     - certificate = mapped Certificate
     - avii_or_above = True
     - carrier_name/naic/code = NULL (certificate-level flag)
     - effective_date/expiration_date = NULL

Alternative approach (per-carrier, if confirmed)

For each certificate where AMBestRatingOK is truthy ("yes"):
  1. For each unique carrier from the Carrier A-F backfill data:
     Create CarrierRating:
       - certificate = mapped Certificate
       - avii_or_above = True
       - carrier_name = carrier's name
       - carrier_naic = carrier's NAIC

Decision #6: ReviewStatus / Reason Fields β€” Drop

Risk Level: Low

Every coverage table has *ReviewStatus and *Reason. These track DTX 1.0 review workflow results.

Drop all. Consistent with Area 4 Decision #11. Rationale: 1. Historical data β€” reviews are complete 2. DTX 2.0 uses a different evaluation system (Area 4's 3-tier pipeline) 3. certificate_status (migrated in Area 4) already captures compliance results 4. Redundant review state would conflict with new evaluation system

Sample values observed: NULL in most GL/Auto/WC/UMB samples. Liquor liability shows "COMPLIANT". Log all distributions in dry-run.

Decision #7: requirement_coverage Table β€” Don't Migrate

Risk Level: Low

Operational tracking table: "vendor X needs coverage Y, producer email is Z, from source W."

Sample: vendor 31739, email rstowe@farmersagent.com, type COI, name "All Risk – Business Personal Property (Contents)", source certificate 1100230.

Don't migrate. DTX 2.0 handles this via: - RequirementsGroup + COIRiskProfile β†’ what's required - EvaluationCoverage + EvaluationDetail β†’ what's met - NotificationSchedule β†’ communication

Keep CSV for reference. The requirement_name values are useful for validating Area 6's requirements mapping.

Decision #8: DescriptionOfOperations β€” Defer to Area 6

Risk Level: Low

DTX 2.0's DescriptionOfOperations model tracks the requirement ("is ops description required?"), not the text itself. Area 4 already mapped certificate.OpsDesc β†’ Certificate.ops_desc.

Defer. This is a requirement-level model (coi_risk_profile FK). Area 6 will create these when building COIRiskProfile templates.

Decision #9: WCExempt Cross-Reference

Risk Level: Low

After migrating workers_comp records:
  For each certificate where WCExempt is truthy ("yes"):
    If WorkersCompensation record exists for this cert:
      β†’ set any_excluded = True
    If no WC record exists:
      β†’ no action (Record.wc_exempt already handles vendor-level exemption)

Decision #10: Coverage Limit Value Format

Risk Level: Low

DTX 1.0 limit formats observed:

Standard:     "1000000", "2000000", "500000"
Combined:     "500000/1000000"
Formatted:    "$2000,000 Limit Ded $2,500"
Special:      "-1" (user_defined_policy = no limit)
Suspicious:   "1000000000" (LL sample β€” $1B, likely data entry error)

Store raw values as-is. Don't transform or format. Map -1 β†’ NULL.

Dry-run must flag suspicious values:

Limits exceeding thresholds:
  GL each_occurrence > $100M:  ???
  Auto single_limit > $50M:   ???
  WC disease limits > $10M:   ???
  LL each_occurrence > $100M:  ???
  UMB each_occurrence > $100M: ???

These are almost certainly data entry errors. Don't fix β€” flag for post-migration review.

Decision #11: Missing Target Fields

Risk Level: Low

Two DTX 1.0 fields have no direct DTX 2.0 target.

GLLimMisc (General Liability)

No gllimmisc field in DTX 2.0. NULL in all samples.

if GLLimMisc:
    gldescmisc = f"{GLDescMisc or ''} | Misc Limit: {GLLimMisc}".strip(' |')

Log all records where GLLimMisc is populated.

Other1Desc1 (Other Coverage)

No other1desc1 field in DTX 2.0. Contains labels like "Per Limit", "Each Claim/ Agg.", "Scheduled", "leased & rented".

# Store raw numeric value in both fields β€” do NOT concatenate description into limit field
limit = Other1Lim1      # primary limit value
other1lim1 = Other1Lim1 # raw backup

# Log Other1Desc1 for reference β€” do not store in a numeric field
# Dry-run: report all unique Other1Desc1 values

Why not concatenate: Storing "Per Limit: 5000000" in other1lim1 (a limit field) would break any downstream code that parses it as a number.

Decision #12: Timestamp Preservation

Risk Level: Medium

DTX 1.0 coverage tables have no create_time / update_time columns. Without intervention, all migrated coverage records get created = migration_date.

Migration approach

For each coverage record:
  1. Look up the parent Certificate's created timestamp
     (from id_mapping_certificates.csv or the Certificate object)
  2. Set coverage_record.created = certificate.created
  3. Set coverage_record.modified = certificate.modified
     (or migration_date if certificate.modified is NULL)

This preserves approximate historical timing. A coverage record created as part of certificate 5307 (created 2015-06-03) will show created = 2015-06-03, not created = 2026-XX-XX.


Field Mapping

Key Design Concepts

Dual FK Pattern

All coverage models have both coi_risk_profile and certificate FKs. For migration: certificate FK set, coi_risk_profile = NULL.

Coverage ↔ Evaluation Relationship

Coverage records (this area) and evaluations (Area 4) are complementary parallel structures linked to the same Certificate: - Coverage records: what was on the certificate (limits, carriers, dates) - Evaluations: whether it met requirements (pass/fail per field)

No direct FK between them. Linkage is by EvaluationCoverage.coverage_type string convention. (See Pre-Migration Action #3.)

Multiple Records Per Certificate

A single certificate typically has records in 3-5 different coverage tables. One table may also have multiple rows per certificate (e.g., cert 13957 has 3 auto policies). This is valid β€” one ACORD form can list multiple policies per coverage type.

Boolean Field Convention

DTX 1.0 Value Maps To Notes
"yes" True Primary truthy value
NULL False Default
"" (empty) False
"no" False Rare but observed
Any other value False Log the value

General Liability: general_liability β†’ GeneralLiability

Sample (row 40718): cert 7262, carrier QBE/General Casualty (NAIC 24414), policy CCX1207877, 2014-07-30 to 2015-07-30, limits: $1M occ / $100K rented / $1M person / $2M agg / $2M prod.

DTX 1.0 DTX 2.0 Notes
general_liability_id (ID mapping) β†’ id_mapping_coverage.csv
certificate_id certificate Via id_mapping_certificates.csv
GLCarrierCode carrier_code NULL in all samples. Used for Carrier A-F (Decision #2)
GLCarrierName carrier_name Direct
GLCarrierNAIC carrier_naic Direct
GLOccur occurrence "yes" β†’ True
GLClaims claims_made "yes" β†’ True
GLDeductible gldeductible Direct
GLDedOccuro gldedoccuro Direct
GLDedClaims gldedclaims Direct
GLAddlInsd additional_insured "yes" β†’ True
GLSubrWvd subrogation_waived "yes" β†’ True
GLPolNumb policy_number Direct
GLPolEffDate effective_date Direct
GLPolExpDate expiration_date Direct
GLPolicy applies_per_policy "yes" β†’ True
GLProject applies_per_project "yes" β†’ True
GLLocation applies_per_location "yes" β†’ True
GLLimEaOcc each_occurrence Direct
GLLimRented damage_to_premises Direct
GLLimEaMed medical_expense Direct
GLLimPerson personal_injury Direct
GLLimAgg general_aggregate Direct
GLLimProd products_comp Direct
GLOtherLimitDesc1 glotherlimitdesc1 Direct. Sample: "Business Owner's Liability"
GLOtherLimit1 glotherlimit1 Direct
GLOtherLimitDesc2 glotherlimitdesc2 Direct. Sample: "Microbial Substance"
GLOtherLimit2 glotherlimit2 Direct
GLOtherLimitDesc3 glotherlimitdesc3 Direct
GLOtherLimit3 glotherlimit3 Direct
GLDescMisc gldescmisc Direct
GLLimMisc (no target) Append to gldescmisc if populated. See Decision #11
GLPrimary primary_non_contributory "yes" β†’ True
GLAddlInsd2033 gladdlinsd2033 Direct
GLAddlInsd2037 gladdlinsd2037 Direct
GLAddlInsd2040 gladdlinsd2040 Direct
GLAddlInsdForms additional_insured_forms truthy β†’ True
GLContLiab contractual_liability truthy β†’ True
GLAddlInsdCompOps additional_insured_completed_ops truthy β†’ True
GLTerm1 glterm1 Direct
GLDesc1 gldesc1 Direct
GLTerm2 glterm2 Direct
GLDesc2 gldesc2 Direct
GLTerm3 glterm3 Direct
GLDesc3 gldesc3 Direct
GLReviewStatus (dropped) Decision #6
GLReason (dropped) Decision #6
(no source) created Certificate's create_time. Decision #12
(no source) coi_risk_profile NULL
(no source) all *_options FKs NULL

Automobile Liability: automobile_liability β†’ AutoLiability

Sample (row 38978): cert 13957, carrier State Farm (NAIC 25178), scheduled auto, $1M single. Note: cert 13957 has 3 rows β€” multiple policies per cert is valid.

DTX 1.0 DTX 2.0 Notes
automobile_liability_id (ID mapping)
certificate_id certificate Via mapping
AutoCarrierCode carrier_code NULL in all samples
AutoCarrierName carrier_name Direct
AutoCarrierNAIC carrier_naic Direct
AutoAny any_auto "yes" β†’ True
AutoOwned all_owned_autos "yes" β†’ True
AutoSched scheduled_autos "yes" β†’ True
AutoHired hired_autos "yes" β†’ True
AutoNonOwned non_owned_autos "yes" β†’ True
AutoAddlInsd additional_insured "yes" β†’ True
AutoSubrWvd subrogation_waived "yes" β†’ True
AutoPolNumb policy_number Direct
AutoPolEffDate effective_date Direct
AutoPolExpDate expiration_date Direct
AutoSingle single_limit_per_accident Direct
AutoBodilyPerson bodily_per_person Direct
AutoBodilyAccident bodily_per_accident Direct
AutoProperty property_per_accident Direct
AutoDescMisc autodescmisc Direct
AutoLimMisc autolimmisc Direct
AutoDescMisc2 autodescmisc2 Direct
AutoLimMisc2 autolimmisc2 Direct
AutoUnknown autounknown Direct
AutoUnknownDesc autounknowndesc Direct
AutoAddlInsdForms additional_insured_forms truthy β†’ True
AutoTerm1 autoterm1 Direct
AutoDesc1 autodesc1 Direct
AutoTerm2 autoterm2 Direct
AutoDesc2 autodesc2 Direct
AutoReviewStatus (dropped) Decision #6
AutoReason (dropped) Decision #6
(no source) created Certificate's create_time. Decision #12

Workers Compensation: workers_comp β†’ WorkersCompensation

Sample (row 34752): cert 11666, carrier State National (NAIC 12831), statutory: yes, $1M all limits.

DTX 1.0 DTX 2.0 Notes
workers_comp_id (ID mapping)
certificate_id certificate Via mapping
WCCarrierCode carrier_code NULL in all samples
WCCarrierName carrier_name Direct
WCCarrierNAIC carrier_naic Direct
WCAddlInsd additional_insured "yes" β†’ True
WCSubrWvd subrogation_waived "yes" β†’ True
WCPolNumb policy_number Direct
WCPolEffDate effective_date Direct
WCPolExpDate expiration_date Direct
WCExec any_excluded truthy β†’ True. Per-coverage flag, distinct from Record.wc_exempt
WCStatutory statutory_limits "yes" β†’ True. All samples: "yes"
WCOther wcother Direct
WCEachAcc disease_accident Direct
WCDiseaseEachEmpl disease_each_employee Direct
WCDiseasePolicyLimit disease_policy Direct
WCDescMisc wcdescmisc Direct
WCLimMisc wclimmisc Direct
WCReviewStatus (dropped) Decision #6
WCReason (dropped) Decision #6
(no source) created Certificate's create_time. Decision #12

Umbrella Liability: umbrella_liability β†’ UmbrellaLiability

Sample (row 30139): cert 12843, Continental Casualty (NAIC 20443), umbrella/occurrence, $10M occ/$10M agg, retention $0.

Critical: UMBRetention holds dollar amounts ("0", "10000"), NOT booleans. UMBRetentionCB is the checkbox (NULL in all samples).

DTX 1.0 DTX 2.0 Notes
umbrella_liability_id (ID mapping)
certificate_id certificate Via mapping
UMBCarrierCode carrier_code NULL in all samples
UMBCarrierName carrier_name Direct
UMBCarrierNAIC carrier_naic Direct
UMBLiability umbliability Direct. Sample: "yes"
UMBExcess excess "yes" β†’ True
UMBOccur occurrence "yes" β†’ True
UMBClaims claims_made "yes" β†’ True
UMBDeduct deduct "yes" β†’ True
UMBRetentionCB retention Checkbox β†’ True/False. DTX 1.0 "CB" = checkbox
UMBRetention retention_cb Dollar amount (e.g., "0", "10000"). NOT a boolean
UMBAddlInsd additional_insured "yes" β†’ True
UMBSubrWvd subrogation_waived "yes" β†’ True
UMBPolNumb policy_number Direct
UMBPolEffDate effective_date Direct
UMBPolExpDate expiration_date Direct
UMBLimEaOcc each_occurrence Direct
UMBCombined umbcombined Direct
UMBLimAgg aggregate Direct
UMBDescMisc umbdescmisc Direct
UMBLimMisc umblimmisc Direct
UMBApplyGl umbapplygl Direct. "yes" or NULL
UMBApplyAuto umbapplyauto Direct. "yes" or NULL
UMBApplyEl umbapplyel Direct. "yes" or NULL
UMBAddlInsdForms additional_insured_forms truthy β†’ True
UMBReviewStatus (dropped) Decision #6
UMBReason (dropped) Decision #6
(no source) created Certificate's create_time. Decision #12

Liquor Liability: liquor_liability β†’ LiquorLiability

Sample (row 12848): cert 333063, CarrierCode "A" β†’ Selective Fire & Casualty (NAIC 14377), $1B limits (likely data entry error), ReviewStatus: COMPLIANT.

Data note: Only table with populated CarrierCode AND ReviewStatus in samples.

DTX 1.0 DTX 2.0 Notes
liquor_liability_id (ID mapping)
certificate_id certificate Via mapping
LLNotApplicable llnotapplicable Direct
LLSamePol llsamepot Direct. Model typo β€” fix per Pre-Migration Action #1
LLCarrierCode carrier_code ACORD insurer letter: "A", "C", etc.
LLCarrierName carrier_name Direct
LLCarrierNAIC carrier_naic Direct
LLAddlInsd additional_insured "yes" β†’ True
LLSubrWvd subrogation_waived "yes" β†’ True
LLPolNumb policy_number Direct
LLPolEffDate effective_date Direct
LLPolExpDate expiration_date Direct
LLLimEaOcc each_occurrence Direct
LLLimAgg general_aggregate Direct
LLDescMisc lldescmisc Direct
LLLimMisc lllimmisc Direct
LLAddlInsdForms additional_insured_forms truthy β†’ True
LLReviewStatus (dropped) Decision #6. Sample: "COMPLIANT"
LLReason (dropped) Decision #6
(no source) created Certificate's create_time. Decision #12

Other Coverage: other_coverage β†’ OtherCoverage or PropertyCoverage

Routing per Decision #3.

How desc/limit pairs work (sample row 25187, type="Poll Legal Liab"):

Other1Desc1="Per Limit"    Other1Lim1="5000000"   β†’ limit + other1lim1
Other1Desc2="Per aggregate" Other1Lim2="5000000"   β†’ other1desc2 + other1lim2
Other1Desc3="SIR"           Other1Lim3="50000"     β†’ other1desc3 + other1lim3
DTX 1.0 DTX 2.0 Notes
other_coverage_id (ID mapping)
certificate_id certificate Via mapping
OtherCarrierCode carrier_code ACORD letter: "A", "C", "F" (sometimes populated)
OtherCarrierName carrier_name Direct
OtherCarrierNAIC carrier_naic Direct. Watch for placeholder "99999"
Other1 type (FK) Create OtherCoverageType or PropertyCoverageType
Other1AddlInsd additional_insured "yes" β†’ True
Other1SubrWvd subrogation_waived "yes" β†’ True
Other1PolNumb policy_number Direct
Other1PolEffDate effective_date Direct
Other1PolExpDate expiration_date Direct
Other1Desc1 (no target) Log unique values. See Decision #11
Other1Lim1 limit + other1lim1 Raw numeric value in both fields
Other1Desc2 other1desc2 Direct
Other1Lim2 other1lim2 Direct
Other1Desc3 other1desc3 Direct
Other1Lim3 other1lim3 Direct
OtherReviewStatus (dropped) Decision #6
OtherReason (dropped) Decision #6
(no source) created Certificate's create_time. Decision #12
(no source) required Default False

User Defined Policy: user_defined_policy β†’ OtherCoverage or PropertyCoverage

Certificate records only. Routing per Decision #4.

DTX 1.0 DTX 2.0 Notes
user_defined_policy_id (ID mapping)
certificate_id certificate Via mapping. NULL = template β†’ skip
requirements_id (deferred) Area 6
customer_id (deferred) Area 6
UDPType type (FK) Create type record (case-insensitive dedup)
UDPCarrierName carrier_name Direct
UDPCarrierNAIC carrier_naic Direct
UDPPolEffDate effective_date Direct
UDPPolExpDate expiration_date Direct
UDPLimits limit Direct. Map -1 and NULL β†’ NULL
UDPAddlInsd additional_insured truthy β†’ True
UDPSubrWvd subrogation_waived truthy β†’ True
UDPLossPayeeCB loss_payee truthy β†’ True (PropertyCoverage only β€” triggers routing)
UDPMortgageeCB mortgagee truthy β†’ True (PropertyCoverage only β€” triggers routing)
UDPPolNumb policy_number Direct
UDPRequired required truthy β†’ True
UDPReviewStatus (dropped) Decision #6
UDPReason (dropped) Decision #6
(no source) created Certificate's create_time. Decision #12

Consolidated: Fields Dropped

Source Field(s) Reason
All 6 coverage tables *ReviewStatus, *Reason Review workflow not migrated (Decision #6)
user_defined_policy UDPReviewStatus, UDPReason Review workflow not migrated
user_defined_policy (templates) requirements_id, customer_id Deferred to Area 6
requirement_coverage (entire table) Don't migrate (Decision #7)

Consolidated: DTX 2.0 Fields with No Source

Model Field Value
All coverage models coi_risk_profile NULL β€” template FK, set by Area 6
All coverage models All *_options FKs NULL β€” UI dropdown config (Action #2)
OtherCoverage required Default False (no DTX 1.0 equivalent on other_coverage)
All coverage models created, modified Certificate's timestamp (Decision #12)

Migration Execution

Migration Order (16 steps)

 1. Validate prerequisites:
    - id_mapping_certificates.csv exists and is complete
    - Pre-migration actions 1-4 resolved
 2. Dry-run validation report
 3. Create OtherCoverageType records (case-insensitive dedup from Other1 + UDPType)
 4. Create PropertyCoverageType records (property-classified types)
 5. Migrate general_liability β†’ GeneralLiability (batched, 1000/batch)
 6. Migrate automobile_liability β†’ AutoLiability (batched)
 7. Migrate workers_comp β†’ WorkersCompensation (batched)
 8. Migrate umbrella_liability β†’ UmbrellaLiability (batched)
 9. Migrate liquor_liability β†’ LiquorLiability (batched)
10. Migrate other_coverage β†’ OtherCoverage or PropertyCoverage (batched, classified)
11. Migrate user_defined_policy (certificate records only) β†’ OtherCoverage or PropertyCoverage
12. Apply WCExempt flag to WorkersCompensation records (Decision #9)
13. Create CarrierRating records from certificate.AMBestRatingOK (Decision #5)
14. Backfill Certificate carrier_a through carrier_f (Decision #2)
15. Generate and export all ID mapping tables
16. Verify

Dry-Run Report

=== DTX 1.0 Coverage & Insurance Migration - Dry Run Report ===

PREREQUISITES:
  id_mapping_certificates.csv:    ??? records loaded
  Certificates skipped in Area 4: ???
  Coverage records orphaned by
    Area 4 skips:                 ??? (across ??? customers)

GENERAL LIABILITY:
  Total records:              ???
  Mapped to certificate:      ???
  Missing certificate_id:     ???
  CarrierCode populated:      ???
  Booleans: GLOccur=yes: ???, GLClaims=yes: ???, GLAddlInsd=yes: ???
  GLLimMisc populated:        ???
  Suspicious limits (>$100M): ???

AUTOMOBILE LIABILITY:
  Total records:              ???
  Multi-row certs (>1 row):   ???
  Auto types: Any: ???, Owned: ???, Hired: ???, NonOwned: ???, Sched: ???
  Suspicious limits (>$50M):  ???

WORKERS COMPENSATION:
  Total records:              ???
  WCStatutory=yes:            ???
  WCExec populated:           ???
  Suspicious limits (>$10M):  ???

UMBRELLA LIABILITY:
  Total records:              ???
  UMBLiability vs UMBExcess:  ???/???
  UMBRetention values:        min: ???, max: ???, avg: ???
  UMBRetentionCB populated:   ???
  Suspicious limits (>$100M): ???

LIQUOR LIABILITY:
  Total records:              ???
  LLNotApplicable populated:  ???
  CarrierCode distribution:   A: ???, B: ???, C: ???, ...
  ReviewStatus values:        [distribution]
  Suspicious limits (>$100M): ???

OTHER COVERAGE:
  Total records:              ???
  Unique Other1 types:        [list all with count]
  Classified as Property:     ??? types β†’ ??? records
  Classified as Other:        ??? types β†’ ??? records
  Wrong-table types flagged:  [list] (e.g., "Commercial Umbrella")
  Other1Desc1 unique values:  [list all]
  CarrierCode populated:      ???
  NAIC=99999 (placeholder):   ???
  Suspicious limits:          ???

USER DEFINED POLICY:
  Total records:              ???
  Template records (no cert): ???  β†’ Area 6
  Certificate records:        ???  β†’ this area
  Unique UDPType values:      [list all]
  With loss_payee:            ???
  With mortgagee:             ???
  [If cert records > 0: full field distribution for every field]

COVERAGE TYPE DEDUP:
  Raw unique names:           ???
  After normalization:        ??? (deduped by case/whitespace)
  OtherCoverageType to create: ???
  PropertyCoverageType to create: ???

CARRIER A-F BACKFILL:
  Certificates to update:     ???
  Avg carriers per cert:      ???
  Max carriers per cert:      ???
  Phase A (CarrierCode):      ??? assignments
  Phase B (NAIC cross-ref):   ??? assignments
  Phase C (remaining):        ??? assignments
  Certs with >6 carriers:     ??? (overflow)
  Carrier name variants
    (same NAIC, different name): [list]

CARRIER RATING:
  AMBestRatingOK=yes:         ???
  AMBestRatingOK=NULL/other:  ???

REQUIREMENT COVERAGE (reference only):
  Total records:              ???
  Unique requirement_names:   [list]

REVIEW STATUS (reference only, not migrated):
  GL:    [distribution]
  Auto:  [distribution]
  WC:    [distribution]
  UMB:   [distribution]
  LL:    [distribution]
  Other: [distribution]
  UDP:   [distribution]

Issues flagged:               ???
Ready to migrate:             ???

Error Handling

Scenario Behavior
certificate_id not in mapping Skip record, log to issues CSV
certificate_id in mapping but cert was Area 4 skip Skip record, log as cascade skip
Unknown Other1 type Default to OtherCoverage, create new type, log
Wrong-table type (e.g., "Commercial Umbrella") Migrate as OtherCoverage, flag for review
UDPLimits = -1 or NULL Set limit = NULL
Boolean field unexpected value Default to False, log the value
CarrierCode not A-F Use next available slot, log
>6 unique carriers per certificate Fill A-F, log overflow
Date parse failure Set to NULL, log
NAIC = 99999 (placeholder) Store as-is, flag in report
Truncated/malformed carrier name Store as-is, flag in report
Limit exceeds threshold Store as-is, flag as suspicious
Duplicate type name (case variant) Merge to normalized form

Output Files

File Purpose
id_mapping_coverage.csv table_name β†’ old_id β†’ new_id β†’ certificate_id β†’ model_name
id_mapping_coverage_types.csv source_value β†’ normalized β†’ target_model β†’ type_id
carrier_backfill_report.csv certificate_id β†’ slots A-F (name + NAIC) β†’ method (code/naic/sequential) β†’ overflow
migration_issues.csv All flagged records with issue description
migration_report.txt Summary statistics and validation results

Verification (post-migration)

# Check Method
1 Record counts DTX 1.0 count per table == DTX 2.0 count (minus skipped)
2 Certificate linkage Every coverage record has a valid Certificate FK
3 Carrier A-F Spot-check 20 certs β€” verify slots match coverage carrier info
4 NAIC cross-reference For spot-checked certs, verify same NAIC is in same slot across tables
5 Type coverage Every OtherCoverage/PropertyCoverage has a valid Type FK
6 Type dedup No duplicate type names (case-insensitive)
7 Limit values Spot-check 20 records per table β€” values match originals
8 Booleans Spot-check 10 records per table β€” booleans match "yes"/NULL
9 Timestamps Coverage created matches parent certificate's created
10 CarrierRating count Count == certificates with AMBestRatingOK=yes
11 No orphans No coverage records pointing to non-existent certificates
12 ID mapping complete Every source PK in every table exists in mapping CSV
13 Wrong-table types Review flagged types (Decision #3) β€” confirm DTX 2.0 can find them
14 Evaluation cross-link Spot-check 10 certs β€” verify EvaluationCoverage.coverage_type strings match model names
15 Cascade check Skipped coverage records == expected (from Area 4 skip count)

Dependencies

Depends On What's Needed
Area 2: Customers β†’ Accounts Account context (indirect, via certificates)
Area 4: Certificates & Documents id_mapping_certificates.csv, certificate.AMBestRatingOK, certificate.WCExempt, certificate create_time for timestamp preservation
Depended On By For
Area 6: Requirements & Compliance COIRiskProfile template coverage (user_defined_policy templates), evaluation validation

Difficulty Assessment

Overall: MEDIUM

Significantly easier than Areas 3 and 4. Most fields map 1:1 with identical names.

Factor Impact
1:1 field mapping (90%+) Makes this manageable
No structural transformation Flat table β†’ flat model
DTX 2.0 "Additional fields" match exactly gldescmisc, autoterm1, etc.
Carrier A-F backfill with NAIC cross-ref Main complexity
other_coverage classification Requires keyword matching + manual review
user_defined_policy untestable 100% templates in sample β€” cert path unknown
4 pre-migration actions Must resolve before starting

Decision Summary

# Decision Status Approach
1 Certificate FK linking Resolved certificate FK set, coi_risk_profile + _options FKs NULL
2 Carrier A-F backfill Resolved NAIC cross-reference from LL/Other CarrierCodes, then sequential for unmatched
3 other_coverage classification Resolved Keyword-match β†’ Property vs Other, case-insensitive dedup, flag wrong-table types
4 user_defined_policy split Resolved Templates β†’ Area 6; cert records β†’ Property (if loss_payee/mortgagee) or Other
5 CarrierRating Pending Action #4 Per-certificate with NULL carrier info (default), or per-carrier (if confirmed)
6 ReviewStatus/Reason Resolved Drop all β€” consistent with Area 4
7 requirement_coverage Resolved Don't migrate β€” operational tracking
8 DescriptionOfOperations Deferred Area 6 β€” requirement-level model
9 WCExempt cross-ref Resolved Set any_excluded=True on WC records for WCExempt certificates
10 Limit value format Resolved Store raw as-is, -1 β†’ NULL, flag suspicious values in dry-run
11 Missing target fields Resolved GLLimMisc β†’ append to gldescmisc; Other1Desc1 β†’ log only, don't pollute limit fields
12 Timestamp preservation Resolved Use parent certificate's create_time for coverage created/modified