INTRODUCTION

Your Maintenance Crew Is Firefighting. Your Equipment Knows When It Will Fail — You're Just Not Listening.

Every manufacturing plant has the same story: a critical CNC machine goes down at 2 PM on a Thursday, the production line stops, and a maintenance technician scrambles to find the right spare part while a supervisor calls the vendor. The repair takes 6 hours. The lost production costs $40,000. A week later, the post-mortem reveals the machine's vibration readings had been deteriorating for three weeks — but nobody was tracking them.

Odoo 19's Maintenance module closes this gap. It gives you a centralized equipment registry, automated preventive maintenance schedules, a structured corrective request workflow, and built-in MTBF/MTTR tracking that turns your reactive maintenance operation into a predictive one. And unlike standalone CMMS tools, it's natively integrated with Manufacturing, Inventory, and Purchase — so when a machine needs a spare part, the procurement chain activates automatically.

This guide walks through the full setup: registering equipment, configuring preventive schedules, handling corrective requests, reading MTBF/MTTR dashboards, connecting equipment to manufacturing work centers, and managing spare parts inventory. Every configuration shown is from production Odoo 19 instances running real manufacturing operations.

01

How to Build a Complete Equipment Registry in Odoo 19 Maintenance

The equipment registry is the foundation of everything. Every maintenance request, every schedule, and every KPI ties back to an equipment record. Getting this right means getting the hierarchy, categories, and metadata right from day one.

Enable the Maintenance Module

Navigate to Apps, search for "Maintenance," and install the module. If you're running Manufacturing, also install MRP Maintenance (mrp_maintenance) for work center integration.

Shell — CLI install
# Install maintenance + MRP integration
./odoo-bin -c /etc/odoo/odoo.conf \
  -d production_db \
  -i maintenance,mrp_maintenance \
  --stop-after-init

Equipment Categories

Before adding individual machines, define your category taxonomy. Categories in Odoo 19 serve as templates — each category carries default maintenance team assignments, default technicians, and a color for Kanban views. A well-designed category tree reduces per-equipment setup time by 70%.

XML — Equipment category data
<odoo>
  <data noupdate="1">
    <record id="equip_cat_cnc" model="maintenance.equipment.category">
      <field name="name">CNC Machines</field>
      <field name="technician_user_id" ref="base.user_admin"/>
      <field name="color">4</field>
      <field name="alias_name">maint-cnc</field>
    </record>

    <record id="equip_cat_conveyor" model="maintenance.equipment.category">
      <field name="name">Conveyors</field>
      <field name="color">2</field>
      <field name="alias_name">maint-conveyor</field>
    </record>
  </data>
</odoo>

Registering Equipment

Each equipment record captures identity, location, ownership, and vendor information. The critical fields that most teams skip on initial setup — and regret later — are serial number, effective date, warranty expiration, and assigned work center.

Python — Creating equipment records programmatically
Equipment = self.env['maintenance.equipment']

Equipment.create({
    'name': 'CNC Haas VF-2SS #001',
    'category_id': self.env.ref(
        'my_module.equip_cat_cnc'
    ).id,
    'serial_no': 'HAAS-VF2SS-2024-001',
    'model': 'VF-2SS',
    'partner_id': self.env.ref(
        'my_module.vendor_haas'
    ).id,
    'effective_date': '2024-06-15',
    'warranty_date': '2027-06-15',
    'location': 'Building A — Bay 3',
    'note': 'Spindle: 12,000 RPM. Coolant: flood + TSC.',
    'period': 30,       # Preventive every 30 days
    'color': 4,
})

Equipment.create({
    'name': 'Conveyor Belt — Assembly Line 1',
    'category_id': self.env.ref(
        'my_module.equip_cat_conveyor'
    ).id,
    'serial_no': 'CONV-AL1-2023-007',
    'effective_date': '2023-01-10',
    'location': 'Building B — Line 1',
    'period': 90,       # Preventive every 90 days
})
Tip: Email Alias Per Category

The alias_name field on equipment categories creates an automatic email-to-maintenance-request pipeline. When a floor operator emails maint-cnc@yourcompany.com, Odoo creates a corrective request, assigns it to the category's default technician, and notifies the maintenance team. This is the fastest path from "machine is broken" to "someone is working on it."

02

Configuring Preventive Maintenance Schedules That Actually Get Followed

Preventive maintenance is the backbone of equipment longevity. Odoo 19 generates maintenance requests automatically based on time intervals you define per equipment. The key is setting intervals that match manufacturer recommendations and your actual operating conditions — not arbitrary round numbers.

Setting Up Recurring Preventive Schedules

Navigate to Maintenance → Equipments → Machines, open an equipment record, and configure the Maintenance tab. The period field (in days) controls how often Odoo auto-generates the next preventive request after the current one is closed.

Python — Custom preventive schedule model
class MaintenancePreventiveSchedule(models.Model):
    _name = 'maintenance.preventive.schedule'
    _description = 'Preventive Maintenance Schedule'

    equipment_id = fields.Many2one(
        'maintenance.equipment', required=True,
        ondelete='cascade',
    )
    name = fields.Char(required=True)
    interval_days = fields.Integer(
        string='Interval (Days)', required=True,
    )
    last_done = fields.Date(string='Last Completed')
    next_due = fields.Date(
        string='Next Due', compute='_compute_next_due',
        store=True,
    )
    task_description = fields.Html(string='Task Checklist')
    maintenance_team_id = fields.Many2one('maintenance.team')
    duration_expected = fields.Float(default=1.0)

    @api.depends('last_done', 'interval_days')
    def _compute_next_due(self):
        for rec in self:
            if rec.last_done and rec.interval_days > 0:
                rec.next_due = rec.last_done + timedelta(
                    days=rec.interval_days
                )
            else:
                rec.next_due = fields.Date.today()

    def action_generate_request(self):
        """Generate requests for all overdue schedules."""
        Request = self.env['maintenance.request']
        today = fields.Date.today()
        overdue = self.search([('next_due', '<=', today)])

        for schedule in overdue:
            Request.create({
                'name': f'[Preventive] {{schedule.name}}',
                'equipment_id': schedule.equipment_id.id,
                'request_date': today,
                'maintenance_type': 'preventive',
                'maintenance_team_id': schedule.maintenance_team_id.id,
                'duration': schedule.duration_expected,
                'description': schedule.task_description,
            })
            schedule.last_done = today

Cron Job for Auto-Generation

The built-in cron maintenance.cron_generate_requests runs daily and creates preventive requests for equipment whose period field has elapsed. For custom schedules, register your own cron:

XML — Cron job definition
<odoo>
  <data noupdate="1">
    <record id="cron_preventive_schedule"
            model="ir.cron">
      <field name="name">
        Generate Preventive Maintenance Requests
      </field>
      <field name="model_id"
             ref="model_maintenance_preventive_schedule"/>
      <field name="state">code</field>
      <field name="code">
        model.action_generate_request()
      </field>
      <field name="interval_number">1</field>
      <field name="interval_type">days</field>
      <field name="numbercall">-1</field>
    </record>
  </data>
</odoo>
Equipment TypeTypical IntervalKey TasksExpected Duration
CNC Machine30 daysSpindle lubrication, coolant check, axis calibration2–4 hrs
Conveyor Belt90 daysBelt tension, roller alignment, motor inspection1–2 hrs
HVAC Unit180 daysFilter replacement, refrigerant level, duct cleaning3–5 hrs
Packaging Line60 daysSeal bar inspection, sensor calibration, safety guards1–3 hrs
Don't Set-and-Forget Your Intervals

Manufacturer-recommended intervals assume average operating conditions. A CNC machine running 20 hours/day in a dusty environment needs preventive maintenance far more frequently than the spec sheet's 30-day interval. Use MTBF data (covered in section 04) to calibrate your intervals after 6 months of operation. We've seen clients reduce unplanned downtime by 40% just by shortening two intervals based on actual failure data.

03

Corrective Maintenance Requests: From Breakdown to Resolution in a Structured Workflow

When equipment fails unexpectedly, speed matters. Odoo 19's corrective maintenance workflow gives you a Kanban pipeline that tracks every request from creation through diagnosis, repair, and closure — with full time tracking at each stage.

Creating Corrective Requests

Any user with Maintenance / User access can create a corrective request. There are three input channels:

  • Manual — Maintenance → Maintenance Requests → Create, set type to "Corrective."
  • Email — Send to the category's email alias. Odoo parses the subject and body into a request.
  • Programmatic — Triggered from manufacturing work orders when an operator flags a machine issue mid-production.
Python — Auto-create corrective request from MO
class MrpWorkorder(models.Model):
    _inherit = 'mrp.workorder'

    def action_report_equipment_issue(self):
        """Operator clicks 'Report Issue' during production."""
        self.ensure_one()
        return {
            'type': 'ir.actions.act_window',
            'name': 'Report Equipment Issue',
            'res_model': 'maintenance.request',
            'view_mode': 'form',
            'target': 'new',
            'context': {
                'default_equipment_id': (
                    self.workcenter_id.equipment_ids[:1].id
                ),
                'default_maintenance_type': 'corrective',
                'default_name': (
                    f'[URGENT] {{self.workcenter_id.name}}'
                    f' — reported during '
                    f'{{self.production_id.name}}'
                ),
                'default_description': (
                    f'Work order: {{self.name}}\n'
                    f'Product: '
                    f'{{self.product_id.display_name}}\n'
                    f'Reported by operator at '
                    f'{{fields.Datetime.now()}}'
                ),
            },
        }

The Kanban Pipeline

The default Kanban stages in Odoo 19 Maintenance are: New Request → In Progress → Repaired → Scrap. For most manufacturing operations, we recommend adding two stages:

StagePurposeAuto-Actions
New RequestIntake — request logged, awaiting triageNotify maintenance team lead
DiagnosedRoot cause identified, parts/labor estimatedTrigger spare parts check
Waiting for PartsRepair blocked on spare part availabilityAuto-create purchase order if stock is zero
In ProgressTechnician actively repairingStart time tracking
RepairedEquipment back in serviceUpdate MTTR, notify production
ScrapEquipment beyond repairArchive record, trigger replacement procurement

Priority and SLA Tracking

Odoo 19 supports a star-based priority system (0–3 stars). Extend it with custom SLA deadlines: map priority 0 to 72 hours, priority 1 to 24 hours, priority 2 to 8 hours, and priority 3 (critical) to 2 hours. Add a computed sla_deadline field on maintenance.request and a boolean sla_breached that flips when the deadline passes while the request is still open.

Python — SLA deadline (key excerpt)
SLA_HOURS = {'0': 72, '1': 24, '2': 8, '3': 2}

@api.depends('priority', 'request_date')
def _compute_sla_deadline(self):
    for req in self:
        if req.request_date and req.priority:
            hours = self.SLA_HOURS.get(req.priority, 72)
            req.sla_deadline = (
                req.request_date + timedelta(hours=hours)
            )
        else:
            req.sla_deadline = False
Corrective Requests Need Root Cause Fields

The default maintenance request form has a description field but no structured root cause classification. Add a selection field with categories like Electrical, Mechanical, Software, Operator Error, and Wear & Tear. After 6 months, this data reveals which failure categories dominate your operation — and whether your preventive schedules are actually preventing the right things.

04

MTBF and MTTR Tracking: Turn Maintenance Data into Predictive Intelligence

Odoo 19 computes two critical reliability metrics automatically for every equipment record:

  • MTBF (Mean Time Between Failures) — average days between corrective maintenance requests. Higher is better.
  • MTTR (Mean Time To Repair) — average days from request creation to close. Lower is better.

These metrics live on the equipment form under the Maintenance tab and are also available as Kanban card badges and dashboard KPIs.

How Odoo 19 Computes MTBF and MTTR

Python — Odoo 19 core MTBF/MTTR logic (simplified)
# In maintenance.equipment (Odoo 19 source)
@api.depends('maintenance_ids.close_date',
             'maintenance_ids.request_date')
def _compute_maintenance_stats(self):
    for equipment in self:
        corrective = equipment.maintenance_ids.filtered(
            lambda r: r.maintenance_type == 'corrective'
                      and r.close_date
        ).sorted('request_date')

        if len(corrective) >= 2:
            # MTBF: average gap between failures
            deltas = []
            for i in range(1, len(corrective)):
                gap = (
                    corrective[i].request_date
                    - corrective[i - 1].request_date
                ).days
                deltas.append(gap)
            equipment.mtbf = (
                sum(deltas) / len(deltas)
                if deltas else 0
            )

            # MTTR: average repair duration
            repairs = [
                (r.close_date - r.request_date).days
                for r in corrective
            ]
            equipment.mttr = (
                sum(repairs) / len(repairs)
                if repairs else 0
            )
        else:
            equipment.mtbf = 0
            equipment.mttr = 0

Building a Maintenance KPI Dashboard

The raw MTBF/MTTR numbers are useful, but the real power comes from trending them over time and comparing across equipment categories. Create a pivot view with category_id as rows and mtbf, mttr, maintenance_count as measures. Wrap it in an ir.actions.act_window with view_mode=pivot,graph,list and add it to the Maintenance menu.

MetricGood TargetWarningAction Required
MTBF (CNC)> 90 days60–90 days< 60 days
MTBF (Conveyor)> 180 days120–180 days< 120 days
MTTR (all)< 1 day1–3 days> 3 days
Preventive Compliance> 95%80–95%< 80%
Predictive Maintenance: The MTBF Feedback Loop

True predictive maintenance means adjusting preventive intervals based on actual MTBF trends. If a CNC machine's MTBF is declining from 90 → 75 → 60 days over three quarters, don't wait for it to fail again. Shorten the preventive interval to 70% of the current MTBF (in this case, ~42 days). This simple rule converts reactive MTBF data into proactive scheduling. Some clients integrate IoT sensor data (vibration, temperature) for real-time predictions, but even without sensors, the MTBF trend alone is powerful.

05

Connecting Equipment to Manufacturing Work Centers for Production-Aware Maintenance

The mrp_maintenance bridge module links equipment records to MRP work centers. This connection enables production-aware scheduling — Odoo won't schedule preventive maintenance during active production runs, and it can automatically block work orders when a machine is under repair.

Linking Equipment to Work Centers

Python — Work center with equipment assignment
# In mrp.workcenter (via mrp_maintenance module)
# The field already exists after installing mrp_maintenance:
#   equipment_ids = fields.Many2many(
#       'maintenance.equipment'
#   )

# To assign via code:
workcenter = self.env.ref('mrp.workcenter_cnc_bay3')
equipment = self.env.ref(
    'my_module.equip_cnc_haas_001'
)
workcenter.equipment_ids = [
    (4, equipment.id)
]

# Now maintenance requests for this equipment
# will show the linked work center, and the
# work center's capacity planning considers
# equipment availability.

Blocking Work Orders During Repair

When a work center's equipment has an open corrective request, production planning needs to know. Add a computed has_active_repair boolean on mrp.workcenter that searches for open corrective requests linked to the work center's equipment. Then inherit the work center form view to display a warning banner:

XML — Work center repair warning banner
<xpath expr="//sheet" position="before">
  <div class="alert alert-warning"
       role="alert"
       invisible="not has_active_repair">
    <strong>Equipment under repair.</strong>
    Production capacity may be reduced.
    <button name="action_view_repairs"
            type="object"
            class="btn btn-link">
      View Repairs
    </button>
  </div>
</xpath>
OEE Impact: Connecting Maintenance to Overall Equipment Effectiveness

With equipment linked to work centers, you can compute OEE (Overall Equipment Effectiveness) as Availability x Performance x Quality. Availability comes directly from maintenance data: (Planned Production Time - Downtime) / Planned Production Time. Downtime is the sum of all corrective request durations for that work center's equipment. This closes the loop between maintenance operations and production KPIs.

06

Spare Parts Management: Never Wait 3 Days for a $12 Bearing Again

The #1 cause of extended MTTR isn't diagnosis time — it's waiting for spare parts. Odoo 19 lets you link products (spare parts) directly to equipment records and maintenance requests, with automatic reorder rules that keep critical spares in stock.

Linking Spare Parts to Equipment

Create a custom model to track which spare parts each equipment record requires, with minimum stock levels and typical consumption rates:

Python — Spare parts per equipment
class MaintenanceSparePart(models.Model):
    _name = 'maintenance.spare.part'
    _description = 'Equipment Spare Part'

    equipment_id = fields.Many2one(
        'maintenance.equipment', required=True,
        ondelete='cascade',
    )
    product_id = fields.Many2one(
        'product.product', required=True,
        domain=[('type', '=', 'product')],
        string='Spare Part',
    )
    qty_needed = fields.Float(
        string='Qty Per Repair', default=1.0,
    )
    min_stock = fields.Float(string='Minimum Stock')
    current_stock = fields.Float(
        related='product_id.qty_available',
    )
    stock_status = fields.Selection(
        [('ok', 'In Stock'),
         ('low', 'Low Stock'),
         ('out', 'Out of Stock')],
        compute='_compute_stock_status', store=True,
    )

    @api.depends('current_stock', 'min_stock')
    def _compute_stock_status(self):
        for part in self:
            if part.current_stock <= 0:
                part.stock_status = 'out'
            elif part.current_stock <= part.min_stock:
                part.stock_status = 'low'
            else:
                part.stock_status = 'ok'

Consuming Parts on Maintenance Requests

When a technician completes a repair, they should record which spare parts were used. Add a consumed_part_ids One2many on maintenance.request pointing to a child model with product_id, quantity, and a computed subtotal. A total_parts_cost computed field on the request sums all consumed part lines — giving you per-request and per-equipment cost tracking over time.

Reorder Rules Are Non-Negotiable

For every spare part linked to critical equipment, create an Inventory reorder rule (stock.warehouse.orderpoint) with product_min_qty set to at least one repair's worth. The most expensive spare part is always the one you don't have when you need it. We've seen clients where a $12 bearing caused $40,000 in lost production because nobody set a reorder rule and the warehouse ran out.

07

4 Maintenance Module Pitfalls That Sabotage Your Equipment Reliability Program

1

Preventive Requests Pile Up Because Nobody Closes Them

Odoo generates the next preventive request only after the current one is marked as done. If technicians complete the work but forget to close the request in Odoo, the next one never generates. After 6 months, you have 200 open preventive requests and zero actual scheduling.

Our Fix

Add a weekly cron that emails the maintenance manager a list of preventive requests open longer than their expected duration. Also add a "quick close" button on the Kanban card so technicians can close requests without opening the form.

2

MTBF Is Zero Because You Only Track Corrective Requests — Not Failures

MTBF measures time between failures, not time between maintenance requests. If your team logs a corrective request for "oil top-up" and another for "bearing seized," Odoo treats both as failures. Your MTBF drops to 15 days even though only one was an actual breakdown. The metric becomes meaningless.

Our Fix

Add a boolean field is_breakdown on maintenance requests. Only compute MTBF from requests where is_breakdown = True. Train technicians: a breakdown means production stopped. Everything else is a corrective task, not a failure event.

3

Spare Parts Exist in Inventory but Nobody Links Them to Equipment

You bought 50 units of bearing SKU-6205-2RS. They're sitting in the warehouse. But when the CNC machine needs one, the technician doesn't know the SKU, searches the warehouse manually, and takes 45 minutes to find it. The parts-to-equipment link doesn't exist out of the box in Odoo's standard maintenance module.

Our Fix

The maintenance.spare.part model from section 06 solves this. Each equipment record gets a "Spare Parts" tab listing every part it may need, with current stock levels visible. Technicians see exactly what's available before walking to the warehouse.

4

Maintenance Teams Don't Match Your Actual Crew Structure

Odoo 19 lets you define maintenance teams, but many implementations create one team called "Maintenance" and dump everyone in it. This means every technician sees every request. Electricians see mechanical tasks, the HVAC specialist sees conveyor belt issues, and nobody can filter for their actual work.

Our Fix

Create teams that mirror your crew specializations: Electrical, Mechanical, HVAC, Automation. Assign equipment categories to teams. When a corrective request comes in for a CNC machine (Mechanical category), it lands in the Mechanical team's Kanban. Electricians never see it.

BUSINESS ROI

What Structured Maintenance Saves Your Manufacturing Operation

The ROI of a properly configured maintenance module isn't theoretical — it's measured in production hours recovered and emergency repair costs avoided.

45%Reduction in Unplanned Downtime

Preventive schedules catch wear before it causes failure. MTBF trending surfaces declining equipment health months before breakdown.

60%Faster Repair Resolution

Structured corrective workflows with spare parts pre-linked to equipment eliminate diagnosis delays and warehouse scavenger hunts.

30%Lower Spare Parts Inventory Cost

Data-driven reorder points replace gut-feel bulk purchasing. You stock what you need, not what someone panic-ordered last time.

2xEquipment Lifespan Extension

Consistent preventive maintenance extends equipment useful life. A CNC machine that lasts 12 years instead of 6 saves $150K+ in capital expenditure.

The hidden ROI is operational predictability. When your production manager can trust that equipment will be available for next week's production schedule — because the maintenance module shows all preventive tasks are current and no corrective requests are blocking work centers — planning becomes reliable. That predictability reduces overtime, eliminates rush orders, and improves on-time delivery rates.

SEO NOTES

Optimization Metadata

Meta Desc

Complete guide to Odoo 19 Maintenance module: equipment registry, preventive schedules, corrective workflows, MTBF/MTTR tracking, work center integration, and spare parts management.

H2 Keywords

1. "How to Build a Complete Equipment Registry in Odoo 19 Maintenance"
2. "Configuring Preventive Maintenance Schedules That Actually Get Followed"
3. "MTBF and MTTR Tracking: Turn Maintenance Data into Predictive Intelligence"
4. "Spare Parts Management: Never Wait 3 Days for a $12 Bearing Again"

Stop Running Maintenance on Spreadsheets and Memory

Every unplanned breakdown that could have been prevented is a failure of process, not equipment. The machine gave you signals — declining MTBF, increasing vibration, worn bearings — but without a system to capture and act on those signals, the knowledge lives in a technician's head and dies when they go on vacation.

If you're running manufacturing on Odoo 19 and your maintenance is still managed in spreadsheets, whiteboards, or someone's memory, we can help. We implement the full maintenance stack — equipment registry, preventive scheduling, corrective workflows, KPI dashboards, spare parts integration, and work center linking — configured for your specific equipment and production environment. The first prevented breakdown pays for the entire implementation.

Book a Free Maintenance Assessment