A repair shop workflow on Odoo 18, from inspection to invoice
A design pattern for running an automobile and heavy-truck repair shop on Odoo 18 Community with OCA modules, flat-rate billing and a few small glue modules.
On this page
This document describes a repair-order flow for an automobile and heavy-truck repair shop on Odoo 18 Community: intake, inspection, a customer-signed estimate, parts, technician time, and an invoice that matches the approved estimate. Use it when you are designing a shop system and want to know which parts Odoo and the OCA already give you, and where you have to write glue.
See Odoo Community and OCA Overview for how Avunu picks modules, and Migrating from QuickBooks to Odoo for the accounting cutover this design assumes.
This is a design pattern. It describes a system we designed and built, not a product you can install in one step, and it makes no claim about any particular shop running it.
The flow at a glance
The repair order is the anchor for the whole job. Everything else hangs off it, and the sale order is the single billing document.
Customer reports an issue
Repair order (draft): vehicle, concern, odometer, scheduled date
Diagnostic template adds the diagnostic fee as a service line
Inspection: severity grades, measurements, photos
Estimate: sale order with firm lines and optional recommended jobs
Customer signs on the portal
Confirm repair: parts reserve, or RFQs are raised
Technicians run timers, record Done quantities
End repair: invoice from the sale order, then paymentInvoicing only flows repair order, then sale order, then invoice. Core Odoo repair has no direct invoicing, so the sale order is both the estimate and the bill. Design everything else around that fact.
Key design decisions
These choices shape every later step. Make them deliberately before you configure anything.
| Decision | Choice |
|---|---|
| Labor billing | Flat rate. The invoice equals the approved estimate, using the "Ordered quantities" invoice policy. Timesheets are internal cost data only. |
| Intake | A draft repair order created directly. No CRM or helpdesk step first. |
| Chart of accounts | The OCA l10n_us_gaap chart, with income split into parts, labor, diagnostic fees and sublet. |
| Inventory valuation | Automated FIFO, set on the product category. |
| Accounting history | A clean cutover from the previous system: chart, partners, active products, open receivables and payables, and an opening trial balance. History stays in the old system. |
Why flat rate
Flat-rate billing means the customer pays the book hours on the estimate, however long the wrench time takes. That makes the approved estimate the invoice, which removes a whole class of disputes at pickup.
Technician timesheets still matter, but only for measuring efficiency against book hours. They never change what the customer owes.
What comes from where
A useful way to scope the build is to sort every capability into one of three buckets.
Core Odoo
The repair order, parts lines, and the Create Quotation step that turns a repair order into a sale order.
Portal online signature on quotations, and optional products on a quote.
The
purchase_repairlink between repair orders and purchase orders, with a Purchase Orders smart button on the repair order.The parts availability indicator on the repair order, which is part of core repair.
OCA modules
The OCA repair suite:
repair_order_templatefor job templates,repair_servicefor labor lines,repair_timesheetfor timesheet lines on repair orders,repair_type, andrepair_quality_control.project_timesheet_time_controlfor the start and stop timer.Fleet add-ons for vehicle ownership and inspection templates:
fleet_vehicle_ownership,fleet_vehicle_inspectionandfleet_vehicle_inspection_template.resource_bookingfor appointment capacity across several technicians.l10n_us_gaapfor the chart of accounts, plus thel10n-usarepository more broadly.account_reconcile_ocafor bank reconciliation and MIS Builder for a trial balance comparison at cutover.
Glue you write yourself
The OCA gives you the parts, but several joins are missing. We wrote small modules for these:
Vehicle on the repair order. Owner-filtered vehicle selection, odometer-in written to the vehicle history, a customer-concern tab, plate and VIN search, a repair-history smart button, and the vehicle printed on the sale order and invoice. It also hides the "product to repair" field, because a vehicle is not a stock product.
Labor lines synced to the sale order. OCA only syncs labor to the sale order at quotation creation. A small module keeps labor lines in step afterward, so a change order lands on the quote.
Timers on the repair order. A small module joins
repair_timesheetto the OCA timer, so the Start Timer and Stop Timer buttons log hours to the repair order.Service lines on repair templates. This lets the diagnostic template carry the diagnostic fee and job templates carry book hours.
Digital inspection with severity grades. Lines graded Good, Today's Services, Future, Needs Attention or Failed State Inspection, with conditions, recommended actions, measurement specs that flag out-of-spec values, photos, and seeded checklists.
A customer-facing inspection report. A severity-grouped PDF, an email that attaches it, and portal pages with token access.
Findings to estimate. Canned service jobs linked to recommended actions, and a wizard that builds the quote from the findings.
Scheduling. A bridge between the core repair order, which has one scheduled date and no capacity model, and
resource_booking, which does.
Intake and diagnostic
Start every job as a draft repair order. Pick the customer and the vehicle, enter the odometer reading, and record the customer's concern in their own words. Nothing is reserved or charged at this stage.
Create the vehicle record properly: plate, VIN, and owner. A real vehicle record makes repeat visits fast, because the repair history hangs off it.
Apply a Diagnostic repair template to the order. The template is a service line carrying the diagnostic fee, a flat-fee product with its own income account. The customer owes it even if they decline the repair.
Warning
Applying a template replaces the existing part and service lines on the order. Apply the template first, then add extra lines.
Digital inspection with severity grades
The technician creates one inspection per checklist from the repair order, with the vehicle, odometer and staff pre-filled. Each line gets a severity grade, the conditions found, recommended actions, measurements, notes and photos.
Severity does real work later. It decides which findings the estimate builder treats as must-do and which it offers as optional.
When the checklists are done, send the inspection report. The customer receives a branded PDF by email and a portal link with full-size photos. Keep internal-only observations in the repair notes, which the customer never sees.
Estimate as a sale order with optional jobs
The estimate is a sale order. There are two ways to fill it.
Build it from findings
Link canned service jobs to the recommended actions that should trigger them. A job is either a fixed menu price or labor plus parts, with a customer-facing description.
The estimate builder then lists each unquoted finding with its linked job. Must-do findings (Today's Services and Failed State Inspection) become firm lines. Everything else becomes an optional product on the quote, declined by default.
Build it by hand
Add parts with a Demand quantity, and add labor on the Services tab with the book hours as the quantity. Then create the quotation from the repair order.
Either way, require an online signature on the quote and print the vehicle and VIN on it.
Portal approval and confirmation
The customer opens the quote on the portal, adds the optional jobs they want, and signs. The total updates as they choose. If you configure online payment or a prepayment percentage, they can sign and pay in one flow.
When the sale order confirms, each approved job explodes into the repair order: parts as stock moves and labor as service lines. Both carry a flag linking them back to the flat-rate sale line, so they do not create their own sale lines.
Warning
Never clear that flag on the exploded moves or services. Doing so re-enables the core repair-to-sale sync and bills the customer twice for the same work.
The repair order chatter records which jobs were approved and which were declined. Keep that trail: it is your record if a declined safety item is ever questioned.
Parts, RFQs and the repair itself
Confirming the repair reserves parts on the shelf and raises purchase orders for the rest. Two routes cover both cases:
Special-order parts. Give the product the Buy and MTO routes and a vendor. Odoo raises a per-job RFQ the moment the repair order confirms. The "Replenish on Order (MTO)" route ships archived in Odoo 18, so unarchive it first under Inventory, Configuration, Routes. That menu only appears once Multi-Step Routes is switched on in the Inventory settings.
Stocked parts. Use a Buy route plus a reordering rule with minimum and maximum both zero, set to auto-trigger. Demand then produces a consolidated RFQ.
The parts availability indicator turns green when everything has arrived, which is the signal to bring the vehicle into the bay. Start the repair when work begins.
Technician timers
Technicians use a Start Timer button on the repair order, which becomes Stop Timer while running. Starting a timer on a second job stops the first, so each person has exactly one clock running. Hours land on the Timesheets tab against an internal "Shop Labor" project.
Technicians must sign in as themselves. The timer belongs to the logged-in user, so a shared tablet login piles everyone's hours on one person.
Note
The OCA hr_timesheet_task_required module forces a task on every timesheet line, which breaks technician time on repair orders. Leave it out of the install set.
Record actual consumption in Done
Record parts actually used in the Done quantity, never in Demand. Editing Demand after approval rewrites the customer-facing sale lines. Editing Done does not.
Work found mid-repair is a change order. Add the parts and labor on the repair order without the flag, and they sync to the sale order. Get the customer's approval before doing the work.
Warning
Do not enable automatic transfer on repair completion (repair.auto_transfer_repair, from repair_picking_after_done). It requires a product on the repair order, and this design leaves that field empty because the vehicle is not a stock product. It blocks Confirm Repair with an error.
End repair and invoice
Click End Repair when the work is done. Parts are consumed at their Done quantities, and removed cores can go to a core-returns location instead of scrap.
Then create the invoice from the sale order and register payment. The invoice equals the approved estimate: flat-rate labor plus quoted parts, with the vehicle and VIN printed on it.
If the customer declines the repair, cancel it. The parts and labor lines on the quote are zeroed and the diagnostic fee line remains, so you invoice only the fee.
Configuration notes
A few setup choices carry more weight than the rest.
Services are separate products. Create Diagnostic Fee, Shop Labor, one product per extra hourly rate, Shop Supplies and Sublet Repair as distinct service products with the "Ordered quantities" invoice policy. Do not model rates as variants. Use pricelists if fleet and retail rates diverge later.
Parts are storable goods in a category with FIFO costing and automated valuation, with vendor prices on the Purchase tab.
Scheduling needs real users. Each technician is an employee linked to a user, on their own shift calendar, with one resource combination each. Without the user link, their own calendar meetings do not mark them busy.
Odometer units. Core Odoo defaults to kilometers. Set a user-defined default of
milesif your shop works in miles.
For the chart of accounts, apply l10n_us_gaap before any account mapping. Validate a trial balance against the old system at the cutover date. That comparison is your go or no-go gate.
Known risks
Module maturity.
repair_typeis Alpha, andrepair_order_template,repair_timesheetandrepair_quality_controlare young Betas. They are small and readable. Pin the submodule revisions and smoke-test after each update.A field collision.
repair_quality_controland a custom inspection module can both defineinspection_idson the repair order. When the custom one wins, Cancel Repair fails and draft inspections can be deleted. Namespace your own field, or skip the QC module.Never add a value to
fleet.vehicle.model.vehicle_type. Core fleet views hide about fifteen fields, including the odometer, unless the type iscar. Put the car, SUV and truck split in a category instead.Scheduling limits. A technician is busy for the appointment window only, so a job stalled on parts does not free them automatically. Assignment is by sequence or random, never least-loaded.
The system is built but not yet deployed, so this design has not been checked against a running shop. The notes above come from the design and its source code.
Related documents
Sources
This article is in the public domain (CC0 1.0), code samples included. Use it however helps you.