From e11e9f710455a182331c52ebaf616a9a5bdae120 Mon Sep 17 00:00:00 2001 From: Alexandre Fayolle Date: Thu, 15 Apr 2021 18:08:36 +0200 Subject: [PATCH] [ADD] stock_measuring_device This is a rework of the stock_cubiscan module to allow supporting different kind of measuring devices, with different communication protocols. We use components from OCA/connector to abstract the implementation of the backend (some devices are synchronous, others are asynchronous...). Others PR to come for stock_measuring_device_cubiscan and stock_measuring_device_zippcube. --- oca_dependencies.txt | 1 + stock_measuring_device/README.rst | 112 +++++ stock_measuring_device/__init__.py | 5 + stock_measuring_device/__manifest__.py | 34 ++ stock_measuring_device/components/__init__.py | 3 + .../components/measuring_device_component.py | 46 ++ stock_measuring_device/data/uom.xml | 9 + stock_measuring_device/models/__init__.py | 5 + .../models/measuring_device.py | 104 ++++ .../models/product_packaging.py | 49 ++ .../models/stock_warehouse.py | 12 + stock_measuring_device/readme/CONFIGURE.rst | 5 + .../readme/CONTRIBUTORS.rst | 3 + stock_measuring_device/readme/DESCRIPTION.rst | 3 + stock_measuring_device/readme/INSTALL.rst | 4 + stock_measuring_device/readme/ROADMAP.rst | 2 + stock_measuring_device/readme/USAGE.rst | 2 + .../security/ir.model.access.csv | 3 + .../static/description/index.html | 458 ++++++++++++++++++ .../static/src/scss/measuring_wizard.scss | 33 ++ stock_measuring_device/views/assets.xml | 16 + .../views/measuring_device_view.xml | 51 ++ stock_measuring_device/views/menu.xml | 9 + stock_measuring_device/wizard/__init__.py | 4 + .../wizard/measuring_wizard.py | 178 +++++++ .../wizard/measuring_wizard.xml | 111 +++++ .../wizard/measuring_wizard_line.py | 74 +++ 27 files changed, 1336 insertions(+) create mode 100644 stock_measuring_device/README.rst create mode 100644 stock_measuring_device/__init__.py create mode 100644 stock_measuring_device/__manifest__.py create mode 100644 stock_measuring_device/components/__init__.py create mode 100644 stock_measuring_device/components/measuring_device_component.py create mode 100644 stock_measuring_device/data/uom.xml create mode 100644 stock_measuring_device/models/__init__.py create mode 100644 stock_measuring_device/models/measuring_device.py create mode 100644 stock_measuring_device/models/product_packaging.py create mode 100644 stock_measuring_device/models/stock_warehouse.py create mode 100644 stock_measuring_device/readme/CONFIGURE.rst create mode 100644 stock_measuring_device/readme/CONTRIBUTORS.rst create mode 100644 stock_measuring_device/readme/DESCRIPTION.rst create mode 100644 stock_measuring_device/readme/INSTALL.rst create mode 100644 stock_measuring_device/readme/ROADMAP.rst create mode 100644 stock_measuring_device/readme/USAGE.rst create mode 100644 stock_measuring_device/security/ir.model.access.csv create mode 100644 stock_measuring_device/static/description/index.html create mode 100644 stock_measuring_device/static/src/scss/measuring_wizard.scss create mode 100644 stock_measuring_device/views/assets.xml create mode 100644 stock_measuring_device/views/measuring_device_view.xml create mode 100644 stock_measuring_device/views/menu.xml create mode 100644 stock_measuring_device/wizard/__init__.py create mode 100644 stock_measuring_device/wizard/measuring_wizard.py create mode 100644 stock_measuring_device/wizard/measuring_wizard.xml create mode 100644 stock_measuring_device/wizard/measuring_wizard_line.py diff --git a/oca_dependencies.txt b/oca_dependencies.txt index 56779b7ed..731715c8d 100644 --- a/oca_dependencies.txt +++ b/oca_dependencies.txt @@ -1,4 +1,5 @@ account-analytic +connector product-attribute server-env server-ux diff --git a/stock_measuring_device/README.rst b/stock_measuring_device/README.rst new file mode 100644 index 000000000..e86198b42 --- /dev/null +++ b/stock_measuring_device/README.rst @@ -0,0 +1,112 @@ +====================== +Stock Measuring Device +====================== + +.. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |badge1| image:: https://img.shields.io/badge/maturity-Alpha-red.png + :target: https://odoo-community.org/page/development-status + :alt: Alpha +.. |badge2| 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 +.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fstock--logistics--warehouse-lightgray.png?logo=github + :target: https://github.com/OCA/stock-logistics-warehouse/tree/13.0/stock_measuring_device + :alt: OCA/stock-logistics-warehouse +.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png + :target: https://translation.odoo-community.org/projects/stock-logistics-warehouse-13-0/stock-logistics-warehouse-13-0-stock_measuring_device + :alt: Translate me on Weblate +.. |badge5| image:: https://img.shields.io/badge/runbot-Try%20me-875A7B.png + :target: https://runbot.odoo-community.org/runbot/153/13.0 + :alt: Try me on Runbot + +|badge1| |badge2| |badge3| |badge4| |badge5| + +Different manufacturers produce devices which are able to measure and weigh +packages and parcels. Each brand has a different communication protocol. This +module provides an framework to interface such devices with Odoo. + +.. IMPORTANT:: + This is an alpha version, the data model and design can change at any time without warning. + Only for development or testing purpose, do not use in production. + `More details on development status `_ + +**Table of contents** + +.. contents:: + :local: + +Installation +============ + +This module by itself does not do anything. + +You will need to install a module implementing the communication with your +device. Look for modules with a name starting with stock_measuring_device. + +Configuration +============= + +The first step is to configure the Packaging Types (Pallet, Box, ...) in Inventory > Configuration > Product Packaging Types. + +Configure the measuring device in Inventory > Configuration > Measuring +Devices, don't forget to set the device type, and any other additional +parameters. + + +Usage +===== + +Use the "Wizard" button on a Measuring Device to open the screen and take +measurements. + +Known issues / Roadmap +====================== + +* The UI could get some improvements +* Being able to open the measurement screen from a product would be nice + +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 +~~~~~~~ + +* Camptocamp + +Contributors +~~~~~~~~~~~~ + +* Patrick Tombez +* Alexandre Fayolle +* Carlos Serra Toro + +Maintainers +~~~~~~~~~~~ + +This module is maintained by the OCA. + +.. image:: https://odoo-community.org/logo.png + :alt: Odoo Community Association + :target: https://odoo-community.org + +OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use. + +This module is part of the `OCA/stock-logistics-warehouse `_ project on GitHub. + +You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/stock_measuring_device/__init__.py b/stock_measuring_device/__init__.py new file mode 100644 index 000000000..0b1d65aed --- /dev/null +++ b/stock_measuring_device/__init__.py @@ -0,0 +1,5 @@ +# Copyright 2021 Camptocamp SA +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html) +from . import components +from . import models +from . import wizard diff --git a/stock_measuring_device/__manifest__.py b/stock_measuring_device/__manifest__.py new file mode 100644 index 000000000..d5be9ba5e --- /dev/null +++ b/stock_measuring_device/__manifest__.py @@ -0,0 +1,34 @@ +# Copyright 2021 Camptocamp SA +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html) +{ + "name": "Stock Measuring Device", + "summary": "Implement a common interface for measuring and weighing devices", + "version": "13.0.1.0.0", + "category": "Warehouse", + "author": "Camptocamp, Odoo Community Association (OCA)", + "license": "AGPL-3", + "depends": [ + "component", + "barcodes", + "stock", + "web_tree_dynamic_colored_field", + "product_packaging_dimension", + "product_packaging_type_required", + "product_dimension", + # For the pop-up message to tell the user to refresh. + "web_notify", + "web_ir_actions_act_view_reload", + ], + "data": [ + "security/ir.model.access.csv", + "data/uom.xml", + "views/assets.xml", + "views/measuring_device_view.xml", + "wizard/measuring_wizard.xml", + "views/menu.xml", + ], + "website": "https://github.com/OCA/stock-logistics-warehouse", + "installable": True, + "development_status": "Alpha", + "maintainers": ["gurneyalex"], +} diff --git a/stock_measuring_device/components/__init__.py b/stock_measuring_device/components/__init__.py new file mode 100644 index 000000000..f1c2b4985 --- /dev/null +++ b/stock_measuring_device/components/__init__.py @@ -0,0 +1,3 @@ +# Copyright 2021 Camptocamp SA +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html) +from . import measuring_device_component diff --git a/stock_measuring_device/components/measuring_device_component.py b/stock_measuring_device/components/measuring_device_component.py new file mode 100644 index 000000000..fa8af855b --- /dev/null +++ b/stock_measuring_device/components/measuring_device_component.py @@ -0,0 +1,46 @@ +# Copyright 2021 Camptocamp SA +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html) + +import logging + +from odoo.addons.component.core import AbstractComponent + +_logger = logging.getLogger(__name__) + + +class MeasuringDevice(AbstractComponent): + _name = "measuring.device.base" + _collection = "measuring.device" + + def test_device(self): + """Test that the device is properly configured. + + Override to e.g. test the connection parameter or send a test command. + + :return: True on success, False otherwise""" + return True + + def preprocess_measures(self, measures): + """perform required parsing, key mapping and possible unit conversion + + :param measures: a dictionary passed to _update_packaging_measures + :return: a dictionary containing values that will be written on the + wizard line + """ + return measures + + def post_update_packaging_measures(self, measures, packaging, wizard_line): + """hook called after the update of the packaging or wizard line update. + + This method can be called to send notifications for instance. + + :return: None""" + pass + + def get_measure(self): + """Get a measure from the device + + the implementation must communicate with the device to trigger a + measure. If this can be done synchronously, call + _update_packaging_measures() to get the update""" + raise NotImplementedError() diff --git a/stock_measuring_device/data/uom.xml b/stock_measuring_device/data/uom.xml new file mode 100644 index 000000000..543ab39ea --- /dev/null +++ b/stock_measuring_device/data/uom.xml @@ -0,0 +1,9 @@ + + + + + mm + + smaller + + diff --git a/stock_measuring_device/models/__init__.py b/stock_measuring_device/models/__init__.py new file mode 100644 index 000000000..5bdb916aa --- /dev/null +++ b/stock_measuring_device/models/__init__.py @@ -0,0 +1,5 @@ +# Copyright 2021 Camptocamp SA +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html) +from . import measuring_device +from . import product_packaging +from . import stock_warehouse diff --git a/stock_measuring_device/models/measuring_device.py b/stock_measuring_device/models/measuring_device.py new file mode 100644 index 000000000..1ba9b6ed9 --- /dev/null +++ b/stock_measuring_device/models/measuring_device.py @@ -0,0 +1,104 @@ +# Copyright 2021 Camptocamp SA +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html) +import logging + +from odoo import _, fields, models + +_logger = logging.getLogger(__name__) + + +class MeasuringDevice(models.Model): + _name = "measuring.device" + _inherit = "collection.base" + _description = "Measuring and Weighing Device" + _order = "warehouse_id, name" + + name = fields.Char("Name", required=True) + warehouse_id = fields.Many2one("stock.warehouse", "Warehouse", required=True) + device_type = fields.Selection( + selection=[], + help="The type of device (e.g. zippcube, cubiscan...) " + "depending on which module are installed.", + ) + state = fields.Selection( + [("not_ready", "Not Ready"), ("ready", "Ready")], + default="not_ready", + readonly=True, + copy=False, + ) + + _sql_constraints = [ + ( + "name_uniq", + "unique (name)", + "The name of the measuring/weighing device must be unique.", + ), + ] + + def _get_measuring_device(self): + with self.work_on(self._name) as work_ctx: + return work_ctx.component(usage=self.device_type) + + def open_wizard(self): + return { + "name": _("Measurement Wizard"), + "res_model": "measuring.wizard", + "type": "ir.actions.act_window", + "view_id": False, + "view_mode": "form", + "context": {"default_device_id": self.id}, + "target": "fullscreen", + "flags": { + "withControlPanel": False, + "form_view_initial_mode": "edit", + "no_breadcrumbs": True, + }, + } + + def _is_being_used(self): + self.ensure_one() + return bool( + self.env["product.packaging"].search_count( + [("measuring_device_id", "=", self.id)] + ) + ) + + def _update_packaging_measures(self, measures): + self.ensure_one() + measures = self._get_measuring_device().preprocess_measures(measures) + line_domain = [ + ("wizard_id.device_id", "=", self.id), + ("scan_requested", "=", True), + ] + + packaging = self.env["product.packaging"]._measuring_device_find_assigned(self) + if packaging: + line_domain += [("packaging_id", "=", packaging.id)] + else: + line_domain += [ + ("packaging_id", "=", False), + ("is_unit_line", "=", True), + ] + + wizard_line = self.env["measuring.wizard.line"].search( + line_domain, order="write_date DESC", limit=1, + ) + if not wizard_line: + _logger.warning("No wizard line found for this measure.") + packaging.write(measures) + else: + measures.update({"scan_requested": False}) + wizard_line.write(measures) + + self._get_measuring_device().post_update_packaging_measures( + measures, packaging, wizard_line + ) + return packaging + + def test_device(self): + for rec in self: + success = rec._get_measuring_device().test_device() + if success and rec.state == "not_ready": + rec.state = "ready" + elif not success and rec.state == "ready": + rec.state = "not_ready" diff --git a/stock_measuring_device/models/product_packaging.py b/stock_measuring_device/models/product_packaging.py new file mode 100644 index 000000000..2f7aa612c --- /dev/null +++ b/stock_measuring_device/models/product_packaging.py @@ -0,0 +1,49 @@ +# Copyright 2021 Camptocamp SA +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html) +import logging + +from odoo import _, fields, models + +_logger = logging.getLogger(__name__) + + +class ProductPackaging(models.Model): + _inherit = "product.packaging" + + measuring_device_id = fields.Many2one( + "measuring.device", + copy=False, + string="Measuring device which will scan the package", + help="Technical field set when an operator uses the device " + "to scan this package", + ) + + def _measuring_device_assign(self, device): + """Assign the measuring device to the current product packaging""" + # self can be an empty recordset, for the unit line which updates the + # product info + if self: + self.measuring_device_id = device + + def _measuring_device_release(self): + """Free the measuring device from the current product packaging""" + # self can be an empty recordset, for the unit line which updates the + # product info + if self: + self.measuring_device_id = False + + def _measuring_device_find_assigned(self, device): + """search packaging being assigned to the specified device""" + packaging = self.search( + [("measuring_device_id", "=", device.id)], order="write_date DESC", limit=2 + ) + if len(packaging) > 1: + warning_msg = _( + "Several packagings ({}) found to update by " + "device {}. Will update the first: {}".format( + packaging, self.name, packaging[0] + ) + ) + _logger.warning(warning_msg) + packaging = packaging[0] + return packaging diff --git a/stock_measuring_device/models/stock_warehouse.py b/stock_measuring_device/models/stock_warehouse.py new file mode 100644 index 000000000..83328dc42 --- /dev/null +++ b/stock_measuring_device/models/stock_warehouse.py @@ -0,0 +1,12 @@ +# Copyright 2019 Camptocamp SA +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html) + +from odoo import fields, models + + +class StockWarehouse(models.Model): + _inherit = "stock.warehouse" + + measuring_device_ids = fields.One2many( + "measuring.device", "warehouse_id", string="Measuring Devices" + ) diff --git a/stock_measuring_device/readme/CONFIGURE.rst b/stock_measuring_device/readme/CONFIGURE.rst new file mode 100644 index 000000000..e270c3f15 --- /dev/null +++ b/stock_measuring_device/readme/CONFIGURE.rst @@ -0,0 +1,5 @@ +The first step is to configure the Packaging Types (Pallet, Box, ...) in Inventory > Configuration > Product Packaging Types. + +Configure the measuring device in Inventory > Configuration > Measuring +Devices, don't forget to set the device type, and any other additional +parameters. diff --git a/stock_measuring_device/readme/CONTRIBUTORS.rst b/stock_measuring_device/readme/CONTRIBUTORS.rst new file mode 100644 index 000000000..61b56b03d --- /dev/null +++ b/stock_measuring_device/readme/CONTRIBUTORS.rst @@ -0,0 +1,3 @@ +* Patrick Tombez +* Alexandre Fayolle +* Carlos Serra Toro diff --git a/stock_measuring_device/readme/DESCRIPTION.rst b/stock_measuring_device/readme/DESCRIPTION.rst new file mode 100644 index 000000000..66bb0cf1c --- /dev/null +++ b/stock_measuring_device/readme/DESCRIPTION.rst @@ -0,0 +1,3 @@ +Different manufacturers produce devices which are able to measure and weigh +packages and parcels. Each brand has a different communication protocol. This +module provides an framework to interface such devices with Odoo. diff --git a/stock_measuring_device/readme/INSTALL.rst b/stock_measuring_device/readme/INSTALL.rst new file mode 100644 index 000000000..c3448c214 --- /dev/null +++ b/stock_measuring_device/readme/INSTALL.rst @@ -0,0 +1,4 @@ +This module by itself does not do anything. + +You will need to install a module implementing the communication with your +device. Look for modules with a name starting with stock_measuring_device. diff --git a/stock_measuring_device/readme/ROADMAP.rst b/stock_measuring_device/readme/ROADMAP.rst new file mode 100644 index 000000000..3d608500b --- /dev/null +++ b/stock_measuring_device/readme/ROADMAP.rst @@ -0,0 +1,2 @@ +* The UI could get some improvements +* Being able to open the measurement screen from a product would be nice diff --git a/stock_measuring_device/readme/USAGE.rst b/stock_measuring_device/readme/USAGE.rst new file mode 100644 index 000000000..a346d91b2 --- /dev/null +++ b/stock_measuring_device/readme/USAGE.rst @@ -0,0 +1,2 @@ +Use the "Wizard" button on a Measuring Device to open the screen and take +measurements. diff --git a/stock_measuring_device/security/ir.model.access.csv b/stock_measuring_device/security/ir.model.access.csv new file mode 100644 index 000000000..290cbbdba --- /dev/null +++ b/stock_measuring_device/security/ir.model.access.csv @@ -0,0 +1,3 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink +access_measuring_device_inventory_manager,measuring.device.inventory.manager,stock_measuring_device.model_measuring_device,stock.group_stock_manager,1,1,1,1 +access_measuring_device_inventory_user,measuring.device.inventory.user,stock_measuring_device.model_measuring_device,stock.group_stock_user,1,0,0,0 diff --git a/stock_measuring_device/static/description/index.html b/stock_measuring_device/static/description/index.html new file mode 100644 index 000000000..4898ded1a --- /dev/null +++ b/stock_measuring_device/static/description/index.html @@ -0,0 +1,458 @@ + + + + + + +Stock Measuring Device + + + +
+

Stock Measuring Device

+ + +

Alpha License: AGPL-3 OCA/stock-logistics-warehouse Translate me on Weblate Try me on Runbot

+

Different manufacturers produce devices which are able to measure and weigh +packages and parcels. Each brand has a different communication protocol. This +module provides an framework to interface such devices with Odoo.

+
+

Important

+

This is an alpha version, the data model and design can change at any time without warning. +Only for development or testing purpose, do not use in production. +More details on development status

+
+

Table of contents

+ +
+

Installation

+

This module by itself does not do anything.

+

You will need to install a module implementing the communication with your +device. Look for modules with a name starting with stock_measuring_device.

+
+
+

Configuration

+

The first step is to configure the Packaging Types (Pallet, Box, …) in Inventory > Configuration > Product Packaging Types.

+

Configure the measuring device in Inventory > Configuration > Measuring +Devices, don’t forget to set the device type, and any other additional +parameters.

+
+
+

Usage

+

Use the “Wizard” button on a Measuring Device to open the screen and take +measurements.

+
+
+

Known issues / Roadmap

+
    +
  • The UI could get some improvements
  • +
  • Being able to open the measurement screen from a product would be nice
  • +
+
+
+

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

+
    +
  • Camptocamp
  • +
+
+
+

Contributors

+ +
+
+

Maintainers

+

This module is maintained by the OCA.

+Odoo Community Association +

OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use.

+

This module is part of the OCA/stock-logistics-warehouse project on GitHub.

+

You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

+
+
+
+ + diff --git a/stock_measuring_device/static/src/scss/measuring_wizard.scss b/stock_measuring_device/static/src/scss/measuring_wizard.scss new file mode 100644 index 000000000..507cfc3bd --- /dev/null +++ b/stock_measuring_device/static/src/scss/measuring_wizard.scss @@ -0,0 +1,33 @@ +.o_web_client.o_fullscreen { + .o_form_view.measuring_wizard { + font-size: 16px; + + @include media-breakpoint-up(x1) { + font-size: 18px; + } + + .btn { + font-size: 1em; + padding: 1em; + margin: 0 5px; + } + + .o_data_cell:not(.o_list_button) { + padding: 0.75em; + font-size: 1.5em; + margin: 0 5px; + } + + .table-responsive { + overflow: hidden; + } + + .o_field_many2one input.o_input { + font-size: 1.5em; + } + + .o_form_statusbar { + display: none; + } + } +} diff --git a/stock_measuring_device/views/assets.xml b/stock_measuring_device/views/assets.xml new file mode 100644 index 000000000..de7d8fba5 --- /dev/null +++ b/stock_measuring_device/views/assets.xml @@ -0,0 +1,16 @@ + + + + diff --git a/stock_measuring_device/views/measuring_device_view.xml b/stock_measuring_device/views/measuring_device_view.xml new file mode 100644 index 000000000..5d5090f1e --- /dev/null +++ b/stock_measuring_device/views/measuring_device_view.xml @@ -0,0 +1,51 @@ + + + + measuring.device.form + measuring.device + +
+
+
+ + + + + + + + + +
+
+
+ + measuring.device.tree + measuring.device + + + + + + + + + + Measuring Devices + measuring.device + tree,form + +
diff --git a/stock_measuring_device/views/menu.xml b/stock_measuring_device/views/menu.xml new file mode 100644 index 000000000..74eb64cb8 --- /dev/null +++ b/stock_measuring_device/views/menu.xml @@ -0,0 +1,9 @@ + + + + diff --git a/stock_measuring_device/wizard/__init__.py b/stock_measuring_device/wizard/__init__.py new file mode 100644 index 000000000..216262ab1 --- /dev/null +++ b/stock_measuring_device/wizard/__init__.py @@ -0,0 +1,4 @@ +# Copyright 2021 Camptocamp SA +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html) +from . import measuring_wizard +from . import measuring_wizard_line diff --git a/stock_measuring_device/wizard/measuring_wizard.py b/stock_measuring_device/wizard/measuring_wizard.py new file mode 100644 index 000000000..f06837fb1 --- /dev/null +++ b/stock_measuring_device/wizard/measuring_wizard.py @@ -0,0 +1,178 @@ +# Copyright 2021 Camptocamp SA +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html) +from odoo import api, fields, models + + +class MeasuringWizard(models.TransientModel): + _name = "measuring.wizard" + _inherit = "barcodes.barcode_events_mixin" + _description = "measuring Wizard" + _rec_name = "device_id" + + product_id = fields.Many2one("product.product", domain=[("type", "=", "product")]) + line_ids = fields.One2many("measuring.wizard.line", "wizard_id") + device_id = fields.Many2one("measuring.device", readonly=True) + + @api.onchange("product_id") + def onchange_product_id(self): + if self.product_id: + to_create = [] + to_create += self._prepare_unit_line() + to_create += self._prepare_packaging_lines() + recs = self.env["measuring.wizard.line"].create(to_create) + self.line_ids = recs + else: + self.line_ids = [(5, 0, 0)] + + def _prepare_unit_line(self): + vals = { + "wizard_id": self.id, + "sequence": 0, + "name": "Unit", + "qty": 1, + "max_weight": self.product_id.weight, + "lngth": self.product_id.product_length, + "width": self.product_id.product_width, + "height": self.product_id.product_height, + "is_unit_line": True, + } + product_dimension_uom = self.product_id.dimensional_uom_id + mm_uom = self.env.ref("stock_measuring_device.product_uom_mm") + if mm_uom != product_dimension_uom: + vals.update( + { + "lngth": product_dimension_uom._compute_quantity( + self.product_id.product_length, mm_uom + ), + "width": product_dimension_uom._compute_quantity( + self.product_id.product_width, mm_uom + ), + "height": product_dimension_uom._compute_quantity( + self.product_id.product_height, mm_uom + ), + } + ) + return [vals] + + def _prepare_packaging_lines(self): + vals_list = [] + product_packaging = self.env["product.packaging"] + packaging_types = self.env["product.packaging.type"].search([]) + for seq, pack_type in enumerate(packaging_types): + pack = product_packaging.search( + [ + ("product_id", "=", self.product_id.id), + ("packaging_type_id", "=", pack_type.id), + ], + limit=1, + ) + vals = { + "wizard_id": self.id, + "sequence": seq + 1, + "name": pack_type.name, + "qty": 0, + "max_weight": 0, + "lngth": 0, + "width": 0, + "height": 0, + "barcode": False, + "packaging_type_id": pack_type.id, + } + if pack: + vals.update( + { + "qty": pack.qty, + "max_weight": pack.max_weight, + "lngth": pack.lngth, + "width": pack.width, + "height": pack.height, + "barcode": pack.barcode, + "packaging_id": pack.id, + "packaging_type_id": pack_type.id, + } + ) + vals_list.append(vals) + return vals_list + + def action_reopen_fullscreen(self): + self.ensure_one() + res = self.device_id.open_wizard() + res["res_id"] = self.id + return res + + def on_barcode_scanned(self, barcode): + self.ensure_one() + prod = self.env["product.product"].search([("barcode", "=", barcode)], limit=1) + self.product_id = prod + + def action_save(self): + self.ensure_one() + product_vals = {} + packaging_ids_list = [] + for line in self.line_ids: + packaging_type = line.packaging_type_id + if packaging_type: + # Handle lines with packaging + vals = { + "name": line.name, + "qty": line.qty, + "max_weight": line.max_weight, + "lngth": line.lngth, + "width": line.width, + "height": line.height, + "barcode": line.barcode, + "packaging_type_id": line.packaging_type_id.id, + } + pack = line.packaging_id + if pack: + packaging_ids_list.append((1, pack.id, vals)) + else: + packaging_ids_list.append((0, 0, vals)) + else: + # Handle unit line + mm_uom = self.env.ref("stock_measuring_device.product_uom_mm") + product_vals.update( + { + "product_length": line.lngth, + "product_width": line.width, + "product_height": line.height, + "dimensional_uom_id": mm_uom.id, + "weight": line.max_weight, + } + ) + product_vals.update({"packaging_ids": packaging_ids_list}) + self.product_id.write(product_vals) + # Call onchange to update volume on product.product + self.product_id.onchange_calculate_volume() + # reload lines + self.onchange_product_id() + + def action_close(self): + self.ensure_one() + return { + "type": "ir.actions.act_window", + "res_model": self.device_id._name, + "res_id": self.device_id.id, + "view_mode": "form", + "target": "main", + "flags": {"headless": False, "clear_breadcrumbs": True}, + } + + def reload(self): + return { + "type": "ir.actions.act_view_reload", + } + + def _notify(self, message): + """Show a gentle notification on the wizard + + We can't use the user set in the current environment because the user + that attends the screen (that opened the wizard, thus created it) may + be not the same than the one (artificial user) that scans and submits + the data, e.g. by using an api call via a controller. We have to send + this original user in the environment because notify_warning checks + that you only notify a user which is the same than the one set in + the environment. + """ + self.ensure_one() + self.create_uid.with_user(self.create_uid.id).notify_warning(message=message) diff --git a/stock_measuring_device/wizard/measuring_wizard.xml b/stock_measuring_device/wizard/measuring_wizard.xml new file mode 100644 index 000000000..41b29509c --- /dev/null +++ b/stock_measuring_device/wizard/measuring_wizard.xml @@ -0,0 +1,111 @@ + + + + measuring.wizard.form + measuring.wizard + +
+
+
+ + +