Skip to content

2026-07-03 MSP invoicing Improvement

Snapshot date: 2026-07-03.

Objective

Document the simple operational plan for yearly seat invoicing and mid-period seat prorates without adding an MSP billing run module.

The goal is to keep CRM as the practical source of truth for the current plan setup, while generated CRM/Xero invoices remain the accounting snapshot once approved in Xero.

Decision

Do not add MSP billing runs for this stage.

Use MSP_Invoice_Lines as the operational charge memory:

  • recurring monthly items remain normal in-range invoice lines;
  • yearly seat invoices are represented by a recurring annual seat invoice line anchored by Start_Date;
  • mid-period seat additions are represented by a bounded one-off prorate invoice line;
  • devices remain manual invoice-line quantities for now.

This keeps the process visible to operators. The generator now uses two line applicability rules: annual seat Products recur yearly from their renewal-month anchor, and all other Products keep the existing date-overlap behavior.

Products

Products remain the main billing catalogue.

Required product data:

  • Product_Code
  • Default_Invoice_Label
  • Unit_Price
  • Xero_Account_Code

Yearly Seat Products

The yearly seat Products are the exact recurring annual set:

MSP-SEAT-ANNUAL-SB
MSP-SEAT-ANNUAL-L1
MSP-SEAT-ANNUAL-L2
MSP-SEAT-ANNUAL-L3

Reason: these Products are seat lines, so quantity is calculated from billing-eligible MSP Seats for the renewal invoice month, but their invoice-line applicability is yearly recurrence rather than simple date overlap.

The annual seat Product price should be the yearly seat price after any standard annual discount. If the discount is client-specific, leave the Product default as the normal annual price and use Unit_Price_Override on that plan's invoice line.

Prorate Product

The prorate product should not start with MSP-SEAT-.

Recommended Product code:

MSP-PRORATE-SEAT

Reason: MSP-SEAT- products currently use calculated active-seat quantity. A prorate charge is for a specific seat addition and should keep the invoice-line Quantity, usually 1.

Yearly Seat Renewal Operation

When a plan bills seats yearly:

  1. Confirm the plan has current MSP Seats with correct Billing_Start and Billing_End values.
  2. Create or update one annual seat MSP_Invoice_Lines record.
  3. Link it to the yearly seat Product, such as MSP-SEAT-ANNUAL-L2.
  4. Set Start_Date to the first day of the first renewal invoice month. This date is the renewal anchor, so avoid partial-month values such as 2026-07-15.
  5. Leave End_Date blank to renew every 12 months. Fill End_Date only when the recurring annual charge should stop.
  6. Leave Quantity blank or use it only as a reference; the generated quantity comes from billing-eligible MSP Seats in the renewal invoice month.
  7. Set Description_Override to a base label if needed; the generator appends the covered annual period. Example generated description:
Annual managed service seats
Covered period: 2026-07-01 to 2027-06-30
  1. Generate or refresh the MSP invoice while the linked Xero invoice is still DRAFT.

The generated invoice is the historical record. The recurring annual invoice line remains in CRM as the operational evidence for why each yearly charge appears.

Mid-Period Seat Prorate Operation

When a new seat is added during an annual seat period:

  1. Create the MSP Seat and set its Billing_Start to the date it becomes billable.
  2. Confirm the seat is linked to the correct Contact and Managed Services Plan.
  3. Create a one-off MSP_Invoice_Lines record for the prorate.
  4. Link it to MSP-PRORATE-SEAT.
  5. Set Quantity to 1.
  6. Set Unit_Price_Override to the calculated prorate amount.
  7. Set Start_Date to the month the prorate should be invoiced.
  8. Set End_Date to the last day of that same month so it only appears once.
  9. Set Description_Override to identify the person and covered period, for example:
Prorated annual seat charge for Jane Smith, 2026-10-15 to 2027-06-30
  1. Generate or refresh the MSP invoice while the linked Xero invoice is still DRAFT.

The prorate invoice line is the simple charge memory. Before creating a prorate line, operators should search existing MSP Invoice Lines for the same person and covered period to avoid double charging.

Prorate Calculation

Use monthly proration unless the commercial agreement requires daily proration.

Recommended simple formula:

annual seat price * remaining full months / 12

If the customer should be charged for the current partial month, count that as a full remaining month. If the customer should not be charged until the next month, start the remaining-month count from the next month.

Daily proration can be used by exception:

annual seat price * remaining days in covered period / total days in covered period

Devices

Devices remain purely invoice-line based for now.

Use the normal device Product and set Quantity directly on the MSP_Invoice_Lines record. Do not introduce a device registry or dynamic device count for this stage.

When device details need to appear on the invoice, include them in Description_Override.

Controls

  • Do not add an MSP billing run module for this stage.
  • Use Start_Date as the annual seat renewal-month anchor. It must be the first day of the invoice month.
  • Leave annual seat End_Date blank for ongoing yearly renewal, or fill it when the charge should stop.
  • Keep prorate lines bounded to one invoice month using Start_Date and End_Date.
  • Use MSP-SEAT- only for lines where quantity should be calculated from MSP Seats.
  • Use MSP-PRORATE-SEAT for one-off prorates where quantity should remain manually controlled.
  • Do not approve or send from the middleware. Finance still reviews and approves in Xero.
  • Once Xero is no longer DRAFT, do not refresh the generated invoice. Use a new adjustment or credit process if a correction is needed.

Later Automation Option

If manual duplicate checks become unreliable, add a lightweight Charge_Key field to MSP_Invoice_Lines.

Example:

plan_id|seat_id|annual-prorate|2026-10-15|2027-06-30

This would provide duplicate detection without introducing a full billing run module.