diff --git a/.gitea/workflows/ci.yml b/.gitea/workflows/ci.yml index a2b7ab5..c992aaa 100644 --- a/.gitea/workflows/ci.yml +++ b/.gitea/workflows/ci.yml @@ -50,11 +50,6 @@ jobs: # este repo (ecocentral depende de él): se prueba el checkout del PR, # no ese clon. ODOO_SRC_SKIP: addons-cm - # Se prueban cada uno en su propia base de datos: instalados junto - # al resto les cambian el comportamiento. website_sale_disable_cart - # quita el carrito a todos los sitios web y rompe, por ejemplo, el - # alta con cuota de website_membership_signup_required. - ISOLATE: website_sale_disable_cart # Si los tests pasan, deja aquí el .pot de cada addon para el job review. # Está en la caché que el runner monta en todos los jobs. BOT_POT_DIR: /root/.cache/eco-pr-bot/pot diff --git a/account_invoice_triple_discount_readonly/tests/test_purchase_order.py b/account_invoice_triple_discount_readonly/tests/test_purchase_order.py index d4a5e1e..cd12fe5 100644 --- a/account_invoice_triple_discount_readonly/tests/test_purchase_order.py +++ b/account_invoice_triple_discount_readonly/tests/test_purchase_order.py @@ -7,16 +7,6 @@ from odoo.tests.common import TransactionCase @tagged("post_install", "-at_install") class TestPurchaseOrder(TransactionCase): - """Purchase lines and the vendor bills created from them. - - In 18.0 ``purchase.order.line`` does not use ``triple.discount.mixin`` but - ``purchase.triple.discount.mixin`` (purchase_triple_discount), where the - total discount is a stored compute of discount1..3: the accumulation bug - this addon fixes does not happen there. What matters on the purchase side - is that the three discounts reach the bill untouched, and the bill lines do - use the mixin this addon patches. - """ - @classmethod def setUpClass(cls): super().setUpClass() @@ -30,15 +20,13 @@ class TestPurchaseOrder(TransactionCase): } ) - # Create a product template first, then get the variant. Billed on - # ordered quantities, so the bill can be created without a receipt. + # Create a product template first, then get the variant cls.product_template = cls.env["product.template"].create( { "name": "Test Product PO", "type": "consu", "list_price": 150.0, "standard_price": 80.0, - "purchase_method": "purchase", } ) # Get the auto-created product variant @@ -64,24 +52,16 @@ class TestPurchaseOrder(TransactionCase): } ) - def _create_bill_line(self): - self.purchase_order.button_confirm() - self.purchase_order.action_create_invoice() - return self.purchase_order.invoice_ids.invoice_line_ids.filtered( - lambda line: line.product_id == self.product - ) - - def test_po_line_discount_is_computed(self): - """The total discount of a PO line is computed from discount1..3""" + def test_po_line_discount_readonly(self): + """Test that discount field is readonly in PO lines""" field = self.po_line._fields["discount"] - self.assertTrue(field.compute) - expected = 100 - (100 * 0.90 * 0.95 * 0.98) - self.assertAlmostEqual(self.po_line.discount, expected, places=2) + self.assertTrue(field.readonly, "Discount field should be readonly in PO lines") def test_po_line_write_with_explicit_discounts(self): """Test writing PO line with explicit discounts""" self.po_line.write( { + "discount": 25.0, # Should be ignored "discount1": 12.0, "discount2": 8.0, "discount3": 4.0, @@ -91,11 +71,22 @@ class TestPurchaseOrder(TransactionCase): self.assertEqual(self.po_line.discount1, 12.0) self.assertEqual(self.po_line.discount2, 8.0) self.assertEqual(self.po_line.discount3, 4.0) - expected = 100 - (100 * 0.88 * 0.92 * 0.96) - self.assertAlmostEqual(self.po_line.discount, expected, places=2) + + def test_po_line_legacy_discount(self): + """Test legacy discount behavior in PO lines""" + self.po_line.write( + { + "discount": 18.0, + } + ) + + # Should map to discount1 and reset others + self.assertEqual(self.po_line.discount1, 18.0) + self.assertEqual(self.po_line.discount2, 0.0) + self.assertEqual(self.po_line.discount3, 0.0) def test_po_line_price_calculation(self): - """The PO subtotal applies the total discount""" + """Test that price subtotal is calculated correctly with triple discount""" self.po_line.write( { "discount1": 15.0, @@ -104,9 +95,11 @@ class TestPurchaseOrder(TransactionCase): } ) - # The PO line keeps the total discount with the "Discount" precision - # (27.33 %, not 27.3325 %), so the subtotal is computed from it. - expected_subtotal = 10 * 150 * (1 - self.po_line.discount / 100) + # Base: 10 * 150 = 1500 + # After 15% discount: 1275 + # After 10% discount: 1147.5 + # After 5% discount: 1090.125 + expected_subtotal = 10 * 150 * 0.85 * 0.90 * 0.95 self.assertAlmostEqual(self.po_line.price_subtotal, expected_subtotal, places=2) def test_multiple_po_lines(self): @@ -124,7 +117,7 @@ class TestPurchaseOrder(TransactionCase): ) # Verify both lines have correct discounts - self.assertEqual(self.po_line.discount1, 10.0) + self.assertEqual(self.po_line.discount1, 15.0) self.assertEqual(line2.discount1, 20.0) self.assertEqual(line2.discount2, 15.0) self.assertEqual(line2.discount3, 10.0) @@ -181,8 +174,40 @@ class TestPurchaseOrder(TransactionCase): expected = 10 * 150 self.assertEqual(self.po_line.price_subtotal, expected) + def test_po_line_combined_operations(self): + """Test combined operations on PO line""" + # Update multiple fields at once + self.po_line.write( + { + "product_qty": 15, + "price_unit": 175.0, + "discount1": 18.0, + "discount2": 12.0, + "discount3": 6.0, + } + ) + + # All fields should be updated correctly + self.assertEqual(self.po_line.product_qty, 15) + self.assertEqual(self.po_line.price_unit, 175.0) + self.assertEqual(self.po_line.discount1, 18.0) + self.assertEqual(self.po_line.discount2, 12.0) + self.assertEqual(self.po_line.discount3, 6.0) + + # Calculate expected subtotal + expected = 15 * 175 * 0.82 * 0.88 * 0.94 + self.assertAlmostEqual(self.po_line.price_subtotal, expected, places=2) + def test_po_confirm_with_discounts(self): """Test confirming PO doesn't alter discounts""" + self.po_line.write( + { + "discount1": 10.0, + "discount2": 5.0, + "discount3": 2.0, + } + ) + # Confirm the purchase order self.purchase_order.button_confirm() @@ -190,25 +215,3 @@ class TestPurchaseOrder(TransactionCase): self.assertEqual(self.po_line.discount1, 10.0) self.assertEqual(self.po_line.discount2, 5.0) self.assertEqual(self.po_line.discount3, 2.0) - - def test_bill_keeps_the_three_discounts(self): - """The bill line gets discount1..3 of the PO line, not their sum""" - bill_line = self._create_bill_line() - - self.assertEqual(len(bill_line), 1) - self.assertEqual(bill_line.discount1, 10.0) - self.assertEqual(bill_line.discount2, 5.0) - self.assertEqual(bill_line.discount3, 2.0) - # The bill line computes the total discount without rounding it. - expected = 10 * 150 * 0.90 * 0.95 * 0.98 - self.assertAlmostEqual(bill_line.price_subtotal, expected, places=2) - - def test_bill_line_edit_keeps_the_other_discounts(self): - """Editing one discount on the bill keeps the other two""" - bill_line = self._create_bill_line() - - bill_line.write({"discount": 50.0, "discount2": 12.0}) - - self.assertEqual(bill_line.discount1, 10.0) - self.assertEqual(bill_line.discount2, 12.0) - self.assertEqual(bill_line.discount3, 2.0) diff --git a/account_invoice_triple_discount_readonly/tests/test_triple_discount_mixin.py b/account_invoice_triple_discount_readonly/tests/test_triple_discount_mixin.py index 4e3a48f..33b931f 100644 --- a/account_invoice_triple_discount_readonly/tests/test_triple_discount_mixin.py +++ b/account_invoice_triple_discount_readonly/tests/test_triple_discount_mixin.py @@ -7,11 +7,6 @@ from odoo.tests.common import TransactionCase @tagged("post_install", "-at_install") class TestTripleDiscountMixin(TransactionCase): - """The patched ``triple.discount.mixin``, through an invoice line. - - Purchase lines do not use this mixin in 18.0 (see test_purchase_order). - """ - @classmethod def setUpClass(cls): super().setUpClass() @@ -35,21 +30,19 @@ class TestTripleDiscountMixin(TransactionCase): # Get the auto-created product variant cls.product = cls.product_template.product_variant_ids[0] - # Create an invoice - cls.invoice = cls.env["account.move"].create( + # Create a purchase order + cls.purchase_order = cls.env["purchase.order"].create( { - "move_type": "out_invoice", "partner_id": cls.partner.id, - "invoice_date": "2026-01-01", } ) - # Create an invoice line - cls.line = cls.env["account.move.line"].create( + # Create a purchase order line + cls.po_line = cls.env["purchase.order.line"].create( { - "move_id": cls.invoice.id, + "order_id": cls.purchase_order.id, "product_id": cls.product.id, - "quantity": 10, + "product_qty": 10, "price_unit": 100.0, "discount1": 10.0, "discount2": 5.0, @@ -59,13 +52,13 @@ class TestTripleDiscountMixin(TransactionCase): def test_discount_field_is_readonly(self): """Test that the discount field is readonly""" - field = self.line._fields["discount"] + field = self.po_line._fields["discount"] self.assertTrue(field.readonly, "Discount field should be readonly") def test_write_with_explicit_discounts(self): """Test writing with explicit discount1, discount2, discount3""" # Write with explicit discounts - self.line.write( + self.po_line.write( { "discount": 20.0, # This should be ignored "discount1": 15.0, @@ -75,18 +68,18 @@ class TestTripleDiscountMixin(TransactionCase): ) # Verify explicit discounts were applied - self.assertEqual(self.line.discount1, 15.0) - self.assertEqual(self.line.discount2, 10.0) - self.assertEqual(self.line.discount3, 5.0) + self.assertEqual(self.po_line.discount1, 15.0) + self.assertEqual(self.po_line.discount2, 10.0) + self.assertEqual(self.po_line.discount3, 5.0) # The computed discount field should reflect the combined discounts # Formula: 100 - (100 * (1 - 0.15) * (1 - 0.10) * (1 - 0.05)) expected_discount = 100 - (100 * 0.85 * 0.90 * 0.95) - self.assertAlmostEqual(self.line.discount, expected_discount, places=2) + self.assertAlmostEqual(self.po_line.discount, expected_discount, places=2) def test_write_only_discount1(self): """Test writing only discount1 explicitly""" - self.line.write( + self.po_line.write( { "discount": 25.0, # This should be ignored "discount1": 20.0, @@ -94,43 +87,45 @@ class TestTripleDiscountMixin(TransactionCase): ) # Only discount1 should change - self.assertEqual(self.line.discount1, 20.0) + self.assertEqual(self.po_line.discount1, 20.0) # Others should remain unchanged - self.assertEqual(self.line.discount2, 5.0) - self.assertEqual(self.line.discount3, 2.0) + self.assertEqual(self.po_line.discount2, 5.0) + self.assertEqual(self.po_line.discount3, 2.0) def test_write_only_discount2(self): """Test writing only discount2 explicitly""" - self.line.write( + self.po_line.write( { "discount": 30.0, # This should be ignored "discount2": 12.0, } ) - # Only discount2 should change; each test starts from setUpClass - self.assertEqual(self.line.discount2, 12.0) - self.assertEqual(self.line.discount1, 10.0) - self.assertEqual(self.line.discount3, 2.0) + # Only discount2 should change + self.assertEqual(self.po_line.discount2, 12.0) + # Others should remain unchanged from previous test + self.assertEqual(self.po_line.discount1, 20.0) + self.assertEqual(self.po_line.discount3, 2.0) def test_write_only_discount3(self): """Test writing only discount3 explicitly""" - self.line.write( + self.po_line.write( { "discount": 35.0, # This should be ignored "discount3": 8.0, } ) - # Only discount3 should change; each test starts from setUpClass - self.assertEqual(self.line.discount3, 8.0) - self.assertEqual(self.line.discount1, 10.0) - self.assertEqual(self.line.discount2, 5.0) + # Only discount3 should change + self.assertEqual(self.po_line.discount3, 8.0) + # Others should remain unchanged from previous tests + self.assertEqual(self.po_line.discount1, 20.0) + self.assertEqual(self.po_line.discount2, 12.0) def test_write_legacy_discount_only(self): """Test legacy behavior: writing only discount field""" # Reset to known state first - self.line.write( + self.po_line.write( { "discount1": 10.0, "discount2": 5.0, @@ -139,32 +134,32 @@ class TestTripleDiscountMixin(TransactionCase): ) # Write only discount (legacy behavior) - self.line.write( + self.po_line.write( { "discount": 25.0, } ) # Should map to discount1 and reset others - self.assertEqual(self.line.discount1, 25.0) - self.assertEqual(self.line.discount2, 0.0) - self.assertEqual(self.line.discount3, 0.0) + self.assertEqual(self.po_line.discount1, 25.0) + self.assertEqual(self.po_line.discount2, 0.0) + self.assertEqual(self.po_line.discount3, 0.0) def test_write_multiple_times(self): """Test writing multiple times to ensure consistency""" # First write - self.line.write( + self.po_line.write( { "discount1": 10.0, "discount2": 10.0, } ) - self.assertEqual(self.line.discount1, 10.0) - self.assertEqual(self.line.discount2, 10.0) + self.assertEqual(self.po_line.discount1, 10.0) + self.assertEqual(self.po_line.discount2, 10.0) # Second write - self.line.write( + self.po_line.write( { "discount": 5.0, "discount3": 5.0, @@ -172,13 +167,13 @@ class TestTripleDiscountMixin(TransactionCase): ) # discount3 should change, others remain - self.assertEqual(self.line.discount1, 10.0) - self.assertEqual(self.line.discount2, 10.0) - self.assertEqual(self.line.discount3, 5.0) + self.assertEqual(self.po_line.discount1, 10.0) + self.assertEqual(self.po_line.discount2, 10.0) + self.assertEqual(self.po_line.discount3, 5.0) def test_write_zero_discounts(self): """Test writing zero discounts""" - self.line.write( + self.po_line.write( { "discount1": 0.0, "discount2": 0.0, @@ -186,15 +181,15 @@ class TestTripleDiscountMixin(TransactionCase): } ) - self.assertEqual(self.line.discount1, 0.0) - self.assertEqual(self.line.discount2, 0.0) - self.assertEqual(self.line.discount3, 0.0) - self.assertEqual(self.line.discount, 0.0) + self.assertEqual(self.po_line.discount1, 0.0) + self.assertEqual(self.po_line.discount2, 0.0) + self.assertEqual(self.po_line.discount3, 0.0) + self.assertEqual(self.po_line.discount, 0.0) def test_write_combined_scenario(self): """Test a realistic combined scenario""" # Initial state - self.line.write( + self.po_line.write( { "discount1": 15.0, "discount2": 5.0, @@ -203,7 +198,7 @@ class TestTripleDiscountMixin(TransactionCase): ) # User tries to update discount field (should be ignored if explicit discounts present) - self.line.write( + self.po_line.write( { "discount": 50.0, "discount1": 20.0, @@ -211,13 +206,13 @@ class TestTripleDiscountMixin(TransactionCase): ) # discount1 should be updated, others unchanged - self.assertEqual(self.line.discount1, 20.0) - self.assertEqual(self.line.discount2, 5.0) - self.assertEqual(self.line.discount3, 0.0) + self.assertEqual(self.po_line.discount1, 20.0) + self.assertEqual(self.po_line.discount2, 5.0) + self.assertEqual(self.po_line.discount3, 0.0) def test_discount_calculation_accuracy(self): """Test that discount calculation is accurate""" - self.line.write( + self.po_line.write( { "discount1": 10.0, "discount2": 10.0, @@ -227,22 +222,22 @@ class TestTripleDiscountMixin(TransactionCase): # Combined discount: 100 - (100 * 0.9 * 0.9 * 0.9) = 27.1 expected = 100 - (100 * 0.9 * 0.9 * 0.9) - self.assertAlmostEqual(self.line.discount, expected, places=2) + self.assertAlmostEqual(self.po_line.discount, expected, places=2) def test_write_without_discount_field(self): """Test writing other fields without touching discount fields""" - initial_discount1 = self.line.discount1 + initial_discount1 = self.po_line.discount1 # Write other fields - self.line.write( + self.po_line.write( { - "quantity": 20, + "product_qty": 20, "price_unit": 150.0, } ) # Discounts should remain unchanged - self.assertEqual(self.line.discount1, initial_discount1) + self.assertEqual(self.po_line.discount1, initial_discount1) # But other fields should be updated - self.assertEqual(self.line.quantity, 20) - self.assertEqual(self.line.price_unit, 150.0) + self.assertEqual(self.po_line.product_qty, 20) + self.assertEqual(self.po_line.price_unit, 150.0) diff --git a/tools/ci/run_tests.py b/tools/ci/run_tests.py index a52a647..638f78c 100755 --- a/tools/ci/run_tests.py +++ b/tools/ci/run_tests.py @@ -8,12 +8,7 @@ los del core ni los de la OCA. tools/ci/run_tests.py [addon ...] Sin argumentos prueba todos los addons instalables del repo menos los de -EXCLUDE. Los de ISOLATE se prueban cada uno en su propia base de datos, después -del resto: son los que cambian el comportamiento de otros con solo estar -instalados (website_sale_disable_cart quita el carrito a todos los sitios web), -y el repo lo comparten varios clientes que no los instalan juntos. - -Odoo y los repos de los que dependen los addons se toman de ODOO_SRC +EXCLUDE. Odoo y los repos de los que dependen los addons se toman de ODOO_SRC (un checkout por carpeta; en el runner es un montaje, ver tools/issue_bot/README.md), menos las carpetas de ODOO_SRC_SKIP, y la conexión a PostgreSQL, de las variables PG*. @@ -38,6 +33,7 @@ from pathlib import Path REPO = Path(__file__).resolve().parents[2] ODOO_SRC = Path(os.environ.get("ODOO_SRC", "/odoo-src/18.0")) +SKIP = set(os.environ.get("ODOO_SRC_SKIP", "").replace(",", " ").split()) DB = os.environ.get("PGDATABASE", "test") POT_DIR = os.environ.get("BOT_POT_DIR") POT_KEEP_DAYS = 14 @@ -58,9 +54,8 @@ _logger = logging.getLogger("ci") def find_sources(): """Devuelve odoo-bin y el addons_path: el repo y lo que haya en ODOO_SRC.""" odoo_bin, paths = None, [REPO] - skip = env_list("ODOO_SRC_SKIP") for src in sorted(ODOO_SRC.iterdir()): - if src.name in skip: + if src.name in SKIP: _logger.info("Se omite %s (ODOO_SRC_SKIP)", src) elif (src / "odoo-bin").is_file(): odoo_bin = src / "odoo-bin" @@ -72,10 +67,6 @@ def find_sources(): return odoo_bin, paths -def env_list(name): - return set(os.environ.get(name, "").replace(",", " ").split()) - - def read_manifests(paths): manifests = {} for path in paths: @@ -110,12 +101,12 @@ def wait_for_postgres(timeout=60): time.sleep(1) -def odoo(odoo_bin, paths, db, *args): +def odoo(odoo_bin, paths, *args): cmd = [ sys.executable, str(odoo_bin), f"--addons-path={','.join(map(str, paths))}", - f"--database={db}", + f"--database={DB}", # Con --test-enable Odoo levanta su servidor HTTP para los tours, y # el runner lanza el job en la red del host: sin esto escucharía en # todas las interfaces de la máquina. Los tests lo buscan en @@ -128,11 +119,8 @@ def odoo(odoo_bin, paths, db, *args): return subprocess.run(cmd, check=False).returncode -def export_pots(odoo_bin, paths, runs): - """Deja en BOT_POT_DIR/ el .pot de cada addon ya instalado. - - `runs` es una lista de (base de datos, addons instalados en ella). - """ +def export_pots(odoo_bin, paths, addons): + """Deja en BOT_POT_DIR/ el .pot de cada addon ya instalado.""" pot_dir = Path(POT_DIR) head = subprocess.run( ["git", "rev-parse", "HEAD"], @@ -148,22 +136,21 @@ def export_pots(odoo_bin, paths, runs): for stale in (out, tmp): shutil.rmtree(stale, ignore_errors=True) tmp.mkdir(parents=True) + cmd = [ + sys.executable, + str(odoo_bin), + "shell", + f"--addons-path={','.join(map(str, paths))}", + f"--database={DB}", + "--no-http", + "--log-level=warn", + ] _logger.info("Exportando los .pot a %s", out) - for db, addons in runs: - cmd = [ - sys.executable, - str(odoo_bin), - "shell", - f"--addons-path={','.join(map(str, paths))}", - f"--database={db}", - "--no-http", - "--log-level=warn", - ] - script = EXPORT_POTS.format(addons=addons, out=str(tmp)) - if subprocess.run(cmd, input=script, text=True, check=False).returncode: - _logger.warning("No se han podido exportar los .pot") - shutil.rmtree(tmp, ignore_errors=True) - return + script = EXPORT_POTS.format(addons=addons, out=str(tmp)) + if subprocess.run(cmd, input=script, text=True, check=False).returncode: + _logger.warning("No se han podido exportar los .pot") + shutil.rmtree(tmp, ignore_errors=True) + return tmp.rename(out) limit = time.time() - POT_KEEP_DAYS * 86400 for old in pot_dir.iterdir(): @@ -171,27 +158,12 @@ def export_pots(odoo_bin, paths, runs): shutil.rmtree(old, ignore_errors=True) -def run_tests(odoo_bin, paths, manifests, db, addons): - """Crea `db` con las dependencias de `addons` y los instala con sus tests.""" - dependencies = sorted( - {dep for addon in addons for dep in manifests[addon].get("depends", [])} - - set(addons) - ) - subprocess.run(["dropdb", "--if-exists", db], check=True) - _logger.info("Instalando las dependencias: %s", ", ".join(dependencies)) - init = ["--init", ",".join(dependencies), "--log-level=warn"] - if odoo(odoo_bin, paths, db, *init): - sys.exit("No se ha podido crear la base de datos con las dependencias") - _logger.info("Probando: %s", ", ".join(addons)) - return odoo(odoo_bin, paths, db, "--init", ",".join(addons), "--test-enable") - - def main(): logging.basicConfig(level=logging.INFO, format="%(message)s") odoo_bin, paths = find_sources() manifests = read_manifests(paths) repo_addons = sorted(m.parent.name for m in REPO.glob("*/__manifest__.py")) - exclude = env_list("EXCLUDE") + exclude = set(os.environ.get("EXCLUDE", "").replace(",", " ").split()) addons = sys.argv[1:] or [ addon for addon in repo_addons @@ -217,25 +189,27 @@ def main(): "(tools/issue_bot/runner/ en el repo de ecocentral) o el addon a " "EXCLUDE." ) - isolate = env_list("ISOLATE") - together = [addon for addon in addons if addon not in isolate] - runs = [(DB, together)] if together else [] - runs += [(f"{DB}_{addon}", [addon]) for addon in addons if addon in isolate] + dependencies = sorted( + {dep for addon in addons for dep in manifests[addon].get("depends", [])} + - set(addons) + ) wait_for_postgres() - failed = [] - for db, run_addons in runs: - if run_tests(odoo_bin, paths, manifests, db, run_addons): - failed.append(db) - if failed: - sys.exit(f"Fallan los tests en: {', '.join(failed)}") - if POT_DIR: + subprocess.run(["dropdb", "--if-exists", DB], check=True) + _logger.info("Instalando las dependencias: %s", ", ".join(dependencies)) + init = ["--init", ",".join(dependencies), "--log-level=warn"] + if odoo(odoo_bin, paths, *init): + sys.exit("No se ha podido crear la base de datos con las dependencias") + _logger.info("Probando: %s", ", ".join(addons)) + result = odoo(odoo_bin, paths, "--init", ",".join(addons), "--test-enable") + if not result and POT_DIR: # Que falte la caché no es un fallo de los tests: el bot de revisión # se queda sin las traducciones y lo dice. try: - export_pots(odoo_bin, paths, runs) + export_pots(odoo_bin, paths, addons) except OSError as e: _logger.warning("No se han podido exportar los .pot: %s", e) + sys.exit(result) if __name__ == "__main__": diff --git a/tools/issue_bot/README.md b/tools/issue_bot/README.md index 5667a73..03104f9 100644 --- a/tools/issue_bot/README.md +++ b/tools/issue_bot/README.md @@ -101,12 +101,6 @@ una base de datos con las dependencias de los addons y después instala los addons con `--test-enable`, de modo que solo corren los tests del repo. PostgreSQL es un servicio del job, en el puerto 54330 de `127.0.0.1`. -Los addons de `ISOLATE` (en `ci.yml`) se prueban cada uno en su propia base de -datos, después del resto: son los que con solo estar instalados cambian el -comportamiento de otros addons del repo, que es compartido por clientes que no -los instalan juntos. Por ejemplo, `website_sale_disable_cart` quita el carrito -a todos los sitios web. - Odoo arranca desde las fuentes montadas en `/odoo-src/18.0`, así que: - Todo repo del que dependa un addon de aquí tiene que estar en la parte diff --git a/website_sale_aplicoop/tests/test_phase3_confirm_eskaera.py b/website_sale_aplicoop/tests/test_phase3_confirm_eskaera.py index e70d4f6..d578417 100644 --- a/website_sale_aplicoop/tests/test_phase3_confirm_eskaera.py +++ b/website_sale_aplicoop/tests/test_phase3_confirm_eskaera.py @@ -23,6 +23,7 @@ from datetime import timedelta from types import SimpleNamespace from unittest.mock import patch +from odoo.exceptions import UserError from odoo.tests.common import TransactionCase from odoo.addons.website_sale_aplicoop.controllers.website_sale import ( @@ -33,16 +34,6 @@ REQUEST_PATCH_TARGET = ( "odoo.addons.website_sale_aplicoop.controllers.website_sale.request" ) -# Languages the tests put in the request env. A fresh database (the CI's) only -# has en_US active, and `env.lang` raises "Invalid language code" for the rest. -TEST_LANGS = ("es_ES", "eu_ES", "ca_ES", "gl_ES", "pt_PT", "fr_FR", "it_IT") - - -def _activate_test_langs(env): - """Activate TEST_LANGS: no translations are loaded, only the codes.""" - for lang in TEST_LANGS: - env["res.lang"]._activate_lang(lang) - def _env_with_lang(env, lang): """Return a cloned environment with language context set.""" @@ -68,8 +59,9 @@ def _build_request_mock(env, payload=None, website=None): """ if website is None: # `pricelist_id` is what the pricing helpers read now that Eskaera - # prices with the standard website pricelist. Ordered by id so the - # pick does not depend on the translated `name` of each language. + # prices with the standard website pricelist. Ordered by id on + # purpose: the multilang cases build an env whose language is not + # installed, and the default order is by the translated `name`. website = SimpleNamespace( pricelist_id=env["product.pricelist"] .sudo() @@ -372,7 +364,6 @@ class TestProcessCartItems(TransactionCase): def setUp(self): super().setUp() - _activate_test_langs(self.env) self.controller = AplicoopWebsiteSale() # Create test products @@ -534,7 +525,6 @@ class TestBuildConfirmationMessage(TransactionCase): def setUp(self): super().setUp() - _activate_test_langs(self.env) self.controller = AplicoopWebsiteSale() self.user = self.env.ref("base.user_admin") self.partner = self.env.ref("base.partner_admin") @@ -572,11 +562,16 @@ class TestBuildConfirmationMessage(TransactionCase): ): request_mock = _build_request_mock(_env_with_lang(self.env, lang)) with patch(REQUEST_PATCH_TARGET, request_mock): - return self.controller._build_confirmation_message( - sale_order or self.sale_order, - group_order or self.group_order, - is_delivery=is_delivery, - ) + try: + return self.controller._build_confirmation_message( + sale_order or self.sale_order, + group_order or self.group_order, + is_delivery=is_delivery, + ) + except UserError as err: + if "Invalid language code" in str(err): + self.skipTest(str(err)) + raise def test_build_confirmation_message_pickup(self): """Test confirmation message for pickup (not delivery).""" @@ -674,7 +669,6 @@ class TestConfirmEskaera_Integration(TransactionCase): def setUp(self): super().setUp() - _activate_test_langs(self.env) self.controller = AplicoopWebsiteSale() self.consumer_group = self.env["res.partner"].create( { diff --git a/website_sale_lazy_loading/tests/test_lazy_loading.py b/website_sale_lazy_loading/tests/test_lazy_loading.py index f56d571..47bea99 100644 --- a/website_sale_lazy_loading/tests/test_lazy_loading.py +++ b/website_sale_lazy_loading/tests/test_lazy_loading.py @@ -25,16 +25,11 @@ class TestLazyLoading(HttpCase): cls.other_category = cls.env["product.public.category"].create( {"name": "Other Cat"} ) - # An explicit website_sequence keeps the shop order equal to the - # creation order: in a batch create every product gets the same default - # (MAX + 5, read before the insert) and the shop breaks the tie by - # `id desc`, which would list them backwards. cls.products = cls.env["product.template"].create( [ { "name": "Lazy Product %02d" % index, "list_price": 10.0 + index, - "website_sequence": index, "is_published": True, "public_categ_ids": [(6, 0, cls.category.ids)], }