diff --git a/nfu_sale_order_batch_packaging/README.rst b/nfu_sale_order_batch_packaging/README.rst index 45c7970..9d14fd3 100644 --- a/nfu_sale_order_batch_packaging/README.rst +++ b/nfu_sale_order_batch_packaging/README.rst @@ -50,7 +50,7 @@ Changelog - 16.0.1.3.4: store all quantities and round open values - 16.0.1.3.6: make sure packages are readable by the user - 16.0.1.3.7: really make sure packages are readable by the user -- 16.0.1.4.0: Add automatiok batch fillup +- 16.0.1.4.0: Add automatic batch fillup Bug Tracker =========== diff --git a/nfu_sale_order_batch_packaging/__manifest__.py b/nfu_sale_order_batch_packaging/__manifest__.py index 3805122..ddd0586 100644 --- a/nfu_sale_order_batch_packaging/__manifest__.py +++ b/nfu_sale_order_batch_packaging/__manifest__.py @@ -10,7 +10,8 @@ "security/ir.model.access.csv", "data/ir_config_parameter.xml", "data/uom_data.xml", - "wizard/sale_order_batch_packaging_reconcile_views.xml", + "wizard/sale_order_batch_packaging_fill_views.xml", + "views/uom_uom_views.xml", "views/sale_order_views.xml", "views/sale_order_batch_views.xml", "views/sale_order_batch_product_views.xml", diff --git a/nfu_sale_order_batch_packaging/data/uom_data.xml b/nfu_sale_order_batch_packaging/data/uom_data.xml index 7dc0480..24246db 100644 --- a/nfu_sale_order_batch_packaging/data/uom_data.xml +++ b/nfu_sale_order_batch_packaging/data/uom_data.xml @@ -1,7 +1,7 @@ - True + 1.0 @@ -10,6 +10,6 @@ smaller 0.01 1.0 - True + 1.0 diff --git a/nfu_sale_order_batch_packaging/i18n/de.po b/nfu_sale_order_batch_packaging/i18n/de.po new file mode 100644 index 0000000..ab39287 --- /dev/null +++ b/nfu_sale_order_batch_packaging/i18n/de.po @@ -0,0 +1,269 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * nfu_sale_order_batch_packaging +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 16.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2026-05-29 21:37+0000\n" +"PO-Revision-Date: 2026-05-29 21:37+0000\n" +"Last-Translator: \n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: \n" + +#. module: nfu_sale_order_batch_packaging +#: model:ir.model.fields,field_description:nfu_sale_order_batch_packaging.field_sale_order_batch_packaging_fill__batch_id +msgid "Batch" +msgstr "Charge" + +#. module: nfu_sale_order_batch_packaging +#: model:ir.model.fields,field_description:nfu_sale_order_batch_packaging.field_uom_uom__batch_fill_precision +msgid "Batch Fill Precision" +msgstr "Füllgenauigkeit" + +#. module: nfu_sale_order_batch_packaging +#: model:ir.model,name:nfu_sale_order_batch_packaging.model_sale_order_batch_packaging_fill +msgid "Batch Packaging Auto-Fill Wizard" +msgstr "Assistent zur automatischen Befüllung" + +#. module: nfu_sale_order_batch_packaging +#: model:ir.model,name:nfu_sale_order_batch_packaging.model_sale_order_batch_packaging_fill_line +msgid "Batch Packaging Auto-Fill Wizard Line" +msgstr "" + +#. module: nfu_sale_order_batch_packaging +#: model:ir.model,name:nfu_sale_order_batch_packaging.model_sale_order_batch_packaging_fill_zero_line +msgid "Batch Packaging Auto-Fill Zero-Out Line" +msgstr "" + +#. module: nfu_sale_order_batch_packaging +#: model:ir.model.fields,field_description:nfu_sale_order_batch_packaging.field_sale_order_batch_packaging_fill_line__batch_product_id +#: model:ir.model.fields,field_description:nfu_sale_order_batch_packaging.field_sale_order_batch_packaging_fill_zero_line__batch_product_id +msgid "Batch Product" +msgstr "Sammelprodukt" + +#. module: nfu_sale_order_batch_packaging +#: model:ir.model.fields,field_description:nfu_sale_order_batch_packaging.field_sale_order_line__batch_uom_max_qty +msgid "Batch UOM Max Qty" +msgstr "" + +#. module: nfu_sale_order_batch_packaging +#: model:ir.model.fields,field_description:nfu_sale_order_batch_packaging.field_sale_order_line__batch_uom_qty +msgid "Batch UOM Qty" +msgstr "" + +#. module: nfu_sale_order_batch_packaging +#: model_terms:ir.ui.view,arch_db:nfu_sale_order_batch_packaging.view_sale_order_batch_packaging_fill_form +msgid "Cancel" +msgstr "Abbrechen" + +#. module: nfu_sale_order_batch_packaging +#: model_terms:ir.ui.view,arch_db:nfu_sale_order_batch_packaging.view_sale_order_batch_packaging_fill_form +msgid "Confirm" +msgstr "Bestätigen" + +#. module: nfu_sale_order_batch_packaging +#: model:ir.model.fields,field_description:nfu_sale_order_batch_packaging.field_sale_order_batch_packaging_fill__create_uid +#: model:ir.model.fields,field_description:nfu_sale_order_batch_packaging.field_sale_order_batch_packaging_fill_line__create_uid +#: model:ir.model.fields,field_description:nfu_sale_order_batch_packaging.field_sale_order_batch_packaging_fill_zero_line__create_uid +msgid "Created by" +msgstr "Erstellt von" + +#. module: nfu_sale_order_batch_packaging +#: model:ir.model.fields,field_description:nfu_sale_order_batch_packaging.field_sale_order_batch_packaging_fill__create_date +#: model:ir.model.fields,field_description:nfu_sale_order_batch_packaging.field_sale_order_batch_packaging_fill_line__create_date +#: model:ir.model.fields,field_description:nfu_sale_order_batch_packaging.field_sale_order_batch_packaging_fill_zero_line__create_date +msgid "Created on" +msgstr "Erstellt am" + +#. module: nfu_sale_order_batch_packaging +#: model:ir.model.fields,field_description:nfu_sale_order_batch_packaging.field_sale_order_batch_packaging_fill__display_name +#: model:ir.model.fields,field_description:nfu_sale_order_batch_packaging.field_sale_order_batch_packaging_fill_line__display_name +#: model:ir.model.fields,field_description:nfu_sale_order_batch_packaging.field_sale_order_batch_packaging_fill_zero_line__display_name +msgid "Display Name" +msgstr "Anzeigename" + +#. module: nfu_sale_order_batch_packaging +#: model_terms:ir.ui.view,arch_db:nfu_sale_order_batch_packaging.view_order_batch_form +#: model_terms:ir.ui.view,arch_db:nfu_sale_order_batch_packaging.view_sale_order_batch_packaging_fill_form +msgid "Fill Packages" +msgstr "Pakete befüllen" + +#. module: nfu_sale_order_batch_packaging +#: model:ir.model.fields.selection,name:nfu_sale_order_batch_packaging.selection__sale_order_batch_product__open_packaging_state__full +msgid "Full" +msgstr "Voll" + +#. module: nfu_sale_order_batch_packaging +#: model:ir.model.fields,field_description:nfu_sale_order_batch_packaging.field_sale_order_batch_packaging_fill__has_fill_lines +msgid "Has Fill Lines" +msgstr "Hat Füllzeilen" + +#. module: nfu_sale_order_batch_packaging +#: model:ir.model.fields,field_description:nfu_sale_order_batch_packaging.field_sale_order_batch__has_fillable_packages +msgid "Has Fillable Packages" +msgstr "Hat befüllbare Pakete" + +#. module: nfu_sale_order_batch_packaging +#: model:ir.model.fields,field_description:nfu_sale_order_batch_packaging.field_sale_order_batch__has_qty_adjusted +#: model:ir.model.fields,field_description:nfu_sale_order_batch_packaging.field_sale_order_batch_product__has_qty_adjusted +msgid "Has Qty Adjusted" +msgstr "Menge angepasst" + +#. module: nfu_sale_order_batch_packaging +#: model:ir.model.fields,field_description:nfu_sale_order_batch_packaging.field_sale_order_batch_packaging_fill__has_zero_lines +msgid "Has Zero Lines" +msgstr "Hat Nullzeilen" + +#. module: nfu_sale_order_batch_packaging +#: model:ir.model.fields,field_description:nfu_sale_order_batch_packaging.field_sale_order_batch_packaging_fill__id +#: model:ir.model.fields,field_description:nfu_sale_order_batch_packaging.field_sale_order_batch_packaging_fill_line__id +#: model:ir.model.fields,field_description:nfu_sale_order_batch_packaging.field_sale_order_batch_packaging_fill_zero_line__id +msgid "ID" +msgstr "ID" + +#. module: nfu_sale_order_batch_packaging +#: model:ir.model.fields,field_description:nfu_sale_order_batch_packaging.field_sale_order_batch_packaging_fill_zero_line__keep_qty +msgid "Keep" +msgstr "Behalten" + +#. module: nfu_sale_order_batch_packaging +#: model:ir.model.fields,field_description:nfu_sale_order_batch_packaging.field_sale_order_batch_packaging_fill____last_update +#: model:ir.model.fields,field_description:nfu_sale_order_batch_packaging.field_sale_order_batch_packaging_fill_line____last_update +#: model:ir.model.fields,field_description:nfu_sale_order_batch_packaging.field_sale_order_batch_packaging_fill_zero_line____last_update +msgid "Last Modified on" +msgstr "Zuletzt geändert am" + +#. module: nfu_sale_order_batch_packaging +#: model:ir.model.fields,field_description:nfu_sale_order_batch_packaging.field_sale_order_batch_packaging_fill__write_uid +#: model:ir.model.fields,field_description:nfu_sale_order_batch_packaging.field_sale_order_batch_packaging_fill_line__write_uid +#: model:ir.model.fields,field_description:nfu_sale_order_batch_packaging.field_sale_order_batch_packaging_fill_zero_line__write_uid +msgid "Last Updated by" +msgstr "Zuletzt aktualisiert von" + +#. module: nfu_sale_order_batch_packaging +#: model:ir.model.fields,field_description:nfu_sale_order_batch_packaging.field_sale_order_batch_packaging_fill__write_date +#: model:ir.model.fields,field_description:nfu_sale_order_batch_packaging.field_sale_order_batch_packaging_fill_line__write_date +#: model:ir.model.fields,field_description:nfu_sale_order_batch_packaging.field_sale_order_batch_packaging_fill_zero_line__write_date +msgid "Last Updated on" +msgstr "Zuletzt aktualisiert am" + +#. module: nfu_sale_order_batch_packaging +#: model:ir.model.fields.selection,name:nfu_sale_order_batch_packaging.selection__sale_order_batch_product__open_packaging_state__last_open +msgid "Last package open" +msgstr "Letztes Paket offen" + +#. module: nfu_sale_order_batch_packaging +#: model:ir.model.fields,field_description:nfu_sale_order_batch_packaging.field_sale_order_batch_packaging_fill__line_ids +msgid "Line" +msgstr "Zeile" + +#. module: nfu_sale_order_batch_packaging +#: model:ir.model.fields,field_description:nfu_sale_order_batch_packaging.field_sale_order_batch_product__product_uom_max_qty +#: model:ir.model.fields,field_description:nfu_sale_order_batch_packaging.field_sale_order_line__product_uom_max_qty +msgid "Max Qty" +msgstr "Max. Menge" + +#. module: nfu_sale_order_batch_packaging +#: model:ir.model.fields.selection,name:nfu_sale_order_batch_packaging.selection__sale_order_batch_product__open_packaging_state__open +msgid "Open" +msgstr "Offen" + +#. module: nfu_sale_order_batch_packaging +#: model:ir.model.fields,field_description:nfu_sale_order_batch_packaging.field_sale_order_batch_product__open_packaging_max_qty +msgid "Open Packaging Max Qty" +msgstr "Max. Menge der offenen Verpackung" + +#. module: nfu_sale_order_batch_packaging +#: model:ir.model.fields,field_description:nfu_sale_order_batch_packaging.field_sale_order_batch_packaging_fill_line__open_packaging_qty +#: model:ir.model.fields,field_description:nfu_sale_order_batch_packaging.field_sale_order_batch_packaging_fill_zero_line__open_packaging_qty +#: model:ir.model.fields,field_description:nfu_sale_order_batch_packaging.field_sale_order_batch_product__open_packaging_qty +msgid "Open Packaging Qty" +msgstr "Menge der offenen Verpackung" + +#. module: nfu_sale_order_batch_packaging +#: model:ir.model.fields,field_description:nfu_sale_order_batch_packaging.field_sale_order_batch_product__open_packaging_state +msgid "Open Packaging State" +msgstr "Status der offenen Verpackung" + +#. module: nfu_sale_order_batch_packaging +#: model:ir.model.fields,field_description:nfu_sale_order_batch_packaging.field_sale_order_line__product_uom_ordered_qty +msgid "Ordered Qty" +msgstr "Bestellte Menge" + +#. module: nfu_sale_order_batch_packaging +#: model:ir.model.fields,field_description:nfu_sale_order_batch_packaging.field_sale_order_batch_packaging_fill_line__batch_fill_precision +msgid "Precision" +msgstr "Genauigkeit" + +#. module: nfu_sale_order_batch_packaging +#: model:ir.model,name:nfu_sale_order_batch_packaging.model_product_template +#: model:ir.model.fields,field_description:nfu_sale_order_batch_packaging.field_sale_order_batch_packaging_fill_line__product_id +#: model:ir.model.fields,field_description:nfu_sale_order_batch_packaging.field_sale_order_batch_packaging_fill_zero_line__product_id +msgid "Product" +msgstr "Produkt" + +#. module: nfu_sale_order_batch_packaging +#: model:ir.model,name:nfu_sale_order_batch_packaging.model_uom_uom +msgid "Product Unit of Measure" +msgstr "Maßeinheit des Produkts" + +#. module: nfu_sale_order_batch_packaging +#: model:ir.model,name:nfu_sale_order_batch_packaging.model_product_product +msgid "Product Variant" +msgstr "Produktvariante" + +#. module: nfu_sale_order_batch_packaging +#: model_terms:ir.ui.view,arch_db:nfu_sale_order_batch_packaging.view_order_batch_form +#: model_terms:ir.ui.view,arch_db:nfu_sale_order_batch_packaging.view_order_batch_product_form +msgid "Restore Ordered Qty" +msgstr "Bestellte Menge wiederherstellen" + +#. module: nfu_sale_order_batch_packaging +#: model:ir.model.fields,help:nfu_sale_order_batch_packaging.field_uom_uom__batch_fill_precision +msgid "" +"Rounding step used by the batch fill wizard for this UOM (e.g. 1.0 for whole" +" units, 0.1 for one decimal)." +msgstr "" +"Rundungsschritt des Füll-Assistenten für diese Maßeinheit (z.B. 1.0" +" für ganze Einheiten, 0.1 für eine Dezimalstelle)." + +#. module: nfu_sale_order_batch_packaging +#: model:ir.model,name:nfu_sale_order_batch_packaging.model_sale_order_batch_product +msgid "Sale Order Batch Product" +msgstr "" + +#. module: nfu_sale_order_batch_packaging +#: model:ir.model,name:nfu_sale_order_batch_packaging.model_sale_order_batch +msgid "Sales Order Batch" +msgstr "Sammelverkaufsauftrag" + +#. module: nfu_sale_order_batch_packaging +#: model:ir.model,name:nfu_sale_order_batch_packaging.model_sale_order_line +msgid "Sales Order Line" +msgstr "Verkaufsauftragszeile" + +#. module: nfu_sale_order_batch_packaging +#: model:uom.uom,name:nfu_sale_order_batch_packaging.product_uom_stueck +msgid "Stück" +msgstr "Stück" + +#. module: nfu_sale_order_batch_packaging +#: model:ir.model.fields,field_description:nfu_sale_order_batch_packaging.field_sale_order_batch_packaging_fill_line__wizard_id +#: model:ir.model.fields,field_description:nfu_sale_order_batch_packaging.field_sale_order_batch_packaging_fill_zero_line__wizard_id +msgid "Wizard" +msgstr "Assistent" + +#. module: nfu_sale_order_batch_packaging +#: model:ir.model.fields,field_description:nfu_sale_order_batch_packaging.field_sale_order_batch_packaging_fill__zero_line_ids +msgid "Zero Line" +msgstr "Nullzeile" + +#. module: nfu_sale_order_batch_packaging +#: model_terms:ir.ui.view,arch_db:nfu_sale_order_batch_packaging.view_sale_order_batch_packaging_fill_form +msgid "Zero Out" +msgstr "Auf Null setzen" diff --git a/nfu_sale_order_batch_packaging/models/sale_order_batch.py b/nfu_sale_order_batch_packaging/models/sale_order_batch.py index c7f877d..f28d741 100644 --- a/nfu_sale_order_batch_packaging/models/sale_order_batch.py +++ b/nfu_sale_order_batch_packaging/models/sale_order_batch.py @@ -45,7 +45,7 @@ class SaleOrderBatch(models.Model): ineligible = self.product_ids.filtered( lambda p: p.open_packaging_qty > 0 and p.open_packaging_max_qty != 0.0 ) - wizard = self.env["sale.order.batch.packaging.reconcile"].create( + wizard = self.env["sale.order.batch.packaging.fill"].create( { "batch_id": self.id, "line_ids": [(0, 0, {"batch_product_id": p.id}) for p in eligible], @@ -56,7 +56,8 @@ class SaleOrderBatch(models.Model): ) return { "type": "ir.actions.act_window", - "res_model": "sale.order.batch.packaging.reconcile", + "name": "Fill Open Packages", + "res_model": "sale.order.batch.packaging.fill", "res_id": wizard.id, "view_mode": "form", "target": "new", diff --git a/nfu_sale_order_batch_packaging/models/sale_order_batch_product.py b/nfu_sale_order_batch_packaging/models/sale_order_batch_product.py index e7cabbd..a8ce5e6 100644 --- a/nfu_sale_order_batch_packaging/models/sale_order_batch_product.py +++ b/nfu_sale_order_batch_packaging/models/sale_order_batch_product.py @@ -99,13 +99,18 @@ class SaleOrderBatchProduct(models.Model): def action_restore_ordered_qty(self): self.ensure_one() lines = self.sale_order_line_ids.filtered( - lambda l: l.product_uom_qty != l.product_uom_ordered_qty + lambda l: l.product_uom_ordered_qty != 0 + and l.product_uom_qty != l.product_uom_ordered_qty ) for line in lines: line.write({"product_uom_qty": line.product_uom_ordered_qty}) - def _reconcile_packaging(self, rounding): + def _fill_packaging(self, rounding): self.ensure_one() + for line in self.sale_order_line_ids.filtered( + lambda l: l.product_uom_ordered_qty == 0 + ): + line.write({"product_uom_ordered_qty": line.product_uom_qty}) target = self.open_packaging_qty eligible = sorted( [ diff --git a/nfu_sale_order_batch_packaging/models/sale_order_line.py b/nfu_sale_order_batch_packaging/models/sale_order_line.py index 4a5bc0c..10c7bd7 100644 --- a/nfu_sale_order_batch_packaging/models/sale_order_line.py +++ b/nfu_sale_order_batch_packaging/models/sale_order_line.py @@ -6,7 +6,7 @@ class SaleOrderLine(models.Model): _inherit = "sale.order.line" product_uom_ordered_qty = fields.Float( - string="Ordered Qty", digits="Product Unit of Measure", default=1.0 + string="Ordered Qty", digits="Product Unit of Measure", default=0.0 ) product_uom_max_qty = fields.Float( string="Max Qty", digits="Product Unit of Measure" @@ -32,16 +32,18 @@ class SaleOrderLine(models.Model): @api.model_create_multi def create(self, vals_list): for vals in vals_list: + product_qty = vals.get("product_uom_qty", 0.0) if ( not vals.get("product_uom_max_qty") or vals.get("product_uom_max_qty") == 0 ): - product_qty = vals.get("product_uom_qty") vals["product_uom_max_qty"] = product_qty + if not vals.get("product_uom_ordered_qty"): + vals["product_uom_ordered_qty"] = product_qty if vals.get("product_id") and not vals.get("product_packaging_id"): product_id = vals.get("product_id") # Make sure we're not sudo - # e.x. when comming from webshop + # e.x. when coming from webshop packaging = self.env["product.packaging"].search( [ ("product_id", "=", product_id), diff --git a/nfu_sale_order_batch_packaging/models/uom_uom.py b/nfu_sale_order_batch_packaging/models/uom_uom.py index bf9d5d5..dd38559 100644 --- a/nfu_sale_order_batch_packaging/models/uom_uom.py +++ b/nfu_sale_order_batch_packaging/models/uom_uom.py @@ -1,11 +1,17 @@ -from odoo import fields, models +from odoo import api, fields, models class UomUom(models.Model): _inherit = "uom.uom" - reconcile_whole_units = fields.Boolean( - string="Reconcile as Whole Units", - default=False, - help="When set, the packaging reconciliation wizard defaults to rounding=1 for this UOM.", + batch_fill_precision = fields.Float( + compute="_compute_batch_fill_precision", + store=True, + readonly=False, + help="Rounding step used by the batch fill wizard for this UOM (e.g. 1.0 for whole units, 0.1 for one decimal).", ) + + @api.depends("rounding") + def _compute_batch_fill_precision(self): + for uom in self: + uom.batch_fill_precision = uom.rounding diff --git a/nfu_sale_order_batch_packaging/readme/HISTORY.rst b/nfu_sale_order_batch_packaging/readme/HISTORY.rst index df31a30..6350d08 100644 --- a/nfu_sale_order_batch_packaging/readme/HISTORY.rst +++ b/nfu_sale_order_batch_packaging/readme/HISTORY.rst @@ -15,4 +15,4 @@ - 16.0.1.3.4: store all quantities and round open values - 16.0.1.3.6: make sure packages are readable by the user - 16.0.1.3.7: really make sure packages are readable by the user -- 16.0.1.4.0: Add automatiok batch fillup \ No newline at end of file +- 16.0.1.4.0: Add automatic batch fillup \ No newline at end of file diff --git a/nfu_sale_order_batch_packaging/security/ir.model.access.csv b/nfu_sale_order_batch_packaging/security/ir.model.access.csv index 3f4b2f2..201b7d5 100644 --- a/nfu_sale_order_batch_packaging/security/ir.model.access.csv +++ b/nfu_sale_order_batch_packaging/security/ir.model.access.csv @@ -1,4 +1,4 @@ id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink -access_batch_packaging_reconcile,sale.order.batch.packaging.reconcile,model_sale_order_batch_packaging_reconcile,base.group_user,1,1,1,1 -access_batch_packaging_reconcile_line,sale.order.batch.packaging.reconcile.line,model_sale_order_batch_packaging_reconcile_line,base.group_user,1,1,1,1 -access_batch_packaging_reconcile_zero_line,sale.order.batch.packaging.reconcile.zero.line,model_sale_order_batch_packaging_reconcile_zero_line,base.group_user,1,1,1,1 +access_batch_packaging_fill,sale.order.batch.packaging.fill,model_sale_order_batch_packaging_fill,base.group_user,1,1,1,1 +access_batch_packaging_fill_line,sale.order.batch.packaging.fill.line,model_sale_order_batch_packaging_fill_line,base.group_user,1,1,1,1 +access_batch_packaging_fill_zero_line,sale.order.batch.packaging.fill.zero.line,model_sale_order_batch_packaging_fill_zero_line,base.group_user,1,1,1,1 diff --git a/nfu_sale_order_batch_packaging/static/description/index.html b/nfu_sale_order_batch_packaging/static/description/index.html index 012dcf4..1dee347 100644 --- a/nfu_sale_order_batch_packaging/static/description/index.html +++ b/nfu_sale_order_batch_packaging/static/description/index.html @@ -411,7 +411,7 @@ and adds a packaging default to sale order batch.

  • 16.0.1.3.4: store all quantities and round open values
  • 16.0.1.3.6: make sure packages are readable by the user
  • 16.0.1.3.7: really make sure packages are readable by the user
  • -
  • 16.0.1.4.0: Add automatiok batch fillup
  • +
  • 16.0.1.4.0: Add automatic batch fillup
  • diff --git a/nfu_sale_order_batch_packaging/tests/__init__.py b/nfu_sale_order_batch_packaging/tests/__init__.py index 4bde748..e23cadf 100644 --- a/nfu_sale_order_batch_packaging/tests/__init__.py +++ b/nfu_sale_order_batch_packaging/tests/__init__.py @@ -1 +1 @@ -from . import test_batch_packaging_reconcile +from . import test_batch_packaging_fill diff --git a/nfu_sale_order_batch_packaging/tests/test_batch_packaging_reconcile.py b/nfu_sale_order_batch_packaging/tests/test_batch_packaging_fill.py similarity index 81% rename from nfu_sale_order_batch_packaging/tests/test_batch_packaging_reconcile.py rename to nfu_sale_order_batch_packaging/tests/test_batch_packaging_fill.py index 027cc1d..2caba11 100644 --- a/nfu_sale_order_batch_packaging/tests/test_batch_packaging_reconcile.py +++ b/nfu_sale_order_batch_packaging/tests/test_batch_packaging_fill.py @@ -3,7 +3,7 @@ from odoo.tests.common import TransactionCase @tagged("post_install", "-at_install") -class TestBatchPackagingReconcile(TransactionCase): +class TestBatchPackagingFill(TransactionCase): def setUp(self): super().setUp() self.uom_kg = self.env.ref("uom.product_uom_kgm") @@ -51,7 +51,7 @@ class TestBatchPackagingReconcile(TransactionCase): return self.batch.product_ids.filtered(lambda p: p.product_id == product) # ------------------------------------------------------------------ - # _reconcile_packaging + # _fill_packaging # ------------------------------------------------------------------ def test_even_split(self): @@ -61,7 +61,7 @@ class TestBatchPackagingReconcile(TransactionCase): bp = self._add_lines(product, [(7.0, 10.0), (8.0, 11.0), (9.0, 12.0)]) self.assertAlmostEqual(bp.open_packaging_qty, 6.0, places=2) - bp._reconcile_packaging(0.001) + bp._fill_packaging(0.001) self.assertAlmostEqual(bp.open_packaging_qty, 0.0, places=2) # each line must have gained exactly 2 kg @@ -80,7 +80,7 @@ class TestBatchPackagingReconcile(TransactionCase): bp = self._add_lines(product, [(8.0, 9.0), (5.0, 15.0), (10.0, 17.0)]) self.assertAlmostEqual(bp.open_packaging_qty, 7.0, places=2) - bp._reconcile_packaging(0.001) + bp._fill_packaging(0.001) self.assertAlmostEqual(bp.open_packaging_qty, 0.0, places=2) capped = bp.sale_order_line_ids.filtered( @@ -93,14 +93,14 @@ class TestBatchPackagingReconcile(TransactionCase): self.assertLessEqual(line.product_uom_qty, line.product_uom_max_qty + 1e-6) def test_whole_units_rounding_produces_integers(self): - """rounding=1.0 yields only whole-number additions.""" + """precision=1.0 yields only whole-number additions.""" product = self._make_product(self.uom_unit, 6.0) # total = 7, open = 5; rooms: 3 and 5 bp = self._add_lines(product, [(3.0, 6.0), (4.0, 9.0)]) self.assertAlmostEqual(bp.open_packaging_qty, 5.0, places=2) orig = {line.id: line.product_uom_qty for line in bp.sale_order_line_ids} - bp._reconcile_packaging(1.0) + bp._fill_packaging(1.0) self.assertAlmostEqual(bp.open_packaging_qty, 0.0, places=2) for line in bp.sale_order_line_ids: @@ -108,29 +108,29 @@ class TestBatchPackagingReconcile(TransactionCase): self.assertAlmostEqual(added, round(added), places=6) def test_decimal_rounding_uses_tenth_steps(self): - """rounding=0.1 distributes in 0.1-unit steps.""" + """precision=0.1 distributes in 0.1-unit steps.""" product = self._make_product(self.uom_kg, 10.0) # total = 7, open = 3; rooms: 7 and 6 → sorted: line2(6), line1(7) bp = self._add_lines(product, [(3.0, 10.0), (4.0, 10.0)]) self.assertAlmostEqual(bp.open_packaging_qty, 3.0, places=2) orig = {line.id: line.product_uom_qty for line in bp.sale_order_line_ids} - bp._reconcile_packaging(0.1) + bp._fill_packaging(0.1) self.assertAlmostEqual(bp.open_packaging_qty, 0.0, places=2) for line in bp.sale_order_line_ids: added = round(line.product_uom_qty - orig[line.id], 6) # must be a multiple of 0.1 (within floating-point tolerance) - self.assertAlmostEqual(added % 0.1, 0.0, places=5) + self.assertAlmostEqual(round(added / 0.1) * 0.1, added, places=5) def test_sub_rounding_remainder_is_attached(self): """A remainder smaller than the rounding step is added directly to the last line.""" product = self._make_product(self.uom_kg, 3.0) - # total = 2.7, open = 0.3; with rounding=1.0 every fair_share rounds to 0 + # total = 2.7, open = 0.3; with precision=1.0 every fair_share rounds to 0 bp = self._add_lines(product, [(0.9, 1.5), (0.9, 1.5), (0.9, 1.5)]) self.assertAlmostEqual(bp.open_packaging_qty, 0.3, places=2) - bp._reconcile_packaging(1.0) + bp._fill_packaging(1.0) # The whole 0.3 remainder must have been placed on one line self.assertAlmostEqual(bp.open_packaging_qty, 0.0, places=2) @@ -139,10 +139,10 @@ class TestBatchPackagingReconcile(TransactionCase): self.assertLessEqual(line.product_uom_qty, line.product_uom_max_qty + 1e-6) def test_no_line_exceeds_max_qty(self): - """After reconciliation no sale order line may exceed its max qty.""" + """After filling no sale order line may exceed its max qty.""" product = self._make_product(self.uom_kg, 10.0) bp = self._add_lines(product, [(6.0, 8.0), (7.0, 12.0), (4.0, 9.0)]) - bp._reconcile_packaging(0.001) + bp._fill_packaging(0.001) for line in bp.sale_order_line_ids: self.assertLessEqual(line.product_uom_qty, line.product_uom_max_qty + 1e-6) @@ -160,63 +160,53 @@ class TestBatchPackagingReconcile(TransactionCase): self._add_lines(ineligible, [(23.0, 24.0)]) action = self.batch.action_fill_packages() - wizard = self.env["sale.order.batch.packaging.reconcile"].browse( - action["res_id"] - ) + wizard = self.env["sale.order.batch.packaging.fill"].browse(action["res_id"]) self.assertEqual(len(wizard.line_ids), 1) self.assertEqual(wizard.line_ids.product_id, eligible) self.assertEqual(len(wizard.zero_line_ids), 1) self.assertEqual(wizard.zero_line_ids.product_id, ineligible) - def test_wizard_default_whole_units_for_unit_uom(self): - """UOMs with reconcile_whole_units=True default use_whole_units to True.""" + def test_wizard_default_precision_for_unit_uom(self): + """UOMs with batch_fill_precision=1.0 default to precision 1.0.""" product = self._make_product(self.uom_unit, 6.0) self._add_lines(product, [(3.0, 9.0)]) action = self.batch.action_fill_packages() - wizard = self.env["sale.order.batch.packaging.reconcile"].browse( - action["res_id"] - ) + wizard = self.env["sale.order.batch.packaging.fill"].browse(action["res_id"]) - self.assertTrue(wizard.line_ids.use_whole_units) + self.assertAlmostEqual(wizard.line_ids.batch_fill_precision, 1.0) - def test_wizard_default_whole_units_for_stueck_uom(self): - """Stück UOM (reconcile_whole_units=True) defaults use_whole_units to True.""" + def test_wizard_default_precision_for_stueck_uom(self): + """Stück UOM (batch_fill_precision=1.0) defaults to precision 1.0.""" uom_stueck = self.env.ref("nfu_sale_order_batch_packaging.product_uom_stueck") product = self._make_product(uom_stueck, 6.0) self._add_lines(product, [(3.0, 9.0)]) action = self.batch.action_fill_packages() - wizard = self.env["sale.order.batch.packaging.reconcile"].browse( - action["res_id"] - ) + wizard = self.env["sale.order.batch.packaging.fill"].browse(action["res_id"]) - self.assertTrue(wizard.line_ids.use_whole_units) + self.assertAlmostEqual(wizard.line_ids.batch_fill_precision, 1.0) - def test_wizard_default_decimal_for_kg_uom(self): - """UOMs without reconcile_whole_units default use_whole_units to False.""" + def test_wizard_default_precision_for_kg_uom(self): + """UOMs with default batch_fill_precision use 0.1.""" product = self._make_product(self.uom_kg, 10.0) self._add_lines(product, [(23.0, 30.0)]) action = self.batch.action_fill_packages() - wizard = self.env["sale.order.batch.packaging.reconcile"].browse( - action["res_id"] - ) + wizard = self.env["sale.order.batch.packaging.fill"].browse(action["res_id"]) - self.assertFalse(wizard.line_ids.use_whole_units) + self.assertAlmostEqual(wizard.line_ids.batch_fill_precision, 0.1) - def test_wizard_reconcile_closes_open_packaging(self): + def test_wizard_fill_closes_open_packaging(self): """End-to-end: confirming the wizard brings open_packaging_qty to zero.""" product = self._make_product(self.uom_kg, 10.0) bp = self._add_lines(product, [(23.0, 30.0)]) self.assertGreater(bp.open_packaging_qty, 0.0) action = self.batch.action_fill_packages() - wizard = self.env["sale.order.batch.packaging.reconcile"].browse( - action["res_id"] - ) - wizard.action_reconcile() + wizard = self.env["sale.order.batch.packaging.fill"].browse(action["res_id"]) + wizard.action_fill() self.assertAlmostEqual(bp.open_packaging_qty, 0.0, places=2) @@ -228,8 +218,8 @@ class TestBatchPackagingReconcile(TransactionCase): """action_restore_ordered_qty on the batch sets every line back to ordered qty.""" product = self._make_product(self.uom_kg, 10.0) bp = self._add_lines(product, [(23.0, 30.0)]) - bp._reconcile_packaging(0.1) - # reconciliation must have increased some qty + bp._fill_packaging(0.1) + # fill must have increased some qty self.assertTrue( any( line.product_uom_qty != line.product_uom_ordered_qty @@ -248,8 +238,8 @@ class TestBatchPackagingReconcile(TransactionCase): product_b = self._make_product(self.uom_kg, 10.0) bp_a = self._add_lines(product_a, [(23.0, 30.0)]) bp_b = self._add_lines(product_b, [(23.0, 30.0)]) - bp_a._reconcile_packaging(0.1) - bp_b._reconcile_packaging(0.1) + bp_a._fill_packaging(0.1) + bp_b._fill_packaging(0.1) bp_a.action_restore_ordered_qty() @@ -265,24 +255,24 @@ class TestBatchPackagingReconcile(TransactionCase): ) ) - def test_has_qty_adjusted_false_before_reconcile(self): + def test_has_qty_adjusted_false_before_fill(self): """has_qty_adjusted is False when no line has been modified.""" product = self._make_product(self.uom_kg, 10.0) self._add_lines(product, [(10.0, 15.0)]) self.assertFalse(self.batch.has_qty_adjusted) - def test_has_qty_adjusted_true_after_reconcile(self): + def test_has_qty_adjusted_true_after_fill(self): """has_qty_adjusted becomes True once a line quantity is increased.""" product = self._make_product(self.uom_kg, 10.0) bp = self._add_lines(product, [(23.0, 30.0)]) - bp._reconcile_packaging(0.1) + bp._fill_packaging(0.1) self.assertTrue(self.batch.has_qty_adjusted) def test_has_qty_adjusted_false_after_restore(self): """has_qty_adjusted returns to False after restoring ordered quantities.""" product = self._make_product(self.uom_kg, 10.0) bp = self._add_lines(product, [(23.0, 30.0)]) - bp._reconcile_packaging(0.1) + bp._fill_packaging(0.1) self.batch.action_restore_ordered_qty() self.assertFalse(self.batch.has_qty_adjusted) @@ -334,12 +324,10 @@ class TestBatchPackagingReconcile(TransactionCase): bp = self.batch.product_ids.filtered(lambda p: p.product_id == ineligible) action = self.batch.action_fill_packages() - wizard = self.env["sale.order.batch.packaging.reconcile"].browse( - action["res_id"] - ) + wizard = self.env["sale.order.batch.packaging.fill"].browse(action["res_id"]) self.assertFalse(wizard.zero_line_ids.keep_qty) - wizard.action_reconcile() + wizard.action_fill() for line in bp.sale_order_line_ids: self.assertAlmostEqual(line.product_uom_qty, 0.0, places=3) @@ -351,33 +339,28 @@ class TestBatchPackagingReconcile(TransactionCase): orig_qtys = {line.id: line.product_uom_qty for line in bp.sale_order_line_ids} action = self.batch.action_fill_packages() - wizard = self.env["sale.order.batch.packaging.reconcile"].browse( - action["res_id"] - ) + wizard = self.env["sale.order.batch.packaging.fill"].browse(action["res_id"]) wizard.zero_line_ids.write({"keep_qty": True}) - wizard.action_reconcile() + wizard.action_fill() for line in bp.sale_order_line_ids: self.assertAlmostEqual(line.product_uom_qty, orig_qtys[line.id], places=3) - def test_wizard_respects_use_whole_units_override(self): - """Toggling use_whole_units on a wizard line changes the applied rounding.""" + def test_wizard_respects_precision_override(self): + """Overriding batch_fill_precision on a wizard line changes the applied rounding.""" product = self._make_product(self.uom_kg, 10.0) - # total = 7, open = 3; 3 lines with room 1 each won't cover 3 with rounding=1 - # but with rounding=0.1 each gets 1.0 exactly — use 2 lines (rooms: 7 and 6) + # total = 7, open = 3; use 2 lines (rooms: 7 and 6) bp = self._add_lines(product, [(3.0, 10.0), (4.0, 10.0)]) action = self.batch.action_fill_packages() - wizard = self.env["sale.order.batch.packaging.reconcile"].browse( - action["res_id"] - ) + wizard = self.env["sale.order.batch.packaging.fill"].browse(action["res_id"]) # Force whole-units even though UOM is kg - wizard.line_ids.write({"use_whole_units": True}) - wizard.action_reconcile() + wizard.line_ids.write({"batch_fill_precision": 1.0}) + wizard.action_fill() self.assertAlmostEqual(bp.open_packaging_qty, 0.0, places=2) for line in bp.sale_order_line_ids: - # additions must be whole numbers when use_whole_units=True + # additions must be whole numbers when precision=1.0 self.assertAlmostEqual( line.product_uom_qty, round(line.product_uom_qty), places=6 ) diff --git a/nfu_sale_order_batch_packaging/views/uom_uom_views.xml b/nfu_sale_order_batch_packaging/views/uom_uom_views.xml new file mode 100644 index 0000000..d0faeb8 --- /dev/null +++ b/nfu_sale_order_batch_packaging/views/uom_uom_views.xml @@ -0,0 +1,13 @@ + + + + uom.uom.form.batch.fill + uom.uom + + + + + + + + diff --git a/nfu_sale_order_batch_packaging/wizard/__init__.py b/nfu_sale_order_batch_packaging/wizard/__init__.py index f468d6b..3b6be7a 100644 --- a/nfu_sale_order_batch_packaging/wizard/__init__.py +++ b/nfu_sale_order_batch_packaging/wizard/__init__.py @@ -1 +1 @@ -from . import sale_order_batch_packaging_reconcile +from . import sale_order_batch_packaging_fill diff --git a/nfu_sale_order_batch_packaging/wizard/sale_order_batch_packaging_reconcile.py b/nfu_sale_order_batch_packaging/wizard/sale_order_batch_packaging_fill.py similarity index 54% rename from nfu_sale_order_batch_packaging/wizard/sale_order_batch_packaging_reconcile.py rename to nfu_sale_order_batch_packaging/wizard/sale_order_batch_packaging_fill.py index 6fb523e..ff3def3 100644 --- a/nfu_sale_order_batch_packaging/wizard/sale_order_batch_packaging_reconcile.py +++ b/nfu_sale_order_batch_packaging/wizard/sale_order_batch_packaging_fill.py @@ -1,14 +1,14 @@ from odoo import api, fields, models -class SaleOrderBatchPackagingReconcile(models.TransientModel): - _name = "sale.order.batch.packaging.reconcile" - _description = "Batch Packaging Auto-Reconcile Wizard" +class SaleOrderBatchPackagingFill(models.TransientModel): + _name = "sale.order.batch.packaging.fill" + _description = "Batch Packaging Auto-Fill Wizard" batch_id = fields.Many2one("sale.order.batch", readonly=True) - line_ids = fields.One2many("sale.order.batch.packaging.reconcile.line", "wizard_id") + line_ids = fields.One2many("sale.order.batch.packaging.fill.line", "wizard_id") zero_line_ids = fields.One2many( - "sale.order.batch.packaging.reconcile.zero.line", "wizard_id" + "sale.order.batch.packaging.fill.zero.line", "wizard_id" ) has_fill_lines = fields.Boolean(compute="_compute_has_fill_lines") has_zero_lines = fields.Boolean(compute="_compute_has_zero_lines") @@ -23,22 +23,19 @@ class SaleOrderBatchPackagingReconcile(models.TransientModel): for wizard in self: wizard.has_zero_lines = bool(wizard.zero_line_ids) - def action_reconcile(self): + def action_fill(self): for line in self.line_ids: - rounding = 1.0 if line.use_whole_units else 0.1 - line.batch_product_id._reconcile_packaging(rounding) + line.batch_product_id._fill_packaging(line.batch_fill_precision) for zero_line in self.zero_line_ids.filtered(lambda l: not l.keep_qty): zero_line.batch_product_id._zero_packaging() return {"type": "ir.actions.act_window_close"} -class SaleOrderBatchPackagingReconcileLine(models.TransientModel): - _name = "sale.order.batch.packaging.reconcile.line" - _description = "Batch Packaging Auto-Reconcile Wizard Line" +class SaleOrderBatchPackagingFillLine(models.TransientModel): + _name = "sale.order.batch.packaging.fill.line" + _description = "Batch Packaging Auto-Fill Wizard Line" - wizard_id = fields.Many2one( - "sale.order.batch.packaging.reconcile", ondelete="cascade" - ) + wizard_id = fields.Many2one("sale.order.batch.packaging.fill", ondelete="cascade") batch_product_id = fields.Many2one("sale.order.batch.product", required=True) product_id = fields.Many2one( related="batch_product_id.product_id", string="Product" @@ -48,29 +45,27 @@ class SaleOrderBatchPackagingReconcileLine(models.TransientModel): string="Open Packaging Qty", digits=[12, 3], ) - use_whole_units = fields.Boolean( - string="Round to 1", - compute="_compute_use_whole_units", + batch_fill_precision = fields.Float( + string="Precision", + compute="_compute_batch_fill_precision", precompute=True, store=True, readonly=False, ) - @api.depends("batch_product_id.product_id.uom_id.reconcile_whole_units") - def _compute_use_whole_units(self): + @api.depends("batch_product_id.product_id.uom_id.batch_fill_precision") + def _compute_batch_fill_precision(self): for line in self: - line.use_whole_units = ( - line.batch_product_id.product_id.uom_id.reconcile_whole_units + line.batch_fill_precision = ( + line.batch_product_id.product_id.uom_id.batch_fill_precision ) -class SaleOrderBatchPackagingReconcileZeroLine(models.TransientModel): - _name = "sale.order.batch.packaging.reconcile.zero.line" - _description = "Batch Packaging Auto-Reconcile Zero-Out Line" +class SaleOrderBatchPackagingFillZeroLine(models.TransientModel): + _name = "sale.order.batch.packaging.fill.zero.line" + _description = "Batch Packaging Auto-Fill Zero-Out Line" - wizard_id = fields.Many2one( - "sale.order.batch.packaging.reconcile", ondelete="cascade" - ) + wizard_id = fields.Many2one("sale.order.batch.packaging.fill", ondelete="cascade") batch_product_id = fields.Many2one("sale.order.batch.product", required=True) product_id = fields.Many2one( related="batch_product_id.product_id", string="Product" diff --git a/nfu_sale_order_batch_packaging/wizard/sale_order_batch_packaging_reconcile_views.xml b/nfu_sale_order_batch_packaging/wizard/sale_order_batch_packaging_fill_views.xml similarity index 79% rename from nfu_sale_order_batch_packaging/wizard/sale_order_batch_packaging_reconcile_views.xml rename to nfu_sale_order_batch_packaging/wizard/sale_order_batch_packaging_fill_views.xml index 96430d0..afb40a3 100644 --- a/nfu_sale_order_batch_packaging/wizard/sale_order_batch_packaging_reconcile_views.xml +++ b/nfu_sale_order_batch_packaging/wizard/sale_order_batch_packaging_fill_views.xml @@ -1,10 +1,10 @@ - - sale.order.batch.packaging.reconcile.form - sale.order.batch.packaging.reconcile + + sale.order.batch.packaging.fill.form + sale.order.batch.packaging.fill -
    + - + @@ -34,7 +34,7 @@