Skip to content

MSP Commercial Service review migration

commercial_services.msp_review configures existing candidate Commercial Services from their linked Managed Services Plans. It adds five optional fields to Commercial Services: Current_Agreement_Deal, Current_Agreement_Start, Current_Agreement_Accepted, Current_Agreement_Renewal and Commercial_Summary. These fields use category-neutral meanings. Only selected MSP records are populated by this command.

The current agreement values come from the captured Plan; its Deal must belong to the same customer. A separately prepared summary describes the operational configuration, accepted agreement evidence and review scope. The original Deal, service identity, Plan link, lifecycle, service start/end and legacy source evidence remain unchanged. A renewal commencement does not replace the start of a continuing service.

The command also fills existing Service_Invoice_Lines coverage fields from the linked native invoice's MSP_Billing_Period_Start and MSP_Billing_Period_End, only for explicit invoice_header decisions. An unresolved decision retains blank coverage and explains the conflict. Invoice date alone is never used. All candidate services and lines remain Needs Review.

Private inputs and preview

Capture full records into a new private directory as source.json. Include only the selected Commercial_Services, their Managed_Services_Plans, current agreement Deals, relevant configured MSP_Invoice_Lines, captured Service_Invoice_Lines and their native Invoices. Preserve field/layout metadata with the capture. Use the repository's read-only CRM client and owner-only file writer. Customer prices, raw records and decisions are private working evidence and are not committed.

decisions.json contains services and lines, keyed by captured record ID. Every captured service requires a nonempty summary and review_reason. Every line requires coverage (invoice_header or unresolved) and a nonempty reason. These are explicit reviewed mappings, not an automatic description parser. Compare line descriptions with configured charge validity and invoice coverage before selecting an inheritance rule.

python -m commercial_services.msp_review --directory .local/msp-service-review

preview.json records the source hash, decisions, optional field definitions, exact target IDs/values and batch hash. It creates no CRM records and changes no remote data. Review the populated summary text as well as the field payloads.

Apply and read back

python -m commercial_services.msp_review \
  --directory .local/msp-service-review \
  --apply --expected-batch <reviewed-batch-hash>

The writer regenerates the preview from the captured source and decisions. It checks all captured business records before any schema or record write, validates customer/Plan/invoice/document relationships and source-line uniqueness, then checks field compatibility and Standard-layout visibility. Only the five named optional service fields can be created.

Updates use trigger: [] and If-Unmodified-Since. Each target is checked again immediately before writing and read back afterward. The writer preserves all other captured business fields, including Products, source-line identities, quantities, prices, discounts, taxes and totals. It rejects changes to any operational Plan, configured charge, native invoice, Deal or other record.

The private journal retains request intent, complete before-images, responses and verified records. applied.json lists changed and already-matching IDs. Repeating a matching batch makes no additional field or record changes. Conflicting human edits, changed sources, metadata conflicts or failed readback stop the command. After interruption, inspect the journal and resume the same batch; there is no destructive rollback.

CRM review surface and limits

Review the candidate Commercial Service's Actual Invoice Lines, or follow its native CRM Invoice to Actual Service Lines. The invoice list shows Commercial Service, Product, Configured MSP Invoice Line, quantity, unit/net amounts, service-period start/end and document status. These are native CRM display settings, separate from business-record writes. Commercial summaries and agreement context are optional; they are not prerequisites for line capture or configured-charge mapping.

This is an operator-run candidate migration, not ongoing synchronisation. Plan agreement fields are source context; the command does not maintain a second editable billing configuration or synchronise commercial edits back to the Plan. Native invoice refresh and Xero sync remain unchanged. The captured line representation retains its financial source identity and values; Product corrections and disputed periods require separate evidence. No Service Month record or Service Terms record is required for this slice.

Verification: tests/commercial_services/test_msp_review.py covers customer and invoice ownership, date inheritance, unresolved periods, business-source drift, financial preservation, schema conflicts, transport restrictions and idempotence. The process rules are in MSP Invoicing.

commercial_services.invoice_charge_links adds the optional Service_Invoice_Lines.Configured_MSP_Invoice_Line lookup to MSP_Invoice_Lines and populates explicitly selected actual lines. Its reverse related list is Actual Invoice Lines on a configured charge. The existing CRM_Invoice lookup supplies the Invoice → actual lines relationship.

Capture complete selected records into private source.json, with a records object containing these five module arrays: Service_Invoice_Lines, MSP_Invoice_Lines, Invoices, Commercial_Services, and Managed_Services_Plans. Include the target lines and all records needed to verify their invoice, service, Plan and configured-charge relationships.

decisions.json maps each selected actual-line ID to charge_id and a nonempty reason. Unselected lines remain untouched. Select matches from evidence; this command does not infer a match from the Product alone.

python -m commercial_services.invoice_charge_links --directory .local/invoice-charge-links
python -m commercial_services.invoice_charge_links \
  --directory .local/invoice-charge-links \
  --apply --expected-batch <preview-batch-hash>

The preview validates that the selected charge and invoice share the service's Plan/customer, the line and charge share a Product, and source document and currency identifiers agree. Missing or duplicate source-line identifiers and different existing charge links stop the preview. Historical quantity and price differences are allowed; those values are preserved. Product identity differences require a separate attribution decision before using this command.

Apply checks captured records for drift before any write, creates only the named optional lookup when missing, and verifies its target and Standard-layout visibility. Each update changes only that lookup, uses trigger: [] and If-Unmodified-Since, and is read back against all captured business values. The private journal retains before-images and responses. A matching rerun skips record writes. Invoice generation, source status/payment synchronisation and changes to operational charge configuration are outside the command.

Verification: tests/commercial_services/test_invoice_charge_links.py covers historical financial preservation, ownership and Product mismatches, source drift, duplicate lines, conflicting links, schema conflicts, idempotence and the write boundary. Success Works' eight INV-0319 mappings are applied with private evidence in .local/commercial-services/invoice-charge-links-20260908/.