[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:
parent
4141fc5ab1
commit
80539f3e36
8 changed files with 163 additions and 56 deletions
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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):
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue