GuideOdoo Field ServiceMarch 13, 2026

Field Service in Odoo 19:
Scheduling, Dispatching & Mobile Workforce

INTRODUCTION

Your Technicians Are Driving 40% More Than They Need To

Most field service operations we audit share the same pattern: dispatchers assign tasks manually from a spreadsheet or a Kanban board, technicians call the office to ask what's next, and nobody knows whether the right parts are in the truck until the tech arrives on site. The result is wasted drive time, missed SLA windows, return visits for missing materials, and invoices that take days to generate because the paperwork sits in someone's truck until Friday.

Odoo 19's Field Service module addresses every one of these pain points. It connects the planning calendar to the dispatch map, puts task details and worksheets on the technician's phone, tracks parts consumption against inventory, captures customer signatures on site, and generates invoices the moment a task is marked done. The entire loop from service request to paid invoice happens inside one system.

This guide covers the complete setup: installing and configuring the module, defining task types and worksheet templates, configuring the scheduling calendar and dispatch logic, enabling the mobile experience for technicians, tracking parts and materials, capturing signatures, automating invoicing, and building the reports that show whether your operation is actually improving. Every menu path, field name, and code example is from Odoo 19 Community and Enterprise.

01

Installing and Configuring the Odoo 19 Field Service Module

Navigate to Apps and search for "Field Service." Install industry_fsm (the base module). If you need worksheet templates and custom forms, also install industry_fsm_report. For stock/parts tracking, install industry_fsm_stock. For sale order integration, install industry_fsm_sale.

After installation, go to Field Service → Configuration → Settings. Enable the features your operation needs:

SettingMenu PathWhat It Enables
WorksheetsField Service → Configuration → SettingsCustom PDF reports filled by technicians on site
Time TrackingField Service → Configuration → SettingsTimesheets linked to each field task for labor costing
Products on TasksField Service → Configuration → SettingsTechnicians can log parts consumed directly on the task
Customer SignaturesField Service → Configuration → SettingsSign-off widget on the task form for proof of service

To automate the module installation and configuration via code (useful for multi-database deployments or CI/CD pipelines), use a post-init hook in a custom wrapper module:

Python — custom_fsm_setup/__manifest__.py
# -*- coding: utf-8 -*-
{{
    'name': 'Field Service Auto-Configuration',
    'version': '19.0.1.0.0',
    'category': 'Field Service',
    'depends': [
        'industry_fsm',
        'industry_fsm_report',
        'industry_fsm_stock',
        'industry_fsm_sale',
    ],
    'data': [
        'data/fsm_project_data.xml',
        'data/fsm_task_type_data.xml',
    ],
    'post_init_hook': '_post_init_configure_fsm',
    'license': 'LGPL-3',
}}
Python — custom_fsm_setup/__init__.py
from odoo import api, SUPERUSER_ID


def _post_init_configure_fsm(env):
    """Enable all field service features after install."""
    config = env['res.config.settings'].create({{
        'group_industry_fsm_report': True,
        'group_industry_fsm_quotation': True,
        'module_industry_fsm_stock': True,
    }})
    config.execute()

    # Create the default FSM project if it doesn't exist
    Project = env['project.project']
    if not Project.search([('is_fsm', '=', True)], limit=1):
        Project.create({{
            'name': 'Field Service',
            'is_fsm': True,
            'allow_timesheets': True,
            'allow_worksheets': True,
            'allow_material': True,
            'type_ids': [
                (0, 0, {{'name': 'New}}),
                (0, 0, {{'name': 'Scheduled}}),
                (0, 0, {{'name': 'In Progress}}),
                (0, 0, {{'name': 'Done', 'is_closed': True}}),
            ],
        }})
Enterprise vs Community

The base industry_fsm module is available in Odoo Community, but the planning calendar, map view, and worksheet designer require Enterprise. If you're on Community, you can still create field tasks, assign them, and track time — but you'll need to build your own scheduling UI or use the standard Gantt/calendar views from the Project module.

02

Defining Task Types, Templates, and SLA Policies for Field Service

Task types are the backbone of an organized field service operation. They determine the default duration, worksheet template, required skills, and checklist items for each kind of job. Navigate to Field Service → Configuration → Stages to define your pipeline stages, then create task templates under Field Service → Configuration → Task Templates.

A well-structured set of task types looks like this:

Task TypeDefault DurationWorksheet TemplateRequired SkillsSLA Target
Installation2 hoursInstallation ChecklistCertified InstallerComplete within 48h of request
Preventive Maintenance1 hourPM Inspection FormLevel 2 TechnicianScheduled within 7 days
Emergency Repair3 hoursRepair ReportSenior TechnicianOn site within 4 hours
Inspection45 minutesSite Inspection FormAny TechnicianComplete within 5 business days

To seed these task types via XML data files (recommended for version-controlled, reproducible setups):

XML — custom_fsm_setup/data/fsm_task_type_data.xml
<?xml version="1.0" encoding="utf-8"?>
<odoo>
  <data noupdate="1">

    <!-- FSM Project reference -->
    <record id="fsm_project_main" model="project.project">
      <field name="name">Field Service</field>
      <field name="is_fsm">True</field>
      <field name="allow_timesheets">True</field>
      <field name="allow_worksheets">True</field>
      <field name="allow_material">True</field>
    </record>

    <!-- Task templates -->
    <record id="fsm_task_template_install" model="project.task">
      <field name="name">Installation</field>
      <field name="project_id" ref="fsm_project_main"/>
      <field name="is_fsm">True</field>
      <field name="planned_hours">2.0</field>
      <field name="tag_ids" eval="[
        (0, 0, {{'name': 'Installation'}}),
      ]"/>
      <field name="description">
        Standard installation procedure:
        1. Site survey and safety check
        2. Equipment unboxing and inspection
        3. Mounting and wiring
        4. System commissioning and testing
        5. Customer walkthrough and sign-off
      </field>
    </record>

    <record id="fsm_task_template_pm" model="project.task">
      <field name="name">Preventive Maintenance</field>
      <field name="project_id" ref="fsm_project_main"/>
      <field name="is_fsm">True</field>
      <field name="planned_hours">1.0</field>
      <field name="tag_ids" eval="[
        (0, 0, {{'name': 'Maintenance'}}),
      ]"/>
    </record>

    <record id="fsm_task_template_emergency" model="project.task">
      <field name="name">Emergency Repair</field>
      <field name="project_id" ref="fsm_project_main"/>
      <field name="is_fsm">True</field>
      <field name="planned_hours">3.0</field>
      <field name="priority">1</field>
      <field name="tag_ids" eval="[
        (0, 0, {{'name': 'Emergency'}}),
      ]"/>
    </record>

  </data>
</odoo>
Template vs. Regular Task

In Odoo 19, task templates are simply regular tasks that you duplicate when creating new jobs. There is no separate "template" model. The recommended approach is to create template tasks in a dedicated stage called "Templates" that is excluded from Kanban views via domain filters. When a new service request comes in, duplicate the template and assign it to the customer and technician.

03

Scheduling Field Tasks with the Planning Calendar and Gantt View

Scheduling is where Odoo 19 Field Service shines over spreadsheet-based operations. The Planning module (Enterprise) integrates directly with field tasks, showing every technician's availability on a single Gantt chart. Navigate to Field Service → Planning to access the scheduling view.

Key scheduling fields on the task form:

  • planned_date_start / planned_date_end — The scheduled window. These populate the Gantt bars and calendar events.
  • user_ids — The assigned technician(s). In Odoo 19, tasks support multiple assignees for jobs requiring a crew.
  • planned_hours — Expected duration. The Gantt view uses this to size the bar when only a start date is set.
  • partner_id — The customer. Their address populates the map pin for route planning.
  • date_deadline — SLA deadline. Tasks approaching this date get highlighted in the calendar.

The Gantt view allows drag-and-drop rescheduling: grab a task bar and move it to a different time slot or a different technician row. Odoo automatically updates planned_date_start, planned_date_end, and user_ids. Conflicts (double-booking a technician) are highlighted with a red overlay.

For recurring maintenance schedules, use the Recurrence feature on the task form. Set the recurrence pattern (daily, weekly, monthly) and Odoo creates new tasks automatically. Each recurrence inherits the template's worksheet, tags, and planned duration.

Scheduling Tip: Working Hours Matter

The Gantt view respects each employee's working schedule (set on the employee form under Work Information → Working Hours). If a technician works Monday through Friday, 8 AM to 5 PM, the planner won't show availability on weekends. Make sure every field technician has the correct working schedule configured — otherwise the planning view shows misleading availability.

04

Dispatch Logic: Map View, Route Optimization, and Skill-Based Assignment

The Map View (Enterprise) shows all unassigned and scheduled tasks as pins on a map, color-coded by status. Navigate to Field Service → My Tasks and switch to the map view icon. Each pin shows the customer address, task type, and scheduled time.

Effective dispatching in Odoo 19 combines three factors:

FactorOdoo FieldHow Dispatchers Use It
Location Proximitypartner_id.partner_latitude, partner_id.partner_longitudeMap view clusters nearby tasks. Assign adjacent pins to the same tech to minimize drive time.
Technician Skillsuser_ids.employee_skill_idsFilter technicians by required skill (e.g., HVAC certification, electrical license).
Current Workloaduser_ids capacity on Gantt viewThe planning view shows remaining capacity per day. Don't overload one tech while another is idle.

For operations that need automated dispatch logic beyond manual map-based assignment, you can extend the task model with a custom assignment method. Here is a simplified skill-based auto-assign example:

Python — custom_fsm_dispatch/models/project_task.py
from odoo import models, fields, api
from odoo.exceptions import UserError


class ProjectTask(models.Model):
    _inherit = 'project.task'

    required_skill_id = fields.Many2one(
        'hr.skill', string='Required Skill',
        help='Skill required to perform this field task',
    )

    def action_auto_assign(self):
        """Auto-assign task to the best available technician
        based on skill match and current workload."""
        self.ensure_one()
        if not self.is_fsm:
            raise UserError("Auto-assign is only for field tasks.")

        domain = [
            ('employee_type', '=', 'employee'),
            ('department_id.name', '=', 'Field Service'),
        ]

        # Filter by required skill if set
        if self.required_skill_id:
            skilled_employees = self.env['hr.employee'].search(
                domain
            ).filtered(
                lambda e: self.required_skill_id.id in
                e.employee_skill_ids.mapped('skill_id').ids
            )
        else:
            skilled_employees = self.env['hr.employee'].search(domain)

        if not skilled_employees:
            raise UserError("No qualified technicians found.")

        # Find the technician with the fewest open tasks today
        best_tech = None
        min_tasks = float('inf')
        today = fields.Date.today()

        for emp in skilled_employees:
            task_count = self.search_count([
                ('is_fsm', '=', True),
                ('user_ids', 'in', [emp.user_id.id]),
                ('planned_date_start', '>=', today),
                ('planned_date_start', '<=', today),
                ('stage_id.is_closed', '=', False),
            ])
            if task_count < min_tasks:
                min_tasks = task_count
                best_tech = emp

        if best_tech and best_tech.user_id:
            self.write({{
                'user_ids': [(4, best_tech.user_id.id)],
            }})
            return {{
                'type': 'ir.actions.client',
                'tag': 'display_notification',
                'params': {{
                    'title': 'Task Assigned',
                    'message': f'Assigned to {{best_tech.name}}'
                               f' ({{min_tasks}} other tasks today)',
                    'type': 'success',
                }},
            }}

To expose this as a button on the task form, add a view extension:

XML — custom_fsm_dispatch/views/project_task_views.xml
<odoo>
  <record id="view_task_form_fsm_dispatch" model="ir.ui.view">
    <field name="name">project.task.form.fsm.dispatch</field>
    <field name="model">project.task</field>
    <field name="inherit_id"
           ref="industry_fsm.project_task_view_form"/>
    <field name="arch" type="xml">
      <xpath expr="//header" position="inside">
        <button name="action_auto_assign"
                string="Auto-Assign"
                type="object"
                class="btn-primary"
                invisible="not is_fsm or user_ids"
                icon="fa-magic"/>
      </xpath>
      <xpath expr="//field[@name='user_ids']" position="after">
        <field name="required_skill_id"
               invisible="not is_fsm"
               options="{{'no_create': True}}"/>
      </xpath>
    </field>
  </record>
</odoo>
05

Mobile Experience: How Technicians Use Odoo Field Service on Their Phones

Technicians access their tasks through the Odoo mobile app (available on iOS and Android) or through a mobile browser pointed at your Odoo instance. The Field Service module provides a simplified mobile interface that strips away the back-office complexity and shows only what the technician needs.

The mobile task flow looks like this:

  • My Tasks list — Shows today's assigned tasks in chronological order. Each card shows the customer name, address, task type, and scheduled time.
  • Navigate button — Taps open Google Maps or Apple Maps with the customer address pre-filled. The address comes from partner_id.contact_address_complete.
  • Start Timer — One-tap timer that creates a timesheet entry linked to the task. The timer runs in the background while the tech works.
  • Log Materials — Scan barcode or search for products. Quantities are deducted from the technician's assigned warehouse (typically a van stock location).
  • Fill Worksheet — The worksheet template renders as a mobile-friendly form. Technicians fill checkboxes, text fields, photo uploads, and readings.
  • Customer Signature — A full-screen signature pad. The customer signs with their finger. The signature is stored as a binary field on the task.
  • Mark as Done — Moves the task to the "Done" stage, stops the timer, and triggers the invoice generation if configured.

For the mobile experience to work well, ensure your technicians' user accounts have the Field Service / User access group (under Settings → Users → [User] → Access Rights). This group limits the visible menus to only Field Service features, reducing confusion for non-office workers.

Offline Considerations

Odoo's mobile app requires an internet connection. If your technicians work in areas with poor connectivity (basements, rural sites, industrial zones), they will lose access to task details. The practical workaround is to have technicians open their task list while they have connectivity (the loaded page remains visible) and use the browser's offline caching. For true offline-first workflows, you'll need a custom PWA or third-party mobile wrapper that syncs when connectivity returns.

06

Worksheet Templates and Customer Signature Capture

Worksheet templates replace paper forms. Navigate to Field Service → Configuration → Worksheet Templates to create them. Each template is a Studio-designed form (Enterprise) or a custom QWeb report (Community) that defines the fields technicians fill out on site.

Common worksheet template patterns:

TemplateFieldsOutput
Installation ChecklistSite readiness (checkbox), equipment serial number, commissioning readings, before/after photosPDF attached to task, emailed to customer
PM Inspection FormComponent condition ratings (dropdown), meter readings, recommended next actions, parts replacedPDF report linked to equipment maintenance record
Repair ReportRoot cause description, work performed, parts consumed, total labor hours, follow-up required (boolean)PDF report attached to helpdesk ticket and invoice

The customer signature is captured via the sign_template_id integration or the built-in signature widget on the task form. When a customer signs, the binary data is stored in worksheet_signature and rendered on the PDF report. This provides legally defensible proof of service completion.

To auto-send the completed worksheet to the customer, configure a server action triggered when the task stage changes to "Done":

  • Go to Settings → Technical → Automation → Automated Actions
  • Model: project.task
  • Trigger: When stage_id is set to your "Done" stage
  • Action: Send email using a template that includes the worksheet PDF as an attachment
  • Filter: [('is_fsm', '=', True)]
07

Invoicing from Field Tasks: Time, Materials, and Fixed-Price Billing

Odoo 19 supports three invoicing models for field service, all configured at the project level under Field Service → Configuration → Projects → [Your FSM Project] → Settings tab:

ModelConfigurationHow It Works
Time & MaterialsEnable timesheets + products on tasks, set service product to "Timesheets" invoicingLabor hours from timesheets + consumed products appear as sale order lines. Invoice after task completion.
Fixed PriceCreate a sale order before the task, link it via sale_order_idThe task is linked to a prepaid sale order. Mark task done to deliver the service line. Invoice the SO.
HybridFixed base price on SO + T&M for extrasThe sale order has a fixed service line plus "delivered quantities" lines for extra labor/parts logged on the task.

The invoicing trigger is the "Create Invoice" button that appears on the task form once a task is marked as done and has billable lines (timesheet entries or consumed products). For automated invoicing, create a scheduled action that runs daily:

  • Find all FSM tasks in "Done" stage with linked sale orders that have uninvoiced delivered quantities
  • Group by customer to create one invoice per customer per day (not one per task)
  • Auto-confirm the invoice and send it via email

The industry_fsm_sale module adds a "Products" smart button on the task form. When a technician adds a product here, it automatically creates a sale order line. This is the bridge between field consumption and invoicing — no manual data entry back at the office.

08

Field Service Reporting: KPIs, Dashboards, and Continuous Improvement

Navigate to Field Service → Reporting to access the built-in analysis views. The default pivot table can be configured to show the KPIs that matter for field operations:

KPIMeasuresGroup ByWhat It Reveals
First-Time Fix RateCount of tasks closed without follow-up / total tasksTechnician, Task TypeWhich technicians or task types require repeat visits
Average Response TimeTime from task creation to planned_date_startPriority, Task TypeWhether emergency tasks are being scheduled fast enough
Utilization RateTotal timesheet hours / available working hoursTechnician, WeekWhether technicians are fully booked or underutilized
Revenue per TaskInvoice total / task countTask Type, MonthProfitability by service type; whether pricing covers costs

For custom dashboards beyond the built-in pivot/graph views, you can create spreadsheet dashboards (Odoo 19 Enterprise) that pull live data from the project.task model. The spreadsheet editor supports Odoo-specific formulas like =ODOO.PIVOT() and =ODOO.LIST() that query directly against your field service data.

The most actionable report for dispatchers is the daily utilization heatmap: a grid of technicians on the Y axis and days on the X axis, with cells colored by utilization percentage. Green cells (70-85% utilization) indicate healthy workloads. Red cells (over 100%) indicate overbooking. White cells (under 40%) indicate capacity that should be filled with preventive maintenance or other scheduled work.

09

3 Field Service Mistakes That Cost You Repeat Visits and Revenue Leakage

1

Technicians Forget to Log Materials, So You Invoice for Labor Only

This is the most common source of revenue leakage in field service. A technician replaces a $200 part, closes the task, but never logs the product on the task form. The invoice goes out with labor hours only. Over a month with 50 technicians averaging 4 tasks per day, even a 10% miss rate on parts logging means thousands of dollars in unbilled materials. The parts still come out of inventory, so your stock counts shrink but your revenue doesn't grow to match.

Our Fix

Add a Python constraint that prevents moving a task to "Done" if the task type requires materials but none are logged. Also configure the worksheet template to include a mandatory "Parts Used" section — if the tech writes "none" that's fine, but they must actively confirm it.

2

Customer Address Geocoding Fails Silently, Breaking the Map View

The map view plots tasks based on the customer's partner_latitude and partner_longitude fields. Odoo geocodes addresses using an external provider (OpenStreetMap Nominatim by default), but this geocoding fails silently for addresses that are incomplete, ambiguous, or in formats the API doesn't recognize. The task simply doesn't appear on the map. Dispatchers don't notice because there's no "tasks missing from map" indicator — they just don't see the pin and assume the task doesn't exist.

Our Fix

Create a scheduled action that runs nightly and flags customers with FSM tasks but no geocode data. Send a daily email to the dispatch team listing these "unmapped" tasks. Also add a banner on the task form that shows when the customer address has no coordinates: invisible="partner_latitude" on a div with a warning message.

3

Van Stock Is Never Reconciled, Leading to Phantom Inventory

When technicians consume parts from their van stock (a virtual warehouse location assigned to each technician), the stock moves happen digitally. But physical parts also disappear — dropped in the customer's yard, given to another tech, or simply miscounted. Without regular van stock reconciliation, the system thinks the van has 15 filters when it actually has 3. The next dispatch sends the tech to a job requiring filters, and they show up empty-handed — resulting in a return visit that costs you drive time and customer satisfaction.

Our Fix

Schedule a weekly inventory adjustment for each van stock location. The technician uses the Odoo Barcode app to scan what's actually in the van every Friday. Discrepancies are flagged as inventory adjustments. Also set minimum stock rules on van locations so replenishment orders are generated automatically when key parts drop below threshold.

BUSINESS ROI

What Proper Field Service Digitization Delivers to Your Bottom Line

Field service profitability comes down to three levers: fewer truck rolls, faster invoicing, and higher first-time fix rates. Here's what our clients measure after implementing Odoo 19 Field Service properly:

30%Fewer Return Visits

Worksheet checklists and parts verification ensure technicians arrive prepared and complete the job right the first time. Fewer second trips means more capacity for new jobs.

5 days → 1 dayInvoice Cycle Time

When materials and labor are logged on the task in real time, invoices generate the same day the job is done. No more waiting for Friday paperwork. Cash flow improves immediately.

15-20%More Revenue from Parts

Mandatory materials logging catches the parts that were previously consumed but never billed. Over a fleet of 20+ technicians, this recovered revenue often pays for the entire Odoo implementation.

The compounding effect is significant. Fewer return visits free up technician time, which means more jobs per day per tech. Faster invoicing improves cash flow, which reduces the need for credit lines. Better parts tracking reduces emergency procurement costs. These improvements compound month over month — the operation gets measurably more profitable every quarter.

SEO NOTES

Optimization Metadata

Meta Desc

Complete guide to Odoo 19 Field Service: module setup, task types, scheduling, dispatch map, mobile technician workflow, worksheets, signature capture, and invoicing.

H2 Keywords

1. "Installing and Configuring the Odoo 19 Field Service Module"
2. "Dispatch Logic: Map View, Route Optimization, and Skill-Based Assignment"
3. "3 Field Service Mistakes That Cost You Repeat Visits and Revenue Leakage"

Stop Managing Field Teams from a Spreadsheet

Every minute a dispatcher spends manually assigning tasks, every return visit caused by a missing part, and every invoice delayed by paperwork sitting in a truck is money your operation is leaving on the table. Odoo 19 Field Service doesn't just digitize your existing process — it restructures the entire loop from service request to paid invoice into a single, automated flow.

If you're running field service operations with Odoo and want to reduce return visits, accelerate invoicing, or build a proper dispatching workflow, we can help. We audit your current field service process, configure the module to match your operation, build custom worksheets, set up van stock locations, and train your technicians on the mobile experience. The implementation typically pays for itself within the first quarter through recovered parts revenue and reduced truck rolls alone.

Book a Free Field Service Assessment