Concorbit HelpAll guides →

PPM Contracts (Recurring Maintenance)

PPM contracts (recurring maintenance)

A PPM contract (planned preventative maintenance) commits a customer to regular service visits on a defined cadence: annual gas safety checks, quarterly AC service, monthly fire-extinguisher walkarounds, biannual legionella inspections. concorbit generates the visit Jobs automatically ahead of each due date, inheriting the contract's customer, address, job type, and preferred technician.

Manage them at Jobs > PPM contracts.

Contract fields

FieldWhat it drives
CustomerThe Job created for each visit is assigned to this company
NameHuman-facing label, e.g. "Annual boiler service"
Frequencyweekly / monthly / quarterly / biannually / annually (defines the visit cadence)
Start dateFirst visit due
End date / PerpetualWhen the contract expires. Perpetual contracts generate visits indefinitely until cancelled
Job typeFeeds into the generated Job: drives SLA targets, form template, callout fee (see Jobs settings)
Preferred technicianDefault assignee on generated Jobs (dispatcher can override per-visit)
Annual priceInformational, used by JCP to compare contract revenue vs labour cost on generated visits
SLA (min)Target completion time; flagged in reporting if visits routinely overrun
Generate N days aheadHow far ahead of next_visit_due the daily scheduler mints the Job. Default 7, so the Monday cron fires visits for the following Monday. 0 = same-day generation

Visit auto-generation

The concorbit:generate-ppm-jobs command runs daily at 06:00. It scans every active contract whose next_visit_due <= today + auto_dispatch_days_before and:

  1. Atomically claims the visit slot via a conditional UPDATE ... WHERE next_visit_due = <current value>, so two workers running the command can't both mint a visit.

  2. Creates a Job via JobService::create with the contract's customer, address (copied from the Company), job type, preferred technician, and scheduled date = next_visit_due at 09:00.

  3. Advances next_visit_due to the next occurrence based on frequency (+1 week, +1 month, +3 months, +6 months, +1 year).

  4. Stamps field_jobs.ppm_contract_id so the Job links back: the job detail page shows a clickable PPM badge in the header, and the contract detail page lists every generated visit.

If the scheduler somehow fails for a day, the next day's run picks up any still-in-window contracts, so no visit gets permanently skipped because of a transient issue.

Pause / resume / cancel

  • Pause: visit generation stops immediately. next_visit_due isn't advanced while paused, so visits simply don't generate until you resume.

  • Resume: back to active. The next visit generates on the next scheduler run if the due date is within the dispatch window.

  • Cancel: terminal. The daily scheduler skips cancelled contracts. Takes an optional reason logged on the contract for audit.

  • Renew: extend the end_date + flip status back to active. Useful for contracts that were allowed to expire before the customer signed a renewal.

Billing

V1 ships contract storage + visit generation. Billing automation is intentionally decoupled:

  • Per-visit billing (most common for MSPs): each generated visit runs through the normal Jobs invoice flow: time entries create Charges via the JCP pipeline, and the dispatcher generates a per-visit invoice.

  • Annual up-front billing: link the contract to a ClientSubscription via the subscription_id field. The Subscription handles the recurring invoice independently of visit generation. V1 requires manual linking; an auto-create flow is a follow-up session.

Contract revenue flows into the JCP dashboard's Field Jobs tab via the generated visits' Charges.

Reporting

The contract detail page shows visit history with dates and completion state. Platform-wide PPM reporting (renewal rate, churn, on-time service rate, most profitable contract types) is a follow-up session. For now, the JCP dashboard's Field Jobs tab provides the underlying per-visit profitability numbers.

Permissions

  • jobs.contracts.view: read the contract list + detail

  • jobs.contracts.manage: create, edit, pause, resume, cancel, renew

Not yet shipped

  • Per-asset tracking: contracts today cover a single service definition. Landing ppm_contract_assets (boilers, extinguishers, AC units, each with its own service history) is a follow-up.

  • Auto-create billing subscription: today you link a Subscription manually in the contract's DB record.

  • Renewal automation: 60-day-ahead auto-creation of a renewal quote + email is planned.