mirror of
https://github.com/OCA/stock-logistics-warehouse.git
synced 2025-01-21 14:27:28 +02:00
@@ -14,17 +14,17 @@ msgstr ""
|
||||
"Plural-Forms: \n"
|
||||
|
||||
#. module: stock_packaging_calculator
|
||||
#: model:ir.model.fields,field_description:stock_packaging_calculator.field_product_product__display_name
|
||||
#: model:ir.model.fields,field_description:stock_packaging_calculator.field_product_qty_by_packaging_mixin__display_name
|
||||
msgid "Display Name"
|
||||
msgstr ""
|
||||
|
||||
#. module: stock_packaging_calculator
|
||||
#: model:ir.model.fields,field_description:stock_packaging_calculator.field_product_product__id
|
||||
#: model:ir.model.fields,field_description:stock_packaging_calculator.field_product_qty_by_packaging_mixin__id
|
||||
msgid "ID"
|
||||
msgstr ""
|
||||
|
||||
#. module: stock_packaging_calculator
|
||||
#: model:ir.model.fields,field_description:stock_packaging_calculator.field_product_product____last_update
|
||||
#: model:ir.model.fields,field_description:stock_packaging_calculator.field_product_qty_by_packaging_mixin____last_update
|
||||
msgid "Last Modified on"
|
||||
msgstr ""
|
||||
|
||||
@@ -38,6 +38,16 @@ msgstr ""
|
||||
msgid "Product"
|
||||
msgstr ""
|
||||
|
||||
#. module: stock_packaging_calculator
|
||||
#: model:ir.model,name:stock_packaging_calculator.model_product_qty_by_packaging_mixin
|
||||
msgid "Product Qty By Packaging (Mixin)"
|
||||
msgstr ""
|
||||
|
||||
#. module: stock_packaging_calculator
|
||||
#: model:ir.model.fields,field_description:stock_packaging_calculator.field_product_qty_by_packaging_mixin__product_qty_by_packaging_display
|
||||
msgid "Qty by packaging"
|
||||
msgstr ""
|
||||
|
||||
#. module: stock_packaging_calculator
|
||||
#: model:ir.model.fields,help:stock_packaging_calculator.field_product_product__packaging_contained_mapping
|
||||
msgid "Technical field to store contained packaging. "
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
from . import product
|
||||
from . import product_qty_by_packaging_mixin
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
# Copyright 2020 Camptocamp SA
|
||||
# @author: Simone Orsi <simone.orsi@camptocamp.com>
|
||||
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl)
|
||||
|
||||
import unicodedata
|
||||
from collections import namedtuple
|
||||
|
||||
from odoo import api, models
|
||||
@@ -9,7 +11,9 @@ from odoo.tools import float_compare
|
||||
from odoo.addons.base_sparse_field.models.fields import Serialized
|
||||
|
||||
# Unify records as we mix up w/ UoM
|
||||
Packaging = namedtuple("Packaging", "id name qty is_unit")
|
||||
Packaging = namedtuple("Packaging", "id name qty barcode is_unit")
|
||||
|
||||
NO_BREAK_SPACE_CHAR = unicodedata.lookup("NO-BREAK SPACE")
|
||||
|
||||
|
||||
class Product(models.Model):
|
||||
@@ -20,6 +24,7 @@ class Product(models.Model):
|
||||
help="Technical field to store contained packaging. ",
|
||||
)
|
||||
|
||||
@api.depends_context("lang")
|
||||
@api.depends("packaging_ids.qty")
|
||||
def _compute_packaging_contained_mapping(self):
|
||||
for rec in self:
|
||||
@@ -80,10 +85,12 @@ class Product(models.Model):
|
||||
the display name of the packaging.
|
||||
"""
|
||||
custom_filter = self.env.context.get("_packaging_filter", lambda x: x)
|
||||
name_getter = self.env.context.get("_packaging_name_getter", lambda x: x.name)
|
||||
name_getter = self.env.context.get(
|
||||
"_packaging_name_getter", self._packaging_name_getter
|
||||
)
|
||||
packagings = sorted(
|
||||
[
|
||||
Packaging(x.id, name_getter(x), x.qty, False)
|
||||
Packaging(x.id, name_getter(x), x.qty, x.barcode, False)
|
||||
for x in self.packaging_ids.filtered(custom_filter)
|
||||
# Exclude the ones w/ zero qty as they are useless for the math
|
||||
if x.qty
|
||||
@@ -96,10 +103,13 @@ class Product(models.Model):
|
||||
# NOTE: the ID here could clash w/ one of the packaging's.
|
||||
# If you create a mapping based on IDs, keep this in mind.
|
||||
# You can use `is_unit` to check this.
|
||||
Packaging(self.uom_id.id, self.uom_id.name, self.uom_id.factor, True)
|
||||
Packaging(self.uom_id.id, self.uom_id.name, self.uom_id.factor, None, True)
|
||||
)
|
||||
return packagings
|
||||
|
||||
def _packaging_name_getter(self, packaging):
|
||||
return packaging.name
|
||||
|
||||
def _product_qty_by_packaging(self, pkg_by_qty, qty, with_contained=False):
|
||||
"""Produce a list of dictionaries of packaging info."""
|
||||
# TODO: refactor to handle fractional quantities (eg: 0.5 Kg)
|
||||
@@ -140,4 +150,64 @@ class Product(models.Model):
|
||||
"qty": qty_per_pkg,
|
||||
"name": packaging.name,
|
||||
"is_unit": packaging.is_unit,
|
||||
"barcode": packaging.barcode,
|
||||
}
|
||||
|
||||
def product_qty_by_packaging_as_str(
|
||||
self, prod_qty, include_total_units=False, only_packaging=False
|
||||
):
|
||||
"""Return a string representing the qty of each packaging.
|
||||
|
||||
:param prod_qty: the qty of current product to translate to pkg qty
|
||||
:param include_total_units: includes total qty required initially
|
||||
:param only_packaging: exclude units if you have only units.
|
||||
IOW: if the qty does not match any packaging and this flag is true
|
||||
you'll get an empty string instead of `N units`.
|
||||
"""
|
||||
self.ensure_one()
|
||||
if not prod_qty:
|
||||
return ""
|
||||
|
||||
qty_by_packaging = self.product_qty_by_packaging(prod_qty)
|
||||
if not qty_by_packaging:
|
||||
return ""
|
||||
|
||||
# Exclude unit qty and reuse it later
|
||||
unit_qty = None
|
||||
has_only_units = True
|
||||
_qty_by_packaging = []
|
||||
for pkg_qty in qty_by_packaging:
|
||||
if pkg_qty["is_unit"]:
|
||||
unit_qty = pkg_qty["qty"]
|
||||
continue
|
||||
has_only_units = False
|
||||
_qty_by_packaging.append(pkg_qty)
|
||||
# Browse them all at once
|
||||
records = self.env["product.packaging"].browse(
|
||||
[x["id"] for x in _qty_by_packaging]
|
||||
)
|
||||
_qty_by_packaging_as_str = self.env.context.get(
|
||||
"_qty_by_packaging_as_str", self._qty_by_packaging_as_str
|
||||
)
|
||||
# Collect all strings representations
|
||||
as_string = []
|
||||
for record, info in zip(records, _qty_by_packaging):
|
||||
bit = _qty_by_packaging_as_str(record, info["qty"])
|
||||
if bit:
|
||||
as_string.append(bit)
|
||||
# Restore unit information if any.
|
||||
include_units = (has_only_units and not only_packaging) or not has_only_units
|
||||
if unit_qty and include_units:
|
||||
as_string.append(f"{unit_qty} {self.uom_id.name}")
|
||||
# We want to avoid line break here as this string
|
||||
# can be used by reports
|
||||
res = f",{NO_BREAK_SPACE_CHAR}".join(as_string)
|
||||
if include_total_units and not has_only_units:
|
||||
res += " " + self._qty_by_packaging_total_units(prod_qty)
|
||||
return res
|
||||
|
||||
def _qty_by_packaging_as_str(self, packaging, qty):
|
||||
return f"{qty} {packaging.name}"
|
||||
|
||||
def _qty_by_packaging_total_units(self, prod_qty):
|
||||
return f"({prod_qty} {self.uom_id.name})"
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
# Copyright 2021 Camptocamp SA
|
||||
# @author: Simone Orsi <simone.orsi@camptocamp.com>
|
||||
# @author: Sébastien Alix <sebastien.alix@camptocamp.com>
|
||||
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl)
|
||||
from odoo import api, fields, models
|
||||
|
||||
|
||||
class ProductQtyByPackagingMixin(models.AbstractModel):
|
||||
"""Allow displaying product qty by packaging."""
|
||||
|
||||
_name = "product.qty_by_packaging.mixin"
|
||||
_description = "Product Qty By Packaging (Mixin)"
|
||||
|
||||
_qty_by_pkg__product_field_name = "product_id"
|
||||
_qty_by_pkg__qty_field_name = None
|
||||
|
||||
product_qty_by_packaging_display = fields.Char(
|
||||
compute="_compute_product_qty_by_packaging_display", string="Qty by packaging"
|
||||
)
|
||||
|
||||
def _product_qty_by_packaging_display_depends(self):
|
||||
depends = []
|
||||
if self._qty_by_pkg__product_field_name:
|
||||
depends.append(self._qty_by_pkg__product_field_name)
|
||||
if self._qty_by_pkg__qty_field_name:
|
||||
depends.append(self._qty_by_pkg__qty_field_name)
|
||||
return depends
|
||||
|
||||
@api.depends_context("lang", "qty_by_pkg_total_units", "qty_by_pkg_only_packaging")
|
||||
@api.depends(lambda self: self._product_qty_by_packaging_display_depends())
|
||||
def _compute_product_qty_by_packaging_display(self):
|
||||
include_total_units = self.env.context.get("qty_by_pkg_total_units", False)
|
||||
only_packaging = self.env.context.get("qty_by_pkg_only_packaging", False)
|
||||
for record in self:
|
||||
value = ""
|
||||
product = record._qty_by_packaging_get_product()
|
||||
if product:
|
||||
value = product.product_qty_by_packaging_as_str(
|
||||
record._qty_by_packaging_get_qty(),
|
||||
include_total_units=include_total_units,
|
||||
only_packaging=only_packaging,
|
||||
)
|
||||
record.product_qty_by_packaging_display = value
|
||||
|
||||
def _qty_by_packaging_get_product(self):
|
||||
return self[self._qty_by_pkg__product_field_name]
|
||||
|
||||
def _qty_by_packaging_get_qty(self):
|
||||
return self[self._qty_by_pkg__qty_field_name]
|
||||
@@ -1 +1,2 @@
|
||||
from . import test_packaging_calc
|
||||
from . import test_pkg_qty_str
|
||||
|
||||
42
stock_packaging_calculator/tests/common.py
Normal file
42
stock_packaging_calculator/tests/common.py
Normal file
@@ -0,0 +1,42 @@
|
||||
# Copyright 2020 Camptocamp SA
|
||||
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl)
|
||||
from odoo.tests import SavepointCase
|
||||
|
||||
|
||||
class TestCommon(SavepointCase):
|
||||
|
||||
at_install = False
|
||||
post_install = True
|
||||
maxDiff = None
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super().setUpClass()
|
||||
cls.env = cls.env(context=dict(cls.env.context, tracking_disable=True))
|
||||
cls.uom_unit = cls.env.ref("uom.product_uom_unit")
|
||||
cls.product_a = cls.env["product.product"].create(
|
||||
{
|
||||
"name": "Product A",
|
||||
"uom_id": cls.uom_unit.id,
|
||||
"uom_po_id": cls.uom_unit.id,
|
||||
}
|
||||
)
|
||||
cls.pkg_box = cls.env["product.packaging"].create(
|
||||
{"name": "Box", "product_id": cls.product_a.id, "qty": 50, "barcode": "BOX"}
|
||||
)
|
||||
cls.pkg_big_box = cls.env["product.packaging"].create(
|
||||
{
|
||||
"name": "Big Box",
|
||||
"product_id": cls.product_a.id,
|
||||
"qty": 200,
|
||||
"barcode": "BIGBOX",
|
||||
}
|
||||
)
|
||||
cls.pkg_pallet = cls.env["product.packaging"].create(
|
||||
{
|
||||
"name": "Pallet",
|
||||
"product_id": cls.product_a.id,
|
||||
"qty": 2000,
|
||||
"barcode": "PALLET",
|
||||
}
|
||||
)
|
||||
@@ -1,63 +1,17 @@
|
||||
# Copyright 2020 Camptocamp SA
|
||||
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl)
|
||||
from odoo.tests import SavepointCase
|
||||
from .common import TestCommon
|
||||
from .utils import make_pkg_values
|
||||
|
||||
|
||||
class TestCalc(SavepointCase):
|
||||
|
||||
at_install = False
|
||||
post_install = True
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super().setUpClass()
|
||||
cls.env = cls.env(context=dict(cls.env.context, tracking_disable=True))
|
||||
cls.uom_unit = cls.env.ref("uom.product_uom_unit")
|
||||
cls.product_a = cls.env["product.product"].create(
|
||||
{
|
||||
"name": "Product A",
|
||||
"uom_id": cls.uom_unit.id,
|
||||
"uom_po_id": cls.uom_unit.id,
|
||||
}
|
||||
)
|
||||
cls.pkg_box = cls.env["product.packaging"].create(
|
||||
{"name": "Box", "product_id": cls.product_a.id, "qty": 50}
|
||||
)
|
||||
cls.pkg_big_box = cls.env["product.packaging"].create(
|
||||
{"name": "Big Box", "product_id": cls.product_a.id, "qty": 200}
|
||||
)
|
||||
cls.pkg_pallet = cls.env["product.packaging"].create(
|
||||
{"name": "Pallet", "product_id": cls.product_a.id, "qty": 2000}
|
||||
)
|
||||
|
||||
class TestCalc(TestCommon):
|
||||
def test_contained_mapping(self):
|
||||
self.assertEqual(
|
||||
self.product_a.packaging_contained_mapping,
|
||||
{
|
||||
str(self.pkg_pallet.id): [
|
||||
{
|
||||
"id": self.pkg_big_box.id,
|
||||
"qty": 10,
|
||||
"name": self.pkg_big_box.name,
|
||||
"is_unit": False,
|
||||
},
|
||||
],
|
||||
str(self.pkg_big_box.id): [
|
||||
{
|
||||
"id": self.pkg_box.id,
|
||||
"qty": 4,
|
||||
"name": self.pkg_box.name,
|
||||
"is_unit": False,
|
||||
},
|
||||
],
|
||||
str(self.pkg_box.id): [
|
||||
{
|
||||
"id": self.uom_unit.id,
|
||||
"qty": 50,
|
||||
"name": self.uom_unit.name,
|
||||
"is_unit": True,
|
||||
},
|
||||
],
|
||||
str(self.pkg_pallet.id): [make_pkg_values(self.pkg_big_box, qty=10)],
|
||||
str(self.pkg_big_box.id): [make_pkg_values(self.pkg_box, qty=4)],
|
||||
str(self.pkg_box.id): [make_pkg_values(self.uom_unit, qty=50)],
|
||||
},
|
||||
)
|
||||
# Update pkg qty
|
||||
@@ -65,132 +19,51 @@ class TestCalc(SavepointCase):
|
||||
self.assertEqual(
|
||||
self.product_a.packaging_contained_mapping,
|
||||
{
|
||||
str(self.pkg_pallet.id): [
|
||||
{
|
||||
"id": self.pkg_big_box.id,
|
||||
"qty": 20,
|
||||
"name": self.pkg_big_box.name,
|
||||
"is_unit": False,
|
||||
},
|
||||
],
|
||||
str(self.pkg_big_box.id): [
|
||||
{
|
||||
"id": self.pkg_box.id,
|
||||
"qty": 4,
|
||||
"name": self.pkg_box.name,
|
||||
"is_unit": False,
|
||||
},
|
||||
],
|
||||
str(self.pkg_box.id): [
|
||||
{
|
||||
"id": self.uom_unit.id,
|
||||
"qty": 50,
|
||||
"name": self.uom_unit.name,
|
||||
"is_unit": True,
|
||||
},
|
||||
],
|
||||
str(self.pkg_pallet.id): [make_pkg_values(self.pkg_big_box, qty=20)],
|
||||
str(self.pkg_big_box.id): [make_pkg_values(self.pkg_box, qty=4)],
|
||||
str(self.pkg_box.id): [make_pkg_values(self.uom_unit, qty=50)],
|
||||
},
|
||||
)
|
||||
|
||||
def test_calc_1(self):
|
||||
"""Test easy behavior 1."""
|
||||
expected = [
|
||||
{
|
||||
"id": self.pkg_pallet.id,
|
||||
"qty": 1,
|
||||
"name": self.pkg_pallet.name,
|
||||
"is_unit": False,
|
||||
},
|
||||
{
|
||||
"id": self.pkg_big_box.id,
|
||||
"qty": 3,
|
||||
"name": self.pkg_big_box.name,
|
||||
"is_unit": False,
|
||||
},
|
||||
{
|
||||
"id": self.pkg_box.id,
|
||||
"qty": 1,
|
||||
"name": self.pkg_box.name,
|
||||
"is_unit": False,
|
||||
},
|
||||
{
|
||||
"id": self.uom_unit.id,
|
||||
"qty": 5,
|
||||
"name": self.uom_unit.name,
|
||||
"is_unit": True,
|
||||
},
|
||||
make_pkg_values(self.pkg_pallet, qty=1),
|
||||
make_pkg_values(self.pkg_big_box, qty=3),
|
||||
make_pkg_values(self.pkg_box, qty=1),
|
||||
make_pkg_values(self.uom_unit, qty=5),
|
||||
]
|
||||
self.assertEqual(self.product_a.product_qty_by_packaging(2655), expected)
|
||||
|
||||
def test_calc_2(self):
|
||||
"""Test easy behavior 2."""
|
||||
expected = [
|
||||
{
|
||||
"id": self.pkg_big_box.id,
|
||||
"qty": 1,
|
||||
"name": self.pkg_big_box.name,
|
||||
"is_unit": False,
|
||||
},
|
||||
{
|
||||
"id": self.pkg_box.id,
|
||||
"qty": 3,
|
||||
"name": self.pkg_box.name,
|
||||
"is_unit": False,
|
||||
},
|
||||
make_pkg_values(self.pkg_big_box, qty=1),
|
||||
make_pkg_values(self.pkg_box, qty=3),
|
||||
]
|
||||
self.assertEqual(self.product_a.product_qty_by_packaging(350), expected)
|
||||
|
||||
def test_calc_3(self):
|
||||
"""Test easy behavior 3."""
|
||||
expected = [
|
||||
{
|
||||
"id": self.pkg_box.id,
|
||||
"qty": 1,
|
||||
"name": self.pkg_box.name,
|
||||
"is_unit": False,
|
||||
},
|
||||
{
|
||||
"id": self.uom_unit.id,
|
||||
"qty": 30,
|
||||
"name": self.uom_unit.name,
|
||||
"is_unit": True,
|
||||
},
|
||||
make_pkg_values(self.pkg_box, qty=1),
|
||||
make_pkg_values(self.uom_unit, qty=30),
|
||||
]
|
||||
self.assertEqual(self.product_a.product_qty_by_packaging(80), expected)
|
||||
|
||||
def test_calc_6(self):
|
||||
"""Test fractional qty is lost."""
|
||||
expected = [
|
||||
{
|
||||
"id": self.pkg_box.id,
|
||||
"qty": 1,
|
||||
"name": self.pkg_box.name,
|
||||
"is_unit": False,
|
||||
},
|
||||
make_pkg_values(self.pkg_box, qty=1),
|
||||
]
|
||||
self.assertEqual(self.product_a.product_qty_by_packaging(50.5), expected)
|
||||
|
||||
def test_calc_filter(self):
|
||||
"""Test packaging filter."""
|
||||
expected = [
|
||||
{
|
||||
"id": self.pkg_big_box.id,
|
||||
"qty": 13,
|
||||
"name": self.pkg_big_box.name,
|
||||
"is_unit": False,
|
||||
},
|
||||
{
|
||||
"id": self.pkg_box.id,
|
||||
"qty": 1,
|
||||
"name": self.pkg_box.name,
|
||||
"is_unit": False,
|
||||
},
|
||||
{
|
||||
"id": self.uom_unit.id,
|
||||
"qty": 5,
|
||||
"name": self.uom_unit.name,
|
||||
"is_unit": True,
|
||||
},
|
||||
make_pkg_values(self.pkg_big_box, qty=13),
|
||||
make_pkg_values(self.pkg_box, qty=1),
|
||||
make_pkg_values(self.uom_unit, qty=5),
|
||||
]
|
||||
self.assertEqual(
|
||||
self.product_a.with_context(
|
||||
@@ -202,30 +75,12 @@ class TestCalc(SavepointCase):
|
||||
def test_calc_name_get(self):
|
||||
"""Test custom name getter."""
|
||||
expected = [
|
||||
{
|
||||
"id": self.pkg_pallet.id,
|
||||
"qty": 1,
|
||||
"name": "FOO " + self.pkg_pallet.name,
|
||||
"is_unit": False,
|
||||
},
|
||||
{
|
||||
"id": self.pkg_big_box.id,
|
||||
"qty": 3,
|
||||
"name": "FOO " + self.pkg_big_box.name,
|
||||
"is_unit": False,
|
||||
},
|
||||
{
|
||||
"id": self.pkg_box.id,
|
||||
"qty": 1,
|
||||
"name": "FOO " + self.pkg_box.name,
|
||||
"is_unit": False,
|
||||
},
|
||||
{
|
||||
"id": self.uom_unit.id,
|
||||
"qty": 5,
|
||||
"name": self.uom_unit.name,
|
||||
"is_unit": True,
|
||||
},
|
||||
make_pkg_values(self.pkg_pallet, qty=1, name="FOO " + self.pkg_pallet.name),
|
||||
make_pkg_values(
|
||||
self.pkg_big_box, qty=3, name="FOO " + self.pkg_big_box.name
|
||||
),
|
||||
make_pkg_values(self.pkg_box, qty=1, name="FOO " + self.pkg_box.name),
|
||||
make_pkg_values(self.uom_unit, qty=5, name=self.uom_unit.name),
|
||||
]
|
||||
self.assertEqual(
|
||||
self.product_a.with_context(
|
||||
@@ -255,55 +110,22 @@ class TestCalc(SavepointCase):
|
||||
def test_calc_sub1(self):
|
||||
"""Test contained packaging behavior 1."""
|
||||
expected = [
|
||||
{
|
||||
"id": self.pkg_pallet.id,
|
||||
"qty": 1,
|
||||
"name": self.pkg_pallet.name,
|
||||
"is_unit": False,
|
||||
"contained": [
|
||||
{
|
||||
"id": self.pkg_big_box.id,
|
||||
"qty": 10,
|
||||
"name": self.pkg_big_box.name,
|
||||
"is_unit": False,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
"id": self.pkg_big_box.id,
|
||||
"qty": 3,
|
||||
"name": self.pkg_big_box.name,
|
||||
"is_unit": False,
|
||||
"contained": [
|
||||
{
|
||||
"id": self.pkg_box.id,
|
||||
"qty": 4,
|
||||
"name": self.pkg_box.name,
|
||||
"is_unit": False,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
"id": self.pkg_box.id,
|
||||
"qty": 1,
|
||||
"name": self.pkg_box.name,
|
||||
"is_unit": False,
|
||||
"contained": [
|
||||
{
|
||||
"id": self.uom_unit.id,
|
||||
"qty": 50,
|
||||
"name": self.uom_unit.name,
|
||||
"is_unit": True,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
"id": self.uom_unit.id,
|
||||
"qty": 5,
|
||||
"name": self.uom_unit.name,
|
||||
"is_unit": True,
|
||||
"contained": None,
|
||||
},
|
||||
make_pkg_values(
|
||||
self.pkg_pallet,
|
||||
qty=1,
|
||||
contained=[make_pkg_values(self.pkg_big_box, qty=10)],
|
||||
),
|
||||
make_pkg_values(
|
||||
self.pkg_big_box,
|
||||
qty=3,
|
||||
contained=[make_pkg_values(self.pkg_box, qty=4)],
|
||||
),
|
||||
make_pkg_values(
|
||||
self.pkg_box,
|
||||
qty=1,
|
||||
contained=[make_pkg_values(self.uom_unit, qty=50)],
|
||||
),
|
||||
make_pkg_values(self.uom_unit, qty=5, contained=None),
|
||||
]
|
||||
self.assertEqual(
|
||||
self.product_a.product_qty_by_packaging(2655, with_contained=True),
|
||||
@@ -311,64 +133,28 @@ class TestCalc(SavepointCase):
|
||||
)
|
||||
|
||||
def test_calc_sub2(self):
|
||||
"""Test contained packaging behavior 1."""
|
||||
"""Test contained packaging behavior 2."""
|
||||
self.pkg_box.qty = 30
|
||||
expected = [
|
||||
{
|
||||
"id": self.pkg_pallet.id,
|
||||
"qty": 1,
|
||||
"name": self.pkg_pallet.name,
|
||||
"is_unit": False,
|
||||
"contained": [
|
||||
{
|
||||
"id": self.pkg_big_box.id,
|
||||
"qty": 10,
|
||||
"name": self.pkg_big_box.name,
|
||||
"is_unit": False,
|
||||
},
|
||||
make_pkg_values(
|
||||
self.pkg_pallet,
|
||||
qty=1,
|
||||
contained=[make_pkg_values(self.pkg_big_box, qty=10)],
|
||||
),
|
||||
make_pkg_values(
|
||||
self.pkg_big_box,
|
||||
qty=3,
|
||||
contained=[
|
||||
make_pkg_values(self.pkg_box, qty=6),
|
||||
make_pkg_values(self.uom_unit, qty=20),
|
||||
],
|
||||
},
|
||||
{
|
||||
"id": self.pkg_big_box.id,
|
||||
"qty": 3,
|
||||
"name": self.pkg_big_box.name,
|
||||
"is_unit": False,
|
||||
"contained": [
|
||||
{
|
||||
"id": self.pkg_box.id,
|
||||
"qty": 6,
|
||||
"name": self.pkg_box.name,
|
||||
"is_unit": False,
|
||||
},
|
||||
{
|
||||
"id": self.uom_unit.id,
|
||||
"qty": 20,
|
||||
"name": self.uom_unit.name,
|
||||
"is_unit": True,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
"id": self.pkg_box.id,
|
||||
"qty": 1,
|
||||
"name": self.pkg_box.name,
|
||||
"is_unit": False,
|
||||
"contained": [
|
||||
{
|
||||
"id": self.uom_unit.id,
|
||||
"qty": 30,
|
||||
"name": self.uom_unit.name,
|
||||
"is_unit": True,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
"id": self.uom_unit.id,
|
||||
"qty": 25,
|
||||
"name": self.uom_unit.name,
|
||||
"is_unit": True,
|
||||
"contained": None,
|
||||
},
|
||||
),
|
||||
make_pkg_values(
|
||||
self.pkg_box,
|
||||
qty=1,
|
||||
contained=[make_pkg_values(self.uom_unit, qty=30)],
|
||||
),
|
||||
make_pkg_values(self.uom_unit, qty=25, contained=None),
|
||||
]
|
||||
self.assertEqual(
|
||||
self.product_a.product_qty_by_packaging(2655, with_contained=True),
|
||||
|
||||
65
stock_packaging_calculator/tests/test_pkg_qty_str.py
Normal file
65
stock_packaging_calculator/tests/test_pkg_qty_str.py
Normal file
@@ -0,0 +1,65 @@
|
||||
# Copyright 2021 Camptocamp SA
|
||||
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl)
|
||||
from .common import TestCommon
|
||||
|
||||
|
||||
class TestAsStr(TestCommon):
|
||||
def test_as_str(self):
|
||||
self.assertEqual(self.product_a.product_qty_by_packaging_as_str(10), "10 Units")
|
||||
self.assertEqual(
|
||||
self.product_a.product_qty_by_packaging_as_str(10, only_packaging=True), ""
|
||||
)
|
||||
self.assertEqual(self.product_a.product_qty_by_packaging_as_str(100), "2 Box")
|
||||
self.assertEqual(
|
||||
self.product_a.product_qty_by_packaging_as_str(250), "1 Big Box,\xa01 Box"
|
||||
)
|
||||
self.assertEqual(
|
||||
self.product_a.product_qty_by_packaging_as_str(255),
|
||||
"1 Big Box,\xa01 Box,\xa05 Units",
|
||||
)
|
||||
# only_packaging has no impact if we get not only units
|
||||
self.assertEqual(
|
||||
self.product_a.product_qty_by_packaging_as_str(255, only_packaging=True),
|
||||
"1 Big Box,\xa01 Box,\xa05 Units",
|
||||
)
|
||||
|
||||
def test_as_str_w_units(self):
|
||||
self.assertEqual(
|
||||
self.product_a.product_qty_by_packaging_as_str(
|
||||
10, include_total_units=True
|
||||
),
|
||||
"10 Units",
|
||||
)
|
||||
self.assertEqual(
|
||||
self.product_a.product_qty_by_packaging_as_str(
|
||||
100, include_total_units=True
|
||||
),
|
||||
"2 Box (100 Units)",
|
||||
)
|
||||
self.assertEqual(
|
||||
self.product_a.product_qty_by_packaging_as_str(
|
||||
250, include_total_units=True
|
||||
),
|
||||
"1 Big Box,\xa01 Box (250 Units)",
|
||||
)
|
||||
self.assertEqual(
|
||||
self.product_a.product_qty_by_packaging_as_str(
|
||||
255, include_total_units=True
|
||||
),
|
||||
"1 Big Box,\xa01 Box,\xa05 Units (255 Units)",
|
||||
)
|
||||
# only_packaging has no impact if we get not only units
|
||||
self.assertEqual(
|
||||
self.product_a.product_qty_by_packaging_as_str(
|
||||
255, include_total_units=True, only_packaging=True
|
||||
),
|
||||
"1 Big Box,\xa01 Box,\xa05 Units (255 Units)",
|
||||
)
|
||||
|
||||
def test_as_str_custom_name(self):
|
||||
self.assertEqual(
|
||||
self.product_a.with_context(
|
||||
_qty_by_packaging_as_str=lambda pkg, qty: f"{pkg.name} {qty} FOO"
|
||||
).product_qty_by_packaging_as_str(250),
|
||||
"Big Box 1 FOO,\xa0Box 1 FOO",
|
||||
)
|
||||
26
stock_packaging_calculator/tests/utils.py
Normal file
26
stock_packaging_calculator/tests/utils.py
Normal file
@@ -0,0 +1,26 @@
|
||||
# Copyright 2021 Camptocamp SA
|
||||
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl)
|
||||
|
||||
|
||||
def make_pkg_values(record, **kw):
|
||||
"""Helper to generate test values for packaging."""
|
||||
name = record.name
|
||||
if record._name == "uom.uom":
|
||||
is_unit = True
|
||||
barcode = None
|
||||
qty = record.factor
|
||||
elif record._name == "product.packaging":
|
||||
qty = record.qty
|
||||
is_unit = False
|
||||
barcode = record.barcode
|
||||
if record.product_id:
|
||||
name = record.product_id._packaging_name_getter(record)
|
||||
values = {
|
||||
"id": record.id,
|
||||
"name": name,
|
||||
"qty": qty,
|
||||
"barcode": barcode,
|
||||
"is_unit": is_unit,
|
||||
}
|
||||
values.update(kw)
|
||||
return values
|
||||
Reference in New Issue
Block a user