Commercial Services CRM Cutover
Implementation date: 1 October 2026. This is the CRM structure tranche of the delivery plan, using the inspected field map.
Retained structure
| Object | Previous module API | Target module API | Retained module ID |
|---|---|---|---|
| Client Services | Managed_Services_Plans |
Client_Services |
42659000014726394 |
| Service Lines | MSP_Invoice_Lines |
Service_Lines |
42659000015245249 |
Both existing objects were renamed in place. Existing field APIs, record IDs, lookup
target IDs, historical invoice keys and source links are preserved. The distinct
standard appointment catalogue, Services__s, keeps its API and records; its
original display remains Service / Services.
The additive installation contains eight Service fields, eighteen Service Line fields and four Product defaults. It also adds Draft/Cancelled lifecycle choices and No labour on the retained line budget picklist. New Service review defaults to Proposed, MSP Seats Enabled to false, and line Billing Authority to Hold. All additions remain optional in CRM metadata. Existing values are not inferred from these defaults.
The Standard Service layout makes only MSP Plan Type and Billing Start optional. Legacy invoice generation still requires Billing Start. Name, Company, Original Deal and Service Status retain their existing requirements; line requirements and the optional Service parent retain their existing state. Quantity remains an integer.
Proposal Lines and Billing Events, including their dependent lookups, remain separate delivery-plan work. Exploratory Commercial Services and its lookup relationships are not consolidated by this cutover.
Compatibility controls
The reviewed 1 October capture contains 28 retained parents, all with an existing MSP tier and Company: 26 Active and two Onboarding. Enable MSP seats explicitly on these 28 records to preserve contact eligibility when active. Existing status checks still exclude the two Onboarding records until activation.
Of 193 configured lines, 167 point to those retained parents. Assign these Legacy MSP ownership to preserve the original generator's scope, including its date-based applicability. Assign the 26 lines without a parent to Hold; they were already outside Plan-based generation. No Product, date, quantity, price, Company, parent, seat or invoice value changes in this control batch.
commercial_services.universal_controls captures private before-images and
requires per-record values and evidence reasons. There is no default selection
or authority inference in the tool. It validates module identities and the exact
preview hash, preflights every selected record, then uses conditional PUTs with
If-Unmodified-Since, workflow triggers disabled and saved readback. Resume
skips a verified matching after-image; stale records require fresh review.
Deployment and verification
The middleware checkout is /home/deployer/pepit, owned by deployer. Root SSH
access is used for Docker operations; Git commands run as the checkout owner.
The pre-cutover deployed revision was f7b500f, with a clean checkout. Runtime
files affected by this change matched that revision before applying the tested
patch. The final runtime is recorded on codex/client-services-deployment,
using only this tranche's runtime files so earlier unrelated changes are not
deployed as part of the cutover. The source implementation commit also contains
tests and the updated documentation. The previous image is retained as pepit-app:before-services-20261001.
Private deployment before-images and the applied file list are outside the
checkout in /home/deployer/commercial-services-cutover-20261001/.
Build the replacement image while the old container is running. Pause middleware mutations during the two module API changes, then restart the matching image and verify authenticated health and scoped reads before resuming operations. Do not rely on old CRM API names remaining aliases.
The installed single/monthly invoice and parent/line reconciliation standalone
functions were inspected in CRM Setup. Their bodies call the tracked middleware
routes using org variables and IDs, with no old module API literals. The installed
Contact-created standalone function likewise calls /zoho/contacts/on-create.
Contact-edited handling exists in repository middleware; no matching installed
function appeared in the CRM Functions search. This cutover does not add a new
contact workflow. Flow, marketplace and external BI configuration have not been
exhaustively audited.
The shared CRM client maps dated source keys explicitly through
zoho_crm/module_names.py. Live reads/writes use the new APIs; archived capture
keys and related-list APIs remain stable. This is a client-side historical-input
mapping, not an assumption about CRM aliases.
The saved layout readback confirms all field and lookup identities and the planned required flags. The parent, Product and original-Deal reverse lists now use Client Services / Service Lines; the current-agreement reverse list is Current Service Agreements. Seats and Invoices retain their concise reverse-list labels. Related-list APIs remain unchanged. The configured native invoice-item lookup is labelled Source Service Line and still points to the retained line module.
Local verification covers wrong organisation/field identities, idempotent schema and defaults, conditional ownership writes, stale batches, contact entitlement, Legacy MSP-only generation and draft protection when ownership changes. All 1,144 tests and both strict documentation builds pass. Authenticated production health returns 200; empty contact and single-invoice requests and an invalid monthly request return 400 before business writes. Production reads verify 28 parents and 193 lines, with 28 explicit seat entitlements, 167 Legacy MSP lines and 26 held lines. The two retained workflow/action definitions also match the original capture by identity and configuration.
The October GET-only billing previews match before and after the API switch,
including all 159 generated lines. Their private comparison digest is
b5c38fbc0b468fdd6712454e11a1287d9f815bef4d75925fa3495afd28ec0765.
No invoices were generated by cutover verification.
Administrative commands
Use the configured local CRM credentials; never copy tokens into captures or
published documentation. Raw metadata, decisions and journals belong in ignored
docs/local/commercial-services/ directories with owner-only permissions.
python -m commercial_services.universal_schema --apply --directory PRIVATE_SCHEMA_DIR
python -m commercial_services.universal_defaults --apply --directory PRIVATE_SCHEMA_DIR
python -m commercial_services.universal_controls --capture --directory PRIVATE_CONTROL_DIR
# Review decisions.json using the captured IDs and evidence before previewing.
python -m commercial_services.universal_controls --directory PRIVATE_CONTROL_DIR
python -m commercial_services.universal_controls --apply --directory PRIVATE_CONTROL_DIR --expected-batch REVIEWED_HASH
# Run after deployment of both guards.
python -m commercial_services.universal_layouts --guards-deployed --directory PRIVATE_SCHEMA_DIR
Custom module display labels use the supported
Update Custom Module API.
Module API names require CRM Setup's API names editor. Zoho rejected the original
Service/Services custom labels as system keywords in both interfaces. The user
selected Client Services / Client_Services; the standard appointment module
retains its original name. Field display labels, section labels and
required flags use the
Update Custom Layout API,
with retained field/section IDs and readback. Lookup reverse-list labels use
the supported lookup display label in the
Update Custom Fields API,
with target/field/list IDs and related-list APIs preserved.
Recovery
Pause affected mutations before recovery. Restore the previous custom module API names through CRM Setup together with the backed-up middleware image; restoring only one side leaves integrations incompatible. Restore display/layout settings from their before-images if needed. Do not delete the additive fields or create replacement modules.
Restore control values only through a separately reviewed conditional batch, using the original before-images and current modification timestamps. Preserve later business edits and investigate any drift. No invoice or accounting rollback is needed for a structure cutover that only ran GET-only financial verification.