📋 Migration Summary

Source Tables: 5
Target Models: 11
Decisions: 13

Migrating compliance requirements, review results, and SOV (Statement of Values) review data. Restructuring flat requirements into grouped, hierarchical structure.

🎯 Key Actions

⚠️ Key Challenges

DTX 1.0 → DTX 2.0 Migration Plan: Requirements & Compliance

Overview

Migration of requirements and compliance review data from DTX 1.0 to DTX 2.0. This is Phase 2 — depends on Accounts (Area 2). This area performs the COIRiskProfile backfill on RequirementsGroup records created in Area 4, and creates coverage template records that define what insurance is required.

The requirements table is the central piece: each row defines a complete set of coverage requirements (GL, Auto, WC, UMB, LL, attachment, AM Best) that gets mapped to a COIRiskProfile template with child coverage template records.

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 (5)

Table Columns Rows (sample) Purpose
requirements 89 61 Master insurance requirements — defines required coverages, limits, booleans per vendor/template
requirements_audit 83 101 BEFORE/AFTER audit trail of requirement changes
review_result 9 101 Document-level review results (COMPLIANT/NON-COMPLIANT)
coi_review_result 8 105 COI certificate review results — serialized PHP form data with per-coverage evaluations
so_review 19 100 Aggregated broker/SO compliance summary — reporting table

Target Models

Model Purpose
COIRiskProfile Template container (is_template=True) defining coverage requirements
GeneralLiability GL coverage template (coi_risk_profile FK)
AutoLiability Auto coverage template
WorkersCompensation WC coverage template
UmbrellaLiability UMB coverage template
LiquorLiability LL coverage template
PropertyCoverage Property coverage template (from user_defined_policy templates)
OtherCoverage Other coverage template (from user_defined_policy templates)
CarrierRating AM Best rating requirement template
DescriptionOfOperations Ops description requirement template
RequirementsGroup Backfill coi_risk_profile FK (already created in Area 4)

Estimated Volume

Item Estimate
COIRiskProfile templates 5K-20K (one per unique requirements record)
GL templates 4K-18K (most requirements define GL)
Auto templates 3K-15K
WC templates 3K-15K
UMB templates 2K-10K
LL templates 500-5K
Property/Other templates 100-2K (from user_defined_policy templates)
CarrierRating templates 1K-10K
DescriptionOfOperations templates 100-2K
RequirementsGroup backfills 5K-50K
Total records created 20K-150K

Pre-Migration Actions Required

These must be resolved before running the migration.

Action 1: Map ATT Fields to DTX 2.0 DocumentType Records

Priority: HIGH — potential data loss

The requirements table has 8 attachment requirement fields (ATT_W9, ATT_COI, ATT_CG2015, ATT_CG2033, ATT_CG2037, ATT_Primary, ATT_HoldHarmless, ATT_SubrWvd) defining which documents a vendor must provide. These map to DocRequirement records in DTX 2.0, but need DocumentType FK targets.

Action: Verify that DTX 2.0 has DocumentType records for each of these:

ATT Field Expected DocumentType Notes
ATT_W9 "W-9" IRS form
ATT_COI "Certificate of Insurance" Standard COI
ATT_CG2015 "CG 20 15" Additional Insured endorsement
ATT_CG2033 "CG 20 33" Additional Insured endorsement (ongoing ops)
ATT_CG2037 "CG 20 37" Additional Insured endorsement (completed ops)
ATT_Primary "Primary & Non-Contributory" Primary insurance endorsement
ATT_HoldHarmless "Hold Harmless Agreement" Indemnification agreement
ATT_SubrWvd "Waiver of Subrogation" Subrogation waiver endorsement

If DocumentType records don't exist, create them as a seed data step. If the names differ, provide a mapping table.

Action 2: Confirm -1 Limit Semantics with DTX 2.0 Team

Priority: HIGH — affects all template coverage records

DTX 1.0 requirements use -1 extensively for limit fields. From sample data: - GLLimEaOcc="1000000" → "must have at least $1M each occurrence" - GLLimEaOcc="-1" → "coverage required, no minimum limit threshold" - GLLimEaOcc=NULL → "not checked / not applicable"

Plan assumes: -1 → store as NULL in template limit field (coverage type IS required because the template record exists, but no specific minimum). Actual dollar values stored as-is.

Action: Confirm with DTX 2.0 evaluation engine: 1. Does a NULL limit on a template coverage record mean "don't check this field"? 2. Or does it mean "any value passes"? 3. Is there a different sentinel value the engine expects?

Action 3: Verify Area 4 RequirementsGroup Mapping File

Priority: HIGH — backfill depends on this

Area 4 created RequirementsGroups from unique (customer_id, requirements_id, document_set_id) combinations. This area needs the mapping file to: 1. Look up which RequirementsGroups reference each requirements_id 2. Backfill RequirementsGroup.coi_risk_profile FK with the newly created COIRiskProfile

Action: Verify id_mapping_requirements_groups.csv exists and contains requirements_idrequirements_group_id associations.

Action 4: Resolve Area 5 user_defined_policy Template Records

Priority: MEDIUM

Area 5 deferred user_defined_policy template records (where certificate_id=NULL) to this area. These have requirements_id and customer_id and define custom coverage types required for a vendor.

Action: Confirm that Area 5's migration output includes a file listing deferred template records, or that the raw CSV is available for this area to process directly.


Known Issues & Risks

Critical

# Issue Impact Mitigation
1 -1 limit semantics ambiguous — DTX 1.0 uses -1 to mean "required but no minimum." DTX 2.0 coverage template fields are CharField with no documented sentinel. If the evaluation engine doesn't handle NULL correctly, templates with -1 fields will either silently pass everything or silently fail everything. Every template coverage record with -1 limits could malfunction Pre-Migration Action #2. Default: map -1 → NULL. Log all -1 occurrences for validation.
2 ATT fields → DocumentType mapping unknown — 8 attachment requirement fields need DocumentType FKs. If DTX 2.0 doesn't have matching DocumentType records, document requirements are lost. Document requirements not migrated Pre-Migration Action #1. Create seed DocumentType records if missing.
3 coi_review_result contains PHP serialized dataform_data field has nested PHP serialize() output with per-coverage evaluation results, waiver reasons, and policy links. Parsing this is fragile and error-prone. Risk of data corruption if parsed incorrectly Don't migrate as primary data (Area 4 already created evaluations from certificate_status). Parse only for waiver extraction if needed.

High

# Issue Impact Mitigation
4 RequirementsGroup.coi_risk_profile backfill is N:1 — Multiple RequirementsGroups can reference the same requirements_id. One COIRiskProfile may serve many groups. If the backfill creates duplicate COIRiskProfiles instead of reusing, the template data fragments. Duplicate templates, inconsistent evaluation Deduplicate: one COIRiskProfile per unique requirements_id. All RequirementsGroups sharing a requirements_id share one COIRiskProfile.
5 requirements_audit missing columns — Audit table has 83 columns vs requirements' 89. Missing: GLContLiab, GLAddlInsdCompOps, WCExec, OpsDesc, OpsDescReviewStatus, UMBMandatory. Changes to these fields were not audited. Incomplete audit history (if audit is migrated) Note in documentation. Don't migrate audit.
6 0000-00-00 00:00:00 timestamps — Multiple requirements records have update_time = "0000-00-00 00:00:00". This is MySQL's zero-date, not a valid datetime. PostgreSQL will reject these values Map 0000-00-00 00:00:00 → NULL.

Medium

# Issue Impact Mitigation
7 Placeholder descriptions — Requirements description field contains " " (single space) and "." (period) as placeholder values. These aren't real descriptions. Cluttered UI with meaningless descriptions Strip to NULL if description is only whitespace or a single punctuation character.
8 requirements.name ambiguity — Some names are vendor names ("JP Morgan Chase Bank"), others are template tier names ("FULL", "MIN/GL", "MID LEVEL - GL/UMB"). No consistent pattern. COIRiskProfile.name may be misleading Store as-is. The name serves as an identifier, not a display label.
9 so_review data inconsistency — Some broker records have total < compliant + non_compliant + expired. Totals don't sum correctly. Unreliable reporting data Don't migrate — DTX 2.0 computes metrics on-the-fly via ComplianceStatus.
10 UMBMandatory field has no DTX 2.0 target — NULL in all samples. May indicate "umbrella coverage is mandatory" but the template record's existence already implies this. Minor data loss Log all non-NULL values. Drop field.

Low

# Issue Impact Mitigation
11 review_result.vendor_id always NULL — Links to document_id only. No vendor context for document reviews. Cannot associate document reviews with vendors Don't migrate — historical review data.
12 coi_review_result.login_ip mostly NULL — Only 2 of 105 sample records have IP addresses. Minor metadata loss Don't migrate — privacy consideration anyway.

Decisions

Decision #1: COIRiskProfile Creation from requirements

Risk Level: HIGH — central to this area

Each requirements record becomes one COIRiskProfile (is_template=True).

Migration approach

For each requirements record:
  1. Create COIRiskProfile:
     - account = customer_id → Account (via id_mapping_accounts.csv)
     - is_template = True
     - name = requirements.name
     - description = requirements.description (strip placeholder values)
     - created = requirements.create_time (preserve timestamp)
     - modified = requirements.update_time (if valid, else create_time)
  2. Record mapping: requirements_id → coi_risk_profile_id
  3. If customer_id not in Account mapping → skip, log to issues CSV

Deduplication

One COIRiskProfile per unique requirements_id. Multiple RequirementsGroups that reference the same requirements_id will share the same COIRiskProfile.

0000-00-00 00:00:00 handling

If update_time == "0000-00-00 00:00:00":
  modified = create_time  (fall back to creation time)
If create_time == "0000-00-00 00:00:00":
  created = migration_date  (should not happen — log if it does)

Decision #2: Coverage Template Records from requirements

Risk Level: HIGH — defines evaluation requirements

For each COIRiskProfile, create child coverage template records from the requirements fields. Only create a template record if the coverage section has at least one populated field.

Detection: "Is this coverage type required?"

GL required if: ANY of GLClaims, GLOccur, GLAddlInsd, ..., GLLimEaOcc, GLLimAgg, GLLimProd
                is non-NULL
Auto required if: ANY of AutoAddlInsd, AutoSubrWvd, ..., AutoSingle, AutoBodilyPerson
                  is non-NULL
WC required if: ANY of WCStatutory, WCSubrWvd, ..., WCEachAcc, WCDiseaseEachEmpl
                is non-NULL
UMB required if: ANY of UMBAddlInsd, UMBSubrWvd, ..., UMBLimEaOcc, UMBLimAgg
                 is non-NULL
LL required if: ANY of LLAddlInsd, LLSubrWvd, LLLimEaOcc, LLLimAgg, LLAddlInsdForms
                is non-NULL

Value mapping rules

DTX 1.0 Value Template Field Value Meaning
"yes" (boolean field) True This flag is REQUIRED to be checked
NULL (boolean field) False Not checked / not required
"1000000" (limit field) "1000000" Minimum $1M required
"-1" (limit field) NULL Coverage required, no minimum limit
NULL (limit field) NULL Not checked

Note: The difference between -1 → NULL (required, no minimum) and NULL → NULL (not checked) is that the TEMPLATE RECORD EXISTS for the first case but not the second.

Sample data patterns

Row 2295 (customer 630, "Cogent Communications"):
  GL: GLOccur=yes, GLAddlInsd=yes, GLSubrWvd=yes, GLLimEaOcc=1000000, GLLimAgg=2000000
  → Create GL template: occurrence=True, additional_insured=True, subrogation_waived=True,
    each_occurrence="1000000", general_aggregate="2000000", all others=NULL
  Auto: AutoSingle=2000000 (only populated field)
  → Create Auto template: single_limit_per_accident="2000000", all others=NULL/False
  WC: WCStatutory=yes, WCEachAcc=1000000
  → Create WC template: statutory_limits=True, disease_accident="1000000"
  UMB: UMBCombined=5000000
  → Create UMB template: umbcombined="5000000"

Row 2355 (customer 525, "Stewart") — richest record:
  GL: GLOccur=yes, GLAddlInsd=yes, GLSubrWvd=yes, GLLocation=yes, GLAddlInsdForms=yes,
      GLLimEaOcc=1000000, GLLimRented=300000, GLLimEaMed=5000, GLLimPerson=1000000,
      GLLimAgg=2000000, GLLimProd=2000000, GLPrimary=yes
  Auto: AutoSingle=-, but no booleans
  WC: WCStatutory=yes, WCEachAcc=1000000, WCDiseaseEachEmpl=1000000, WCDiseasePolicyLimit=1000000
  UMB: UMBSubrWvd=yes, UMBOccur=yes, UMBLimEaOcc=-, UMBCombined=1000000, UMBLimAgg=1000000
  LL: (all -1 or NULL)

Decision #3: Backfill RequirementsGroup.coi_risk_profile

Risk Level: HIGH — links templates to vendor assignments

Area 4 created RequirementsGroups with coi_risk_profile=NULL. This area fills them in.

For each RequirementsGroup in id_mapping_requirements_groups.csv:
  1. Look up the requirements_id associated with this group
  2. Look up the COIRiskProfile created from that requirements_id (Decision #1)
  3. Update RequirementsGroup.coi_risk_profile = found COIRiskProfile
  4. If requirements_id is NULL or has no matching COIRiskProfile → leave NULL, log

Important: This is a Django ORM UPDATE, not a CREATE. The RequirementsGroup already exists.

Decision #4: ATT Fields → DocRequirement Records

Risk Level: MEDIUM (pending Action #1)

The requirements table has 8 ATT_* fields defining document requirements. These map to DocRequirement records linking RequirementsGroups to DocumentTypes.

For each requirements record where ANY ATT field is truthy ("yes"):
  For each truthy ATT field:
    1. Look up the corresponding DocumentType (via Action #1 mapping)
    2. For EACH RequirementsGroup that references this requirements_id:
       Create DocRequirement:
         - requirements_group = the RequirementsGroup
         - document_type = mapped DocumentType
         - status = "In Review" (default — no historical status)
         - is_exempted = False

Sample data

Row 2355 ("Stewart") has: ATT_W9=yes, ATT_COI=yes, ATT_CG2015=yes (3 of 8 populated). Most other sample rows: all ATT fields NULL.

Decision #5: DescriptionOfOperations Templates

Risk Level: Low

Requirements OpsDesc field → DescriptionOfOperations template record.

For each requirements record where OpsDesc is truthy ("yes"):
  Create DescriptionOfOperations:
    - coi_risk_profile = mapped COIRiskProfile
    - certificate = NULL (template)
    - description_required = True
    - All other fields = NULL

Sample: OpsDesc appears to be NULL in most sample rows. The OpsDescReviewStatus field is dropped (review workflow not migrated, consistent with Area 5 Decision #6).

Decision #6: CarrierRating Templates from AMBestRatingOK

Risk Level: Low

Requirements AMBestRatingOK field → CarrierRating template record on the COIRiskProfile.

For each requirements record where AMBestRatingOK is truthy ("yes"):
  Create CarrierRating:
    - coi_risk_profile = mapped COIRiskProfile
    - certificate = NULL (template)
    - avii_or_above = True
    - All carrier fields = NULL (requirement-level, not carrier-specific)

Sample: Most rows have AMBestRatingOK=NULL. Some have truthy values. Consistent with Area 5 Decision #5 (certificate-level CarrierRating).

Decision #7: user_defined_policy Templates → Coverage Templates

Risk Level: MEDIUM

Area 5 deferred template records from user_defined_policy (where certificate_id=NULL). These define custom coverage types required by a customer.

For each user_defined_policy template record:
  1. Find or create the COIRiskProfile for this requirements_id
     (should already exist from Decision #1)
  2. Classify UDPType → PropertyCoverage or OtherCoverage
     (same keyword matching as Area 5 Decision #3)
  3. Find or create PropertyCoverageType / OtherCoverageType
     (case-insensitive dedup, reuse types created in Area 5)
  4. Create coverage template record:
     - coi_risk_profile = matched COIRiskProfile
     - certificate = NULL
     - type = found/created type
     - required = UDPRequired ("yes" → True)
     - loss_payee = UDPLossPayeeCB (PropertyCoverage only)
     - mortgagee = UDPMortgageeCB (PropertyCoverage only)
     - limit = UDPLimits (-1 → NULL, else direct)

Sample data

All 100 sample records are templates (certificate_id=NULL). Single customer 815. Three repeating UDPType values: "Professional Liability", "Cyber Liability", "Pollution Liability". All UDPLimits=-1.

Decision #8: requirements_audit — Don't Migrate

Risk Level: Low

BEFORE/AFTER snapshot audit of requirement changes. 83 columns per row.

Don't migrate. Rationale: 1. DTX 2.0 uses Django's built-in audit trail (django-simple-history or similar) for change tracking 2. Missing 6 columns vs live requirements table — audit is incomplete 3. Historical audit data (2014-2016 in samples) has limited operational value 4. Massive table (82 fields × N rows × 2 for BEFORE/AFTER) for minimal benefit

Keep CSV for reference. Log total record count and date range in migration report.

Decision #9: review_result — Don't Migrate

Risk Level: Low

Simple document review results: document_idreview_status (COMPLIANT/NON-COMPLIANT).

Don't migrate. Rationale: 1. Links to document_id — Area 4 already migrated documents with status 2. vendor_id is NULL in all samples — no vendor context 3. DTX 2.0 uses DocRequirement.status for document compliance tracking 4. Historical review snapshots (2017 samples) — reviews are complete 5. Comments are minimal ("coi" or NULL)

Keep CSV for reference. Cross-reference unique document_ids against Area 4's mapping if validation is needed.

Decision #10: coi_review_result — Don't Migrate (Parse for Waivers Only)

Risk Level: MEDIUM

COI review results with serialized PHP form_data. Contains per-coverage evaluation results that OVERLAP with Area 4's evaluation pipeline (built from certificate_status).

Don't migrate as primary data. Rationale: 1. Area 4 already created Evaluation → EvaluationCoverage → EvaluationDetail from certificate_status 2. Serialized PHP parsing is fragile and error-prone 3. Contains navigation state (coi_prev_page, coi_next_page) not needed in DTX 2.0 4. IP addresses are a privacy concern

However: form_data contains WAIVED statuses with reasons (e.g., ReviewStatus="WAIVED", Reason="gl ok"). This information is NOT in certificate_status.

Waiver extraction (optional post-migration enrichment):
  1. Parse form_data for each coi_review_result record
  2. For coverage sections with ReviewStatus="WAIVED":
     a. Log: certificate_id, coverage_type, Reason, user_id, create_time
     b. Output to waiver_candidates.csv
  3. DTX 2.0 team can review and create WaiverRequest records if needed

PHP serialized data structure observed

form_data = {
  "coi_prev_page": "Acord_25",
  "page_name": "Acord_25",
  "coi_next_page": "last_page",
  "Cert": {
    "starting_page": "Acord_25",
    "old_broker_id": "114147",
    "old_requirements_id": "8666",      ← links back to requirements table
    "old_document_set_id": "1219"       ← links to document_set
  },
  "GL": {
    "GL": {
      "DefinedType": "GL",
      "DeclaredType": "254240",          ← certificate_id
      "EvalStatus": "NON-COMPLIANT",
      "ReviewStatus": "WAIVED",          ← WAIVER INFORMATION
      "Reason": "gl ok"                  ← WAIVER REASON
    }
  },
  "Auto": { ... },
  "WC": { ... },
  "Other": {                             ← property/other coverage types
    "All Risk": {
      "DefinedType": "All Risk",
      "EvalStatus": "COMPLIANT",
      "PolicyLink": "(serialized carrier + policy info)"
    },
    "Business Income": { ... },
    "Business Personal Property (Content)": { ... }
  },
  "OpsDescReason": "",
  "HolderReason": "",
  "notice_action": "send_notice",
  "override_reason": ""
}

Decision #11: so_review — Don't Migrate

Risk Level: Low

Aggregated broker/SO compliance summary with counts (total, compliant, non_compliant, expired, etc.).

Don't migrate. Rationale: 1. This is a reporting/summary table, not transactional data 2. DTX 2.0 computes compliance metrics on-the-fly via ComplianceStatus model 3. Data inconsistency: some rows have total < compliant + non_compliant + expired 4. broker_id links to Area 9 (Brokers) — not yet migrated

Keep CSV for reference. Useful for validating post-migration compliance metrics.

Decision #12: ComplianceStatus and ComplianceThresholds — Create Empty

Risk Level: Low

These are DTX 2.0-native models with no DTX 1.0 equivalent.

ComplianceStatus:
  - Created per-RequirementsGroup AFTER all migration areas complete
  - Initial status = "no_requirements" (default)
  - Will be recalculated by DTX 2.0's evaluation engine on first run

ComplianceThresholds:
  - Created per-Account with defaults AFTER all migration areas complete
  - Uses DTX 2.0 default values (warning_days=30, stale_days=7, etc.)

Note: Do NOT create these during Area 6 migration. Create them in a post-migration "bootstrap" step after all areas are complete, or let DTX 2.0 create them lazily on first access.

Decision #13: requirements.comments — Store as COIRiskProfile.description Supplement

Risk Level: Low

The comments field contains operational notes like "No WC requirement in Temp License Agmt" and "NO EMPLOYEES!!!!". These are distinct from description.

If requirements.comments is non-NULL and non-whitespace:
  Append to COIRiskProfile.description:
    description = f"{clean_description}\n\nMigration note: {comments}".strip()

This preserves operational context without creating a new field.


Field Mapping

Key Design Concepts

Template vs Certificate Coverage Records

All coverage models have both coi_risk_profile and certificate FKs: - Template records (this area): coi_risk_profile set, certificate = NULL. Define REQUIREMENTS. - Certificate records (Area 5): certificate set, coi_risk_profile = NULL. Store ACTUAL VALUES.

The evaluation engine compares template records against certificate records.

-1 Semantics (Pending Action #2)

DTX 1.0 limit = "-1"   → DTX 2.0 template limit = NULL
  Meaning: "This coverage type is required (template record exists),
            but no specific dollar minimum."

DTX 1.0 limit = "1000000" → DTX 2.0 template limit = "1000000"
  Meaning: "Must have at least $1M."

DTX 1.0 limit = NULL    → No template record created for this field/type
  Meaning: "Not checked."

requirementsCOIRiskProfile

DTX 1.0 DTX 2.0 Notes
requirements_id (ID mapping) id_mapping_coi_risk_profiles.csv
customer_id account Via id_mapping_accounts.csv
name name Direct. Mix of vendor names and template tier names
description description Strip placeholder values (" ", "."). Append comments if present
(hardcoded) is_template True
create_time created Direct
update_time modified Map 0000-00-00 00:00:00create_time
user_id owner Via id_mapping_users.csv (if available)
GL fields (26) GeneralLiability template Decision #2
Auto fields (18) AutoLiability template Decision #2
WC fields (7) WorkersCompensation template Decision #2
UMB fields (14) UmbrellaLiability template Decision #2
LL fields (5) LiquorLiability template Decision #2
AMBestRatingOK CarrierRating template Decision #6
OpsDesc DescriptionOfOperations template Decision #5
OpsDescReviewStatus (dropped) Review workflow not migrated
ATT fields (8) DocRequirement records Decision #4
comments COIRiskProfile.description (appended) Decision #13

requirements GL fields → GeneralLiability template

Create if ANY GL field is non-NULL. All *_options FKs = NULL. certificate = NULL.

DTX 1.0 (requirements) DTX 2.0 (GeneralLiability) Notes
(from parent) coi_risk_profile FK to new COIRiskProfile
GLClaims claims_made "yes" → True
GLOccur occurrence "yes" → True
GLAddlInsd additional_insured "yes" → True
GLSubrWvd subrogation_waived "yes" → True
GLPolicy applies_per_policy "yes" → True
GLProject applies_per_project "yes" → True
GLLocation applies_per_location "yes" → True
GLLimEaOcc each_occurrence -1 → NULL, else direct
GLLimRented damage_to_premises -1 → NULL, else direct
GLLimEaMed medical_expense -1 → NULL, else direct
GLLimPerson personal_injury -1 → NULL, else direct
GLLimAgg general_aggregate -1 → NULL, else direct
GLLimProd products_comp -1 → NULL, else direct
GLOther1LimitDesc glotherlimitdesc1 Direct
GLOther1Limit glotherlimit1 Direct
GLOther2LimitDesc glotherlimitdesc2 Direct
GLOther2Limit glotherlimit2 Direct
GLOther3LimitDesc glotherlimitdesc3 Direct
GLOther3Limit glotherlimit3 Direct
GLPrimary primary_non_contributory "yes" → True
GLAddlInsd2033 gladdlinsd2033 Direct
GLAddlInsd2037 gladdlinsd2037 Direct
GLAddlInsd2040 gladdlinsd2040 Direct
GLAddlInsdForms additional_insured_forms "yes" → True
GLContLiab contractual_liability "yes" → True
GLAddlInsdCompOps additional_insured_completed_ops "yes" → True

requirements Auto fields → AutoLiability template

Create if ANY Auto field is non-NULL.

DTX 1.0 (requirements) DTX 2.0 (AutoLiability) Notes
(from parent) coi_risk_profile FK to new COIRiskProfile
AutoAddlInsd additional_insured "yes" → True
AutoSubrWvd subrogation_waived "yes" → True
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
AutoSingle single_limit_per_accident -1 → NULL, else direct
AutoBodilyPerson bodily_per_person -1 → NULL, else direct
AutoBodilyAccident bodily_per_accident -1 → NULL, else direct
AutoProperty property_per_accident -1 → NULL, else direct
AutoOther1LimitDesc autodescmisc Direct
AutoOther1Limit autolimmisc Direct
AutoOther2LimitDesc autodescmisc2 Direct
AutoOther2Limit autolimmisc2 Direct
AutoOther3LimitDesc (no target) NULL in all samples. Log if populated
AutoOther3Limit (no target) NULL in all samples. Log if populated
AutoAddlInsdForms additional_insured_forms "yes" → True

requirements WC fields → WorkersCompensation template

Create if ANY WC field is non-NULL.

DTX 1.0 (requirements) DTX 2.0 (WorkersCompensation) Notes
(from parent) coi_risk_profile FK to new COIRiskProfile
WCStatutory statutory_limits "yes" → True
WCSubrWvd subrogation_waived "yes" → True
WCExec any_excluded "yes" → True
WCEachAcc disease_accident -1 → NULL, else direct
WCDiseaseEachEmpl disease_each_employee -1 → NULL, else direct
WCDiseasePolicyLimit disease_policy -1 → NULL, else direct
WCOther wcother Direct

requirements UMB fields → UmbrellaLiability template

Create if ANY UMB field is non-NULL.

DTX 1.0 (requirements) DTX 2.0 (UmbrellaLiability) Notes
(from parent) coi_risk_profile FK to new COIRiskProfile
UMBAddlInsd additional_insured "yes" → True
UMBSubrWvd subrogation_waived "yes" → True
UMBLiability umbliability Direct
UMBExcess excess "yes" → True
UMBOccur occurrence "yes" → True
UMBClaims claims_made "yes" → True
UMBDeduct deduct "yes" → True
UMBRetentionCB retention "yes" → True (checkbox)
UMBRetention retention_cb -1 → NULL, else direct (dollar amount)
UMBLimEaOcc each_occurrence -1 → NULL, else direct
UMBCombined umbcombined -1 → NULL, else direct
UMBLimAgg aggregate -1 → NULL, else direct
UMBAddlInsdForms additional_insured_forms "yes" → True
UMBMandatory (no target) NULL in all samples. Log if populated

requirements LL fields → LiquorLiability template

Create if ANY LL field is non-NULL.

DTX 1.0 (requirements) DTX 2.0 (LiquorLiability) Notes
(from parent) coi_risk_profile FK to new COIRiskProfile
LLAddlInsd additional_insured "yes" → True
LLSubrWvd subrogation_waived "yes" → True
LLLimEaOcc each_occurrence -1 → NULL, else direct
LLLimAgg general_aggregate -1 → NULL, else direct
LLAddlInsdForms additional_insured_forms "yes" → True

Consolidated: Fields Dropped

Source Field(s) Reason
requirements OpsDescReviewStatus Review workflow not migrated
requirements UMBMandatory No DTX 2.0 target. NULL in all samples
requirements AutoOther3LimitDesc, AutoOther3Limit No DTX 2.0 target. NULL in all samples
requirements_audit (entire table) Don't migrate (Decision #8)
review_result (entire table) Don't migrate (Decision #9)
coi_review_result (entire table) Don't migrate as primary data (Decision #10)
so_review (entire table) Don't migrate (Decision #11)

Consolidated: DTX 2.0 Fields with No Source

Model Field Value
All coverage templates certificate NULL — template records
All coverage templates All *_options FKs NULL — UI dropdown config
All coverage templates effective_date, expiration_date, policy_number NULL — templates don't have dates/policies
All coverage templates carrier_code, carrier_naic, carrier_name NULL — templates define requirements, not carriers
ComplianceStatus All fields Created in post-migration bootstrap
ComplianceThresholds All fields Created in post-migration bootstrap with defaults

Migration Execution

Migration Order (14 steps)

 1. Validate prerequisites:
    - id_mapping_accounts.csv exists and is complete
    - id_mapping_requirements_groups.csv exists with requirements_id associations
    - Pre-migration actions 1-4 resolved
    - Area 5's user_defined_policy template records available
 2. Dry-run validation report
 3. Create COIRiskProfiles from requirements (Decision #1)
 4. Create GL templates (Decision #2)
 5. Create Auto templates (Decision #2)
 6. Create WC templates (Decision #2)
 7. Create UMB templates (Decision #2)
 8. Create LL templates (Decision #2)
 9. Create user_defined_policy templates → PropertyCoverage/OtherCoverage (Decision #7)
10. Create CarrierRating templates (Decision #6)
11. Create DescriptionOfOperations templates (Decision #5)
12. Create DocRequirement records from ATT fields (Decision #4)
13. Backfill RequirementsGroup.coi_risk_profile (Decision #3)
14. Generate and export all ID mapping tables
15. Extract waiver candidates from coi_review_result (Decision #10, optional)
16. Verify

Dry-Run Report

=== DTX 1.0 Requirements & Compliance Migration - Dry Run Report ===

PREREQUISITES:
  id_mapping_accounts.csv:              ??? records loaded
  id_mapping_requirements_groups.csv:   ??? records loaded
  DocumentType records for ATT fields:  ??? of 8 present

REQUIREMENTS:
  Total records:                 ???
  Mapped to account:             ???
  Missing customer_id in mapping:???
  Unique customer_ids:           ???

  Timestamp issues:
    update_time = 0000-00-00:    ???
    create_time = 0000-00-00:    ???

  Description cleanup:
    NULL descriptions:           ???
    Placeholder (" ", "."):      ???
    With comments:               ???

COI RISK PROFILES TO CREATE:      ???

COVERAGE TEMPLATES:
  GL templates:                  ???
    GLLimEaOcc populated:        ???  (non-NULL, non-"-1")
    GLLimEaOcc = "-1":           ???  (required, no minimum)
    GLAddlInsd = "yes":          ???
    GLSubrWvd = "yes":           ???
    GLPrimary = "yes":           ???
    GLContLiab = "yes":          ???

  Auto templates:                ???
    AutoSingle populated:        ???
    AutoAny = "yes":             ???
    AutoHired = "yes":           ???
    AutoOther3LimitDesc populated: ???  (should be 0)

  WC templates:                  ???
    WCStatutory = "yes":         ???
    WCSubrWvd = "yes":           ???
    WCExec = "yes":              ???

  UMB templates:                 ???
    UMBLimEaOcc populated:       ???
    UMBMandatory populated:      ???  (should be 0)

  LL templates:                  ???
    LLLimEaOcc populated:        ???

  Total coverage templates:      ???

USER DEFINED POLICY TEMPLATES:
  Total deferred records:        ???
  Unique UDPType values:         [list all]
  Property-classified:           ???
  Other-classified:              ???

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

DESCRIPTION OF OPERATIONS:
  OpsDesc truthy:                ???
  OpsDescReviewStatus values:    [distribution]

ATTACHMENT REQUIREMENTS:
  ATT_W9:                        ???
  ATT_COI:                       ???
  ATT_CG2015:                    ???
  ATT_CG2033:                    ???
  ATT_CG2037:                    ???
  ATT_Primary:                   ???
  ATT_HoldHarmless:              ???
  ATT_SubrWvd:                   ???
  Total DocRequirement records:  ???

REQUIREMENTS GROUP BACKFILL:
  Groups with requirements_id:   ???
  Groups without requirements_id:???
  Unique requirements_id values: ???
  Groups that will get COIRiskProfile: ???

TABLES NOT MIGRATED (reference counts):
  requirements_audit:            ??? records, date range: ??? to ???
  review_result:                 ??? records, date range: ??? to ???
  coi_review_result:             ??? records, date range: ??? to ???
    WAIVED statuses found:       ???
  so_review:                     ??? records

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

Error Handling

Scenario Behavior
customer_id not in Account mapping Skip requirements record, log to issues CSV
requirements_id not in RequirementsGroup mapping Create COIRiskProfile but skip backfill, log
0000-00-00 00:00:00 timestamp Map to NULL or create_time fallback
-1 limit value Map to NULL in template field
ATT field truthy but DocumentType missing Skip DocRequirement, log
user_defined_policy template with no requirements_id match Create standalone, log
Boolean field unexpected value (not "yes"/NULL) Default to False, log the value
Duplicate requirements_id (should not exist) Use first occurrence, log duplicate
Description placeholder (" ", ".") Strip to NULL
PHP serialization parse failure (coi_review_result) Log and skip that record

Output Files

File Purpose
id_mapping_coi_risk_profiles.csv requirements_idcoi_risk_profile_idaccount_id
id_mapping_coverage_templates.csv requirements_idcoverage_typetemplate_record_id
requirements_group_backfill.csv requirements_group_idcoi_risk_profile_idstatus (updated/skipped)
doc_requirements_created.csv requirements_group_iddocument_typedoc_requirement_id
waiver_candidates.csv certificate_idcoverage_typewaived_reasonuser_id (from coi_review_result)
migration_issues.csv All flagged records with issue description
migration_report.txt Summary statistics and validation results

Verification (post-migration)

# Check Method
1 COIRiskProfile count One per unique requirements_id. No duplicates.
2 COIRiskProfile.is_template All created profiles have is_template=True
3 Account linkage Every COIRiskProfile has a valid Account FK
4 GL template count Matches requirements rows with ANY GL field non-NULL
5 Auto template count Matches requirements rows with ANY Auto field non-NULL
6 WC template count Matches requirements rows with ANY WC field non-NULL
7 UMB template count Matches requirements rows with ANY UMB field non-NULL
8 LL template count Matches requirements rows with ANY LL field non-NULL
9 Limit values Spot-check 20 templates per type — limits match source (with -1 → NULL)
10 Boolean values Spot-check 20 templates — booleans match "yes" → True
11 RequirementsGroup backfill Every group with a requirements_id now has coi_risk_profile set
12 Shared COIRiskProfile Groups sharing a requirements_id share the same COIRiskProfile FK
13 DocRequirement count Matches sum of truthy ATT fields × affected RequirementsGroups
14 DocRequirement uniqueness No duplicate (requirements_group, document_type) pairs
15 CarrierRating template count Matches requirements rows with AMBestRatingOK=yes
16 DescriptionOfOperations count Matches requirements rows with OpsDesc=yes
17 User_defined_policy templates All deferred template records processed. No orphans.
18 Timestamps COIRiskProfile.created matches requirements.create_time
19 No certificate FKs on templates All coverage templates have certificate=NULL
20 ID mapping complete Every requirements_id in source exists in mapping CSV

Dependencies

Depends On What's Needed
Area 2: Customers → Accounts id_mapping_accounts.csv for customer_id → Account FK
Area 4: Certificates & Documents id_mapping_requirements_groups.csv for RequirementsGroup backfill
Area 5: Coverage & Insurance PropertyCoverageType / OtherCoverageType records (for user_defined_policy template dedup), user_defined_policy template records
Depended On By For
Area 5: Coverage & Insurance Evaluation validation (coverage_type string matching between templates and certificate records)
Area 10: Reporting Compliance metrics depend on COIRiskProfile templates

Difficulty Assessment

Overall: MEDIUM-HIGH

More complex than Area 5 due to the structural transformation (flat 89-column requirements → multiple models), cross-area backfill, and ATT → DocumentType mapping.

Factor Impact
Single 89-column source table → 10+ target models Significant structural transformation
Coverage fields are ACORD-standard and match DTX 2.0 Reduces field-level mapping complexity
-1 limit semantics need team confirmation Potential blocker if wrong assumption
RequirementsGroup backfill is a cross-area UPDATE Requires Area 4 mapping data
ATT → DocumentType mapping requires seed data Potential blocker if DocumentTypes don't exist
3 of 5 source tables → don't migrate Reduces scope significantly
user_defined_policy templates from Area 5 Cross-area dependency adds complexity
PHP serialized data (coi_review_result) Parse-only for waivers, not primary migration

Decision Summary

# Decision Status Approach
1 COIRiskProfile creation Resolved One per requirements record, is_template=True, with name/description
2 Coverage templates Resolved Create child templates per coverage type if ANY field non-NULL. -1 → NULL
3 RequirementsGroup backfill Resolved Update coi_risk_profile FK on existing groups. N:1 — groups share profiles
4 ATT → DocRequirement Pending Action #1 Create DocRequirement per truthy ATT field × RequirementsGroup. Needs DocumentType mapping
5 DescriptionOfOperations Resolved Create template if OpsDesc=yes. description_required=True
6 CarrierRating templates Resolved Create template if AMBestRatingOK=yes. avii_or_above=True
7 user_defined_policy templates Resolved Route to Property/Other coverage template on matching COIRiskProfile
8 requirements_audit Resolved Don't migrate — incomplete schema, historical
9 review_result Resolved Don't migrate — historical document reviews
10 coi_review_result Resolved Don't migrate as primary data. Parse for WAIVED statuses optionally
11 so_review Resolved Don't migrate — reporting/summary table
12 ComplianceStatus/Thresholds Resolved Create in post-migration bootstrap, not during Area 6
13 requirements.comments Resolved Append to COIRiskProfile.description