Initial commit: Odoo 18.0-20251222 extra-addons
Some checks failed
pre-commit / pre-commit (push) Has been cancelled
tests / Detect unreleased dependencies (push) Has been cancelled
tests / test with OCB (push) Has been cancelled
tests / test with Odoo (push) Has been cancelled

This commit is contained in:
tocmo0nlord
2026-03-13 20:43:25 +00:00
parent 36e847a7df
commit adbe430761
9472 changed files with 1265727 additions and 0 deletions

View File

@@ -0,0 +1,3 @@
from . import test_minimum_sellable_qty
from . import test_sale_only_by_packaging
from . import test_sale_line_onchanges

View File

@@ -0,0 +1,14 @@
# Copyright 2020 Camptocamp SA
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl)
from odoo.addons.product_packaging_level_salable.tests.common import Common
TU_PRODUCT_QTY = 20
PL_PRODUCT_QTY = TU_PRODUCT_QTY * 30
class SellOnlyByPackagingCommon(Common):
@classmethod
def setUpClass(cls):
super().setUpClass()
cls.env.user.groups_id += cls.env.ref("product.group_stock_packaging")

View File

@@ -0,0 +1,58 @@
# Copyright 2020 Camptocamp SA
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl)
from odoo.tests import TransactionCase
class TestMinimumSellableQty(TransactionCase):
@classmethod
def setUpClass(cls):
super().setUpClass()
cls.env = cls.env(context=dict(cls.env.context, tracking_disable=True))
cls.product = cls.env.ref("product.product_product_9")
cls.packaging_level_1 = cls.env["product.packaging.level"].create(
{"name": "Packaging level 1", "code": "PL1", "sequence": 1}
)
cls.packaging_level_2 = cls.env["product.packaging.level"].create(
{"name": "Packaging level 1", "code": "PL2", "sequence": 2}
)
cls.packaging_level_3 = cls.env["product.packaging.level"].create(
{"name": "Packaging level 3", "code": "PL3", "sequence": 3}
)
cls.packaging1 = cls.env["product.packaging"].create(
{
"name": "Packaging of five",
"product_id": cls.product.id,
"qty": 5.0,
"packaging_level_id": cls.packaging_level_1.id,
}
)
cls.packaging2 = cls.env["product.packaging"].create(
{
"name": "Packing of 3",
"product_id": cls.product.id,
"qty": 3.0,
"packaging_level_id": cls.packaging_level_2.id,
}
)
def test_min_sellable_qty(self):
"""Check the computation of the minimum sellable quantity."""
self.product.product_tmpl_id.sell_only_by_packaging = False
self.assertEqual(self.product.min_sellable_qty, 0)
self.product.product_tmpl_id.sell_only_by_packaging = True
self.assertEqual(self.product.min_sellable_qty, 3)
self.assertEqual(self.product.product_tmpl_id.min_sellable_qty, 3)
self.packaging2.sales = False
self.assertEqual(self.product.min_sellable_qty, 5)
self.assertEqual(self.product.product_tmpl_id.min_sellable_qty, 5)
self.packaging3 = self.env["product.packaging"].create(
{
"name": "Packing of 2",
"product_id": self.product.id,
"qty": 2.0,
"packaging_level_id": self.packaging_level_3.id,
}
)
self.assertEqual(self.product.min_sellable_qty, 2)
self.assertEqual(self.product.product_tmpl_id.min_sellable_qty, 2)

View File

@@ -0,0 +1,32 @@
# Copyright 2020 Camptocamp SA
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl)
import logging
from odoo.tests import Form
from odoo.addons.product_packaging_level_salable.tests.common import (
PL_PRODUCT_QTY,
TU_PRODUCT_QTY,
)
from .common import SellOnlyByPackagingCommon
class TestPackaging(SellOnlyByPackagingCommon):
def test_compute_qties(self):
with Form(self.order) as so, self.assertLogs(level=logging.WARNING) as logs:
with so.order_line.edit(0) as line:
line.product_packaging_id = self.packaging_tu
line.product_packaging_qty = 31
# catch WARNING by _onchange_product_packaging_id
self.assertEqual(len(logs.records), 1)
self.assertEqual(logs.records[0].levelno, logging.WARNING)
# (20*30)+20 = 31*20 = 620
expected_qty = TU_PRODUCT_QTY + PL_PRODUCT_QTY
self.assertEqual(self.order_line.product_uom_qty, expected_qty)
with Form(self.order) as so:
with so.order_line.edit(0) as line:
line.product_packaging_qty = 30
# 20*30 = 600
expected_qty = PL_PRODUCT_QTY
self.assertEqual(self.order_line.product_uom_qty, expected_qty)

View File

@@ -0,0 +1,113 @@
# Copyright 2020 Camptocamp SA
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl)
import logging
from odoo.exceptions import ValidationError
from odoo.tests import Form
from odoo.tools import mute_logger
from .common import SellOnlyByPackagingCommon
class TestSaleProductByPackagingOnly(SellOnlyByPackagingCommon):
def test_write_auto_fill_packaging(self):
order_line = self.order.order_line
self.assertFalse(order_line.product_packaging_id)
self.assertFalse(order_line.product_packaging_qty)
order_line.write({"product_uom_qty": 3.0})
self.assertFalse(order_line.product_packaging_id)
self.assertFalse(order_line.product_packaging_qty)
self.product.write({"sell_only_by_packaging": True})
self.assertFalse(order_line.product_packaging_id)
self.assertFalse(order_line.product_packaging_qty)
order_line.write({"product_uom_qty": self.packaging_tu.qty * 2})
self.assertTrue(order_line.product_packaging_id)
self.assertTrue(order_line.product_packaging_qty)
self.assertEqual(order_line.product_packaging_id, self.packaging_tu)
self.assertEqual(order_line.product_packaging_qty, 2)
with self.assertRaises(ValidationError):
order_line.write({"product_packaging_id": False})
def test_error_sale_packaging(self):
# If qty does not match a packaging qty, an exception should be raised
self.product.sell_only_by_packaging = True
with self.assertRaises(ValidationError):
with Form(self.order) as so:
with so.order_line.new() as so_line:
so_line.product_id = self.product
so_line.product_uom_qty = 2
@mute_logger("odoo.tests.common.onchange")
def test_convert_packaging_qty(self):
"""
Test if the function _convert_packaging_qty is correctly applied
during SO line create/edit and if qties are corrects.
:return:
"""
self.product.sell_only_by_packaging = True
packaging = self.packaging_tu
# For this step, the qty is not forced on the packaging
# But the warning will be raise because the value of packaging qty is
# not integer package
with self.assertRaises(ValidationError):
self.order_line.write(
{
"product_packaging_id": packaging,
"product_packaging_qty": 0.6,
}
)
# Now force the qty on the packaging
packaging.force_sale_qty = True
with Form(self.order) as sale_order:
with (
sale_order.order_line.edit(0) as so_line,
self.assertLogs(level=logging.WARNING) as logs,
):
so_line.product_packaging_id = packaging
so_line.product_uom_qty = 52
self.assertAlmostEqual(
so_line.product_uom_qty, 60, places=self.precision
)
so_line.product_uom_qty = 40
self.assertAlmostEqual(
so_line.product_uom_qty, 40, places=self.precision
)
so_line.product_uom_qty = 38
self.assertAlmostEqual(
so_line.product_uom_qty, 40, places=self.precision
)
so_line.product_uom_qty = 22
self.assertAlmostEqual(
so_line.product_uom_qty, 40, places=self.precision
)
so_line.product_uom_qty = 72
self.assertAlmostEqual(
so_line.product_uom_qty, 80, places=self.precision
)
so_line.product_uom_qty = 209.98
self.assertAlmostEqual(
so_line.product_uom_qty, 220, places=self.precision
)
# catch WARNING by _onchange_product_packaging_id
self.assertEqual(len(logs.records), 1)
self.assertEqual(logs.records[0].levelno, logging.WARNING)
def test_onchange_qty_is_not_pack_multiple(self):
"""Check package when qantity is not a multiple of package quantity.
When the uom quantity is changed for a value not a multpile of a
possible package an error is raised.
"""
self.product.write({"sell_only_by_packaging": True})
self.order_line.product_uom_qty = 40 # 2 packs
with self.assertRaises(ValidationError):
self.order_line.product_packaging_qty = 0.9
self.assertAlmostEqual(
self.order_line.product_uom_qty, 18, places=self.precision
)