Add initial Odoo FSM modules and deployment make targets
This commit is contained in:
1
odoo/addons/dsrpt_repair_work_orders/__init__.py
Normal file
1
odoo/addons/dsrpt_repair_work_orders/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
from . import models
|
||||
22
odoo/addons/dsrpt_repair_work_orders/__manifest__.py
Normal file
22
odoo/addons/dsrpt_repair_work_orders/__manifest__.py
Normal file
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"name": "DSRPT Repair Work Orders",
|
||||
"summary": "Work order flow from intake to close",
|
||||
"version": "19.0.1.0.0",
|
||||
"category": "Services",
|
||||
"author": "DisruptLab",
|
||||
"license": "LGPL-3",
|
||||
"depends": [
|
||||
"base",
|
||||
"mail",
|
||||
"dsrpt_repair_main",
|
||||
"dsrpt_repair_customers",
|
||||
"dsrpt_repair_technicians",
|
||||
"dsrpt_repair_materials",
|
||||
],
|
||||
"data": [
|
||||
"security/ir.model.access.csv",
|
||||
"data/sequence.xml",
|
||||
"views/work_order_views.xml",
|
||||
],
|
||||
"installable": True,
|
||||
}
|
||||
9
odoo/addons/dsrpt_repair_work_orders/data/sequence.xml
Normal file
9
odoo/addons/dsrpt_repair_work_orders/data/sequence.xml
Normal file
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<odoo noupdate="1">
|
||||
<record id="seq_repair_work_order" model="ir.sequence">
|
||||
<field name="name">Repair Work Order</field>
|
||||
<field name="code">repair.work.order</field>
|
||||
<field name="prefix">WO%(y)s%(month)s-</field>
|
||||
<field name="padding">5</field>
|
||||
</record>
|
||||
</odoo>
|
||||
1
odoo/addons/dsrpt_repair_work_orders/models/__init__.py
Normal file
1
odoo/addons/dsrpt_repair_work_orders/models/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
from . import work_order
|
||||
123
odoo/addons/dsrpt_repair_work_orders/models/work_order.py
Normal file
123
odoo/addons/dsrpt_repair_work_orders/models/work_order.py
Normal 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.")
|
||||
@@ -0,0 +1,4 @@
|
||||
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
|
||||
access_repair_work_order_user,repair.work.order user,model_repair_work_order,base.group_user,1,1,1,1
|
||||
access_repair_work_order_time_user,repair.work.order.time user,model_repair_work_order_time,base.group_user,1,1,1,1
|
||||
access_repair_work_order_material_user,repair.work.order.material user,model_repair_work_order_material,base.group_user,1,1,1,1
|
||||
|
@@ -0,0 +1,92 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<odoo>
|
||||
<record id="view_repair_work_order_tree" model="ir.ui.view">
|
||||
<field name="name">repair.work.order.tree</field>
|
||||
<field name="model">repair.work.order</field>
|
||||
<field name="arch" type="xml">
|
||||
<list>
|
||||
<field name="name"/>
|
||||
<field name="customer_id"/>
|
||||
<field name="zone_id"/>
|
||||
<field name="scheduled_datetime"/>
|
||||
<field name="technician_id"/>
|
||||
<field name="state"/>
|
||||
<field name="total_time_hours"/>
|
||||
<field name="total_material_cost"/>
|
||||
</list>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="view_repair_work_order_form" model="ir.ui.view">
|
||||
<field name="name">repair.work.order.form</field>
|
||||
<field name="model">repair.work.order</field>
|
||||
<field name="arch" type="xml">
|
||||
<form>
|
||||
<header>
|
||||
<button name="action_confirm" type="object" string="Confirm" invisible="state != 'draft'" class="btn-primary"/>
|
||||
<button name="action_assign_to_me" type="object" string="Assign to me" invisible="state not in ('confirmed','assigned')" class="btn-primary"/>
|
||||
<button name="action_start" type="object" string="Start" invisible="state != 'assigned'"/>
|
||||
<button name="action_done" type="object" string="Done" invisible="state not in ('in_progress','assigned')" class="btn-primary"/>
|
||||
<button name="action_cancel" type="object" string="Cancel" invisible="state in ('done','cancelled')"/>
|
||||
<button name="action_reset_draft" type="object" string="Reset to Draft" invisible="state == 'draft'"/>
|
||||
<field name="state" widget="statusbar" statusbar_visible="draft,confirmed,assigned,in_progress,done,cancelled"/>
|
||||
</header>
|
||||
<sheet>
|
||||
<group>
|
||||
<group>
|
||||
<field name="name" readonly="1"/>
|
||||
<field name="customer_id"/>
|
||||
<field name="customer_contact_id"/>
|
||||
<field name="address_id"/>
|
||||
<field name="zone_id"/>
|
||||
</group>
|
||||
<group>
|
||||
<field name="requested_datetime"/>
|
||||
<field name="scheduled_datetime"/>
|
||||
<field name="technician_id"/>
|
||||
<field name="assigned_user_id" readonly="1"/>
|
||||
<field name="description"/>
|
||||
</group>
|
||||
</group>
|
||||
|
||||
<notebook>
|
||||
<page string="Time Logs">
|
||||
<field name="time_line_ids" context="{'default_work_order_id': id, 'default_technician_id': technician_id}">
|
||||
<list editable="bottom">
|
||||
<field name="description"/>
|
||||
<field name="technician_id"/>
|
||||
<field name="hours"/>
|
||||
</list>
|
||||
</field>
|
||||
</page>
|
||||
<page string="Material Logs">
|
||||
<field name="material_line_ids" context="{'default_work_order_id': id}">
|
||||
<list editable="bottom">
|
||||
<field name="material_id"/>
|
||||
<field name="qty"/>
|
||||
<field name="unit_cost" readonly="1"/>
|
||||
<field name="subtotal" readonly="1"/>
|
||||
</list>
|
||||
</field>
|
||||
</page>
|
||||
<page string="Totals">
|
||||
<group>
|
||||
<field name="total_time_hours" readonly="1"/>
|
||||
<field name="total_material_cost" readonly="1"/>
|
||||
</group>
|
||||
</page>
|
||||
</notebook>
|
||||
</sheet>
|
||||
<chatter/>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="action_repair_work_order" model="ir.actions.act_window">
|
||||
<field name="name">Work Orders</field>
|
||||
<field name="res_model">repair.work.order</field>
|
||||
<field name="view_mode">list,form</field>
|
||||
</record>
|
||||
|
||||
<menuitem id="menu_repair_work_orders" name="Work Orders" parent="dsrpt_repair_main.menu_repair_root" action="action_repair_work_order" sequence="10"/>
|
||||
</odoo>
|
||||
Reference in New Issue
Block a user