Commercial Services CRM Structure Preparation
Prepared on 1 October 2026 for Phase 1 of the delivery plan. Status: 30 optional fields, safe defaults and explicit compatibility controls installed; module/API cutover and Standard layout generalisation verified. This is the field-gap and dependency map for reusing Plans as Services and configured MSP Invoice Lines as Service Lines. The dated original inventory below is preserved for comparison; the cutover record owns implementation evidence.
Direction updated on 1 October 2026: rename both module API names and display labels, with integration changes tracked in this repository. The installed names in the inventory are dated evidence from before the cutover.
MSP Seat ownership is implemented by the Account seat migration. Its Account lookup and Account related list own the roster; the inspected Seat Plan relationship below is retained only as migration evidence.
Original inspection evidence and boundary
The original preparation inspection read CRM v8 module, field, layout, related-list and
workflow metadata. Fields were requested with type=all, including unused
fields. Workflow conditions and instant/scheduled action summaries were read
per relevant module. CRM Setup was also inspected for Plan/Line buttons and
validation rules. That inspection made no changes. Subsequent installation and
verification are recorded in the cutover reference.
| Structure before installation | Original inspection result |
|---|---|
Managed_Services_Plans |
41 fields; module ID 42659000014726394; Standard layout 42659000014726393 |
MSP_Invoice_Lines |
35 fields; module ID 42659000015245249; Standard layout 42659000015245248 |
| Related metadata | MSP Seats: 29 fields; Commercial Services: 48; Products: 40; Invoices: 63; Invoiced Items: 20; Deals: 61 |
| Workflow rules | 17 returned globally, including one active Plan rule and one active Line rule |
| New structures | No Proposal_Lines or Billing_Events module API name returned |
Raw responses and the capture script remain private and ignored under
docs/local/commercial-services/crm-structure-20261001/. The capture contains
timestamps, request paths, parameters and HTTP statuses. Repository code supplies
middleware behaviour. The initial inspection did not independently verify the
deployed revision or function bodies; implementation subsequently verified five
installed wrappers and deployed matching middleware. Workflow metadata establishes
the installed action names, not the contents of those functions. Other automation
surfaces, including Flow, schedules, client scripts, Blueprints and marketplace
configuration, need a scoped deployment check before enabling the pilot.
Mandatory fields below come from the Standard layouts, not just Fields Metadata. Zoho documents that the latter does not describe layout-specific requirements. See Fields Metadata, Layouts Metadata and Workflow Rules.
Display and API rename map
Rename the existing custom modules in place and update their consumers through the cutover below. Preserve module, field and record IDs and lookup target identities. Re-read metadata after each change. Field and related-list APIs in the table describe the current inventory; the required module API changes are explicitly mapped in its first two rows.
| Surface | Installed display | Prepared display | API mapping / retained identity |
|---|---|---|---|
| Parent module, singular / plural | Managed Services Plan / Managed Services Plans | Client Service / Client Services | Managed_Services_Plans → Client_Services; retain module 42659000014726394 |
| Component module, singular / plural | MSP Invoice Line / MSP Invoice Lines | Service Line / Service Lines | MSP_Invoice_Lines → Service_Lines; retain module 42659000015245249 |
| Parent name | Managed Services Plan Name | Service Name | Name, field 42659000014726449 |
| Parent owner | Managed Services Plan Owner | Service Owner | Owner, field 42659000014726451 |
| Parent original Deal | Deal | Original Deal | Deal, field 42659000014774902 |
| Parent lifecycle | Plan Status | Service Status | Plan_Status, field 42659000014774944; dependency gate below |
| MSP support package | Plan Type | MSP Plan Type | Plan_Type, field 42659000014775086; preserve L1/L2/L3/SB values |
| Line name / owner | MSP Invoice Line Name / Owner | Service Line Name / Owner | Name / Owner, fields 42659000015245272 / 42659000015245273 |
| Line parent | Plan | Service | Plan, field 42659000015245826, targets Managed_Services_Plans |
| Line description | Description Overwrite | Description Override | Description_Override, field 42659000015245846 |
| Seat owner | No direct Account lookup in the initial inspection | Account | Additive MSP_Seats.Account, field 42659000018700058, points to Accounts; it does not retarget the old lookup |
| Seat legacy Service | Plan | Hidden migration evidence | MSP_Seats.Plan, field 42659000014776394, retains existing values in Unused Items |
| Native invoice parent | MSP Plan | Service | Invoices.MSP_Plan, field 42659000014932183 |
| Native item configured component | Configured MSP Invoice Line | Source Service Line | Invoiced_Items.Configured_MSP_Invoice_Line, field 42659000018166226 |
| Parent's configured-line related list | Invoice Lines | Service Lines | Invoice_Lines, related-list 42659000015245828 |
| Product's configured-line reverse list | MSP Invoice Lines | Service Lines | Existing Product lookup reverse API MSP_Invoice_Lines |
| Reverse original-Deal / Company lists | Managed Services Plan | Client Services | Existing lookup reverse API Managed_Services_Plan |
| Reverse current-agreement Deal list | Current MSP Agreements | Current Service Agreements | Existing reverse API Current_MSP_Agreements |
Naming prerequisite: CRM already has the visible standard module
Services__s, module ID 42659000003562326, labelled Service / Services and
paired with Appointments. It is a distinct object from the retained Plan module.
Zoho rejected Service/Services as reserved keywords through both the custom-module
API and CRM Setup, even after testing a distinct appointment display name. The
user selected Client Service / Client Services, API Client_Services. The
appointment module retains its original name, API and records.
Keep the two existing invoice buttons labelled Generate MSP Invoice and Generate MSP Invoices while they still invoke the legacy MSP path. A universal widget action will need its own reviewed entry point. Update the module API references behind both legacy buttons as part of cutover while retaining their existing MSP billing responsibilities. Check the generated name/owner labels, image sections and information sections after module renaming. The installed Standard sections now use Service and Service Line wording.
API rename cutover and repository dependencies
Zoho supports changing API names for custom modules, fields and related lists;
default-object API names cannot be changed. Both retained modules were returned
as generated_type = custom. The existing standard Services__s remains a
separate object; verify the user-selected custom API name Client_Services after the reserved
Service/Services labels were rejected. Do not create
replacement modules to obtain the names. See
Zoho Modules API.
The required target module APIs are Client_Services and Service_Lines. Existing
field API names such as Plan, Plan_Status, MSP_Plan and
Configured_MSP_Invoice_Line are not automatically renamed by this decision.
Prepare a separate explicit field/related-list mapping for any universal naming
cleanup, preserving field IDs and genuinely MSP-specific configuration.
Zoho's custom-field update reference
requires field API-name changes through the UI; do not assume a metadata PATCH
can perform them.
The first repository search found the following consumers. This is a starting inventory, not proof that production has no other integrations. Trace imported constants and generated payloads as well as literal names.
| Consumer | Tracked starting points | Required preparation |
|---|---|---|
| Legacy billing and reconciliation | msp_imports/msp_invoices/constants.py, clients.py, service.py, reconcile.py; app/blueprints/msp_invoices.py; wrappers under zoho_crm/deluge/ |
Change module constants, record reads, field metadata calls, related-list requests and any direct Deluge module literals. Retain legacy billing keys and issued source identities. |
| Contacts / MSP entitlement | app/automations/contact_created.py, contact_edited.py |
Update module references including shared constants, together with entitlement guards. |
| Commercial reporting | app/commercial_margin/scoped_sources.py, services.py, native_items.py; app/crm_reports/msp.py, fields.py; other Plan-based report readers |
Update live source module names. Keep saved-source dictionary keys/report identities explicitly mapped where they are internal contracts rather than CRM APIs. |
| Schema installers and historical maintenance | commercial_services/sources.py, schema.py, cleanup.py, native_invoice_schema.py, service_line_labour.py, service_line_costing.py; related migrations/review commands |
Update lookup-target specifications and metadata requests. Existing captured evidence retains old names; adapt its input boundary rather than rewriting dated journals. Recheck scripts before future application. |
| Plan/Seat imports and agreements | msp_imports/create_msp_plans_from_deals.py, migrate_delivery_models_to_msp_plans.py, plan_agreements.py, staff_registry_to_msp_seats.py |
Update live reads/writes and field installers, preserving agreement/seat identities. |
| Tests, configuration and reporting exports | tests/, config/, pbi/, module field lists and generated payload fixtures |
Update external API expectations and verify preserved internal/report contracts; check deployed Power BI queries/datasets independently. |
| CRM-only and external configuration | Installed workflow/function/button definitions, Flow, schedules, widgets, client scripts, Finance integration and deployed middleware | Compare against tracked source; capture missing integration definitions in the repository with secrets kept in local configuration. Verify each consumer's cutover owner and deployed revision. |
Prepare and test code for the new module names before changing CRM. Pause the affected mutating entry points during the coordinated switch; deploy matching middleware/Deluge consumers and CRM API renames, then verify metadata and scoped reads before resuming generation. Do not rely on Zoho accepting the old API names as aliases. Retain the old/new naming map and compatible deployment revision for recovery; preserve all record, charge and invoice identities. This API rename is distinct from the later exploratory-to-universal record consolidation.
Completion checks: same module and record IDs, correct lookup target IDs, successful new-name reads, passing meaningful offline API-contract and billing regressions, both invoice-button paths and contact handlers verified against the deployed revision, and no active live consumers using the old module APIs. Historical evidence can legitimately retain the old names.
Services: installed fields to reuse
| Installed field(s) | Type / current requirement | Mapping and boundary |
|---|---|---|
Name, Owner, Currency, Exchange_Rate |
Name required; standard identity/ownership/currency | Preserve records and currency. Name is text, length 120. |
Account |
Accounts lookup; required | Universal customer; validate every related Deal and line against it. Display is already Company. |
Deal |
Deals lookup; required | Original sale; preserve independently of the current agreement. |
Current_Agreement_Deal |
Deals lookup; optional | Reuse for the currently accepted agreement. |
Current_Agreement_Start, Current_Agreement_Accepted, Current_Agreement_Renewal |
Dates; optional | Already installed; no duplicate agreement fields needed. Renewal date is not an instruction to generate another charge. |
Plan_Status |
Required picklist: Onboarding, Active, Ended, plus blank sentinel | Reuse for universal lifecycle only after MSP contact eligibility is explicitly gated. Add Draft and Cancelled for the new flow; keep existing actual values. Acceptance and activation remain separate. |
Plan_Type |
Required picklist: L1, L2, L3, SB, plus blank sentinel | MSP package only. Make optional at layout level; new non-MSP Services must not invent an MSP tier. Keep conditional MSP validation. |
Billing_Start, Billing_End |
Dates; start required | Retain legacy Plan billing window. Make start optional in the universal layout; require it for legacy billing. These are not universal agreement/delivery dates. |
Invoice_Generation_Mode |
Optional picklist: Monthly Auto / Manual; layout default Monthly Auto | Existing setting, not an authority gate. It is absent from the generator's fetched Plan fields. Do not reuse it as Billing Authority or assume Manual prevents processing. |
Timesheet_Scope_Type, Timesheet_Scope_ID |
Optional picklist / text | Reuse parent delivery scope. Installed types: Project, Tasklist, Task, Phase. The ID is a text source identity, not a native CRM Projects lookup. |
Expected_Support_Hours_per_Month, Fallback_Monthly_Revenue, Margin_Budget_Source |
Optional double / currency / picklist | Preserve MSP/reporting compatibility; new components use line-owned budgets and schedules. |
Plan_Label, PO_User_Count, Include_Users_Invoice, Onboarding_Form, Offboarding_Form, May_2026_Invoice_Ref |
Optional text / integer / booleans | Preserve MSP and historical use; group in an MSP operations section rather than require them for projects. |
The original Standard layout required Name, Account, Deal, Plan_Status,
Billing_Start and Plan_Type. The installed layout makes only the last two
optional. Only Name is system-mandatory in field metadata.
CRM Setup showed no configured Plan layout rules or validation rules.
Installed Services additions
The eight additions below were installed with exact API-name readback. They were absent from the original captured Plan fields. Created field IDs and exact API names are saved in private readback evidence. Keep additions optional in CRM metadata initially and enforce new-flow requirements in the backend. A missing historical value must not become a fabricated default.
| Proposed API name | Type / proposed values | Requirement and purpose |
|---|---|---|
Service_Key |
Unique text, length 255 | Required for new integration writes; stable commitment identity and replay matching. Preserve record ID through renewals. |
Service_Role |
Picklist: MSP, Project, Onboarding, Hardware sale, Recurring agreement, Ad hoc, Home, Unconfirmed | Required before activation. Map reviewed exploratory roles; role does not restrict a line's Products or choose billing authority. |
Service_Start, Service_End |
Dates | Commitment lifecycle dates, separate from the legacy billing window and current agreement. Map exploratory Start/End only with evidence. |
Review_State |
Picklist: Proposed, Needs Review, Reviewed, Confirmed | New flow explicitly saves Proposed; activation requires confirmed, complete accepted terms. Preserve later reporting inclusion semantics. |
Review_Reason |
Small textarea | Explain unresolved configuration, source or ownership; no silent zero/default substitutes. |
Commercial_Summary |
Small textarea | Human-readable accepted scope; transfer reviewed exploratory summaries later. |
MSP_Seats_Enabled |
Boolean; new-record default false | Explicit contact-enrolment gate, independent of Service Role and billing. Seats belong to the Account; this flag does not make the Service their owner. A mixed service may enable seats. |
Avoid a second customer lookup, second original/current Deal relationship or a
service-level recurrence field. Keep exploratory Business_Category and
Arrangement compatibility in the later reader migration; neither should
control new line recurrence. Lifecycle evidence and historical budget revisions
need preservation during consolidation, not an automatic copy onto new projects.
Service Lines: installed fields to reuse
| Installed field(s) | Type / current requirement | Mapping and boundary |
|---|---|---|
Name, Owner, Currency, Exchange_Rate |
Name required; standard fields | Preserve line ID and existing monetary context. |
Plan |
Plan lookup; optional | Correct universal Service target already exists. Require for every new accepted line in backend validation; leave globally optional until non-MSP historical lines are mapped. |
Product |
Products lookup; required | Universal catalogue identity. |
Quantity |
Integer, length 9; required | Reuse for whole-unit pilots. It cannot faithfully hold fractional accepted quantities. Do not round Qwilr values into it; fractional selling quantities need a separately reviewed double field or verified type-change migration. |
Unit_Price_Override |
Currency, two decimals; optional | Save effective accepted unit price explicitly, including zero; otherwise legacy preview follows current Product Unit Price. |
Description_Override |
Large textarea; optional | Save effective accepted description so catalogue edits cannot change accepted wording. |
Xero_Account_Code_Override |
Text; optional | Reuse permitted override; resolve/validate and snapshot the effective account on each event. |
Start_Date, End_Date |
Dates; start required | Legacy billing applicability. Reuse as line applicability boundaries for new schedules; store first-charge and delivery dates separately. |
Active |
Boolean; optional; layout default false | Retain. A saved draft is inactive; activation must also pass authority/accepted-term checks. The current legacy applicability function does not gate on Active. |
Sort_Order |
Integer; optional | Reuse display order. |
Labour_Budget_Type |
Optional picklist: Fixed budget / Recurring allowance | Reuse and propose adding No labour without changing current actual values. Missing hours on a labour-bearing line remain incomplete. |
Expected_Labour_Hours |
Double, two decimals; optional | Reuse total fixed hours or allowance hours; never derive from selling Quantity for converted lines. |
Expected_Labour_Tier |
Optional picklist: Tier 1–4 | Reuse current Consulting planning-tier semantics. Preserve MSP compatibility; not a selling price or private staff cost. |
External_Hourly_Cost |
Double, two decimals; optional | Reuse external consultant planning cost. |
Commercial_Service |
Optional lookup to Commercial_Services |
Historical exploration relationship only. Do not rename it to Service or fill it for new pilot lines. |
Line_Type |
Picklist: Base, Seat, Device, License, Subscription, Adjusment | Exists in field metadata but is absent from the Standard layout and generator field selection. Preserve legacy values, including the installed spelling; it is not Price Basis or Billing Mode. |
Standard currently requires Name, Product, Quantity and Start_Date.
Plan is optional. Active and End_Date live in the Auto section. CRM Setup
showed no configured Line layout or validation rules, and its Buttons page
listed no installed buttons.
Installed Service Line additions
Eighteen independent additions below were installed with exact API-name readback. Accepted Proposal Line remains deferred until its target module exists. Unsupported billing, tax and rollover policies remain blocked until agreed.
| Proposed API name | Type / proposed values | Purpose / new-flow validation |
|---|---|---|
Service_Line_Key |
Unique text, length 255 | Stable component identity; accepted revisions must reconcile against it rather than create a second active charge. |
Billing_Authority |
Picklist: Legacy MSP, Billing Events, Hold; default Hold | Required before generation. Explicitly map reviewed existing charges to Legacy MSP; new widget lines use Billing Events only after review. Blank/unknown remains held. |
Price_Basis |
Picklist: Fixed fee, Per unit, Per hour, Per seat | Explain selling quantity/rate independently of delivery hours. Supported bases resolve once at acceptance. |
Billing_Mode |
Picklist: One-off, Recurring, Instalments | Distinguish finite charges from recurrence. |
Billing_Frequency |
Picklist: Monthly, Annual | Required for supported recurring schedules; instalment dates/releases belong to individual events. |
First_Charge_Date |
Date | First scheduled charge, independent of line applicability and delivery start. |
Delivery_Start, Delivery_End |
Dates | Component delivery coverage; annual billing can cover monthly allowances. |
Allowance_Period |
Picklist: Month initially | Required for a recurring labour allowance; fixed budgets have no repeating interval. Additional intervals require calculation support. |
Rollover_Policy |
Picklist: Needs Review, No rollover, Agreed rollover | Explicit contract policy, not inferred from recurring billing. Agreed rollover remains unsupported until its detailed limit/expiry rules exist. |
Delivery_Status, Delivery_Completed_On |
Picklist: Not started, In progress, Completed, Cancelled; date | Capture line closeout/effective date; cancellation/reopening requires review. |
Timesheet_Scope_Type, Timesheet_Scope_ID |
Same picklist / text pair as parent | Line scope for later allocation; copied only when the relationship is evidenced. Parent work is not duplicated onto every line. |
Xero_Tax_Type_Override |
Text | Permitted explicit tax override; effective tax must validate against the accounting mapping. |
Expected_Non_Labour_Cost |
Currency | Accepted/planned non-labour cost override where applicable; missing cost differs from confirmed zero. Specify whole-line basis. |
Accepted_Proposal_Line |
Lookup to proposed Proposal Lines | Preserve the source line and accepted revision; capability proof required for the new module. |
Accepted_Revision_Key, Accepted_At |
Text / datetime | Immutable accepted-source version and timestamp; retain full evidence outside mutable catalogue defaults. |
Before accepting a new line, resolve and save effective Product, Quantity, price, description, budget and permitted overrides. Billing Events then hold the final effective accounting and financial snapshots. Discounts must retain their sign; quote totals, selected options, currency and accepted values must reconcile. Do not overwrite issued items when a line is revised.
Product defaults and native invoice relationships
Products already contain Unit_Price, Default_Invoice_Label, Xero_Account_Code,
Tax, Taxable, Cost_Price, Costing_Category, Usage_Unit,
Billing_Frequency and Commitment_terms. Product Billing Frequency already
supports Monthly / Annual / One-off. Reuse supported defaults in the widget;
Commitment_terms is contractual context, not a complete event schedule.
Product labour budget defaults, default hours, allowance period and explicit
price basis were absent from the original capture. The four installed defaults
are Labour_Budget_Type,
Expected_Labour_Hours, Allowance_Period and Price_Basis. Keep them optional;
copy effective values to the accepted line. Existing Usage_Unit does not
express a fixed fee versus per-seat price basis, and Costing_Category is not a
labour budget type. New tax policy must distinguish CRM tax labels/rates from
Xero tax codes; catalogue Tax alone does not prove their mapping.
| Existing relationship | Prepared treatment |
|---|---|
Invoices.MSP_Plan → Plans |
Reuse as the universal header owner. Fill only for a single-Service invoice; mixed-service headers stay blank. No second universal header lookup needed. |
Invoiced_Items.Configured_MSP_Invoice_Line → shared Lines |
Retain and relabel. This already supplies a durable route from an item through its source line to the Service. |
Invoiced_Items.Service_Period_Start / Service_Period_End |
Reuse charge coverage snapshots; do not confuse with invoice date or accounting recognition. |
Invoiced_Items.Xero_Line_Item_ID, Invoice Xero IDs/status/amounts |
Preserve external financial identities and issued snapshots. CRM subform row ID is a separate identity. |
Commercial_Service on Lines, Invoices and Invoiced Items |
All three still target exploratory Commercial_Services. A display rename cannot retarget them. Preserve until reviewed consolidation; new pilot writes use Plan/Line relationships. |
Proposed Invoiced_Items.Billing_Event → Billing Events |
Add optional lookup after module creation/capability validation; mirror event/source identity when creating the native item. |
| Plan related lists | Seats, Invoice Lines and Invoices already exist. No direct native Invoiced Items related list was returned. Use the item → Line → Plan query path until a verified scoped related list/report exists. |
During draft refresh the legacy writer can replace native subform rows after Product/order changes. Events must retain stable source keys and Xero line IDs, update the current CRM item reference through guarded readback, and treat a recreated row as the same charge. A row ID alone is not an idempotency key.
Proposal Lines and Billing Events: required relationship contract
Neither proposed module is installed. These are minimum schema candidates, not a verified Qwilr API contract or a module-creation request.
| Proposed module | Required candidate fields and relationship |
|---|---|
Proposal_Lines |
Name; Deal → Deals; unique text Proposal_Line_Key; text Proposal_Revision_Key; text Qwilr_Page_ID and Qwilr_Item_ID when proven; Product → Products; selling quantity, effective unit price/description and currency; the supported billing/delivery/budget settings from the Line map; selection/acceptance state and evidence reference. Proposed state picklist: Draft, Quoted, Accepted, Superseded, Needs Review. Use double selling quantity here so unsupported fractional selections are visible rather than truncated. |
Billing_Events |
Name; Service_Line → retained Lines; unique text Event_Key; text accepted-revision and occurrence/milestone identity; Due_Date; Service_Period_Start / Service_Period_End; effective Quantity, Unit Price, discount and ex-tax amount; Currency; effective text Xero_Account_Code / Xero_Tax_Type plus tax snapshot; release condition and release time; state and review reason; Invoice → native Invoices; text CRM_Item_ID and Xero_Line_Item_ID. Proposed state picklist: Planned, Held, Ready, Creating, Drafted, Issued, Cancelled, Needs Review. |
Event identity must include the stable source line, accepted schedule revision and occurrence or milestone; date and amount alone are insufficient. Enforce one event per intended charge, exclusive creation claims and recoverable invoice results. Instalments represent separate amounts against one fixed delivery budget. Milestone release is an event-level condition, not an Active flag on the line. An invoice-item subform lookup on the Event is not assumed supported; the proposed item-to-Event lookup plus guarded item-ID readback is the candidate.
Installed workflows, buttons and middleware dependencies
| Installed automation | Verified trigger/action | Preparation decision |
|---|---|---|
Plan Reconcile, 42659000015326174 |
Active; any change to Plan_Type, repeat enabled, no additional condition; instant function call_reconcile_msp_plan_invoice_lines; no scheduled action returned |
Preserve API references. Repository wrapper calls /zoho/msp-invoices/reconcile-plan-lines; current repo handler reads the Plan/lines and returns not_applicable without updating overrides. Verify deployed handler/function parity before changing fields. |
Line Reconcile, 42659000015326166 |
Active; create or edit, repeat enabled, all records; instant function call_reconcile_msp_invoice_line; no scheduled action returned |
Repository wrapper calls /zoho/msp-invoices/reconcile-line; current invoice_line_date_updates returns an empty mapping. Do not rely on this workflow to supply new dates/budgets or validate universal lines. |
Generate MSP Invoice button, 42659000015213037 |
Plan Standard layout, Record - Details; Function; Administrator and Standard | Repo button wrapper calls single-Plan legacy generation. Guard direct /zoho/msp-invoices/generate-plan as well as the UI. |
Generate MSP Invoices button, 42659000015213034 |
All Plan layouts, List - Util Menu; Function; Administrator and Standard | Guard batch /zoho/msp-invoices/generate-all; hiding a button does not protect middleware. |
| Contact-created / edited seat logic | Repo _active_plans_for_account in both handlers filters only Company and Plan_Status = Active |
Add the explicit MSP entitlement gate before reusing Active for projects. Otherwise a project can receive a seat or create multiple-Plan ambiguity for an existing MSP customer. Preserve reviewed existing MSP eligibility. |
| Legacy billing selection | Repo preview uses Plan Billing Start/End and line Start/End/Product rules; annual-seat rules are separate | Add Billing Authority to fetched fields and enforce it in shared preview and write paths. Plan_Type, Plan_Status, Invoice_Generation_Mode, Line_Type and Active currently provide no authority guard. |
| Reporting / migrations | Readers use exploratory service IDs and Commercial_Service, with Plan-based MSP fallbacks; Plan creators write Active/tier/billing fields |
Keep explicit compatibility until reader cutover. Update creators to save reviewed role/entitlement/authority; do not let defaults silently enrol new records. |
Deal create_associated_project, 42659000011764161 |
Active; Deal creation, all records; call_create_project_on_deal_create |
Reuse/project-link review; widget must not create a second delivery project. |
Deal Won Deal Automations, 42659000014367298 |
Active; Stage becomes Reserved, Business Category MSP or Tech Projects; call_on_deal_reserved_approval_payment_pending |
Existing Reserved work remains separate from Qwilr acceptance and Service activation. Review duplicate project/Delivery Model setup. |
Deal Create Invoice, 42659000010948251 |
Active; Stage becomes Recurring Monthly Project; task Send Welcome Email to Customer plus email Please Create Invoice | Its returned actions are task/email, not an invoice writer. Do not infer accounting behaviour from the workflow name. Inspect separate Finance integration before pilot invoice writes. |
| Deal Big Deal Rule | Active; create/edit, Amount ≥ 1,000; New Deal Alert email | Widget Deal amount changes can trigger staff alerts; consider deliberately selected record-write triggers. |
MSP Seats, Products, Invoices and exploratory Commercial Services returned no workflow rules through the scoped metadata requests. This does not establish that other integrations cannot write to them. The installed Plan buttons above were inspected in Setup, not executed. The global workflow list includes active Contacts On Create Automations but no Contacts edit rule. The edited-contact handler is a repository dependency; its live invocation path remains to be checked rather than assumed installed.
Main code references: msp_imports/msp_invoices/constants.py, preview.py,
service.py, reconcile.py, app/blueprints/msp_invoices.py,
app/automations/contact_created.py, contact_edited.py,
app/commercial_margin/services.py, engine.py, native_items.py,
commercial_services/cleanup.py, msp_imports/create_msp_plans_from_deals.py
and the matching wrappers under zoho_crm/deluge/.
Prepared configuration sequence and review checks
- Resolve the existing Services display-name collision and confirm the supported module/field label-edit method. Recheck function bodies, deployed middleware, relevant integration actions and operator permissions. Preserve a metadata before-image and every existing module/field/record identity.
- Finalise candidate enums, accounting overrides, accepted-revision storage and supported quantity/rollover policies. Create optional additive fields only after fresh metadata compatibility checks; read back IDs, API names, types, lookup targets, layout placement and unique-key support.
- Capture and review existing charge ownership and MSP entitlement. Deploy both generator authority guards and both contact-handler entitlement guards with an explicit compatibility mapping. Blank ownership holds generation; new records default to Hold/false. Route each configured charge once, including seat-based quantity rules. Do not cut over existing MSP billing by bulk default.
- Apply API/display renames through the coordinated integration cutover, plus universal layout requirements and those guards. Keep Name/Company/Original Deal/Service Status required. Remove unconditional Plan Type and Billing Start requirements; retain legacy-specific validation. Require new lines' Plan in backend validation while historical unmapped lines remain readable. Organise shared context, billing, delivery, review and MSP compatibility into clear sections.
- Prove the first new-project fixture: one Service; one fixed-fee Consulting line; one independently stored hours budget; one event-owned charge; accepted revision evidence; one invoice draft with source links. Replays create no new charge. Both legacy single and batch routes exclude it, and contact automation cannot create a project-owned MSP Seat.
- Extend to the two planned mixed/instalment fixtures. Retain existing MSP date, seat/proration, annual-seat and issued-invoice behaviour. Add meaningful offline regression coverage when the guards and writers are implemented.
The later consolidation crosswalk must retain exploratory record ID/service key,
destination Plan ID/service key, customer, evidence, review state and migration
batch. Reuse an existing MSP Plan where verified; match/create one Service for
non-MSP commitments. Map line parents first, then native item/header ownership,
source links and readers; keep unresolved rows for review. The existing
Invoices.MSP_Plan and item-to-configured-Line paths avoid adding duplicate
universal ownership lookups. Do not delete exploratory fields or records until
their evidence and readers/writers have been reconciled.
Preparation is complete for the requested field/workflow map. Schema writes, the Services naming resolution, guard implementation, Qwilr capability proof and first invoice-flow validation remain delivery work. Current operating procedures and the implemented workflow register remain unchanged by this inspection. See the MSP procedure, MSP rules and invoice integration.