[IMP] website_sale_aplicoop: propagate consumer_group_id directly from group_order

- Add consumer_group_id to sale.order for tracking the consumer group
- Fix stock.picking consumer_group_id to use sale_id.consumer_group_id
- Add group_ids inverse relation in res.partner for bidirectional access
- Remove auto-calculation of consumer_group_id, data comes directly from group_order.group_ids[0]
- Add debug logging for consumer_group propagation
- commitment_date propagates directly from group_order (no recalculation)
This commit is contained in:
snt 2026-03-06 13:49:13 +01:00
parent 4141fc5ab1
commit 80539f3e36
8 changed files with 163 additions and 56 deletions

View file

@ -929,6 +929,26 @@ class AplicoopWebsiteSale(WebsiteSale):
Returns the sale.order record.
"""
# consumer_group_id comes directly from group_order (first/only group)
# No calculations - data propagates directly from the order context
consumer_group_id = (
group_order.group_ids[0].id if group_order.group_ids else False
)
_logger.info(
"[CONSUMER_GROUP DEBUG] _create_or_update_sale_order: "
"group_order=%s, group_order.group_ids=%s, consumer_group_id=%s",
group_order.id,
group_order.group_ids.ids,
consumer_group_id,
)
# commitment_date: use provided or calculate from group_order
if not commitment_date:
commitment_date = (
group_order.delivery_date if is_delivery else group_order.pickup_date
)
if existing_order:
# Update existing order with new lines and propagate fields
# Use sudo() to avoid permission issues with portal users
@ -939,13 +959,15 @@ class AplicoopWebsiteSale(WebsiteSale):
existing_order_sudo.pickup_day = group_order.pickup_day
existing_order_sudo.pickup_date = group_order.pickup_date
existing_order_sudo.home_delivery = is_delivery
existing_order_sudo.consumer_group_id = consumer_group_id
if commitment_date:
existing_order_sudo.commitment_date = commitment_date
_logger.info(
"Updated existing sale.order %d: commitment_date=%s, home_delivery=%s",
"Updated existing sale.order %d: commitment_date=%s, home_delivery=%s, consumer_group_id=%s",
existing_order.id,
commitment_date,
is_delivery,
consumer_group_id,
)
return existing_order
@ -957,6 +979,7 @@ class AplicoopWebsiteSale(WebsiteSale):
"pickup_day": group_order.pickup_day,
"pickup_date": group_order.pickup_date,
"home_delivery": is_delivery,
"consumer_group_id": consumer_group_id,
}
if commitment_date:
order_vals["commitment_date"] = commitment_date
@ -974,11 +997,12 @@ class AplicoopWebsiteSale(WebsiteSale):
# Create order with sudo to avoid permission issues with portal users
sale_order = request.env["sale.order"].sudo().create(order_vals)
_logger.info(
"sale.order created successfully: %d with group_order_id=%d, pickup_day=%s, home_delivery=%s",
"sale.order created successfully: %d with group_order_id=%d, pickup_day=%s, home_delivery=%s, consumer_group_id=%s",
sale_order.id,
group_order.id,
group_order.pickup_day,
group_order.home_delivery,
consumer_group_id,
)
return sale_order
@ -989,27 +1013,39 @@ class AplicoopWebsiteSale(WebsiteSale):
Returns created sale.order record.
"""
# consumer_group_id comes directly from group_order (first/only group)
# No calculations - data propagates directly from the order context
consumer_group_id = (
group_order.group_ids[0].id if group_order.group_ids else False
)
_logger.info(
"[CONSUMER_GROUP DEBUG] _create_draft_sale_order: "
"group_order=%s, group_order.group_ids=%s, consumer_group_id=%s",
group_order.id,
group_order.group_ids.ids,
consumer_group_id,
)
# commitment_date comes from group_order (delivery_date or pickup_date)
commitment_date = (
group_order.delivery_date
if group_order.home_delivery
else group_order.pickup_date
)
order_vals = {
"partner_id": current_user.partner_id.id,
"order_line": sale_order_lines,
"state": "draft",
"group_order_id": order_id,
"pickup_day": group_order.pickup_day,
"pickup_date": group_order.pickup_date,
"home_delivery": group_order.home_delivery,
"consumer_group_id": consumer_group_id,
"commitment_date": commitment_date,
}
# Propagate fields from group order
if group_order.pickup_day:
order_vals["pickup_day"] = group_order.pickup_day
if group_order.pickup_date:
order_vals["pickup_date"] = group_order.pickup_date
if group_order.home_delivery:
order_vals["home_delivery"] = group_order.home_delivery
# Add commitment/commitment_date if provided
if pickup_date:
order_vals["commitment_date"] = pickup_date
elif group_order.pickup_date:
order_vals["commitment_date"] = group_order.pickup_date
# Get salesperson for order creation (portal users need this)
salesperson = self._get_salesperson_for_order(current_user.partner_id)
if salesperson:
@ -1233,7 +1269,30 @@ class AplicoopWebsiteSale(WebsiteSale):
existing_drafts,
order_id,
):
"""Replace existing draft (if any) with new lines, else create it."""
"""Replace existing draft (if any) with new lines, else create it.
All fields (commitment_date, consumer_group_id, etc.) come from group_order.
"""
# consumer_group_id comes directly from group_order (first/only group)
# No calculations - data propagates directly from the order context
consumer_group_id = (
group_order.group_ids[0].id if group_order.group_ids else False
)
_logger.info(
"[CONSUMER_GROUP DEBUG] _merge_or_replace_draft: "
"group_order=%s, group_order.group_ids=%s, consumer_group_id=%s",
group_order.id,
group_order.group_ids.ids,
consumer_group_id,
)
# commitment_date comes directly from group_order
commitment_date = (
group_order.delivery_date
if group_order.home_delivery
else group_order.pickup_date
)
if existing_drafts:
draft = existing_drafts[0].sudo()
@ -1249,6 +1308,8 @@ class AplicoopWebsiteSale(WebsiteSale):
"pickup_day": group_order.pickup_day,
"pickup_date": group_order.pickup_date,
"home_delivery": group_order.home_delivery,
"consumer_group_id": consumer_group_id,
"commitment_date": commitment_date,
}
)
return draft
@ -1261,6 +1322,8 @@ class AplicoopWebsiteSale(WebsiteSale):
"pickup_day": group_order.pickup_day,
"pickup_date": group_order.pickup_date,
"home_delivery": group_order.home_delivery,
"consumer_group_id": consumer_group_id,
"commitment_date": commitment_date,
}
# Get salesperson for order creation (portal users need this)
salesperson = self._get_salesperson_for_order(current_user.partner_id)

View file

@ -18,14 +18,26 @@ class ResPartner(models.Model):
)
# Relación para los miembros de un grupo (si is_group es True)
# Este campo se usa en el GRUPO para listar sus MIEMBROS
member_ids = fields.Many2many(
"res.partner",
"res_partner_group_members_rel",
"group_id",
"member_id",
string="Members",
help="Members that belong to this consumer group",
)
# Relación inversa: grupos a los que pertenece un MIEMBRO
# Este campo se usa en el MIEMBRO para listar sus GRUPOS
group_ids = fields.Many2many(
"res.partner",
"res_partner_group_members_rel",
"member_id",
"group_id",
domain=[("is_group", "=", True)],
string="Consumer Groups",
help="Consumer Groups this partner belongs to",
help="Consumer groups this partner belongs to",
)
# Inverse relation: group orders this group participates in

View file

@ -22,7 +22,7 @@ class SaleOrder(models.Model):
pickup_day = fields.Selection(
selection=_get_pickup_day_selection,
help="Day of week when this order will be picked up (inherited from group order)",
help="Day of week when this order will be picked up",
)
group_order_id = fields.Many2one(
@ -30,13 +30,19 @@ class SaleOrder(models.Model):
help="Reference to the consumer group order that originated this sale order",
)
consumer_group_id = fields.Many2one(
"res.partner",
domain="[('is_group', '=', True)]",
help="Consumer group for this order",
)
pickup_date = fields.Date(
help="Calculated pickup/delivery date (inherited from consumer group order)",
help="Pickup/delivery date",
)
home_delivery = fields.Boolean(
default=False,
help="Whether this order includes home delivery (inherited from consumer group order)",
help="Whether this order includes home delivery",
)
def _get_name_portal_content_view(self):

View file

@ -35,10 +35,10 @@ class StockPicking(models.Model):
consumer_group_id = fields.Many2one(
"res.partner",
related="sale_id.partner_id",
related="sale_id.consumer_group_id",
string="Consumer Group",
store=True,
readonly=True,
domain=[("is_group", "=", True)],
help="Consumer group (partner) from sale order for warehouse grouping",
help="Consumer group (res.partner with is_group=True) from sale order",
)

View file

@ -9,11 +9,12 @@ class TestResPartnerExtension(TransactionCase):
def setUp(self):
super().setUp()
# Crear grupos (res.partner with is_company=True)
# Crear grupos (res.partner with is_group=True)
self.group1 = self.env["res.partner"].create(
{
"name": "Grupo 1",
"is_company": True,
"is_group": True,
"email": "grupo1@test.com",
}
)
@ -22,6 +23,7 @@ class TestResPartnerExtension(TransactionCase):
{
"name": "Grupo 2",
"is_company": True,
"is_group": True,
"email": "grupo2@test.com",
}
)
@ -39,16 +41,16 @@ class TestResPartnerExtension(TransactionCase):
"""Test que un partner (usuario) puede pertenecer a múltiples grupos."""
partner = self.user.partner_id
# Agregar partner a grupos (usar campo member_ids)
partner.member_ids = [(6, 0, [self.group1.id, self.group2.id])]
# Agregar partner a grupos usando group_ids (desde el lado del miembro)
partner.group_ids = [(6, 0, [self.group1.id, self.group2.id])]
# Verificar que pertenece a ambos grupos
self.assertIn(self.group1, partner.member_ids)
self.assertIn(self.group2, partner.member_ids)
self.assertEqual(len(partner.member_ids), 2)
self.assertIn(self.group1, partner.group_ids)
self.assertIn(self.group2, partner.group_ids)
self.assertEqual(len(partner.group_ids), 2)
def test_group_can_have_multiple_users(self):
"""Test que un grupo puede tener múltiples usuarios."""
def test_group_can_have_multiple_members(self):
"""Test que un grupo puede tener múltiples miembros."""
user2 = self.env["res.users"].create(
{
"name": "Test User 2",
@ -57,24 +59,28 @@ class TestResPartnerExtension(TransactionCase):
}
)
# Agregar usuarios al grupo
self.group1.user_ids = [(6, 0, [self.user.id, user2.id])]
# Agregar miembros al grupo usando member_ids (desde el lado del grupo)
self.group1.member_ids = [
(6, 0, [self.user.partner_id.id, user2.partner_id.id])
]
# Verificar que el grupo tiene ambos usuarios
self.assertIn(self.user, self.group1.user_ids)
self.assertIn(user2, self.group1.user_ids)
self.assertEqual(len(self.group1.user_ids), 2)
# Verificar que el grupo tiene ambos miembros
self.assertIn(self.user.partner_id, self.group1.member_ids)
self.assertIn(user2.partner_id, self.group1.member_ids)
self.assertEqual(len(self.group1.member_ids), 2)
def test_user_group_relationship_is_bidirectional(self):
"""Test que se puede modificar la relación desde el lado del partner o el grupo."""
partner = self.user.partner_id
# Opción 1: Agregar grupo al usuario (desde el lado del usuario/partner)
partner.member_ids = [(6, 0, [self.group1.id])]
self.assertIn(self.group1, partner.member_ids)
# Opción 1: Agregar grupo al usuario (desde el lado del miembro/partner)
partner.group_ids = [(6, 0, [self.group1.id])]
self.assertIn(self.group1, partner.group_ids)
# Opción 2: Agregar usuario al grupo (desde el lado del grupo)
# Nota: Esto es una relación Many2many independiente
# Verificar que la relación inversa también funciona
self.assertIn(partner, self.group1.member_ids)
# Opción 2: Agregar partner al grupo (desde el lado del grupo)
user2 = self.env["res.users"].create(
{
"name": "Test User 2",
@ -82,12 +88,14 @@ class TestResPartnerExtension(TransactionCase):
"email": "testuser2@test.com",
}
)
self.group2.user_ids = [(6, 0, [user2.id])]
self.assertIn(user2, self.group2.user_ids)
self.group2.member_ids = [(4, user2.partner_id.id)]
self.assertIn(user2.partner_id, self.group2.member_ids)
# Verificar relación inversa
self.assertIn(self.group2, user2.partner_id.group_ids)
def test_empty_group_ids(self):
"""Test que un partner sin grupos tiene group_ids vacío."""
partner = self.user.partner_id
# Sin agregar a ningún grupo
self.assertEqual(len(partner.member_ids), 0)
self.assertEqual(len(partner.group_ids), 0)

View file

@ -9,29 +9,36 @@
<field name="inherit_id" ref="base.view_partner_tree"/>
<field name="arch" type="xml">
<xpath expr="//list" position="inside">
<field name="member_ids" optional="hide"/>
<field name="group_ids" optional="hide"/>
<field name="is_group" optional="hide"/>
<field name="group_order_ids" optional="hide"/>
</xpath>
</field>
</record>
<!-- Extend res.partner form view to show group_ids -->
<!-- Extend res.partner form view to show group membership -->
<record id="view_res_partner_form_inherit" model="ir.ui.view">
<field name="name">res.partner.form.inherit</field>
<field name="model">res.partner</field>
<field name="inherit_id" ref="base.view_partner_form"/>
<field name="arch" type="xml">
<!-- Add group_ids page -->
<!-- Add group membership page -->
<xpath expr="//notebook/page[@name='internal_notes']" position="before">
<page name="group_orders" string="Group Orders">
<group>
<group name="group_membership">
<group name="group_config" string="Group Configuration">
<field name="is_group" colspan="2"/>
<field name="group_order_ids" widget="many2many_tags" colspan="2" help="Consumer Group orders this group manages"/>
<field name="group_order_ids" widget="many2many_tags" colspan="2"
invisible="not is_group"
help="Consumer Group orders this group participates in"/>
<field name="member_ids" widget="many2many_tags" colspan="2"
invisible="not is_group"
help="Members that belong to this consumer group"/>
</group>
<group name="members">
<field name="member_ids" widget="many2many_tags" colspan="2" help="Consumer Groups this partner belongs to"/>
<group name="membership" string="Membership">
<field name="group_ids" widget="many2many_tags" colspan="2"
invisible="is_group"
help="Consumer groups this partner belongs to"/>
</group>
</group>
</page>

View file

@ -12,7 +12,10 @@
<xpath expr="//field[@name='note']" position="before">
<group string="Group Purchase Information" groups="base.group_user">
<field name="group_order_id" readonly="True" />
<field name="consumer_group_id" readonly="True" />
<field name="pickup_day" readonly="True" />
<field name="pickup_date" readonly="True" />
<field name="home_delivery" readonly="True" />
</group>
</xpath>
</field>

View file

@ -471,10 +471,18 @@
<div class="info-item">
<t t-if="group_order.pickup_day and group_order.pickup_date">
<label t-att-class="'info-label'">Store Pickup Day</label>
<span class="info-value" t-attf-data-pickup-date="{{ group_order.pickup_date }}" t-attf-data-delivery-date="{{ group_order.delivery_date }}">
<t t-esc="day_names[int(group_order.pickup_day) % 7]" />
<span class="info-date">(<t t-esc="group_order.pickup_date.strftime('%d/%m/%Y')" />)</span>
</span>
<t t-if="group_order.home_delivery">
<span class="info-value" t-attf-data-pickup-date="{{ group_order.pickup_date }}" t-attf-data-delivery-date="{{ group_order.delivery_date }}">
<t t-esc="day_names[int(group_order.pickup_day) % 7]" />
<span class="info-date">(<t t-esc="group_order.pickup_date.strftime('%d/%m/%Y')" />)</span>
</span>
</t>
<t t-else="">
<span class="info-value" t-attf-data-pickup-date="{{ group_order.pickup_date }}">
<t t-esc="day_names[int(group_order.pickup_day) % 7]" />
<span class="info-date">(<t t-esc="group_order.pickup_date.strftime('%d/%m/%Y')" />)</span>
</span>
</t>
</t>
</div>
</div>