Skip to the lesson
CivOps AI Academy · M18Field Service: the Installed Base, Contracts and SLAs, Dispatch, Offline Technicians and Van Stock
0%

Chapter 1 · Purpose, scope and standards

Service after delivery: the installed base and cover

A sale is not the end of the customer's relationship with the equipment. Field service management keeps track of what you delivered, what it is covered by, and who must go and fix it. This chapter draws the edge of the module and builds the first idea it depends on: an installed base that starts from the serials you shipped.

20 min10 capabilities3 tiers: MVP, STD, BICPitfall 1: assets without serials

By the end of this chapter you can

  • Say what the module does, and what it leaves to M19, M05 and accounting.
  • Explain how an installed asset is created from a shipped serial, with its warranty dates.
  • Apply the entitlement check: covered by warranty, covered by contract, or billable.

What the module does

Field service management (FSM) is the work of servicing the installed base at customer sites: the pumps, drives and other equipment you delivered and still look after. The module holds the installed assets and their warranties, the service contracts and their service level agreements (SLAs: the promised time to respond and to resolve), the work orders that come from cases or preventive plans, the scheduling and dispatch of technicians by skill, territory and travel, an offline technician app, van stock, and the claims that recover the cost of failed parts from suppliers.

The edge of field serviceInside: installed base and sites, contracts and SLAs, work orders, dispatch board, technician app, van stock and parts, billing hand-off and warranty claims. Outside, each with its owner: case intake in M19, invoicing and AR in accounting, internal plant maintenance in M05. INSIDE FIELD SERVICE (M18)Installed base, sitesContracts and SLAsWork ordersDispatch boardTech app (offline)Van stock and partsBilling hand-offWarranty claimsOUTSIDE, WITH ITS OWNERCase intake, support chatowned by M19Invoicing and ARowned by accountingInternal plant maintenanceowned by M05
The edge of the module. Taking the customer's call is M19; the invoice and accounts receivable stay in accounting; the plant's own machines are M05. Field service starts when a job at a customer's site is needed and ends when billable lines are handed on.

This is the family of tools sold as Salesforce Field Service, Microsoft Dynamics 365 Field Service, ServiceTitan, PTC ServiceMax, IFS FSM, Jobber, Housecall Pro and FieldEdge. Vendor training sites such as Salesforce's Trailhead explain their words for the same ideas: service territories, work orders, appointments and mobile workers [3].

The standards behind it

StandardWhat it asks of a service businessWhere the module meets it
ISO 9001:2015, clause 8.5.5Post-delivery activities: meet the requirements for what happens after delivery, such as warranty provisions and maintenance service [1]Installed assets with warranty dates, contracts, work orders, signed service reports
ISO 10002:2018Guidelines for handling complaints [2]A job keeps its link to the case that caused it (case_ref), so a complaint and the visit it led to can be followed together
ITIL 4 service level managementDefine service levels with the customer, then measure themResponse and resolution hours on the contract; SLA attainment measured from the job's own times

You do not need to buy the standards for this course; the pages cited are the official catalogue entries.

Ten capabilities, three tiers

Each capability carries a tier. MVP must exist for a small or mid-size business to cancel its field service subscription with confidence. STD is parity with mainstream tools, what a buyer expects in a demo. BIC marks a best-in-class difference. MVP and STD are required for the course; BIC is advanced work.

CapabilityWhat it doesTier
Installed baseCustomer sites; installed assets by serial with parent and child, warranty dates and service history, linked to the sales order that shipped themMVP
Contracts and entitlementsWarranty, maintenance and full-service contracts with SLAs; covered assets; an entitlement check at bookingSTD
Work ordersInstall, repair, preventive, inspection, warranty and upgrade jobs; from cases, PM plans or IoT alerts; priority and SLA dueMVP
Scheduling and dispatchA dispatch board; skills, territory, availability and travel-time-aware suggestionsMVP
Technician app (offline)Job details and history, checklists, parts used, time and travel, photos, customer signature, service reportMVP
Van stock and partsVehicle storage locations, replenishment, parts requests, returnsSTD
Customer communicationAppointment confirmations, on-my-way notices, portal bookingSTD
Billing hand-offBillable lines (labour, parts, travel), warranty coverage, export to accountingSTD
Warranty recoveryClaims to suppliers for failed componentsBIC
AnalyticsFirst-time fix, SLA attainment, mean time to repair, technician utilisation, repeat visitsSTD

The installed base starts at the shipment

An installed asset is a customer-owned product in service. Each one keeps its serial number as text, a link to the serial unit that shipped (serial_unit_id), a soft link to the sales order (sales_order_ref), the service site, and optionally a parent asset: a drive module hangs under the pump it belongs to. Warranty start and end sit on the asset. Both empty means no warranty.

From a shipment to an installed assetSales order SO-1 ships a pump unit and a drive module as two serial shipments; each becomes an installed asset, the drive module a child of the pump, with warranty dates counted from the shipping date. A pitfall box warns against an installed base typed in by hand. Sales order SO-1shipped 2025-09-01ship-to address: Site AShipment: SN-1001Pump unit,serial controlledShipment: SN-1001-DDrive module,serial controlledAsset SN-1001warranty 2025-09-01to 2026-08-31Asset SN-1001-Dchild of SN-1001,same datesINSTALLED ASSETSwarranty_end = shipping date + the item's warranty months − 1 day (12 months from 2025-09-01 ends 2026-08-31)Pitfall 1: an installed base disconnected from shipped serialsTyped by hand, an asset has no order and no serial unit, so nobody can prove its warranty
One order, two assets. Sales order SO-1 shipped a pump unit and its drive module. Each serial becomes an installed asset, the drive a child of the pump, and the warranty runs from the shipping date for the item's warranty months, less one day.

Contracts and the entitlement check

A service contract is one of five types (warranty, maintenance, full service, time and materials, subscription) with a start and end date, response and resolution hours, and an entitlements block saying which line types it covers and when its clock runs. Contract rows list the assets they cover. A contract in draft never covers anything; neither does one that has expired or been cancelled.

The entitlement checkA job is booked for an asset on a date. If warranty dates are set and include the date, it is covered by warranty and not billable. Otherwise, if an active contract lists the asset and includes the date, it is covered by contract and not billable. Otherwise it is not covered and billable. Job bookedWarranty dates set andthe date between them(both ends included)?yesUnder warranty: not billablenoAn active contract liststhe asset, and its startand end include the date?yesUnder contract: not billablenoNot covered: billablecontract_id is anyactive contractcovering the date,even when thewarranty applies:it still sets the SLA
The entitlement check. Run when a job is booked, as of the booking date. Warranty is tried first, then contract. Only an asset covered by neither is billable.

The lab seed used through the module (made-up customers, serials and dates, loaded in Session 2) gives a table to check any version of the rule against:

Asset and dateResultWhy
SN-1001 on 2026-06-18Warranty, contract C-100, not billableWarranty runs 2025-09-01 to 2026-08-31, and C-100 is active and lists it
SN-1002 on 2026-06-18Contract C-100, not billableIts warranty ended 2025-01-14, but the contract covers it
SN-1003 on 2026-06-11Warranty, not billableThe last day of its warranty counts: both ends are included
SN-1003 on 2026-06-12Not covered, billableWarranty over, and contract C-200 has expired
SN-1004 on 2026-06-18Not covered, billableNo warranty dates, and C-300 is only a draft

A job booked for an asset that is out of warranty and out of contract is flagged billable at booking. That is the first thing the module is checked against at the end of the course.

Knowledge check

An asset's warranty ended yesterday, and the only contract that ever listed it has expired. A job is booked today. What does the entitlement check return?

Knowledge check

Why does an installed asset keep serial_unit_id and sales_order_ref as well as the serial text?

Exercise · Test your installed base against your shipped serials40 minutes

You need: Your field service tool's asset list, your shipping records (packing lists, sales orders, invoices or your ERP), and a spreadsheet or text file

This is the first pitfall of the module, tested on your own data. Customer names, addresses and gate codes are personal data: write serials and order numbers only, and do not paste anything with a customer's details into a chat or an AI prompt.

Outcome: A short table of 10 assets with a match result each, the matched share, and three entitlement answers beside the tool's own.

References

  1. ISO 9001:2015, Quality management systems: requirements (clause 8.5.5, post-delivery activities). https://www.iso.org/standard/62085.html
  2. ISO 10002:2018, Customer satisfaction: guidelines for complaints handling in organizations. https://www.iso.org/standard/71580.html
  3. Salesforce Trailhead: Field Service basics. https://trailhead.salesforce.com/content/learn/modules/field_service_basics/field_service_basics_intro

Chapter 2 · The data model

The thirteen tables

How service data is stored decides what you can ever ask of it. Thirteen fsm_ tables sit on the shared spine, with rules the database itself holds, so a wrong state cannot be saved even by a bug.

20 min13 tables10 work order statuses4 roles, 4 screens

By the end of this chapter you can

  • Name the thirteen fsm_ tables and say what each one holds.
  • Name the rules the database holds itself, and the JSON the rules read.
  • Match each role to what it does and the screen it uses.

Thirteen tables on the shared spine

Every table starts with the standard columns every platform table has: id, tenant_id, created_at, created_by, updated_at, updated_by, row_version, archived_at and ext (a JSON column for extra values). The module has no customer list, parts list, people table or stock location of its own: it points at core_party, core_address, core_item, core_serial_unit, core_person, core_storage_location, core_calendar and core_attachment.

The thirteen tablesThirteen fsm_ tables in four groups: where and what (territory, service site, installed asset), cover (contract, contract asset, PM plan), the work (work order, appointment, service line, customer signoff) and people and parts (technician, parts request, warranty claim), all resting on the shared core_ tables. WHERE AND WHATfsm_territoryfsm_service_sitefsm_installed_assetCOVERfsm_service_contractfsm_contract_assetfsm_service_pm_planTHE WORKfsm_service_work_orderfsm_appointmentfsm_service_linefsm_customer_signoffPEOPLE AND PARTSfsm_technicianfsm_parts_requestfsm_warranty_claimTHE SPINE · USED, NEVER REPEATEDcore_party · core_address · core_item · core_serial_unitcore_person · core_storage_location · core_attachment
The thirteen tables. Where and what: territories, sites and installed assets. Cover: contracts, the assets they cover, and preventive plans. The work: orders, visits, lines and sign-offs. People and parts: technicians, parts requests and warranty claims.

The table below is the full definition: every column the module adds to the standard ones, with its type and whether it is required (req). Types are as in the specification: text, int, num (a decimal number), bool, date, ts (a timestamp with time zone), money, qty (a quantity), uuid and json; an arrow means a foreign key to that table. A soft link is a uuid with no foreign key, because the other module may not be installed yet. Vendors may use other names for the same records; Salesforce's Field Service basics module, for one, speaks of work orders and service appointments [1], and [2] is an overview of that product. It is what your agent builds from in Session 2.

TableHoldsColumns: type, req = required
fsm_territoryA service territoryname text req, unique; geo_polygon json; base_site_id → core_site; manager_id → core_person
fsm_service_siteA customer location servedparty_id → core_party req; address_id → core_address req; name text req; access_notes text; timezone text; territory_id → fsm_territory
fsm_installed_assetA customer-owned product in serviceparty_id → core_party req; service_site_id → fsm_service_site; item_id → core_item req; serial_no text; serial_unit_id → core_serial_unit; parent_installed_asset_id → fsm_installed_asset; installed_at date; warranty_start date; warranty_end date; status text req: active, inactive, decommissioned or returned; sales_order_ref uuid soft link to inv_sales_order
fsm_service_contractA contract with its SLA and entitlementscontract_no text req, unique; party_id → core_party req; contract_type text req: warranty, maintenance, full_service, time_and_materials or subscription; start_at date req; end_at date req; sla_response_hours num; sla_resolution_hours num; entitlements json; billing_terms text; status text req: draft, active, expired or cancelled
fsm_contract_assetThe assets a contract coversservice_contract_id → fsm_service_contract req; installed_asset_id → fsm_installed_asset req
fsm_technicianA field technician's profileperson_id → core_person req, unique; territory_id → fsm_territory; home_lat num; home_lon num; vehicle_location_id → core_storage_location; calendar_id → core_calendar; active bool req
fsm_service_work_orderA field service jobswo_no text req, unique; party_id → core_party req; service_site_id → fsm_service_site req; installed_asset_id → fsm_installed_asset; service_contract_id → fsm_service_contract; case_ref uuid soft link to svc_ticket; swo_type text req: install, repair, preventive, inspection, warranty, upgrade or decommission; priority text req: p1, p2, p3 or p4; status text req: the ten below; problem_description text; resolution text; billable bool req; sla_due_at ts; first_time_fix bool
fsm_appointmentA scheduled visitservice_work_order_id → fsm_service_work_order req; technician_id → fsm_technician req; window_start ts; window_end ts; scheduled_start ts req; scheduled_end ts req; actual_arrival ts; actual_departure ts; travel_minutes num; status text req: scheduled, dispatched, en_route, on_site, completed, cancelled or no_access
fsm_service_lineA labour, part, travel or expense lineservice_work_order_id → fsm_service_work_order req; line_type text req: labor, part, travel or expense; item_id → core_item; description text; qty qty req; unit_price money; billable bool req; warranty_covered bool req; storage_location_id → core_storage_location
fsm_customer_signoffThe customer's signature and the service reportappointment_id → fsm_appointment req; signer_name text req; signed_at ts req; signature_attachment_id → core_attachment; report_attachment_id → core_attachment; satisfaction int
fsm_parts_requestA technician's parts requesttechnician_id → fsm_technician req; item_id → core_item req; qty qty req; needed_by ts; service_work_order_id → fsm_service_work_order; status text req: requested, picked, shipped, received or cancelled; fulfil_from_location_id → core_storage_location
fsm_warranty_claimA claim on a supplier for a failed partclaim_no text req, unique; service_work_order_id → fsm_service_work_order req; installed_asset_id → fsm_installed_asset; supplier_party_id → core_party req; failed_item_id → core_item; amount_claimed money; amount_recovered money; status text req: draft, submitted, approved, rejected or paid
fsm_service_pm_planA preventive service plan for an assetinstalled_asset_id → fsm_installed_asset req; service_contract_id → fsm_service_contract; interval_months int req; checklist_template_ref uuid soft link to frm_template; next_due_at date; active bool req

Money and quantities are numeric, never text or floating point, and every timestamp stores one exact instant, shown in the reader's time zone. In the lab seed, money figures are lab values, not rates.

Rules the database itself holds

  • No second asset for a serial. A partial unique index on tenant, item and serial_no (where the serial is not empty) refuses a serial installed twice. A check refuses an asset that is its own parent.
  • Numbers are unique per business. swo_no and claim_no come from a number sequence, never from the phone; contract_no is typed by the service manager and the database refuses a duplicate.
  • Dates make sense. A contract's end_at is not before its start_at; an appointment's scheduled_end is after its scheduled_start.
  • One open job per preventive plan. A partial unique index on the plan id in the job's ext applies only while the job is new, scheduled, dispatched, en_route, on_site or incomplete. A second open job for one plan is refused by the database, so a repeated run of the plan engine makes nothing new.
  • Parts move through the inventory ledger, which is append-only. A used part is a ledger row from the van (module M12 holds the ledger), and a mistake is a reversing row, never an edit. A security-definer trigger on the ledger (it runs with its owner's rights) keeps each location's balance, so no route writes a balance itself.
  • A sign-off cannot be changed. A trigger refuses any update or delete of fsm_customer_signoff, and the service report is stored as an attachment with its hash.

The JSON the rules read

JSON is a plain text format for lists and named values, so one column can hold a whole weekly timetable. Two shapes matter most. The contract's entitlements say what is covered and when the clock runs; a contract with no business_hours counts every hour. The technician's ext says what they can do and what their van should hold.

Entitlements and technician ext. Use your own zone name and part numbers.
fsm_service_contract.entitlements
{ "covers": ["labor", "travel"],
  "business_hours": {
    "timezone": "Region/City",
    "schedule": { "mon": [["08:00","17:00"]], "tue": [["08:00","17:00"]],
                  "wed": [["08:00","17:00"]], "thu": [["08:00","17:00"]],
                  "fri": [["08:00","17:00"]], "sat": [], "sun": [] },
    "holidays": ["2026-06-19"] } }

fsm_technician.ext
{ "skills": ["pump", "controls"],
  "van_min": { "PUMP-SEAL": 2, "FILTER": 2 },
  "replenish_from": "WH-1" }

Ten statuses for a work order

The ten work order statusesMain path: new, scheduled, dispatched, en_route, on_site, completed, ready_to_bill, billed. A visit that does not finish ends as incomplete, which is still open, and the dispatcher moves it back to scheduled for a revisit; a job in any open status can be cancelled. newscheduleddispatcheden_routeon_sitecompletedready_to_billbilledincompletestill open:needs a visitrevisit: back to scheduledcancelledfrom any openstatusDispatcher and technician: top and middle rows;the dispatcher books a revisit or cancels.Service manager: completed to ready_to_bill,only with the right evidence.billed: only after the export.
The status of a job. Dispatcher and technician move it along the top and middle rows. Completed becomes ready_to_bill only when the service manager releases it with the right evidence, and billed only after the export. An incomplete job goes back to scheduled for a revisit, and a job in any open status can be cancelled.

An incomplete job is still open: the visit ended without the fix and another is needed. A visit has its own status too (scheduled, dispatched, en_route, on_site, completed, cancelled, no_access), because one job can take more than one visit.

Who uses it, and on which screen

RoleWhat the role doesScreen
DispatcherBooks jobs and schedules them/service/dispatch: the dispatch board
TechnicianDoes the work, on a phone, often offline/service/work: the phone screen
Customer contactSigns off the work, sees only their own company's jobs/service/signoff: the sign-off screen
Service managerOwns contracts, territories, billing release, warranty claims and the measures/service/manage: the manager's desk

Machine accounts do the rest, each with its own login and no person behind it: one creates installed assets from shipments, one generates preventive jobs and watches SLA clocks, one posts parts to the ledger, and one, used only while the old tool's data is moved in (Sessions 5 and 6), imports it. Giving each job its own login is what lets the access matrix stay small and checkable.

Events, and personal data

PublishesConsumes
fsm.work_order.completedsvc.ticket.created, when a field visit is needed
fsm.appointment.dispatchedinv.sales_order.shipped, which creates the installed base
fsm.sla.breach_riskplm.eco.approved, for a field retrofit

Knowledge check

Which table holds the visit: the technician, the time window and the actual arrival and departure?

Knowledge check

A repeated run of the preventive engine must not create a second open job for the same plan. What makes sure of it?

References

  1. Salesforce Trailhead: Field Service basics. https://trailhead.salesforce.com/content/learn/modules/field_service_basics/field_service_basics_intro
  2. ERP Research: Salesforce Field Service overview. https://erpresearch.com/erp-add-ons/field-service/salesforce-field-service

Chapter 3 · The workflow

Book, dispatch, complete and bill

A job moves from a booking that knows what it is covered by, to a technician chosen by skill, territory and the drive, to a phone with no signal, to a van that stays honest and a line that reaches accounting. Each step has a rule you can check by hand.

30 min3 definition-of-done rulesPitfall 2: no travel timeFree way first

By the end of this chapter you can

  • Book a job with the entitlement result, and know which jobs carry an SLA.
  • Work out an SLA due time by hand from a contract's business hours and holidays.
  • Explain how dispatch counts travel, how an offline job reaches the server once, and how van stock follows the ledger.

Book the job

Booking is where cover is decided. When a dispatcher books a job for an installed asset, the server runs the entitlement check at the booking date and then sets the party and site from the asset; billable, the covering contract and covered_by (warranty, contract or none) from the check; the skills needed from the item; and the job number from the number sequence. A job asked for by a customer case keeps the case's id as case_ref, a soft link, so booking works where the help desk module is not installed.

Only repair and warranty jobs under a contract carry an SLA due time, taken from the contract's clock. Preventive, inspection, install, upgrade and decommission jobs are planned work and have none. A preventive job is made by an engine: each active plan whose next-due date has arrived, and that has no open job carrying that plan's id, books one and moves its next-due date forward from the previous due date, so a late visit does not shift the plan.

The service clock counts business hours

ITIL 4's service level management is about measuring a target the way it was agreed. A contract that promises a four-hour response during business hours does not mean four hours on the wall clock. The clock counts only hours that fall inside an opening period on a day that is not a holiday. If the job is booked outside opening hours, counting starts at the next opening. A contract with no business hours counts every hour.

The service clock counts business hoursFive day-strips to scale. Thursday 16:00 to 17:00 counts one hour; Friday is a holiday and the weekend is closed; Monday 08:00 to 11:00 counts three hours, so a 4-hour response from Thursday 16:00 is due Monday at 11:00. Response clock, contract C-100: 4 hours from Thursday 16:00 (open Monday to Friday 08:00 to 17:00; Friday 19 June is a holiday)00:0004:0008:0012:0016:0020:0024:00Thu 18 JunFri 19 JunSat 20 JunSun 21 JunMon 22 Jun1 hour countedholiday: 0weekend: 0weekend: 03 hours counteddue Monday 22 June, 11:00not Thursday 20:00countedopen hoursclosed: not counted
Four hours from Thursday 16:00, to scale. One hour counts on Thursday. Friday is a holiday and the weekend is closed. Three more hours count on Monday morning, so the response is due Monday at 11:00, not Thursday at 20:00.
ContractStartsTargetDue
C-100 (open Mon to Fri 08:00 to 17:00)Fri 12 Jun 16:004 hoursMon 15 Jun 11:00
C-100Fri 12 Jun 16:0024 hoursWed 17 Jun 13:00
C-100, Fri 19 Jun is a holidayThu 18 Jun 16:0024 hoursWed 24 Jun 13:00
C-200 (no business hours)Fri 12 Jun 16:008 hoursSat 13 Jun 00:00

Dispatch by skills, territory and travel

The dispatch board suggests a technician only if they are active, in the job's territory, and hold every skill the job needs. It then orders them by the earliest arrival: ETA = the later of (a) the window start and (b) the previous appointment's end, or the shift start, plus the travel time from the previous place. The drive is added to (b) only, never to the window start, so a technician whose drive from home ends before the window opens can arrive when it opens. On a desktop the dispatcher drags a job onto a technician; on a phone the same move is two taps, because dragging with a thumb is unreliable.

Travel time between jobsSix places on one line of longitude drawn to scale: technician homes and sites, 11, 22, 11 and 11 minutes apart within the North and South territories. For job SWO-0014 at Site A, Technician Two arrives at 09:00 and Technician One at 09:22. Latitude of each place, drawn to scale: 4 px = 1 minute of drivingTechnician Two home 41.40Site B 41.30Site A 41.10Technician One home 41.00Site C 40.60Technician Three home 40.5011 min22 min11 min11 minNorth territorySouth territoryMonday 22 June,window 09:00 to 12:00SWO-0014 at Site A, skill: pumpTechnician Two: 0 min,ETA 09:00 (their 07:00job is at Site A)Technician One: 22 min fromSite B, ETA 09:22Technician Three: not offered(South territory)Ignore travel: One and Twoboth look like 09:00Straight line at 60 km/h, 111.2 km per degree of latitude: lab values. Roads are longer than straight lines.
The drive decides. For the same job at Site A, two technicians in North look equal if travel is ignored. Counting the 22-minute drive from Site B puts Technician One at 09:22. Technician Three is in South and is not offered.

Travel time here is the straight-line distance between the previous place (the previous job's site, or the technician's home) and the job's site, from the coordinates in core_address, at 111.2 km per degree of latitude and an average of 60 km/h, rounded to whole minutes. These are lab values: replace the speed with one measured from your own drives, and remember that roads are longer than straight lines. The 111.2 km per degree is right only along the lab's single meridian; for real sites use a great-circle (haversine) distance, the shortest path between two latitude and longitude points over the earth's curve. A paid routing service gives real road times and costs whatever its provider's price page says; nothing in this course needs one, so the free way comes first. For how one vendor presents its dispatch app, see [1]. Left out: the specification marks schedule optimisation (planning many jobs at once) as best-in-class; this course suggests technicians one job at a time, and optimisation is extension work.

The technician's phone, offline

A technician is often where there is no signal. When they sign in, the phone saves the next days' jobs, sites, assets, history and the van's parts list to IndexedDB, the browser's own database on the phone, which survives closing the app [3]. Each job's moves (on my way, arrived, time, parts, photos, resolution, signature, complete) go into a queue, and every new row gets an id made on the phone with crypto.randomUUID(), so the same entry can be sent twice and a repeat adds nothing: the server calls this being idempotent.

An offline job reaching the server onceOn the phone with no signal: download jobs, work, customer signs, complete into a queue entry with its own id. On the server when the signal returns: skip an entry already seen, refuse a job cancelled meanwhile, insert the visit with prices set by the server, and post parts from the van once per line. ON THE PHONE · NO SIGNAL1 · Downloadjobs, assets,history, van2 · Work offlineown arrival anddeparture times3 · Customer signssignature keptas a file4 · Completequeue entry,id made hereON THE SERVER · SIGNAL BACKSeen this entry?yes: add nothingJob cancelled?conflict: refuse,keep and show itInsert the visitphone times kept,received time notedPost van partsone ledger rowper linesync when the signal returns
Once, however many times it is sent. The phone keeps its own arrival and departure times; the server notes when it received them. If the dispatcher cancelled the job in the meantime, the entry is refused with a conflict, kept in the queue and shown to the technician.

The server, never the phone, sets each line's price from the item's list price, and decides billable and warranty_covered from the job. On completion it builds the service report, a printable page listing the work, parts and times. An HTML file that any browser prints to PDF is the free way. The customer's signature is captured on the phone, uploaded with the entry, and stored in a sign-off row that cannot be changed. A job with one visit is marked first-time fix.

Van stock follows the ledger

A technician's van is a storage location. When a part line is accepted, a second route posts one ledger row from the van, tagged with the line, and writes the ledger id back onto the line, so a repeat posts nothing. A used part is a fact, so the route never refuses it for lack of stock; replenishment shows the shortfall instead. For each technician and part, if the van's balance is below its minimum and no request is open, one parts request is made for the difference.

Pump seals on the van and in the warehouseThree stages drawn to scale. At the start VAN-2 holds 1 pump seal and WH-1 holds 20. After SWO-0010 uses one, VAN-2 holds 0. After the parts request for 2 is received, VAN-2 holds 2 and WH-1 holds 18, each move a row in the ledger. Seed: opening stockVAN-2WH-1120Opening stock: adjustment rows in theledger; a trigger keeps the balanceAfter SWO-0010 uses one sealVAN-2WH-1020Ledger: the part leaves VAN-2 for the jobref_type fsm_service_line, once per lineAfter the request is receivedVAN-2WH-1218Request for 2 (minimum 2, balance 0):transfer_out WH-1 + transfer_in VAN-2,one transactionTHE REQUEST, IN ORDERrequestedpickedshippedreceivedReturnsunused parts, or a returnto a supplier:reversing entries
Parts follow the ledger. A request moves requested, picked, shipped, received. Receiving posts two rows in one transaction, out of the warehouse and into the van. Parts returned unused, or sent back to a supplier on a return authorisation, are reversing entries.

Billing hand-off and warranty recovery

LineBillable?Rule
Any line on a job covered by warrantyNowarranty_covered is true
A line whose type is in the contract's coversNoCovered by contract
Any line on a job the manager waivedNoext.billing_waived true on the job, with the reason in a core_comment; applies to all lines
Any other lineYesPriced by the server from the item's list price

The service manager's release to billing moves a completed job to ready_to_bill only when it has at least one billable line, a sign-off, and a ledger id on every part line. A CSV file of the billable lines, which the accounting package imports, is the free way to hand them on, and it moves those jobs to billed so a second export returns nothing. For one vendor's list of the features buyers look for in field service software, see [2]. A paid accounting connection would cost what its provider charges. A job's own billable flag only records that the work was not covered when it was booked; the lines decide the bill, so a job covered by a contract can still carry one billable part the contract does not cover.

Warranty recovery is a BIC capability. When a part failed under warranty, the service manager can claim its cost from the supplier: one claim per job and failed item, the amount claimed is the sum of the job's warranty-covered part lines, and the amount recovered stays empty until the claim is paid. Whether a supplier also pays labour depends on its written warranty terms: read them, do not guess.

Exercise · Write the three rules and check them by hand35 minutes

You need: Your AI coding agent, your repository with the Foundation schema, and the lab seed from Session 2 (or a table you draw on paper)

The point is to write each rule down in words before any code, so you can tell an agent's answer is right. Every function takes the time as a parameter; none reads today's date.

Outcome: docs/fsm-rules.md with the three rules, three tested functions, and every table row in this chapter and chapter 1 passing.

Knowledge check

Contract C-100 counts Monday to Friday, 08:00 to 17:00, with no holiday that week. A 4-hour response starts on Friday at 16:00. When is it due?

Knowledge check

What is the free way to hand billable lines to accounting?

References

  1. ServiceTitan: field service dispatch app. https://servicetitan.com/blog/field-service-dispatch-app
  2. FieldProxy: seven must-have features in modern field service management software. https://www.fieldproxy.ai/resources/blog/7-must-have-features-in-modern-field-service-management-software-d1-38
  3. W3C: Indexed Database API (the browser database used for offline work). https://www.w3.org/TR/IndexedDB/

Chapter 4 · From the old tool to the new

Measure it, move in, retire the old tool

The module is finished when four measures can be counted from its own data, the old tool's history is in, and the old subscription is cancelled with the saving written down. Each step has a number you can check against a count by hand.

20 min4 measures6 imports in orderCosts: none to run, savings from invoices

By the end of this chapter you can

  • Define first-time fix, SLA attainment, utilisation and repeat visits, and count them from the seed.
  • Import the old tool's data in order, and prove nothing was lost or added twice.
  • Run old and new side by side, cut over, and record the saving from real invoices.

Four measures

MeasureSpecification's definitionHow the module counts it
First-time fix rateJobs resolved on first visit divided by jobsJobs that have reached completed (status completed, ready_to_bill or billed) with first_time_fix true, divided by those jobs where it is filled in
SLA attainmentJobs meeting response and resolution SLA divided by jobs under SLAJobs that have reached completed (status completed, ready_to_bill or billed) whose first arrival is not after the response due time and whose last departure is not after sla_due_at, divided by those jobs that have an sla_due_at
Technician utilisationWrench plus travel time divided by available timeOn-site minutes plus travel minutes of completed visits, divided by the shift's minutes less the paid break (a lab definition: a paid break is normally paid time, so write your own choice down)
Repeat visit rateRepeat visits within 30 days for the same assetRepair and warranty jobs that have reached completed (status completed, ready_to_bill or billed) and begin within 30 days after an earlier such job on the same asset, divided by repair and warranty jobs that have reached completed

The specification names first-time fix, SLA attainment, mean time to repair, technician utilisation and repeat visits as the module's analytics; for how vendors describe their features, see [1] and [2]. A name says little without its definition, so this module writes each one down. A job billed since it was completed still counts: test the status for completed, ready_to_bill or billed, not completed alone, or the measures drop every job that has been handed to accounting. Left out: mean time to repair is not one of this course's four measures; it is extension work, and if you add it, write its definition beside it the same way.

The four measures from the seedHorizontal bars to scale: first-time fix 75.0 percent, SLA attainment 50.0 percent, repeat visits 16.7 percent and technician utilisation 46.3 percent. 0%25%50%75%100%First-time fix6 of 8 completed jobs75.0%SLA attainment2 of 4 jobs with an SLA50.0%Repeat visits1 of 6 repair jobs16.7%Utilisation250 of 540 minutes, one day46.3%Seed hand count, 1 April to 11 June 2026. Utilisation: Technician One on Tuesday 2 June, 195 on site plus 55 travel.
The seed, counted by hand. Eight completed jobs, 1 April to 11 June 2026: two needed a second visit (6 of 8). Of four jobs with an SLA, two met both targets. One of six repair jobs followed an earlier one within 30 days. Technician One's Tuesday: 250 of 540 minutes.

Move in, in order

The specification's advice is to export customers, sites, assets, contracts and open jobs, and to import closed jobs as history for first-time-fix baselines. This module moves them in six imports: sites with their customers, assets, contracts, preventive plans, open jobs and closed jobs. The order matters, because each kind depends on the one before it.

Moving out of the old toolSix imports in order: sites and territories, assets by shipped serial, contracts, PM plans, open jobs and closed jobs as history. Then counts reconcile, pre-cutover checks, a side-by-side week, cut-over, and cancelling with the saving recorded. 1 · Siteswith theirterritories2 · Assetsby shippedserial3 · Contractswith anend date4 · PM plansnext duedates5 · Open jobskeep olddue times6 · Closedjobs, ashistory onlyCounts reconcilerows: imported,matched orskippedCut-over checkseach check is 0or has a decisionSide by sideone week, bothtools running,gaps explainedCut overold toolread-only, deltaimport runCancel, recordfinal exportkept; savingfrom invoices
Six imports, then the road to cut-over. Parents before children inside each kind; closed jobs last, because they depend on everything else.
  • Every imported row keeps where it came from (the old tool's name and the old id in ext), and the script finds rows by them, so a re-run or a resumed import adds nothing twice. A dry run prints the counts first.
  • Assets are matched to shipped serials with three outcomes: matched (the unit and order are taken from the shipment), near match (a person decides), and no match (imported with the serial as text, and listed). Write the matched share. This is pitfall 1, handled on real data.
  • Every contract needs an end date. A contract the old tool rolls over with no end gets its next renewal date and is listed for the service manager. One whose end is past is imported as expired. A contract that counts calendar hours gets no business hours.
  • Open jobs keep the old number (as OLD- followed by it, so it can never clash with the sequence) and the old tool's due times. Then run the entitlement check as of each job's booking date and list the jobs where the new answer differs from the old billable flag; decide each one and record the reason.
  • Closed jobs arrive in their final state as history: lines marked as history with the old prices, first-time fix true for one visit and false for more, and left empty where the export has no visit data. Nothing fires, nothing is posted to the ledger, and nothing is billed a second time. Import enough earlier history, at least 30 days before the period you measure, for repeat visits.
  • Counts reconcile. For each kind, rows in the export equal rows imported plus matched plus skipped, and every skipped row is listed and explained.

Before cut-over, a read-only check file returns zero for: installed assets with no service site; active contracts whose end date is already past; assets of serial-controlled items with no serial unit (each needs a written decision); open jobs with no site; scheduled or dispatched jobs with no appointment; and two assets with one item and serial. Fix the data, not the check.

Cut over and retire

  1. Run side by side for at least a week. The dispatcher books every job of one pilot territory in both tools, and the technicians complete it in both. Put the old tool's figures and the platform's side by side and explain every difference.
  2. Check what the module must keep: every job for an asset with no cover is billable (or has a manager's reason); every completed part line has a ledger id and each van's balance equals its ledger; three SLA due times worked out by hand match the stored ones; every completed job has a sign-off or a written reason, and a sign-off cannot be changed; every asset has a site.
  3. Test a phone in airplane mode against the seed and the synthetic batch, and force a second send: every count must stay the same.
  4. Cut over on a date you wrote down, with a rollback plan, the final delta import, and the old tool read-only. When the delta counts reconcile, remove the import login and its rows from the matrix. Customers should be told in plain words that any texts or emails the old tool sent will stop, because this module sends none.
  5. Take a final full export first, then cancel or write the date the plan ends. Read the vendor's terms (notice period, renewal date, how long you can still export) on your own account page. The saving is the old cost, plus the helper tools you drop, less what the module costs on services you already pay for, worked out from real invoices and never from estimates.
Exercise · Baseline your old tool's first-time fix35 minutes

You need: Your field service tool's reports or a small export of closed jobs, and a spreadsheet

This produces the baseline the new platform is compared with in Session 6. Work from ten recently closed jobs; do not copy customer names or addresses into the sheet, only job numbers, asset serials and dates.

Outcome: A first-time-fix and repeat-visit baseline from 10 real jobs, beside the old tool's figure, with every difference explained.

Knowledge check

In a period 10 jobs were completed. 4 of them carried an SLA, and 3 of those met both targets. What is SLA attainment?

Knowledge check

Why are closed jobs from the old tool imported as history?

References

  1. FieldProxy: seven must-have features in modern field service management software. https://www.fieldproxy.ai/resources/blog/7-must-have-features-in-modern-field-service-management-software-d1-38
  2. ERP Research: Salesforce Field Service overview. https://erpresearch.com/erp-add-ons/field-service/salesforce-field-service

Chapter 5 · 12 questions · 80% passes

Final assessment

Twelve questions across the element. Score 80% (10 of 12) to pass. Your LMS records your score and each answer; you can review the chapters and try again.

15 min12 questions≈ 15 minutesRetake allowed

Choose one answer for each question, then submit. You will see the right answer and why for every question.

1. Which module owns taking the customer's call and the support conversation, which field service leaves out?
2. An asset's warranty ran to 2026-06-11 and no active contract lists it. A job is booked on 2026-06-12. What is the result?
3. What does pitfall 1, an installed base disconnected from shipped serials, cost the business?
4. Contract C-100 counts Monday to Friday, 08:00 to 17:00, and Friday is a holiday. A 4-hour response starts on Thursday at 16:00. When is it due?
5. How does the dispatch board work out a technician's earliest arrival at a job?
6. A phone sends the same completed-job entry twice because the first confirmation was lost. What must the server do?
7. Who decides the unit price and the billable flag on a service line?
8. How does a part used on a job reduce the van's stock?
9. What is SLA attainment?
10. In a period 8 jobs were completed and 2 of them needed a second visit. What is the first-time fix rate?
11. How are closed jobs from the old tool imported?
12. What is the amount_claimed on a supplier warranty claim for a job?