"custom" said nothing about what the module does. It is really about collecting goods into baskets: the extra detailed-operation columns, the is_collected flag, the Product Summary tab, the per-company validation restrictions and the Basket Assembly operator view all serve that one job. Rename it accordingly. Invert the dependency while at it. A generic warehouse addon was dragging in an entire eCommerce application, and the whole coupling was a single field: stock.move.line.home_delivery, related to picking_id.home_delivery. Everything else was already duck-typed. website_sale_aplicoop now depends on this module and injects its own consumer group columns into these views. This removes duplicated logic rather than relocating it: stock.picking .batch_consumer_group_id re-derived from sale_id a value aplicoop already stored as stock.picking.consumer_group_id, and the duplicate carried no @api.depends, so it never recomputed reliably. The batch transfers list now shows the stored field, which is sortable and groupable. The two aplicoop tests that probed information_schema for the res_company batch_* columns can drop that guard: a real dependency guarantees them. Renaming an addon is not something a migrations/ script can do, since a renamed addon is a brand new module to Odoo and its migration scripts never run. A pre_init_hook does it instead: it fires on install after the Python is imported but before registry.load(), which is the window where remapping ir_model_data makes Odoo reuse the existing tables and columns. is_collected, the summary line table and the company settings all survive untouched. Two details the hook has to get right: - ir_model_constraint.module and ir_model_relation.module are integer FKs with ON DELETE CASCADE, so they must be repointed before the old module row is deleted or the bookkeeping goes with it. - Deleting an ir_model_data row does not cascade to the record it points at. Artifacts handed over to aplicoop only need the xmlid dropped, but artifacts that disappear need the record deleted too, or the field survives as an orphan manual field and the view as a custom view referencing it. Verified against a restored copy of the dev database: 50 xmlids moved, 19 summary lines and 5 collected move lines preserved, no orphans, both test suites green, and the module installs cleanly on a database without website_sale_aplicoop. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
210 lines
7.3 KiB
Python
210 lines
7.3 KiB
Python
# Copyright 2025 Criptomart
|
|
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl)
|
|
|
|
from datetime import datetime
|
|
from datetime import timedelta
|
|
|
|
from odoo.exceptions import ValidationError
|
|
from odoo.tests.common import TransactionCase
|
|
|
|
|
|
class TestMultiCompanyGroupOrder(TransactionCase):
|
|
"""Test suite para el soporte multicompañía en group.order."""
|
|
|
|
def setUp(self):
|
|
super().setUp()
|
|
|
|
# Crear dos compañías. `stock_picking_batch_collect` es dependencia del
|
|
# módulo, así que los campos de restricción de lote siempre existen.
|
|
company_model = self.env["res.company"]
|
|
|
|
def _company_vals(name):
|
|
return {
|
|
"name": name,
|
|
"batch_summary_restriction_scope": "processed",
|
|
"batch_detailed_restriction_scope": "processed",
|
|
}
|
|
|
|
self.company1 = company_model.create(_company_vals("Company 1"))
|
|
self.company2 = company_model.create(_company_vals("Company 2"))
|
|
|
|
# Crear grupos en diferentes compañías
|
|
self.group1 = self.env["res.partner"].create(
|
|
{
|
|
"name": "Grupo Company 1",
|
|
"is_company": True,
|
|
"email": "grupo1@test.com",
|
|
"company_id": self.company1.id,
|
|
}
|
|
)
|
|
|
|
self.group2 = self.env["res.partner"].create(
|
|
{
|
|
"name": "Grupo Company 2",
|
|
"is_company": True,
|
|
"email": "grupo2@test.com",
|
|
"company_id": self.company2.id,
|
|
}
|
|
)
|
|
|
|
# Crear productos en cada compañía
|
|
self.product1 = self.env["product.product"].create(
|
|
{
|
|
"name": "Producto Company 1",
|
|
"type": "consu",
|
|
"list_price": 10.0,
|
|
"company_id": self.company1.id,
|
|
}
|
|
)
|
|
|
|
self.product2 = self.env["product.product"].create(
|
|
{
|
|
"name": "Producto Company 2",
|
|
"type": "consu",
|
|
"list_price": 20.0,
|
|
"company_id": self.company2.id,
|
|
}
|
|
)
|
|
|
|
def test_group_order_has_company_id(self):
|
|
"""Test que group.order tenga el campo company_id."""
|
|
order = self.env["group.order"].create(
|
|
{
|
|
"name": "Pedido Company 1",
|
|
"group_ids": [(6, 0, [self.group1.id])],
|
|
"company_id": self.company1.id,
|
|
"type": "regular",
|
|
"start_date": datetime.now().date(),
|
|
"end_date": (datetime.now() + timedelta(days=7)).date(),
|
|
"period": "weekly",
|
|
"pickup_day": "5",
|
|
"cutoff_day": "0",
|
|
}
|
|
)
|
|
|
|
self.assertTrue(order.exists())
|
|
self.assertEqual(order.company_id, self.company1)
|
|
|
|
def test_group_order_default_company(self):
|
|
"""Test que company_id por defecto sea la compañía del usuario."""
|
|
# Crear usuario con compañía específica
|
|
user = self.env["res.users"].create(
|
|
{
|
|
"name": "Test User",
|
|
"login": "testuser",
|
|
"password": "test123",
|
|
"company_id": self.company1.id,
|
|
"company_ids": [(6, 0, [self.company1.id])],
|
|
}
|
|
)
|
|
|
|
order = (
|
|
self.env["group.order"]
|
|
.with_user(user)
|
|
.create(
|
|
{
|
|
"name": "Pedido Default Company",
|
|
"group_ids": [(6, 0, [self.group1.id])],
|
|
"type": "regular",
|
|
"start_date": datetime.now().date(),
|
|
"end_date": (datetime.now() + timedelta(days=7)).date(),
|
|
"period": "weekly",
|
|
"pickup_day": "5",
|
|
"cutoff_day": "0",
|
|
}
|
|
)
|
|
)
|
|
|
|
# Verificar que se asignó la compañía del usuario
|
|
self.assertEqual(order.company_id, self.company1)
|
|
|
|
def test_group_order_company_constraint(self):
|
|
"""Test que solo grupos de la misma compañía se puedan asignar."""
|
|
# Intentar asignar un grupo de otra compañía
|
|
with self.assertRaises(ValidationError):
|
|
self.env["group.order"].create(
|
|
{
|
|
"name": "Pedido Mixed Companies",
|
|
"group_ids": [(6, 0, [self.group1.id, self.group2.id])],
|
|
"company_id": self.company1.id,
|
|
"type": "regular",
|
|
"start_date": datetime.now().date(),
|
|
"end_date": (datetime.now() + timedelta(days=7)).date(),
|
|
"period": "weekly",
|
|
"pickup_day": "5",
|
|
"cutoff_day": "0",
|
|
}
|
|
)
|
|
|
|
def test_group_order_multi_company_filter(self):
|
|
"""Test que get_active_orders_for_week() respete company_id."""
|
|
# Crear órdenes en diferentes compañías
|
|
order1 = self.env["group.order"].create(
|
|
{
|
|
"name": "Pedido Company 1",
|
|
"group_ids": [(6, 0, [self.group1.id])],
|
|
"company_id": self.company1.id,
|
|
"type": "regular",
|
|
"state": "open",
|
|
"start_date": datetime.now().date(),
|
|
"end_date": (datetime.now() + timedelta(days=7)).date(),
|
|
"period": "weekly",
|
|
"pickup_day": "5",
|
|
"cutoff_day": "0",
|
|
}
|
|
)
|
|
|
|
self.env["group.order"].create(
|
|
{
|
|
"name": "Pedido Company 2",
|
|
"group_ids": [(6, 0, [self.group2.id])],
|
|
"company_id": self.company2.id,
|
|
"type": "regular",
|
|
"state": "open",
|
|
"start_date": datetime.now().date(),
|
|
"end_date": (datetime.now() + timedelta(days=7)).date(),
|
|
"period": "weekly",
|
|
"pickup_day": "5",
|
|
"cutoff_day": "0",
|
|
}
|
|
)
|
|
|
|
# Obtener órdenes activas de company1
|
|
active_orders = (
|
|
self.env["group.order"]
|
|
.with_context(allowed_company_ids=[self.company1.id])
|
|
.get_active_orders_for_week()
|
|
)
|
|
|
|
# Debería contener solo order1
|
|
self.assertIn(order1, active_orders)
|
|
# order2 podría no estar en el resultado si se implementa
|
|
# el filtro de compañía correctamente
|
|
|
|
def test_product_company_isolation(self):
|
|
"""Test que los productos de diferentes compañías estén aislados."""
|
|
# Crear categoría para products
|
|
category = self.env["product.category"].create(
|
|
{
|
|
"name": "Test Category",
|
|
}
|
|
)
|
|
|
|
order = self.env["group.order"].create(
|
|
{
|
|
"name": "Pedido con Categoría",
|
|
"group_ids": [(6, 0, [self.group1.id])],
|
|
"category_ids": [(6, 0, [category.id])],
|
|
"company_id": self.company1.id,
|
|
"type": "regular",
|
|
"start_date": datetime.now().date(),
|
|
"end_date": (datetime.now() + timedelta(days=7)).date(),
|
|
"period": "weekly",
|
|
"pickup_day": "5",
|
|
"cutoff_day": "0",
|
|
}
|
|
)
|
|
|
|
self.assertTrue(order.exists())
|
|
self.assertEqual(order.company_id, self.company1)
|
|
self.assertIn(category, order.category_ids)
|