MSP Plan agreement migration
msp_imports.plan_agreements adds four optional current-agreement fields to
Managed_Services_Plans and fills reviewed values on explicitly selected Plans.
It reuses each Plan's identity and original Deal. The
operating rules
define their meaning and distinguish agreement dates from billing dates.
Capture and prepare
Use a fresh private directory for each capture. The capture mode refuses to overwrite existing source evidence and uses a CRM client that rejects business writes. Supply only the Plan IDs being reviewed.
python -m msp_imports.plan_agreements \
--directory .local/msp-plan-agreements \
--capture --plan-id <plan-id> --plan-id <another-plan-id>
The command saves plans-and-metadata.json with the selected records and module
field/layout metadata. Write decisions.json in the same private directory with
owner-only permissions. The following identities and dates are synthetic:
{
"10001": {
"deal_id": "20001",
"start": "2026-05-15",
"accepted": "2026-06-05",
"renewal": "2027-05-15",
"evidence": "Private signed agreement and reviewed source reference"
}
}
Each decision requires an existing Deal ID, three evidenced ISO dates and an evidence reference. Acceptance may follow commencement; renewal must follow commencement. Omit a Plan whose current agreement remains unresolved. A shared Company, Deal closing date or Plan billing start does not prove agreement terms.
python -m msp_imports.plan_agreements --directory .local/msp-plan-agreements
This local-only command writes preview.json: proposed field definitions,
before/after values, preserved Plan fields, deferred IDs and a batch hash.
Review the exact payload and its sources before application.
Apply and verify
python -m msp_imports.plan_agreements \
--directory .local/msp-plan-agreements \
--apply --expected-batch <reviewed-batch-hash>
The writer verifies the preview against its captured source and decisions. It checks all reviewed Plans and their agreement Deals before creating fields. Existing fields must have compatible types and the correct Deals lookup target; conflicting labels or API names stop the run. Missing fields are added to the Standard layout and checked by metadata readback.
Record updates are restricted to the preview's Plan IDs and four agreement fields,
with trigger: [] and If-Unmodified-Since. Existing custom Plan values,
identity, Company, original Deal, owner and currency must remain unchanged.
Readback checks both the intended values and preserved fields. Missing or changed
records, conflicting agreement values, an unrelated Deal or failed readback stop
the run. There are no writes to other modules' business records.
journal.json records request intent, responses and verified records privately.
applied.json records updated, already-matching and deferred Plan IDs. A rerun
skips verified matching values and existing compatible fields. A partially
completed run can therefore resume; differing human edits require a fresh capture
and review. No destructive rollback or automatic deletion is implemented.
The optional fields exist across the Plan module. Only reviewed target records are filled; other Plans remain blank. The Deal lookup adds a related list on Deals without editing Deal records. Invoice generation and operational field layouts are otherwise unchanged.
Offline verification: tests/test_msp_plan_agreements.py covers preservation,
missing evidence, date boundaries, customer mismatch, metadata conflicts,
source drift, restricted writes and repeated application. Existing Plan creation
and MSP invoice tests cover the operational behaviour alongside it.
API references: Zoho custom field creation and conditional record updates.