diff --git a/mrp_workorder_auto_final_lot/.gitignore b/mrp_workorder_auto_final_lot/.gitignore new file mode 100644 index 000000000..e6b1919da --- /dev/null +++ b/mrp_workorder_auto_final_lot/.gitignore @@ -0,0 +1,65 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] + +# C extensions +*.so + +# Distribution / packaging +.Python +env/ +bin/ +build/ +develop-eggs/ +dist/ +eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +*.egg-info/ +.installed.cfg +*.egg + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.coverage +.cache +nosetests.xml +coverage.xml + +# Translations +*.mo + +# Pycharm +.idea + +# Eclipse +.settings/ + +# Mr Developer +.mr.developer.cfg +.project +.pydevproject + +# Rope +.ropeproject + +# Django stuff: +*.log + +# Sphinx documentation +docs/_build/ + +# Backup files +*~ +*.swp + +.settings/ + diff --git a/mrp_workorder_auto_final_lot/.travis.yml b/mrp_workorder_auto_final_lot/.travis.yml new file mode 100644 index 000000000..90bed8677 --- /dev/null +++ b/mrp_workorder_auto_final_lot/.travis.yml @@ -0,0 +1,37 @@ +language: python + +python: + - "3.5" + +sudo: false +cache: pip + +addons: + postgresql: "9.6" + apt: + packages: + - expect-dev # provides unbuffer utility + - python-lxml # because pip installation is slow + - python-unidecode + - antiword # document module + - cups + - libcups2-dev + +env: + global: + - VERSION="12.0" TESTS="0" LINT_CHECK="0" EXCLUDE="mrp_hook,mrp_production_custom_variants_inherited_attributes,product_variant_custom,product_variant_custom_dimensions,product_variant_custom_dimensions_purchase,product_variant_custom_purchase,product_variant_custom_sale,product_variant_custom_sale_mrp_link" + matrix: + - LINT_CHECK="1" + - TESTS="1" ODOO_REPO="OCA/OCB" + - TESTS="1" ODOO_REPO="odoo/odoo" + +install: + - git clone --depth=1 https://github.com/avanzosc/maintainer-quality-tools.git ${HOME}/maintainer-quality-tools + - export PATH=${HOME}/maintainer-quality-tools/travis:${PATH} + - travis_install_nightly + +script: + - travis_wait travis_run_tests + +after_success: + - travis_after_tests_success diff --git a/mrp_workorder_auto_final_lot/README.rst b/mrp_workorder_auto_final_lot/README.rst new file mode 100644 index 000000000..1b7ec798d --- /dev/null +++ b/mrp_workorder_auto_final_lot/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_auto_final_lot/__init__.py b/mrp_workorder_auto_final_lot/__init__.py new file mode 100644 index 000000000..0650744f6 --- /dev/null +++ b/mrp_workorder_auto_final_lot/__init__.py @@ -0,0 +1 @@ +from . import models diff --git a/mrp_workorder_auto_final_lot/__manifest__.py b/mrp_workorder_auto_final_lot/__manifest__.py new file mode 100644 index 000000000..97d9d04e5 --- /dev/null +++ b/mrp_workorder_auto_final_lot/__manifest__.py @@ -0,0 +1,22 @@ +# Copyright 2020 Mikel Arregi Etxaniz - AvanzOSC +# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html +{ + "name": "Auto final lot", + "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", + "views/stock_production_lot_view.xml", + "views/mrp_production_view.xml", + "views/stock_move_line_view.xml", + ], + 'demo': [], + 'installable': True, + 'auto_install': False, +} diff --git a/mrp_workorder_auto_final_lot/models/__init__.py b/mrp_workorder_auto_final_lot/models/__init__.py new file mode 100644 index 000000000..7d9156d53 --- /dev/null +++ b/mrp_workorder_auto_final_lot/models/__init__.py @@ -0,0 +1,4 @@ +from . import mrp_workorder +from . import mrp_production +from . import mrp_production_lot +from . import stock_location diff --git a/mrp_workorder_auto_final_lot/models/mrp_production.py b/mrp_workorder_auto_final_lot/models/mrp_production.py new file mode 100644 index 000000000..ff8ad805e --- /dev/null +++ b/mrp_workorder_auto_final_lot/models/mrp_production.py @@ -0,0 +1,42 @@ +# 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 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_auto_final_lot/models/mrp_production_lot.py b/mrp_workorder_auto_final_lot/models/mrp_production_lot.py new file mode 100644 index 000000000..c7be2fe37 --- /dev/null +++ b/mrp_workorder_auto_final_lot/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_auto_final_lot/models/mrp_workorder.py b/mrp_workorder_auto_final_lot/models/mrp_workorder.py new file mode 100644 index 000000000..544082446 --- /dev/null +++ b/mrp_workorder_auto_final_lot/models/mrp_workorder.py @@ -0,0 +1,88 @@ +# 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 +from odoo.exceptions import UserError +from odoo.tools import float_compare, float_round + + +class MrpWorkorder(models.Model): + _inherit = "mrp.workorder" + + @api.multi + def generate_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': lot_name, + 'product_id': self.production_id.product_id.id}) + + @api.multi + def button_start(self): + res = super().button_start() + if self.move_raw_ids and self.product_id.tracking != 'none': + self.final_lot_id = self.generate_lot(int(self.qty_produced)) + return res + + @api.multi + def record_production(self): + if not self.move_raw_ids: + self.qty_produced = self.production_id.product_qty + self.button_finish() + return True + super().record_production() + if self.state == 'progress' and self.product_id.tracking != 'none': + self.final_lot_id = self.generate_lot(int(self.qty_produced)) + + @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_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)) + 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_auto_final_lot/models/stock_location.py b/mrp_workorder_auto_final_lot/models/stock_location.py new file mode 100644 index 000000000..b5794a927 --- /dev/null +++ b/mrp_workorder_auto_final_lot/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_auto_final_lot/views/mrp_production_view.xml b/mrp_workorder_auto_final_lot/views/mrp_production_view.xml new file mode 100644 index 000000000..ed92bd3bc --- /dev/null +++ b/mrp_workorder_auto_final_lot/views/mrp_production_view.xml @@ -0,0 +1,14 @@ + + + + mrp.production.produced.lots.form + mrp.production + + + + + + diff --git a/mrp_workorder_auto_final_lot/views/mrp_workorder_view.xml b/mrp_workorder_auto_final_lot/views/mrp_workorder_view.xml new file mode 100644 index 000000000..c7eae9c22 --- /dev/null +++ b/mrp_workorder_auto_final_lot/views/mrp_workorder_view.xml @@ -0,0 +1,60 @@ + + + + mrp.workorder.moves.form + stock.move.line + + + + + + + + + + workorder.stock.move.form + mrp.workorder + + + + + + + + + {'invisible': [('move_raw_ids', '=', [])]} + + + + + + + + + + + + + + + + + + + + + + + diff --git a/mrp_workorder_auto_final_lot/views/stock_move_line_view.xml b/mrp_workorder_auto_final_lot/views/stock_move_line_view.xml new file mode 100644 index 000000000..64ca4d6ca --- /dev/null +++ b/mrp_workorder_auto_final_lot/views/stock_move_line_view.xml @@ -0,0 +1,17 @@ + + + + stock.move.line.production.lot.form + stock.move.line + + + + top + + + + + + + + \ No newline at end of file diff --git a/mrp_workorder_auto_final_lot/views/stock_production_lot_view.xml b/mrp_workorder_auto_final_lot/views/stock_production_lot_view.xml new file mode 100644 index 000000000..4fc6d4a95 --- /dev/null +++ b/mrp_workorder_auto_final_lot/views/stock_production_lot_view.xml @@ -0,0 +1,18 @@ + + + + stock.production.lot.moves.form + stock.production.lot + + + + + + + + + + + + + \ No newline at end of file