Split off from #1163 (component-policy invoice coverage) — the parent-side slice, which is a supplier-data problem, not an activity_pricing backfill.
Context
The Trip Order invoice render resolves each component's T&C as activity_pricing text → supplier default → null (coalesce at apps/api/src/financials/trip-order.service.ts:1857-1858 parent, :1927-1928 child). The supplier default is joined by free-text name: LEFT JOIN suppliers s ON s.name = ap.supplier — activity_pricing.supplier is a varchar(255) NAME, not an FK.
Gap (prod, read-only, from the #1163 diagnostic)
Of 1,163 parent components, 938 render BLANK terms, and 609 of those have a supplier NAME set on activity_pricing.supplier but the coalesce still yields blank — i.e. the name either doesn't string-match any suppliers.name row, or the matched supplier's default_terms_and_conditions is itself empty. (Cancellation: 471 parent blank.)
Because render already coalesces to the supplier default, populating/linking suppliers.default_terms_and_conditions / default_cancellation_policy (and reconciling the free-text activity_pricing.supplier names to real suppliers rows) auto-fixes the invoice for every row referencing that supplier at once — no activity_pricing write needed.
Suggested work
- DRY report: for the 609 blank-with-supplier-name parent rows, bucket by (a) supplier name has no matching
suppliers row (orphan free-text name) vs (b) matches a supplier whose default_* is empty.
- For (b): populate the supplier defaults (Al-gated) → render self-heals.
- For (a): reconcile the free-text names to canonical
suppliers rows (the merged_into_supplier_id global/mergeable model already exists), or create supplier records with defaults.
- Consider making
activity_pricing.supplier reference suppliers.id longer-term to remove the fragile name-string join (larger change).
Notes
Relates to #1163, #1042, #1161.
Split off from #1163 (component-policy invoice coverage) — the parent-side slice, which is a supplier-data problem, not an
activity_pricingbackfill.Context
The Trip Order invoice render resolves each component's T&C as
activity_pricing text → supplier default → null(coalesce atapps/api/src/financials/trip-order.service.ts:1857-1858parent,:1927-1928child). The supplier default is joined by free-text name:LEFT JOIN suppliers s ON s.name = ap.supplier—activity_pricing.supplieris avarchar(255)NAME, not an FK.Gap (prod, read-only, from the #1163 diagnostic)
Of 1,163 parent components, 938 render BLANK terms, and 609 of those have a supplier NAME set on
activity_pricing.supplierbut the coalesce still yields blank — i.e. the name either doesn't string-match anysuppliers.namerow, or the matched supplier'sdefault_terms_and_conditionsis itself empty. (Cancellation: 471 parent blank.)Because render already coalesces to the supplier default, populating/linking
suppliers.default_terms_and_conditions/default_cancellation_policy(and reconciling the free-textactivity_pricing.suppliernames to realsuppliersrows) auto-fixes the invoice for every row referencing that supplier at once — noactivity_pricingwrite needed.Suggested work
suppliersrow (orphan free-text name) vs (b) matches a supplier whosedefault_*is empty.suppliersrows (themerged_into_supplier_idglobal/mergeable model already exists), or create supplier records with defaults.activity_pricing.supplierreferencesuppliers.idlonger-term to remove the fragile name-string join (larger change).Notes
scripts/migration/report-child-policy-coverage-1163.ts).Relates to #1163, #1042, #1161.