mirror of
https://github.com/OCA/manufacture.git
synced 2025-01-28 16:37:15 +02:00
Create module mrp_workorder_lock_planning
This commit is contained in:
1
mrp_workorder_lock_planning/README.rst
Normal file
1
mrp_workorder_lock_planning/README.rst
Normal file
@@ -0,0 +1 @@
|
||||
to be generated
|
||||
1
mrp_workorder_lock_planning/__init__.py
Normal file
1
mrp_workorder_lock_planning/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
from . import models
|
||||
21
mrp_workorder_lock_planning/__manifest__.py
Normal file
21
mrp_workorder_lock_planning/__manifest__.py
Normal file
@@ -0,0 +1,21 @@
|
||||
# Copyright 2023 Camptocamp SA
|
||||
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl)
|
||||
{
|
||||
"name": "MRP Workorder Lock Planning",
|
||||
"summary": "Lock the planning of a MRP workorder to avoid rescheduling",
|
||||
"version": "15.0.1.0.0",
|
||||
"development_status": "Beta",
|
||||
"category": "Manufacturing/Manufacturing",
|
||||
"website": "https://github.com/OCA/manufacture",
|
||||
"author": "Camptocamp, Odoo Community Association (OCA)",
|
||||
"maintainers": ["grindtildeath"],
|
||||
"license": "AGPL-3",
|
||||
"application": False,
|
||||
"installable": True,
|
||||
"depends": [
|
||||
"mrp",
|
||||
],
|
||||
"data": [
|
||||
"views/mrp_workorder.xml",
|
||||
],
|
||||
}
|
||||
1
mrp_workorder_lock_planning/models/__init__.py
Normal file
1
mrp_workorder_lock_planning/models/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
from . import mrp_workorder
|
||||
45
mrp_workorder_lock_planning/models/mrp_workorder.py
Normal file
45
mrp_workorder_lock_planning/models/mrp_workorder.py
Normal file
@@ -0,0 +1,45 @@
|
||||
# Copyright 2023 Camptocamp SA
|
||||
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl)
|
||||
from odoo import _, api, fields, models
|
||||
from odoo.exceptions import ValidationError
|
||||
|
||||
|
||||
class MrpWorkorder(models.Model):
|
||||
_inherit = "mrp.workorder"
|
||||
|
||||
lock_planning = fields.Boolean(
|
||||
help="If set, any change to the planning will be restricted",
|
||||
readonly=True,
|
||||
)
|
||||
|
||||
@api.constrains("lock_planning")
|
||||
def _check_lock_planning(self):
|
||||
for workorder in self:
|
||||
if not workorder.lock_planning:
|
||||
continue
|
||||
if not (workorder.date_planned_start and workorder.date_planned_finished):
|
||||
raise ValidationError(
|
||||
_("Cannot lock planning of workorder if workorder is not planned")
|
||||
)
|
||||
|
||||
@api.constrains(
|
||||
"date_planned_start",
|
||||
"date_planned_finished",
|
||||
"duration_expected",
|
||||
"workcenter_id",
|
||||
)
|
||||
def _check_lock_planning_fields(self):
|
||||
for workorder in self:
|
||||
if not workorder.lock_planning:
|
||||
continue
|
||||
raise ValidationError(
|
||||
_(
|
||||
"Workorder is locked and modification of Scheduled dates, "
|
||||
"Expected duration or Workcenter is not allowed. "
|
||||
"Please unlock the planning first."
|
||||
)
|
||||
)
|
||||
|
||||
def toggle_lock_planning(self):
|
||||
self.ensure_one()
|
||||
self.lock_planning = not self.lock_planning
|
||||
1
mrp_workorder_lock_planning/readme/CONTRIBUTORS.rst
Normal file
1
mrp_workorder_lock_planning/readme/CONTRIBUTORS.rst
Normal file
@@ -0,0 +1 @@
|
||||
* Akim Juillerat <akim.juillerat@camptocamp.com>
|
||||
4
mrp_workorder_lock_planning/readme/DESCRIPTION.rst
Normal file
4
mrp_workorder_lock_planning/readme/DESCRIPTION.rst
Normal file
@@ -0,0 +1,4 @@
|
||||
This module allows to lock the planning of workorders to avoid
|
||||
any unintended rescheduling.
|
||||
|
||||
A Lock/Unlock button will be displayed once a workorder is planned.
|
||||
0
mrp_workorder_lock_planning/tests/__init__.py
Normal file
0
mrp_workorder_lock_planning/tests/__init__.py
Normal file
@@ -0,0 +1,37 @@
|
||||
# Copyright 2023 Camptocamp SA
|
||||
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl)
|
||||
from odoo.exceptions import ValidationError
|
||||
from odoo.tests import Form, TransactionCase
|
||||
|
||||
|
||||
class TestMrpWorkorderLockDatePlanned(TransactionCase):
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super().setUpClass()
|
||||
cls.bom_with_routing = cls.env.ref("mrp.mrp_bom_drawer_rout")
|
||||
cls.product_with_routing = (
|
||||
cls.bom_with_routing.product_tmpl_id.product_variant_id
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def _create_manufacturing_order(cls, qty=1.0):
|
||||
mo_form = Form(cls.env["mrp.production"])
|
||||
mo_form.product_id = cls.product_with_routing
|
||||
mo_form.bom_id = cls.bom_with_routing
|
||||
mo_form.product_qty = qty
|
||||
return mo_form.save()
|
||||
|
||||
def test_lock_planning_cannot_lock(self):
|
||||
mo = self._create_manufacturing_order()
|
||||
mo.action_confirm()
|
||||
with self.assertRaisesRegex(ValidationError, "Cannot lock planning"):
|
||||
mo.workorder_ids[0].toggle_lock_planning()
|
||||
|
||||
def test_lock_planning_is_locked(self):
|
||||
mo = self._create_manufacturing_order()
|
||||
mo.action_confirm()
|
||||
mo.button_plan()
|
||||
mo.workorder_ids[0].toggle_lock_planning()
|
||||
self.assertTrue(mo.workorder_ids[0].lock_planning)
|
||||
with self.assertRaisesRegex(ValidationError, "Workorder is locked"):
|
||||
mo.button_unplan()
|
||||
121
mrp_workorder_lock_planning/views/mrp_workorder.xml
Normal file
121
mrp_workorder_lock_planning/views/mrp_workorder.xml
Normal file
@@ -0,0 +1,121 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<odoo>
|
||||
<record model="ir.ui.view" id="mrp_production_workorder_tree_editable_view_inherit">
|
||||
<field name="name">mrp.production.work.order.tree.editable.inherit</field>
|
||||
<field name="model">mrp.workorder</field>
|
||||
<field
|
||||
name="inherit_id"
|
||||
ref="mrp.mrp_production_workorder_tree_editable_view"
|
||||
/>
|
||||
<field name="arch" type="xml">
|
||||
<field name="date_planned_start" position="before">
|
||||
<field name="lock_planning" invisible="1" />
|
||||
<button
|
||||
name="toggle_lock_planning"
|
||||
type="object"
|
||||
class="fa fa-unlock"
|
||||
title="Lock planning"
|
||||
string="Lock"
|
||||
attrs="{'invisible': ['|', '|', ('lock_planning', '=', True), ('date_planned_start', '=', False),('date_planned_finished', '=', False)]}"
|
||||
/>
|
||||
<button
|
||||
name="toggle_lock_planning"
|
||||
type="object"
|
||||
class="fa fa-lock text-danger"
|
||||
title="Unlock planning"
|
||||
string="Unlock"
|
||||
attrs="{'invisible': [('lock_planning', '=', False)]}"
|
||||
/>
|
||||
</field>
|
||||
<field name="date_planned_start" position="attributes">
|
||||
<attribute
|
||||
name="attrs"
|
||||
>{'readonly': [('lock_planning', '=', True)]}</attribute>
|
||||
<attribute name="force_save">1</attribute>
|
||||
</field>
|
||||
<field name="date_planned_finished" position="attributes">
|
||||
<attribute
|
||||
name="attrs"
|
||||
>{'readonly': [('lock_planning', '=', True)]}</attribute>
|
||||
<attribute name="force_save">1</attribute>
|
||||
</field>
|
||||
<field name="duration_expected" position="attributes">
|
||||
<attribute
|
||||
name="attrs"
|
||||
>{'readonly': [('lock_planning', '=', True)]}</attribute>
|
||||
<attribute name="force_save">1</attribute>
|
||||
</field>
|
||||
<field name="workcenter_id" position="attributes">
|
||||
<attribute
|
||||
name="attrs"
|
||||
>{'readonly': [('lock_planning', '=', True)]}</attribute>
|
||||
<attribute name="force_save">1</attribute>
|
||||
</field>
|
||||
</field>
|
||||
</record>
|
||||
<record model="ir.ui.view" id="mrp_production_workorder_form_view_inherit">
|
||||
<field name="name">mrp.production.work.order.form.inherit</field>
|
||||
<field name="model">mrp.workorder</field>
|
||||
<field name="inherit_id" ref="mrp.mrp_production_workorder_form_view_inherit" />
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//field[@name='json_popover']" position="after">
|
||||
<field name="lock_planning" invisible="1" />
|
||||
<button
|
||||
name="toggle_lock_planning"
|
||||
type="object"
|
||||
class="fa fa-unlock"
|
||||
title="Lock planning"
|
||||
string="Lock"
|
||||
attrs="{'invisible': ['|', '|', ('lock_planning', '=', True), ('date_planned_start', '=', False),('date_planned_finished', '=', False)]}"
|
||||
/>
|
||||
<button
|
||||
name="toggle_lock_planning"
|
||||
type="object"
|
||||
class="fa fa-lock text-danger"
|
||||
title="Unlock planning"
|
||||
string="Unlock"
|
||||
attrs="{'invisible': [('lock_planning', '=', False)]}"
|
||||
/>
|
||||
</xpath>
|
||||
<field name="date_planned_start" position="attributes">
|
||||
<attribute
|
||||
name="attrs"
|
||||
>{'readonly': [('lock_planning', '=', True)]}</attribute>
|
||||
<attribute name="force_save">1</attribute>
|
||||
</field>
|
||||
<field name="date_planned_finished" position="attributes">
|
||||
<attribute
|
||||
name="attrs"
|
||||
>{'readonly': [('lock_planning', '=', True)]}</attribute>
|
||||
<attribute name="force_save">1</attribute>
|
||||
</field>
|
||||
<field name="duration_expected" position="attributes">
|
||||
<attribute
|
||||
name="attrs"
|
||||
>{'readonly': [('lock_planning', '=', True)]}</attribute>
|
||||
<attribute name="force_save">1</attribute>
|
||||
</field>
|
||||
<field name="workcenter_id" position="attributes">
|
||||
<attribute
|
||||
name="attrs"
|
||||
>{'readonly': [('lock_planning', '=', True)]}</attribute>
|
||||
<attribute name="force_save">1</attribute>
|
||||
</field>
|
||||
</field>
|
||||
</record>
|
||||
<record id="mrp_workorder_view_gantt_inherit" model="ir.ui.view">
|
||||
<field name="name">mrp.workorder.view.gantt.inherit</field>
|
||||
<field name="model">mrp.workorder</field>
|
||||
<field name="inherit_id" ref="mrp.mrp_workorder_view_gantt" />
|
||||
<field name="arch" type="xml">
|
||||
<field name="json_popover" position="after">
|
||||
<field name="lock_planning" />
|
||||
</field>
|
||||
<xpath expr="//ul" position="inside">
|
||||
<li t-if="lock_planning" class="text-danger"><span
|
||||
class="fa fa-lock"
|
||||
> </span><strong>Locked planning</strong></li>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
</odoo>
|
||||
@@ -0,0 +1 @@
|
||||
../../../../mrp_workorder_lock_planning
|
||||
6
setup/mrp_workorder_lock_planning/setup.py
Normal file
6
setup/mrp_workorder_lock_planning/setup.py
Normal file
@@ -0,0 +1,6 @@
|
||||
import setuptools
|
||||
|
||||
setuptools.setup(
|
||||
setup_requires=['setuptools-odoo'],
|
||||
odoo_addon=True,
|
||||
)
|
||||
Reference in New Issue
Block a user