Skip to content

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

  1. 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.
  2. 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.
  3. 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.
  4. 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.
  5. 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.
  6. 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.