Concorbit HelpAll guides →

Quote Outcomes

Quote outcomes

When a deal is won, concorbit doesn't assume what should happen next, it's the operator's call. The won banner on the deal page offers a Choose outcomes button that opens a picker. Tick any combination, configure the specifics inline, and click Generate. Outcome tiles only appear for modules the tenant has enabled (no Jobs module means no "Field-service job" tile, and so on), and the server rejects any outcome whose module is off.

The six outcomes

OutcomeReadsCreates
Draft invoiceOne-time linesA single draft Invoice carrying every one-time line. Status stays draft; admin reviews + clicks send from Billing.
Pending chargesOne-time linesOne pending Charge per one-time line. Flows through the monthly billing run, not a standalone invoice.
Client subscription(s)Recurring linesOne ClientSubscription per recurring line. Annual-commitment lines raise an e-sign contract via SubscriptionCommitmentService.
ProjectDeal metadataA Project wrapping the delivery; phases, milestones, tasks follow.
TicketDeal metadata + configAn onboarding or service-delivery Ticket for the team, with a configurable subject, priority, and type.
Field-service jobDeal metadata + configA Job inheriting customer + address from the deal, with configurable title, date, type, and assigned engineer.

Invoice and Charges are mutually exclusive

A one-time line bills exactly once. Invoice turns the one-time lines into a standalone draft invoice ready to send; Charges accumulates them as pending charges that roll into the next billing run alongside time entries, expenses, and contract overages. You pick one or the other, never both, so the same line can't be billed twice. The picker greys out the other tile once you tick one, and the rule also holds across separate picker runs (if you invoiced the lines last week, you can't charge them this week).

Optional lines

A quote line marked Optional in the builder is excluded from billing, subscriptions, and purchase orders by default. When optional lines exist, the picker lists them at the top with a tickbox each, so you choose which ones the customer went ahead with. Only the ticked optional lines are included in the outcomes you generate.

Raise a purchase order

If the Procurement module is enabled, the Invoice and Charges tiles carry an also raise a purchase order tickbox. When ticked, concorbit raises purchase orders for the billed lines that are resold goods (a product with a vendor), grouped so each supplier gets its own PO. Labour and pure-service lines never generate a PO. Raising a PO is idempotent and deduplicated by line and vendor, so ticking it on a repeat run, or on the alternative billing outcome, never procures the same line twice. POs land in Procurement > Drafts for review.

Labour billed up front

If you bill quoted labour as a fixed charge (the bill quoted labour billing setting), the ticket or project created from this win flow is flagged so its logged time does not bill again, since the labour was already charged. Other charges added to that ticket (products, manual charges) still bill as normal. With the setting off (the default) the ticket bills its logged time as usual. If genuinely out-of-scope work happens on that ticket, an engineer can still tick a time entry as billable to charge it; the explicit choice overrides the pre-billed default.

Idempotency

Each outcome is stamped on the deal's deal_outcomes JSONB column when it's generated. The picker reads this on open and greys out anything already created, so re-running it simply adds the missing outcomes rather than duplicating. A second visit to the picker a week later will let you add, say, a Job to a deal that was already invoiced, without creating a second invoice.

Concurrent picker submissions (two dispatchers click Generate at once) serialise cleanly: DealOutcomeService::generate takes a row-level lock on the deal inside its transaction.

What changed from the previous flow

Before this (S239 and earlier), the system auto-created an invoice + subscription + onboarding ticket the moment a quote was accepted, via a hard-coded pipeline triggered by the DealObserver. That's gone. Now:

  • The observer only stamps acceptance_processed_at so analytics + webhooks see the transition

  • Outcome creation is deliberate and per-deal

  • Tenants who want automation back can wire it through the canvas automation steps (CreateInvoiceStep, CreateSubscriptionStep, CreateTicketStep, CreateProjectStep, CreatePurchaseOrderStep, CreateChargeStep) from a deal_won trigger, opt-in per-tenant, not platform default.

Archiving the deal

Once the outcomes are recorded, archive the deal (the Archive button on the won banner) so it leaves the active pipeline and moves to the Archived list, reachable from the pipeline. Archiving keeps the deal in won-revenue reporting; it just takes it off the active board. Reopening a deal (moving it back to an active stage) restores it to the pipeline. Archiving is a deliberate step, not automatic, so you can run the picker more than once to add outcomes before archiving.

Permissions

Any user with crm.edit can open the outcome picker (it's the same permission that lets you win a deal). The money outcomes additionally require their own capability so a CRM-only operator can't mint billing or procurement records:

  • Draft invoice / Pending charges → billing.create

  • Client subscriptions → subscriptions.create

  • Raise a purchase order → procurement.create

Archiving and restoring a deal (and viewing the Archived list) require crm.deals.archive. Each created outcome then operates under its own module's permissions for subsequent actions.

Not yet shipped

  • Picker on proposals (proposals wrap deals; acceptance via proposal e-sign still uses the legacy ProposalAcceptanceService path). Planned as a follow-up so the picker becomes the single path regardless of acceptance surface.

  • Line-level outcome routing ("use charges for these lines, invoice for those"); the picker currently treats one-time lines as a set per outcome.

  • Partial catch-up: editing quote lines after the first outcome generation and re-running the picker won't create charges for newly-added lines. Edit quote lines before generating outcomes, or generate the additional charges manually.