# Copyright 2025 Criptomart # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl) import logging from odoo import api from odoo import fields from odoo import models # Pylint: the explicit 'string' parameter is intentional for clarity in views. # Some fields may trigger 'attribute-string-redundant' warnings; silence them # locally where appropriate. # pylint: disable=attribute-string-redundant _logger = logging.getLogger(__name__) class SaleOrder(models.Model): _inherit = "sale.order" def _get_pickup_day_selection(self): """Return pickup day selection options with translations.""" return [ ("0", self.env._("Monday")), ("1", self.env._("Tuesday")), ("2", self.env._("Wednesday")), ("3", self.env._("Thursday")), ("4", self.env._("Friday")), ("5", self.env._("Saturday")), ("6", self.env._("Sunday")), ] pickup_day = fields.Selection( selection=_get_pickup_day_selection, help="Day of week when this order will be picked up", ) group_order_id = fields.Many2one( "group.order", 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="Pickup/delivery date", ) pickup_slot_label = fields.Char( string="Pickup Slot Label", compute="_compute_pickup_slot_label", store=True, readonly=True, ) home_delivery = fields.Boolean( default=False, help="Whether this order includes home delivery", ) @api.depends( "group_order_id", "group_order_id.next_pickup_slot_id", "group_order_id.next_pickup_slot_id.label", "group_order_id.next_pickup_slot_id.start_hour", "group_order_id.next_pickup_slot_id.end_hour", "pickup_date", "pickup_day", ) def _compute_pickup_slot_label(self): """Compute a human readable label for the pickup information. Priority: 1. Use the group order's current `next_pickup_slot_id` if available 2. Fallback to legacy `pickup_day` / `pickup_date` fields Note: we deliberately do NOT store a Many2one reference to the slot on the sale.order anymore — we compute the label dynamically from the related group order to avoid persisting slot IDs. """ for order in self: slot = False if order.group_order_id and order.group_order_id.next_pickup_slot_id: slot = order.group_order_id.next_pickup_slot_id if slot: if slot.label: label = slot.label else: sh = float(slot.start_hour or 0.0) eh = float(slot.end_hour or 0.0) sh_h = int(sh) sh_m = int(round((sh - sh_h) * 60)) eh_h = int(eh) eh_m = int(round((eh - eh_h) * 60)) label = f"{sh_h:02d}:{sh_m:02d}-{eh_h:02d}:{eh_m:02d}" if order.pickup_date: try: date_str = ( order.pickup_date.strftime("%d/%m/%Y") if hasattr(order.pickup_date, "strftime") else str(order.pickup_date) ) label = f"{label} ({date_str})" except ( Exception ) as exc: # log format errors, but don't break compute _logger.debug( "_compute_pickup_slot_label: failed to format pickup_date for order %s: %s", order.id if order and order.id else None, exc, exc_info=True, ) order.pickup_slot_label = label else: # Fallback to single-day fields if order.pickup_day: try: day_map = dict(order._get_pickup_day_selection()) day_name = day_map.get(order.pickup_day, order.pickup_day) except Exception as exc: _logger.debug( "_compute_pickup_slot_label: failed to map pickup_day for order %s: %s", order.id if order and order.id else None, exc, exc_info=True, ) day_name = order.pickup_day if order.pickup_date: try: date_str = ( order.pickup_date.strftime("%d/%m/%Y") if hasattr(order.pickup_date, "strftime") else str(order.pickup_date) ) order.pickup_slot_label = f"{day_name} ({date_str})" except Exception as exc: _logger.debug( "_compute_pickup_slot_label: failed to format pickup_date (fallback) for order %s: %s", order.id if order and order.id else None, exc, exc_info=True, ) order.pickup_slot_label = day_name else: order.pickup_slot_label = day_name else: order.pickup_slot_label = False def _get_name_portal_content_view(self): """Override to return custom portal content template with group order info. This method is called by the portal template to determine which content template to render. We return our custom template that includes the group order information (Consumer Group, Delivery/Pickup info, etc.) """ self.ensure_one() if self.group_order_id: return "website_sale_aplicoop.sale_order_portal_content_aplicoop" return super()._get_name_portal_content_view()