From a4caeb6331d7de762a6b54dc054f326afe1cb3f2 Mon Sep 17 00:00:00 2001 From: Mikel Arregi Date: Tue, 1 Sep 2020 16:21:31 +0200 Subject: [PATCH 1/4] [ADD] mrp_workorder_moves_view --- mrp_workorder_moves_view/README.rst | 43 +++++++++++++++++++ mrp_workorder_moves_view/__init__.py | 0 mrp_workorder_moves_view/__manifest__.py | 19 ++++++++ .../views/mrp_workorder_view.xml | 17 ++++++++ 4 files changed, 79 insertions(+) create mode 100644 mrp_workorder_moves_view/README.rst create mode 100644 mrp_workorder_moves_view/__init__.py create mode 100644 mrp_workorder_moves_view/__manifest__.py create mode 100644 mrp_workorder_moves_view/views/mrp_workorder_view.xml diff --git a/mrp_workorder_moves_view/README.rst b/mrp_workorder_moves_view/README.rst new file mode 100644 index 000000000..1b7ec798d --- /dev/null +++ b/mrp_workorder_moves_view/README.rst @@ -0,0 +1,43 @@ +================== +Moves in workorder +================== + +Adds the moves related to the operations + +.. |badge1| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png + :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html + :alt: License: AGPL-3 + +|badge1| + +Adds a new level in products with the custom values specified in a order +line, manufacturing order or a lot. + +**Table of contents** + +.. contents:: + :local: + +Bug Tracker +=========== + +Bugs are tracked on `GitHub Issues `_. +In case of trouble, please check there if your issue has already been reported. +If you spotted it first, help us smashing it by providing a detailed and welcomed +`feedback `_. + +Do not contact contributors directly about support or help with technical issues. + +Credits +======= + +Authors +~~~~~~~ + +* AvanzOSC + +Contributors +~~~~~~~~~~~~ + +* Mikel Arregi +* Ana Juaristi diff --git a/mrp_workorder_moves_view/__init__.py b/mrp_workorder_moves_view/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/mrp_workorder_moves_view/__manifest__.py b/mrp_workorder_moves_view/__manifest__.py new file mode 100644 index 000000000..62cc53438 --- /dev/null +++ b/mrp_workorder_moves_view/__manifest__.py @@ -0,0 +1,19 @@ +# Copyright 2020 Mikel Arregi Etxaniz - AvanzOSC +# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html +{ + "name": "Workorder moves in view", + "version": "12.0.1.0.0", + "license": "AGPL-3", + "depends": [ + "mrp", + ], + "author": "AvanzOSC", + "website": "http://www.avanzosc.es", + "category": "Manufacturing", + "data": [ + "views/mrp_workorder_view.xml" + ], + 'demo': [], + 'installable': True, + 'auto_install': False, +} diff --git a/mrp_workorder_moves_view/views/mrp_workorder_view.xml b/mrp_workorder_moves_view/views/mrp_workorder_view.xml new file mode 100644 index 000000000..d07e1916d --- /dev/null +++ b/mrp_workorder_moves_view/views/mrp_workorder_view.xml @@ -0,0 +1,17 @@ + + + + workorder.stock.move.form + mrp.workorder + + + + + {'invisible': [('move_raw_ids', '=', [])]} + + + + + + + \ No newline at end of file From 8fd98e4217968311005a2f6a322d392d8c16aff4 Mon Sep 17 00:00:00 2001 From: Mikel Arregi Date: Fri, 4 Sep 2020 10:32:31 +0200 Subject: [PATCH 2/4] [IMP] mrp_workorder_moves_view: button in workorder to execute all production movements in serial type products --- mrp_workorder_moves_view/__init__.py | 1 + mrp_workorder_moves_view/models/__init__.py | 3 + .../models/mrp_production.py | 0 .../models/mrp_workorder.py | 74 +++++++++++++++++++ .../models/stock_move_line.py | 14 ++++ .../views/mrp_workorder_view.xml | 48 +++++++++++- .../views/stock_production_lot_view.xml | 16 ++++ 7 files changed, 153 insertions(+), 3 deletions(-) create mode 100644 mrp_workorder_moves_view/models/__init__.py create mode 100644 mrp_workorder_moves_view/models/mrp_production.py create mode 100644 mrp_workorder_moves_view/models/mrp_workorder.py create mode 100644 mrp_workorder_moves_view/models/stock_move_line.py create mode 100644 mrp_workorder_moves_view/views/stock_production_lot_view.xml diff --git a/mrp_workorder_moves_view/__init__.py b/mrp_workorder_moves_view/__init__.py index e69de29bb..0650744f6 100644 --- a/mrp_workorder_moves_view/__init__.py +++ b/mrp_workorder_moves_view/__init__.py @@ -0,0 +1 @@ +from . import models diff --git a/mrp_workorder_moves_view/models/__init__.py b/mrp_workorder_moves_view/models/__init__.py new file mode 100644 index 000000000..728ba8c19 --- /dev/null +++ b/mrp_workorder_moves_view/models/__init__.py @@ -0,0 +1,3 @@ +from . import mrp_workorder +from . import mrp_production +from . import stock_move_line diff --git a/mrp_workorder_moves_view/models/mrp_production.py b/mrp_workorder_moves_view/models/mrp_production.py new file mode 100644 index 000000000..e69de29bb diff --git a/mrp_workorder_moves_view/models/mrp_workorder.py b/mrp_workorder_moves_view/models/mrp_workorder.py new file mode 100644 index 000000000..a2be49f15 --- /dev/null +++ b/mrp_workorder_moves_view/models/mrp_workorder.py @@ -0,0 +1,74 @@ +# Copyright 2020 Mikel Arregi Etxaniz - AvanzOSC +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). +from odoo import api, fields, models +from odoo.tools.safe_eval import safe_eval +from odoo.models import expression + + +class MrpWorkorder(models.Model): + _inherit = "mrp.workorder" + + @api.multi + def _create_serial_lot(self, suffix): + return self.env['stock.production.lot'].create( + {'name': "{}-{}".format(self.production_id.name, suffix), + 'product_id': self.production_id.product_id.id}) + + @api.multi + def button_execute_all_moves(self): + for order in self: + lot_incremental = 0 + while (order.qty_production != order.qty_produced or + order.state != 'done'): + lot_incremental += 1 + serial_lot = order._create_serial_lot(lot_incremental) + order.final_lot_id = serial_lot + order.record_production() + + @api.multi + def button_open_active_move_lines(self): + self.ensure_one() + self = self.with_context( + default_workorder_id=self.id) + action = self.env.ref('stock.stock_move_line_action') + action_dict = action.read()[0] if action else {} + action_dict['context'] = safe_eval( + action_dict.get('context', '{}')) + action_dict['context'].pop('search_default_done', False) + action_dict['context'].pop('search_default_groupby_product_id', False) + action_dict['context'].update({ + 'default_workorder_id': self.id, + 'search_default_groupby_lot_produced_id': 1, + }) + domain = expression.AND([ + [('workorder_id', '=', self.id)], + safe_eval(action.domain or '[]')]) + action_dict.update({'domain': domain}) + return action_dict + + def button_open_lots(self): + self.ensure_one() + location_obj = self.env['stock.location'] + physical_location = location_obj.search( + [('name', '=', 'Physical Locations')]) + virtual_location = location_obj.search( + [('name', '=', 'Virtual Locations')]) + move_lines = self.active_move_lines | self.active_move_lines + produce_lines = move_lines.filtered( + lambda x: x.location_id._has_parent(virtual_location) and + x.location_dest_id._has_parent(physical_location)) + produce_lots = produce_lines.mapped('lot_id') + action = self.env.ref('stock.action_production_lot_form') + action_dict = action.read()[0] if action else {} + action_dict['context'] = safe_eval( + action_dict.get('context', '{}')) + action_dict['context'].pop('search_default_group_by_product', False) + action_dict['context'].update({ + 'default_workorder_id': self.id, + }) + action_dict.update['ids'] = produce_lots + domain = expression.AND([ + [('id', 'in', produce_lots.ids)], + safe_eval(action.domain or '[]')]) + action_dict.update({'domain': domain}) + return action_dict \ No newline at end of file diff --git a/mrp_workorder_moves_view/models/stock_move_line.py b/mrp_workorder_moves_view/models/stock_move_line.py new file mode 100644 index 000000000..ac85b1338 --- /dev/null +++ b/mrp_workorder_moves_view/models/stock_move_line.py @@ -0,0 +1,14 @@ +# Copyright 2020 Mikel Arregi Etxaniz - AvanzOSC +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). +from odoo import api, fields, models + + +class StockProductionLot(models.Model): + _inherit = "stock.production.lot" + + produce_move_line_ids = fields.One2many( + comodel_name="stock.move.line", inverse_name='lot_id', + string="Produced Lines") + consume_move_line_ids = fields.One2many( + comodel_name="stock.move.line", inverse_name='lot_produced_id', + string="Consumed Lines") diff --git a/mrp_workorder_moves_view/views/mrp_workorder_view.xml b/mrp_workorder_moves_view/views/mrp_workorder_view.xml index d07e1916d..c4a98d7d6 100644 --- a/mrp_workorder_moves_view/views/mrp_workorder_view.xml +++ b/mrp_workorder_moves_view/views/mrp_workorder_view.xml @@ -1,17 +1,59 @@ + + mrp.workorder.moves.form + stock.move.line + + + + + + + + workorder.stock.move.form mrp.workorder - + + + + + {'invisible': [('move_raw_ids', '=', [])]} + + + - + + + + + + + + + + + + + + - \ No newline at end of file + diff --git a/mrp_workorder_moves_view/views/stock_production_lot_view.xml b/mrp_workorder_moves_view/views/stock_production_lot_view.xml new file mode 100644 index 000000000..27ff92b6a --- /dev/null +++ b/mrp_workorder_moves_view/views/stock_production_lot_view.xml @@ -0,0 +1,16 @@ + + + + stock.production.lot.moves.form + stock.production.lot + + + + + + + + + + + \ No newline at end of file From d541d13bc4d2debda20f7b073243f0e2701522d9 Mon Sep 17 00:00:00 2001 From: Mikel Arregi Date: Mon, 7 Sep 2020 18:25:09 +0200 Subject: [PATCH 3/4] [IMP] mrp_workorder_moves_view: added multiple shortcuts for usability --- mrp_workorder_moves_view/__manifest__.py | 4 +- mrp_workorder_moves_view/models/__init__.py | 3 +- .../models/mrp_production.py | 40 +++++++++++++++++++ .../models/mrp_production_lot.py | 34 ++++++++++++++++ .../models/mrp_workorder.py | 6 +-- .../models/stock_location.py | 15 +++++++ .../models/stock_move_line.py | 14 ------- .../views/mrp_production_view.xml | 14 +++++++ .../views/mrp_workorder_view.xml | 2 +- .../views/stock_production_lot_view.xml | 4 +- 10 files changed, 115 insertions(+), 21 deletions(-) create mode 100644 mrp_workorder_moves_view/models/mrp_production_lot.py create mode 100644 mrp_workorder_moves_view/models/stock_location.py delete mode 100644 mrp_workorder_moves_view/models/stock_move_line.py create mode 100644 mrp_workorder_moves_view/views/mrp_production_view.xml diff --git a/mrp_workorder_moves_view/__manifest__.py b/mrp_workorder_moves_view/__manifest__.py index 62cc53438..8bd2c4fbe 100644 --- a/mrp_workorder_moves_view/__manifest__.py +++ b/mrp_workorder_moves_view/__manifest__.py @@ -11,7 +11,9 @@ "website": "http://www.avanzosc.es", "category": "Manufacturing", "data": [ - "views/mrp_workorder_view.xml" + "views/mrp_workorder_view.xml", + "views/stock_production_lot_view.xml", + "views/mrp_production_view.xml", ], 'demo': [], 'installable': True, diff --git a/mrp_workorder_moves_view/models/__init__.py b/mrp_workorder_moves_view/models/__init__.py index 728ba8c19..7d9156d53 100644 --- a/mrp_workorder_moves_view/models/__init__.py +++ b/mrp_workorder_moves_view/models/__init__.py @@ -1,3 +1,4 @@ from . import mrp_workorder from . import mrp_production -from . import stock_move_line +from . import mrp_production_lot +from . import stock_location diff --git a/mrp_workorder_moves_view/models/mrp_production.py b/mrp_workorder_moves_view/models/mrp_production.py index e69de29bb..1f3b2c757 100644 --- a/mrp_workorder_moves_view/models/mrp_production.py +++ b/mrp_workorder_moves_view/models/mrp_production.py @@ -0,0 +1,40 @@ +# Copyright 2020 Mikel Arregi Etxaniz - AvanzOSC +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). +from odoo import api, models +from odoo.tools.safe_eval import safe_eval +from odoo.models import expression + +class MrpProduction(models.Model): + _inherit = "mrp.production" + + @api.multi + def button_open_lots(self): + self.ensure_one() + location_obj = self.env['stock.location'] + physical_location = location_obj.search( + [('name', '=', 'Physical Locations')]) + virtual_location = location_obj.search( + [('name', '=', 'Virtual Locations')]) + move_lines = self.env['stock.move.line'] + for workorder in self.env['mrp.workorder'].search( + [('production_id', '=', self.id)]): + move_lines |= workorder.active_move_line_ids + move_lines |= workorder.move_line_ids + produce_lines = move_lines.filtered( + lambda x: x.location_id._has_parent(virtual_location) and + x.location_dest_id._has_parent(physical_location)) + produce_lots = produce_lines.mapped('lot_id') + action = self.env.ref('stock.action_production_lot_form') + action_dict = action.read()[0] if action else {} + action_dict['context'] = safe_eval( + action_dict.get('context', '{}')) + action_dict['context'].pop('search_default_group_by_product', False) + action_dict['context'].update({ + 'default_workorder_id': self.id, + }) + action_dict['ids'] = produce_lots + domain = expression.AND([ + [('id', 'in', produce_lots.ids)], + safe_eval(action.domain or '[]')]) + action_dict.update({'domain': domain}) + return action_dict diff --git a/mrp_workorder_moves_view/models/mrp_production_lot.py b/mrp_workorder_moves_view/models/mrp_production_lot.py new file mode 100644 index 000000000..c7be2fe37 --- /dev/null +++ b/mrp_workorder_moves_view/models/mrp_production_lot.py @@ -0,0 +1,34 @@ +# Copyright 2020 Mikel Arregi Etxaniz - AvanzOSC +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). +from odoo import api, fields, models + + +class StockProductionLot(models.Model): + _inherit = "stock.production.lot" + + produce_move_line_ids = fields.One2many( + comodel_name="stock.move.line", inverse_name='lot_id', + string="Produced Lines") + consume_move_line_ids = fields.One2many( + comodel_name="stock.move.line", inverse_name='lot_produced_id', + string="Consumed Lines") + wo_produce_move_line_ids = fields.One2many( + comodel_name="stock.move.line", compute="_compute_lot_moves", + string="Produced Lines") + wo_consume_move_line_ids = fields.One2many( + comodel_name="stock.move.line", compute="_compute_lot_moves", + string="Consumed Lines") + + def _compute_lot_moves(self): + for lot in self: + workorder_id = self._context.get('moves_workorder_id', False) + if workorder_id: + workorder_id = self._context.get('active_id', False) + workorder_moves = self.env['stock.move.line'].search([( + 'workorder_id', '=', workorder_id)]) + lot.wo_produce_move_line_ids = \ + lot.produce_move_line_ids.filtered( + lambda x: x.id in workorder_moves.ids) + lot.wo_consume_move_line_ids = \ + lot.consume_move_line_ids.filtered( + lambda x: x.id in workorder_moves.ids) diff --git a/mrp_workorder_moves_view/models/mrp_workorder.py b/mrp_workorder_moves_view/models/mrp_workorder.py index a2be49f15..92d45a4e7 100644 --- a/mrp_workorder_moves_view/models/mrp_workorder.py +++ b/mrp_workorder_moves_view/models/mrp_workorder.py @@ -1,6 +1,6 @@ # Copyright 2020 Mikel Arregi Etxaniz - AvanzOSC # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). -from odoo import api, fields, models +from odoo import api, models from odoo.tools.safe_eval import safe_eval from odoo.models import expression @@ -53,7 +53,7 @@ def button_open_lots(self): [('name', '=', 'Physical Locations')]) virtual_location = location_obj.search( [('name', '=', 'Virtual Locations')]) - move_lines = self.active_move_lines | self.active_move_lines + move_lines = self.active_move_line_ids | self.move_line_ids produce_lines = move_lines.filtered( lambda x: x.location_id._has_parent(virtual_location) and x.location_dest_id._has_parent(physical_location)) @@ -66,7 +66,7 @@ def button_open_lots(self): action_dict['context'].update({ 'default_workorder_id': self.id, }) - action_dict.update['ids'] = produce_lots + action_dict['ids'] = produce_lots domain = expression.AND([ [('id', 'in', produce_lots.ids)], safe_eval(action.domain or '[]')]) diff --git a/mrp_workorder_moves_view/models/stock_location.py b/mrp_workorder_moves_view/models/stock_location.py new file mode 100644 index 000000000..b5794a927 --- /dev/null +++ b/mrp_workorder_moves_view/models/stock_location.py @@ -0,0 +1,15 @@ +# Copyright 2020 Mikel Arregi Etxaniz - AvanzOSC +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). +from odoo import api, models + + +class StockLocation(models.Model): + _inherit = "stock.location" + + def _has_parent(self, parent_location): + if not self.location_id: + return False + elif self.location_id == parent_location: + return True + else: + return self.location_id._has_parent(parent_location) diff --git a/mrp_workorder_moves_view/models/stock_move_line.py b/mrp_workorder_moves_view/models/stock_move_line.py deleted file mode 100644 index ac85b1338..000000000 --- a/mrp_workorder_moves_view/models/stock_move_line.py +++ /dev/null @@ -1,14 +0,0 @@ -# Copyright 2020 Mikel Arregi Etxaniz - AvanzOSC -# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). -from odoo import api, fields, models - - -class StockProductionLot(models.Model): - _inherit = "stock.production.lot" - - produce_move_line_ids = fields.One2many( - comodel_name="stock.move.line", inverse_name='lot_id', - string="Produced Lines") - consume_move_line_ids = fields.One2many( - comodel_name="stock.move.line", inverse_name='lot_produced_id', - string="Consumed Lines") diff --git a/mrp_workorder_moves_view/views/mrp_production_view.xml b/mrp_workorder_moves_view/views/mrp_production_view.xml new file mode 100644 index 000000000..ed92bd3bc --- /dev/null +++ b/mrp_workorder_moves_view/views/mrp_production_view.xml @@ -0,0 +1,14 @@ + + + + mrp.production.produced.lots.form + mrp.production + + + + + + diff --git a/mrp_workorder_moves_view/views/mrp_workorder_view.xml b/mrp_workorder_moves_view/views/mrp_workorder_view.xml index c4a98d7d6..f91817d89 100644 --- a/mrp_workorder_moves_view/views/mrp_workorder_view.xml +++ b/mrp_workorder_moves_view/views/mrp_workorder_view.xml @@ -25,7 +25,7 @@ - diff --git a/mrp_workorder_moves_view/views/stock_production_lot_view.xml b/mrp_workorder_moves_view/views/stock_production_lot_view.xml index 27ff92b6a..4fc6d4a95 100644 --- a/mrp_workorder_moves_view/views/stock_production_lot_view.xml +++ b/mrp_workorder_moves_view/views/stock_production_lot_view.xml @@ -3,12 +3,14 @@ stock.production.lot.moves.form stock.production.lot - + + + From 61d2977e7cd86d9293997bf1cd359725baf46c9b Mon Sep 17 00:00:00 2001 From: Mikel Arregi Date: Tue, 1 Dec 2020 16:15:12 +0100 Subject: [PATCH 4/4] [IMP] mrp_workorder_moves_view --- mrp_workorder_moves_view/__manifest__.py | 1 + mrp_workorder_moves_view/models/__init__.py | 1 + .../models/mrp_production.py | 4 +- .../models/mrp_workorder.py | 239 +++++++++++++++++- mrp_workorder_moves_view/models/res_config.py | 28 ++ .../views/mrp_workorder_view.xml | 1 + .../views/stock_move_line_view.xml | 17 ++ 7 files changed, 281 insertions(+), 10 deletions(-) create mode 100644 mrp_workorder_moves_view/models/res_config.py create mode 100644 mrp_workorder_moves_view/views/stock_move_line_view.xml diff --git a/mrp_workorder_moves_view/__manifest__.py b/mrp_workorder_moves_view/__manifest__.py index 8bd2c4fbe..1d9d525da 100644 --- a/mrp_workorder_moves_view/__manifest__.py +++ b/mrp_workorder_moves_view/__manifest__.py @@ -14,6 +14,7 @@ "views/mrp_workorder_view.xml", "views/stock_production_lot_view.xml", "views/mrp_production_view.xml", + "views/stock_move_line_view.xml", ], 'demo': [], 'installable': True, diff --git a/mrp_workorder_moves_view/models/__init__.py b/mrp_workorder_moves_view/models/__init__.py index 7d9156d53..f853e1667 100644 --- a/mrp_workorder_moves_view/models/__init__.py +++ b/mrp_workorder_moves_view/models/__init__.py @@ -2,3 +2,4 @@ from . import mrp_production from . import mrp_production_lot from . import stock_location +from . import res_config diff --git a/mrp_workorder_moves_view/models/mrp_production.py b/mrp_workorder_moves_view/models/mrp_production.py index 1f3b2c757..ff8ad805e 100644 --- a/mrp_workorder_moves_view/models/mrp_production.py +++ b/mrp_workorder_moves_view/models/mrp_production.py @@ -1,12 +1,14 @@ # Copyright 2020 Mikel Arregi Etxaniz - AvanzOSC # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). -from odoo import api, models +from odoo import api, fields, models from odoo.tools.safe_eval import safe_eval from odoo.models import expression + class MrpProduction(models.Model): _inherit = "mrp.production" + @api.multi def button_open_lots(self): self.ensure_one() diff --git a/mrp_workorder_moves_view/models/mrp_workorder.py b/mrp_workorder_moves_view/models/mrp_workorder.py index 92d45a4e7..a000f021a 100644 --- a/mrp_workorder_moves_view/models/mrp_workorder.py +++ b/mrp_workorder_moves_view/models/mrp_workorder.py @@ -1,29 +1,250 @@ # Copyright 2020 Mikel Arregi Etxaniz - AvanzOSC # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). -from odoo import api, models +from odoo import api, models, _ from odoo.tools.safe_eval import safe_eval from odoo.models import expression +from odoo.exceptions import UserError +from odoo.tools import float_compare, float_round class MrpWorkorder(models.Model): _inherit = "mrp.workorder" + def _generate_final_lot_ids(self, final_lot): + """ Generate stock move lines """ + self.ensure_one() + MoveLine = self.env['stock.move.line'] + tracked_moves = self.move_raw_ids.filtered( + lambda move: move.state not in ('done', 'cancel') and move.product_id.tracking != 'none' and move.product_id != self.production_id.product_id and move.bom_line_id) + for move in tracked_moves: + qty = move.unit_factor * self.qty_producing + if move.product_id.tracking == 'serial': + while float_compare(qty, 0.0, precision_rounding=move.product_uom.rounding) > 0: + MoveLine.create({ + 'move_id': move.id, + 'product_uom_qty': 0, + 'product_uom_id': move.product_uom.id, + 'qty_done': min(1, qty), + 'production_id': self.production_id.id, + 'workorder_id': self.id, + 'product_id': move.product_id.id, + 'done_wo': False, + 'location_id': move.location_id.id, + 'location_dest_id': move.location_dest_id.id, + 'lot_produced_id': final_lot.id, + }) + qty -= 1 + else: + MoveLine.create({ + 'move_id': move.id, + 'product_uom_qty': 0, + 'product_uom_id': move.product_uom.id, + 'qty_done': qty, + 'product_id': move.product_id.id, + 'production_id': self.production_id.id, + 'workorder_id': self.id, + 'done_wo': False, + 'location_id': move.location_id.id, + 'location_dest_id': move.location_dest_id.id, + 'lot_produced_id': final_lot.id, + }) + @api.multi def _create_serial_lot(self, suffix): + lot_name = "{}-{}".format(self.production_id.name, suffix) + lot_obj = self.env['stock.production.lot'] + lot_id = lot_obj.search([("name", "=", lot_name)]) + if lot_id: + return lot_id return self.env['stock.production.lot'].create( - {'name': "{}-{}".format(self.production_id.name, suffix), + {'name': lot_name, 'product_id': self.production_id.product_id.id}) + # @api.multi + # def button_execute_all_moves(self): + # for order in self: + # lot_incremental = 0 + # while (order.qty_production != order.qty_produced or + # order.state != 'done' and not order.final_lot_id): + # lot_incremental += 1 + # serial_lot = order._create_serial_lot(lot_incremental) + # order.final_lot_id = serial_lot + # order.all_record_production() + # order._make_active_moves() + @api.multi + def button_start(self): + res = super().button_start() + self.button_create_all_moves() + return res + @api.multi def button_execute_all_moves(self): for order in self: - lot_incremental = 0 - while (order.qty_production != order.qty_produced or - order.state != 'done'): - lot_incremental += 1 - serial_lot = order._create_serial_lot(lot_incremental) - order.final_lot_id = serial_lot - order.record_production() + order.all_record_production() + order._make_active_moves() + order.final_workorder() + order.qty_produced += order.qty_producing + rounding = order.production_id.product_uom_id.rounding + if float_compare(order.qty_produced, order.production_id.product_qty, + precision_rounding=rounding) >= 0: + order.button_finish() + + + @api.multi + def generate_lots(self): + lot_incremental = 0 + serial_lot = self.env['stock.production.lot'] + i=0 + while (self.qty_production - i > 0): + lot_incremental += 1 + serial_lot |= self._create_serial_lot(lot_incremental) + i += 1 + return serial_lot + + @api.multi + def button_create_all_moves(self): + i = 1 + final_lots = self.generate_lots() + active_moves = self.active_move_line_ids.filtered( + lambda x: x.product_id.tracking != 'none') + if active_moves: + active_moves.lot_produced_id = final_lots[0].id + while (self.qty_production - i > 0): + self._generate_final_lot_ids(final_lots[i]) + i += 1 + + @api.multi + def all_record_production(self): + if not self: + return True + + self.ensure_one() + if self.qty_producing <= 0: + raise UserError(_('Please set the quantity you are currently producing. It should be different from zero.')) + + generated_lots = self.env['stock.production.lot'] + if (self.production_id.product_id.tracking != 'none') and not self.final_lot_id and self.move_raw_ids: + generated_lots = self.generate_lots() + + # Update quantities done on each raw material line + # For each untracked component without any 'temporary' move lines, + # (the new workorder tablet view allows registering consumed quantities for untracked components) + # we assume that only the theoretical quantity was used + for lot in generated_lots: + for move in self.move_raw_ids: + if move.has_tracking == 'none' and (move.state not in ('done', 'cancel')) and move.bom_line_id\ + and move.unit_factor and not move.move_line_ids.filtered(lambda ml: not ml.done_wo): + rounding = move.product_uom.rounding + if self.product_id.tracking != 'none': + qty_to_add = float_round(self.qty_producing * move.unit_factor, precision_rounding=rounding) + move._generate_consumed_move_line(qty_to_add, lot) + elif len(move._get_move_lines()) < 2: + move.quantity_done += float_round(self.qty_producing * move.unit_factor, precision_rounding=rounding) + else: + move._set_quantity_done(move.quantity_done + float_round(self.qty_producing * move.unit_factor, precision_rounding=rounding)) + return True + + + def _make_active_moves(self): + # Transfer quantities from temporary to final move lots or make them final + for move_line in self.active_move_line_ids: + # Check if move_line already exists + if move_line.qty_done <= 0: # rounding... + move_line.sudo().unlink() + continue + if move_line.product_id.tracking != 'none' and not move_line.lot_id: + raise UserError(_( + 'You should provide a lot/serial number for a component.')) + # Search other move_line where it could be added: + lots = self.move_line_ids.filtered( + lambda x: (x.lot_id.id == move_line.lot_id.id) and ( + not x.lot_produced_id) and (not x.done_move) and ( + x.product_id == move_line.product_id)) + if lots: + lots[0].qty_done += move_line.qty_done + #lots[0].lot_produced_id = self.final_lot_id + self._link_to_quality_check(move_line, lots[0]) + move_line.sudo().unlink() + else: + #move_line.lot_produced_id = self.final_lot_id + move_line.done_wo = True + + # self.move_line_ids.filtered( + # lambda move_line: not move_line.done_move and not move_line.lot_produced_id and move_line.qty_done > 0 + # ).write({ + # 'lot_produced_id': self.final_lot_id.id, + # 'lot_produced_qty': self.qty_producing + # }) + + def final_workorder(self): + # If last work order, then post lots used + # TODO: should be same as checking if for every workorder something has been done? + generated_lots = self.generate_lots() + if not self.next_work_order_id: + production_move = self.production_id.move_finished_ids.filtered( + lambda x: (x.product_id.id == self.production_id.product_id.id) and (x.state not in ('done', 'cancel'))) + if production_move.product_id.tracking != 'none': + move_line = production_move.move_line_ids.filtered(lambda x: + x.lot_id.id in generated_lots) + if move_line: + for line in move_line: + move_line.product_uom_qty = self.qty_production + move_line.qty_done = self.qty_production + else: + for lot in generated_lots: + location_dest_id = production_move.location_dest_id.get_putaway_strategy(self.product_id).id or production_move.location_dest_id.id + move_line.create({'move_id': production_move.id, + 'product_id': production_move.product_id.id, + 'lot_id': lot.id, + 'product_uom_qty': self.qty_producing, + 'product_uom_id': production_move.product_uom.id, + 'qty_done': self.qty_producing, + 'workorder_id': self.id, + 'location_id': production_move.location_id.id, + 'location_dest_id': location_dest_id, + }) + else: + production_move._set_quantity_done(self.qty_production) + + if not self.next_work_order_id: + for by_product_move in self._get_byproduct_move_to_update(): + if by_product_move.has_tracking != 'serial': + values = self._get_byproduct_move_line(by_product_move, self.qty_production * by_product_move.unit_factor) + self.env['stock.move.line'].create(values) + elif by_product_move.has_tracking == 'serial': + qty_todo = by_product_move.product_uom._compute_quantity(self.qty_production * by_product_move.unit_factor, by_product_move.product_id.uom_id) + for i in range(0, int(float_round(qty_todo, precision_digits=0))): + values = self._get_byproduct_move_line(by_product_move, 1) + self.env['stock.move.line'].create(values) + + # # Update workorder quantity produced + # self.qty_produced += self.qty_producing + # + # if self.final_lot_id: + # self.final_lot_id.use_next_on_work_order_id = self.next_work_order_id + # self.final_lot_id = False + # + # # One a piece is produced, you can launch the next work order + # self._start_nextworkorder() + # + # # Set a qty producing + # rounding = self.production_id.product_uom_id.rounding + # if float_compare(self.qty_produced, self.production_id.product_qty, precision_rounding=rounding) >= 0: + # self.qty_producing = 0 + # elif self.production_id.product_id.tracking == 'serial': + # self._assign_default_final_lot_id() + # self.qty_producing = 1.0 + # self._generate_lot_ids() + # else: + # self.qty_producing = float_round(self.production_id.product_qty - self.qty_produced, precision_rounding=rounding) + # self._generate_lot_ids() + # + # if self.next_work_order_id and self.next_work_order_id.state not in ['done', 'cancel'] and self.production_id.product_id.tracking != 'none': + # self.next_work_order_id._assign_default_final_lot_id() + + # if float_compare(self.qty_produced, self.production_id.product_qty, precision_rounding=rounding) >= 0: + # self.button_finish() + return True @api.multi def button_open_active_move_lines(self): diff --git a/mrp_workorder_moves_view/models/res_config.py b/mrp_workorder_moves_view/models/res_config.py new file mode 100644 index 000000000..4f24cee8f --- /dev/null +++ b/mrp_workorder_moves_view/models/res_config.py @@ -0,0 +1,28 @@ +# Copyright 2020 Mikel Arregi Etxaniz - AvanzOSC +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). +from odoo import api, fields, models + +PARAMS = [ + ("do_all", "mrp_workorder_moves_view.do_all"), +] + +class ResConfingSettings(models.TransientModel): + _inherit = "res.config.settings" + + do_all = fields.boolean(string="Process whole workorder") + + @api.multi + def set_params(self): + self.ensure_one() + field_name = "mrp_workorder_moves_view.do_all" + key_name = "do_all" + value = getattr(self, field_name, '').strip() + self.env['ir.config_parameter'].set_param(key_name, value) + + @api.multi + def get_default_params(self, cr, uid, fields, context=None): + res = {} + for field_name, key_name in PARAMS: + res[field_name] = self.env['ir.config_parameter'].get_param( + key_name, '').strip() + return res \ No newline at end of file diff --git a/mrp_workorder_moves_view/views/mrp_workorder_view.xml b/mrp_workorder_moves_view/views/mrp_workorder_view.xml index f91817d89..9af321022 100644 --- a/mrp_workorder_moves_view/views/mrp_workorder_view.xml +++ b/mrp_workorder_moves_view/views/mrp_workorder_view.xml @@ -18,6 +18,7 @@