diff --git a/website_sale_aplicoop/controllers/website_sale.py b/website_sale_aplicoop/controllers/website_sale.py index 2f9de17..9a8794d 100644 --- a/website_sale_aplicoop/controllers/website_sale.py +++ b/website_sale_aplicoop/controllers/website_sale.py @@ -188,9 +188,7 @@ class AplicoopWebsiteSale(WebsiteSale): "error_unknown": env_lang._("Unknown error"), "error_invalid_data": env_lang._("Invalid data provided"), "error_order_not_found": env_lang._("Order not found"), - "error_no_draft_orders": env_lang._( - "No draft orders found for the current order period" - ), + "error_no_draft_orders": env_lang._("No draft orders found for this week"), "invalid_quantity": env_lang._("Please enter a valid quantity"), # ============ CONFIRMATION MESSAGES ============ "save_draft_confirm": env_lang._( @@ -209,7 +207,7 @@ class AplicoopWebsiteSale(WebsiteSale): # ============ DRAFT MODAL LABELS ============ "draft_already_exists": env_lang._("Draft Already Exists"), "draft_exists_message": env_lang._( - "A saved draft already exists for the current order period." + "A saved draft already exists for this week." ), "draft_two_options": env_lang._("You have two options:"), "draft_option1_title": env_lang._("Option 1: Merge with Existing Draft"), @@ -1430,41 +1428,34 @@ class AplicoopWebsiteSale(WebsiteSale): status=status, ) - def _find_recent_draft_order(self, partner_id, group_order): - """Find most recent draft sale.order for partner in the active order period. + def _find_recent_draft_order(self, partner_id, order_id): + """Find most recent draft sale.order for partner and group_order in current week. - Priority for period matching: - 1) If group_order.pickup_date is set, match that exact pickup_date - (prevents reusing stale drafts from previous cycles of same group.order). - 2) Fallback to current-week create_date bounds when pickup_date is not set. - - Returns the recordset (limit=1) or empty recordset. + Returns the record or empty recordset. """ + from datetime import datetime + from datetime import timedelta + today = datetime.now().date() start_of_week = today - timedelta(days=today.weekday()) end_of_week = start_of_week + timedelta(days=6) - domain = [ - ("partner_id", "=", partner_id), - ("group_order_id", "=", group_order.id), - ("state", "=", "draft"), - ] - - if group_order.pickup_date: - domain.append(("pickup_date", "=", group_order.pickup_date)) - else: - domain.extend( - [ - ("create_date", ">=", f"{start_of_week} 00:00:00"), - ("create_date", "<=", f"{end_of_week} 23:59:59"), - ] - ) - - return ( + drafts = ( request.env["sale.order"] .sudo() - .search(domain, order="create_date desc", limit=1) + .search( + [ + ("partner_id", "=", partner_id), + ("group_order_id", "=", order_id), + ("state", "=", "draft"), + ("create_date", ">=", f"{start_of_week} 00:00:00"), + ("create_date", "<=", f"{end_of_week} 23:59:59"), + ], + order="create_date desc", + limit=1, + ) ) + return drafts @http.route(["/eskaera/"], type="http", auth="user", website=True) def eskaera_shop(self, order_id, **post): @@ -2267,7 +2258,7 @@ class AplicoopWebsiteSale(WebsiteSale): csrf=False, ) def load_draft_cart(self, **post): - """Load items from the most recent draft sale.order for current period.""" + """Load items from the most recent draft sale.order for this week.""" import json try: @@ -2327,18 +2318,16 @@ class AplicoopWebsiteSale(WebsiteSale): status=400, ) - # Find the most recent draft sale.order for this partner in active period - # The helper _find_recent_draft_order computes the period criteria itself, + # Find the most recent draft sale.order for this partner from this week + # The helper _find_recent_draft_order computes the week bounds itself, # so we only need to call it here. # Find the most recent matching draft order using helper draft_orders = self._find_recent_draft_order( - current_user.partner_id.id, group_order + current_user.partner_id.id, order_id ) if not draft_orders: - error_msg = request.env._( - "No draft orders found for the current order period" - ) + error_msg = request.env._("No draft orders found for this week") return request.make_response( json.dumps({"error": error_msg}), [("Content-Type", "application/json")], @@ -2507,9 +2496,17 @@ class AplicoopWebsiteSale(WebsiteSale): status=400, ) - # Check if a draft already exists for this user in current order period - existing_drafts = self._find_recent_draft_order( - current_user.partner_id.id, group_order + # Check if a draft already exists for this group order and user + existing_drafts = ( + request.env["sale.order"] + .sudo() + .search( + [ + ("group_order_id", "=", order_id), + ("partner_id", "=", current_user.partner_id.id), + ("state", "=", "draft"), + ] + ) ) _logger.info( @@ -2636,9 +2633,18 @@ class AplicoopWebsiteSale(WebsiteSale): status=400, ) - # Reuse only draft from the current group_order cycle (not stale ones) - existing_order = self._find_recent_draft_order( - current_user.partner_id.id, group_order + # First, check if there's already a draft sale.order for this user in this group order + existing_order = ( + request.env["sale.order"] + .sudo() + .search( + [ + ("partner_id", "=", current_user.partner_id.id), + ("group_order_id", "=", group_order.id), + ("state", "=", "draft"), + ], + limit=1, + ) ) if existing_order: @@ -2928,7 +2934,7 @@ class AplicoopWebsiteSale(WebsiteSale): translations = { "es_ES": { "Draft Already Exists": "El Borrador Ya Existe", - "A saved draft already exists for the current order period.": "Un borrador guardado ya existe para el período actual del pedido.", + "A saved draft already exists for this week.": "Un borrador guardado ya existe para esta semana.", "You have two options:": "Tienes dos opciones:", "Option 1: Merge with Existing Draft": "Opción 1: Fusionar con Borrador Existente", "Combine your current cart with the existing draft.": "Combina tu carrito actual con el borrador existente.", @@ -2951,7 +2957,7 @@ class AplicoopWebsiteSale(WebsiteSale): }, "eu_ES": { "Draft Already Exists": "Zirriborro Dagoeneko Badago", - "A saved draft already exists for the current order period.": "Gordetako zirriborro bat dagoeneko badago uneko eskaera-aldirako.", + "A saved draft already exists for this week.": "Gordetako zirriborro bat dagoeneko badago asteburu honetarako.", "You have two options:": "Bi aukera dituzu:", "Option 1: Merge with Existing Draft": "1. Aukera: Existentea Duen Zirriborroarekin Batu", "Combine your current cart with the existing draft.": "Batu zure gaur-oraingo saskia existentea duen zirriborroarekin.", @@ -2975,7 +2981,7 @@ class AplicoopWebsiteSale(WebsiteSale): # Also support 'eu' as a variant "eu": { "Draft Already Exists": "Zirriborro Dagoeneko Badago", - "A saved draft already exists for the current order period.": "Gordetako zirriborro bat dagoeneko badago uneko eskaera-aldirako.", + "A saved draft already exists for this week.": "Gordetako zirriborro bat dagoeneko badago asteburu honetarako.", "You have two options:": "Bi aukera dituzu:", "Option 1: Merge with Existing Draft": "1. Aukera: Existentea Duen Zirriborroarekin Batu", "Combine your current cart with the existing draft.": "Batu zure gaur-oraingo saskia existentea duen zirriborroarekin.", diff --git a/website_sale_aplicoop/i18n/es.po b/website_sale_aplicoop/i18n/es.po index b9901a8..1c8479c 100644 --- a/website_sale_aplicoop/i18n/es.po +++ b/website_sale_aplicoop/i18n/es.po @@ -240,8 +240,15 @@ msgstr "Día de Recogida" #. module: website_sale_aplicoop #. odoo-python #: code:addons/website_sale_aplicoop/controllers/website_sale.py:0 -msgid "A saved draft already exists for the current order period." -msgstr "Ya existe un borrador guardado para el período actual del pedido." +msgid "A draft already exists for this week." +msgstr "Ya existe un borrador para esta semana." + +#. module: website_sale_aplicoop +#. odoo-python +#: code:addons/website_sale_aplicoop/controllers/website_sale.py:0 +#: code:addons/website_sale_aplicoop/models/js_translations.py:0 +msgid "A saved draft already exists for this week." +msgstr "Ya existe un borrador guardado para esta semana." #. module: website_sale_aplicoop #: model_terms:ir.ui.view,arch_db:website_sale_aplicoop.eskaera_shop @@ -911,8 +918,9 @@ msgstr "" #. odoo-python #: code:addons/website_sale_aplicoop/controllers/website_sale.py:0 #: code:addons/website_sale_aplicoop/models/js_translations.py:0 -msgid "No draft orders found for the current order period" -msgstr "No se encontraron borradores para el período actual del pedido" +#, fuzzy +msgid "No draft orders found for this week" +msgstr "Ya existe un borrador para esta semana." #. module: website_sale_aplicoop #: model_terms:ir.ui.view,arch_db:website_sale_aplicoop.eskaera_page @@ -1445,16 +1453,46 @@ msgstr "con el borrador guardado." msgid "{{ product.name }}" msgstr "{{ product.name }}" +#. module: website_sale_aplicoop +#: model_terms:ir.ui.view,arch_db:website_sale_aplicoop.sale_order_portal_content_aplicoop +msgid "Consumer Group" +msgstr "Grupo de Consumidores" + +#. module: website_sale_aplicoop +#: model_terms:ir.ui.view,arch_db:website_sale_aplicoop.sale_order_portal_content_aplicoop +msgid "Delivery Information" +msgstr "Información de Envío" + #. module: website_sale_aplicoop #: model_terms:ir.ui.view,arch_db:website_sale_aplicoop.sale_order_portal_content_aplicoop msgid "Delivery Date" msgstr "Fecha de Entrega" +#. module: website_sale_aplicoop +#: model_terms:ir.ui.view,arch_db:website_sale_aplicoop.sale_order_portal_content_aplicoop +msgid "Pickup Date" +msgstr "Fecha de Recogida" + +#. module: website_sale_aplicoop +#: model_terms:ir.ui.view,arch_db:website_sale_aplicoop.sale_order_portal_content_aplicoop +msgid "Delivery Notice" +msgstr "Aviso de Envío" + +#. module: website_sale_aplicoop +#: model_terms:ir.ui.view,arch_db:website_sale_aplicoop.sale_order_portal_content_aplicoop +msgid "No special delivery instructions" +msgstr "Sin instrucciones especiales de entrega" + #. module: website_sale_aplicoop #: model_terms:ir.ui.view,arch_db:website_sale_aplicoop.sale_order_portal_content_aplicoop msgid "Pickup Location" msgstr "Lugar de Recogida" +#. module: website_sale_aplicoop +#: model_terms:ir.ui.view,arch_db:website_sale_aplicoop.portal_order_page_sidebar_button +msgid "Load in Cart" +msgstr "Recargar Carrito" + #. module: website_sale_aplicoop #: model:product.ribbon,name:website_sale_aplicoop.out_of_stock_ribbon msgid "Out of Stock" diff --git a/website_sale_aplicoop/i18n/eu.po b/website_sale_aplicoop/i18n/eu.po index e6886d9..20e8355 100644 --- a/website_sale_aplicoop/i18n/eu.po +++ b/website_sale_aplicoop/i18n/eu.po @@ -241,8 +241,15 @@ msgstr "Biltzeko Lekua:" #. module: website_sale_aplicoop #. odoo-python #: code:addons/website_sale_aplicoop/controllers/website_sale.py:0 -msgid "A saved draft already exists for the current order period." -msgstr "Gordetako zirriborro bat dagoeneko badago uneko eskaera-aldirako." +msgid "A draft already exists for this week." +msgstr "Zirriborro bat dagoeneko existitzen da astean honetan." + +#. module: website_sale_aplicoop +#. odoo-python +#: code:addons/website_sale_aplicoop/controllers/website_sale.py:0 +#: code:addons/website_sale_aplicoop/models/js_translations.py:0 +msgid "A saved draft already exists for this week." +msgstr "Gordetako zirriborro bat dagoeneko existitzen da astean honetan." #. module: website_sale_aplicoop #: model_terms:ir.ui.view,arch_db:website_sale_aplicoop.eskaera_shop @@ -910,8 +917,9 @@ msgstr "" #. odoo-python #: code:addons/website_sale_aplicoop/controllers/website_sale.py:0 #: code:addons/website_sale_aplicoop/models/js_translations.py:0 -msgid "No draft orders found for the current order period" -msgstr "Ez da zirriborrorik aurkitu uneko eskaera-aldirako" +#, fuzzy +msgid "No draft orders found for this week" +msgstr "Zirriborro bat dagoeneko existitzen da astean honetan." #. module: website_sale_aplicoop #: model_terms:ir.ui.view,arch_db:website_sale_aplicoop.eskaera_page @@ -1445,16 +1453,46 @@ msgstr "gordetako zirriborroarekin." msgid "{{ product.name }}" msgstr "{{ product.name }}" +#. module: website_sale_aplicoop +#: model_terms:ir.ui.view,arch_db:website_sale_aplicoop.sale_order_portal_content_aplicoop +msgid "Consumer Group" +msgstr "Kontsumo Taldea" + +#. module: website_sale_aplicoop +#: model_terms:ir.ui.view,arch_db:website_sale_aplicoop.sale_order_portal_content_aplicoop +msgid "Delivery Information" +msgstr "Delibatua Informazioa" + #. module: website_sale_aplicoop #: model_terms:ir.ui.view,arch_db:website_sale_aplicoop.sale_order_portal_content_aplicoop msgid "Delivery Date" msgstr "Entrega Data" +#. module: website_sale_aplicoop +#: model_terms:ir.ui.view,arch_db:website_sale_aplicoop.sale_order_portal_content_aplicoop +msgid "Pickup Date" +msgstr "Biltzea Data" + +#. module: website_sale_aplicoop +#: model_terms:ir.ui.view,arch_db:website_sale_aplicoop.sale_order_portal_content_aplicoop +msgid "Delivery Notice" +msgstr "Delibatua Oharra" + +#. module: website_sale_aplicoop +#: model_terms:ir.ui.view,arch_db:website_sale_aplicoop.sale_order_portal_content_aplicoop +msgid "No special delivery instructions" +msgstr "Ez dago entregaren instrukzio bereziak" + #. module: website_sale_aplicoop #: model_terms:ir.ui.view,arch_db:website_sale_aplicoop.sale_order_portal_content_aplicoop msgid "Pickup Location" msgstr "Biltzeko Lekua" +#. module: website_sale_aplicoop +#: model_terms:ir.ui.view,arch_db:website_sale_aplicoop.portal_order_page_sidebar_button +msgid "Load in Cart" +msgstr "Saskia Berrkargatu" + #. module: website_sale_aplicoop #: model:product.ribbon,name:website_sale_aplicoop.out_of_stock_ribbon msgid "Out of Stock" diff --git a/website_sale_aplicoop/models/group_order.py b/website_sale_aplicoop/models/group_order.py index 8165b8e..487b882 100644 --- a/website_sale_aplicoop/models/group_order.py +++ b/website_sale_aplicoop/models/group_order.py @@ -645,65 +645,16 @@ class GroupOrder(models.Model): Only updates orders in 'draft' or 'open' states. """ orders = self.search([("state", "in", ["draft", "open"])]) - cron_started_at = fields.Datetime.now() - _logger.info( - "Cron: Starting group.order date update at %s for %d active orders (ids=%s)", - cron_started_at, - len(orders), - orders.ids, - ) - processed_orders = 0 - failed_orders = [] + _logger.info("Cron: Updating dates for %d active group orders", len(orders)) for order in orders: - before_values = { - "state": order.state, - "period": order.period, - "start_date": order.start_date, - "end_date": order.end_date, - "cutoff_day": order.cutoff_day, - "pickup_day": order.pickup_day, - "cutoff_date": order.cutoff_date, - "pickup_date": order.pickup_date, - "delivery_date": order.delivery_date, - "home_delivery": order.home_delivery, - } - _logger.info( - "Cron: Processing group order %s (%s) with values before recompute: %s", - order.id, - order.name, - before_values, - ) - try: - # Confirm BEFORE recomputing dates: cutoff_date still points to the - # current cycle's cutoff (today or past), so the check works correctly. - # After confirmation, recompute dates so they advance to the next cycle. - order._confirm_linked_sale_orders() - order._compute_cutoff_date() - order._compute_pickup_date() - order._compute_delivery_date() - processed_orders += 1 - _logger.info( - "Cron: Finished group order %s (%s). Dates after recompute: cutoff=%s, pickup=%s, delivery=%s", - order.id, - order.name, - order.cutoff_date, - order.pickup_date, - order.delivery_date, - ) - except Exception: - failed_orders.append(order.id) - _logger.exception( - "Cron: Error while processing group order %s (%s). Initial values were: %s", - order.id, - order.name, - before_values, - ) - _logger.info( - "Cron: Date update completed. processed=%d failed=%d failed_ids=%s", - processed_orders, - len(failed_orders), - failed_orders, - ) + # Confirm BEFORE recomputing dates: cutoff_date still points to the + # current cycle's cutoff (today or past), so the check works correctly. + # After confirmation, recompute dates so they advance to the next cycle. + order._confirm_linked_sale_orders() + order._compute_cutoff_date() + order._compute_pickup_date() + order._compute_delivery_date() + _logger.info("Cron: Date update completed") def _confirm_linked_sale_orders(self): """Confirm draft/sent sale orders linked to this group order. @@ -718,26 +669,13 @@ class GroupOrder(models.Model): today = fields.Date.today() - if not self.cutoff_date: - _logger.warning( - "Cron: Group order %s (%s) has no cutoff_date (state=%s, period=%s, cutoff_day=%s, start_date=%s). Skipping sale order confirmation for this cycle.", - self.id, - self.name, - self.state, - self.period, - self.cutoff_day, - self.start_date, - ) - return - # Skip if cutoff hasn't passed yet (the cycle is still open for orders) if self.cutoff_date >= today: _logger.info( - "Cron: Skipping group order %s (%s) - cutoff date %s not yet passed (today=%s)", + "Cron: Skipping group order %s (%s) - cutoff date %s not yet passed", self.id, self.name, self.cutoff_date, - today, ) return diff --git a/website_sale_aplicoop/models/js_translations.py b/website_sale_aplicoop/models/js_translations.py index a7edf65..01e24ad 100644 --- a/website_sale_aplicoop/models/js_translations.py +++ b/website_sale_aplicoop/models/js_translations.py @@ -42,7 +42,7 @@ def _register_translations(): # Draft Modal Labels # ======================== _("Draft Already Exists") - _("A saved draft already exists for the current order period.") + _("A saved draft already exists for this week.") _("You have two options:") _("Option 1: Merge with Existing Draft") _("Combine your current cart with the existing draft.") @@ -89,7 +89,7 @@ def _register_translations(): # Error Messages # ======================== _("Error: Order ID not found") - _("No draft orders found for the current order period") + _("No draft orders found for this week") _("Connection error") _("Error loading order") _("Error loading draft") diff --git a/website_sale_aplicoop/static/src/js/website_sale.js b/website_sale_aplicoop/static/src/js/website_sale.js index cb7ad3b..0682e33 100644 --- a/website_sale_aplicoop/static/src/js/website_sale.js +++ b/website_sale_aplicoop/static/src/js/website_sale.js @@ -1015,7 +1015,7 @@ all_categories: "All categories", // Draft modal labels draft_already_exists: "Draft Already Exists", - draft_exists_message: "A saved draft already exists for the current order period.", + draft_exists_message: "A saved draft already exists for this week.", draft_two_options: "You have two options:", draft_option1_title: "Option 1: Merge with Existing Draft", draft_option1_desc: "Combine your current cart with the existing draft.", diff --git a/website_sale_aplicoop/tests/test_date_calculations.py b/website_sale_aplicoop/tests/test_date_calculations.py index e6d66a1..50d2cee 100644 --- a/website_sale_aplicoop/tests/test_date_calculations.py +++ b/website_sale_aplicoop/tests/test_date_calculations.py @@ -566,49 +566,3 @@ class TestDateCalculations(TransactionCase): order.pickup_date, f"Pickup date should be set for active order {order.name}", ) - - def test_cron_update_dates_skips_orders_without_cutoff_date(self): - """Cron should not crash if an active order lacks cutoff_date. - - A malformed or partially configured active group order should be logged - and skipped for sale-order confirmation, but must not block recomputing - the rest of active orders. - """ - today = fields.Date.today() - - valid_order = self.env["group.order"].create( - { - "name": "Valid Cron Order", - "group_ids": [(6, 0, [self.group.id])], - "start_date": today, - "pickup_day": "2", # Wednesday - "cutoff_day": "0", # Monday - "period": "weekly", - "state": "open", - } - ) - invalid_order = self.env["group.order"].create( - { - "name": "Invalid Cron Order Without Cutoff", - "group_ids": [(6, 0, [self.group.id])], - "start_date": today, - "pickup_day": "2", # Wednesday - "cutoff_day": False, - "period": "weekly", - "state": "open", - } - ) - - self.assertFalse( - invalid_order.cutoff_date, - "Precondition failed: invalid order should not have cutoff_date", - ) - - self.env["group.order"]._cron_update_dates() - - valid_order.invalidate_recordset() - invalid_order.invalidate_recordset() - - self.assertTrue(valid_order.cutoff_date) - self.assertTrue(valid_order.pickup_date) - self.assertFalse(invalid_order.cutoff_date) diff --git a/website_sale_aplicoop/tests/test_phase3_confirm_eskaera.py b/website_sale_aplicoop/tests/test_phase3_confirm_eskaera.py index 3562c90..1614ab2 100644 --- a/website_sale_aplicoop/tests/test_phase3_confirm_eskaera.py +++ b/website_sale_aplicoop/tests/test_phase3_confirm_eskaera.py @@ -667,54 +667,3 @@ class TestConfirmEskaera_Integration(TransactionCase): existing_order.invalidate_recordset() self.assertEqual(len(existing_order.order_line), 1) self.assertEqual(existing_order.order_line[0].product_uom_qty, 5) - - @patch("odoo.http.request") - def test_confirm_eskaera_ignores_old_period_draft(self, mock_request): - """Old draft from previous pickup_date must not be reused.""" - mock_request.env = self.env.with_user(self.user) - mock_request.env.lang = "es_ES" - mock_request.httprequest = Mock() - - old_draft = self.env["sale.order"].create( - { - "partner_id": self.partner.id, - "group_order_id": self.group_order.id, - "state": "draft", - "pickup_date": date.today() - timedelta(days=7), - "order_line": [ - ( - 0, - 0, - { - "product_id": self.product.id, - "product_uom_qty": 1, - "price_unit": 20.0, - }, - ) - ], - } - ) - - data = { - "order_id": self.group_order.id, - "items": [ - { - "product_id": self.product.id, - "quantity": 4, - "product_price": 20.0, - } - ], - "is_delivery": False, - } - - mock_request.httprequest.data = json.dumps(data).encode("utf-8") - - response = self.controller.confirm_eskaera() - response_data = json.loads(response.data.decode("utf-8")) - - self.assertTrue(response_data.get("success")) - self.assertNotEqual(response_data["sale_order_id"], old_draft.id) - - old_draft.invalidate_recordset() - self.assertEqual(old_draft.state, "draft") - self.assertEqual(old_draft.order_line[0].product_uom_qty, 1)