Add initial Odoo FSM modules and deployment make targets

This commit is contained in:
Ruslan Bakiev
2026-02-13 15:04:50 +07:00
parent 9ec614aa23
commit 43e76b2e8b
35 changed files with 793 additions and 0 deletions

View File

@@ -0,0 +1 @@
from . import work_order

View File

@@ -0,0 +1,123 @@
from odoo import api, fields, models
from odoo.exceptions import ValidationError
class RepairWorkOrder(models.Model):
_name = "repair.work.order"
_description = "Repair Work Order"
_inherit = ["mail.thread"]
_order = "id desc"
name = fields.Char(default="New", copy=False, readonly=True, tracking=True)
customer_id = fields.Many2one("repair.customer", required=True, tracking=True)
customer_contact_id = fields.Many2one(
"repair.customer.contact",
domain="[('customer_id', '=', customer_id)]",
tracking=True,
)
address_id = fields.Many2one(
"repair.customer.address",
domain="[('customer_id', '=', customer_id)]",
string="Service Address",
tracking=True,
)
zone_id = fields.Many2one("repair.fsm.zone", string="FSM Zone", tracking=True)
description = fields.Text()
requested_datetime = fields.Datetime(default=fields.Datetime.now)
scheduled_datetime = fields.Datetime(tracking=True)
technician_id = fields.Many2one("repair.technician", tracking=True)
assigned_user_id = fields.Many2one("res.users", related="technician_id.user_id", store=True)
state = fields.Selection(
selection=[
("draft", "Draft"),
("confirmed", "Confirmed"),
("assigned", "Assigned"),
("in_progress", "In Progress"),
("done", "Done"),
("cancelled", "Cancelled"),
],
default="draft",
tracking=True,
)
time_line_ids = fields.One2many("repair.work.order.time", "work_order_id", string="Time Logs")
material_line_ids = fields.One2many("repair.work.order.material", "work_order_id", string="Material Logs")
total_time_hours = fields.Float(compute="_compute_totals", store=True)
total_material_cost = fields.Float(compute="_compute_totals", store=True)
@api.model_create_multi
def create(self, vals_list):
for vals in vals_list:
if vals.get("name", "New") == "New":
vals["name"] = self.env["ir.sequence"].next_by_code("repair.work.order") or "New"
return super().create(vals_list)
@api.depends("time_line_ids.hours", "material_line_ids.subtotal")
def _compute_totals(self):
for rec in self:
rec.total_time_hours = sum(rec.time_line_ids.mapped("hours"))
rec.total_material_cost = sum(rec.material_line_ids.mapped("subtotal"))
@api.onchange("address_id")
def _onchange_address_id(self):
for rec in self:
if rec.address_id and rec.address_id.zone_id:
rec.zone_id = rec.address_id.zone_id
def action_confirm(self):
self.write({"state": "confirmed"})
def action_assign_to_me(self):
technician = self.env["repair.technician"].search([("user_id", "=", self.env.user.id)], limit=1)
if not technician:
raise ValidationError("No technician profile is linked to your user.")
self.write({"technician_id": technician.id, "state": "assigned"})
def action_start(self):
self.write({"state": "in_progress"})
def action_done(self):
self.write({"state": "done"})
def action_cancel(self):
self.write({"state": "cancelled"})
def action_reset_draft(self):
self.write({"state": "draft"})
class RepairWorkOrderTime(models.Model):
_name = "repair.work.order.time"
_description = "Repair Work Order Time"
work_order_id = fields.Many2one("repair.work.order", required=True, ondelete="cascade")
technician_id = fields.Many2one("repair.technician")
description = fields.Char(required=True)
hours = fields.Float(required=True)
@api.constrains("hours")
def _check_hours(self):
for rec in self:
if rec.hours <= 0:
raise ValidationError("Hours must be greater than zero.")
class RepairWorkOrderMaterial(models.Model):
_name = "repair.work.order.material"
_description = "Repair Work Order Material"
work_order_id = fields.Many2one("repair.work.order", required=True, ondelete="cascade")
material_id = fields.Many2one("repair.material", required=True)
qty = fields.Float(default=1.0, required=True)
unit_cost = fields.Float(related="material_id.standard_cost", store=True, readonly=True)
subtotal = fields.Float(compute="_compute_subtotal", store=True)
@api.depends("qty", "unit_cost")
def _compute_subtotal(self):
for rec in self:
rec.subtotal = rec.qty * rec.unit_cost
@api.constrains("qty")
def _check_qty(self):
for rec in self:
if rec.qty <= 0:
raise ValidationError("Material quantity must be greater than zero.")