diff --git a/website_sale_aplicoop/static/src/js/website_sale.js b/website_sale_aplicoop/static/src/js/website_sale.js index ad4a311..5326427 100644 --- a/website_sale_aplicoop/static/src/js/website_sale.js +++ b/website_sale_aplicoop/static/src/js/website_sale.js @@ -806,6 +806,30 @@ }); } + // On checkout page: apply sessionStorage delivery preference to checkbox. + // The shop-page toggle may have stored a "false" preference even though + // the checkbox is checked by default in the template. + var checkoutCheckbox = document.getElementById("home-delivery-checkbox"); + if (checkoutCheckbox) { + var storedDeliveryPref = sessionStorage.getItem( + "eskaera_is_delivery_" + self.orderId + ); + if (storedDeliveryPref !== null) { + checkoutCheckbox.checked = storedDeliveryPref === "true"; + console.log( + "[CHECKOUT] Restored delivery checkbox from sessionStorage:", + checkoutCheckbox.checked + ); + } + // Sync sessionStorage when user manually changes the checkbox + checkoutCheckbox.addEventListener("change", function () { + sessionStorage.setItem( + "eskaera_is_delivery_" + self.orderId, + this.checked ? "true" : "false" + ); + }); + } + // Button to reload from draft (in My Cart header - cart pages) var reloadCartBtn = document.getElementById("reload-cart-btn"); console.log("[_attachEventListeners] reload-cart-btn found:", !!reloadCartBtn); @@ -830,6 +854,28 @@ }); } + // Home-delivery toggle button (shop sidebar) + var homeDeliveryBtn = document.getElementById("home-delivery-btn"); + if (homeDeliveryBtn) { + // Restore persisted preference on page load + var storedDelivery = sessionStorage.getItem( + "eskaera_is_delivery_" + self.orderId + ); + if (storedDelivery === "true") { + homeDeliveryBtn.classList.add("active"); + } + homeDeliveryBtn.addEventListener("click", function (e) { + e.preventDefault(); + var isNowActive = !homeDeliveryBtn.classList.contains("active"); + homeDeliveryBtn.classList.toggle("active", isNowActive); + sessionStorage.setItem( + "eskaera_is_delivery_" + self.orderId, + isNowActive ? "true" : "false" + ); + console.log("[HOME_DELIVERY_BTN] toggled to:", isNowActive); + }); + } + // Buttons to clear cart (header + footer) var clearCartBtns = [ document.getElementById("clear-cart-btn"), @@ -1553,6 +1599,17 @@ merge_action: "replace", }; + // Include delivery preference: read from toggle button state, + // falling back to the page-level home-delivery-enabled attribute. + var deliveryBtn = document.getElementById("home-delivery-btn"); + if (deliveryBtn) { + orderData.is_delivery = deliveryBtn.classList.contains("active"); + } else { + var wrap = document.getElementById("wrap"); + var enabledAttr = wrap && wrap.getAttribute("data-home-delivery-enabled"); + orderData.is_delivery = enabledAttr === "true"; + } + var xhr = new XMLHttpRequest(); xhr.open("POST", "/eskaera/save-order", true); xhr.setRequestHeader("Content-Type", "application/json"); diff --git a/website_sale_aplicoop/tests/__init__.py b/website_sale_aplicoop/tests/__init__.py index d423f33..a8db682 100644 --- a/website_sale_aplicoop/tests/__init__.py +++ b/website_sale_aplicoop/tests/__init__.py @@ -15,3 +15,4 @@ from . import test_pricing_with_pricelist # noqa: F401 from . import test_portal_sale_order_creation # noqa: F401 from . import test_cron_picking_batch # noqa: F401 from . import test_group_order_status_endpoint # noqa: F401 +from . import test_home_delivery # noqa: F401 diff --git a/website_sale_aplicoop/tests/test_home_delivery.py b/website_sale_aplicoop/tests/test_home_delivery.py new file mode 100644 index 0000000..b2b288f --- /dev/null +++ b/website_sale_aplicoop/tests/test_home_delivery.py @@ -0,0 +1,232 @@ +# Copyright 2026 Criptomart +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl) + +"""Tests for home_delivery auto-computation on group.order. + +Covers: +- group.order.home_delivery derives from delivery_product_id +- Recomputes correctly when product is set/cleared +- sale.order.home_delivery is set when effective delivery is requested +- _get_effective_delivery_context honours the computed flag +""" + +from datetime import timedelta + +from odoo import fields +from odoo.tests.common import TransactionCase + +from ..controllers.website_sale import AplicoopWebsiteSale + + +class TestHomeDeliveryCompute(TransactionCase): + """Unit tests for group.order.home_delivery computed field.""" + + def setUp(self): + super().setUp() + + self.group = self.env["res.partner"].create( + { + "name": "Delivery Test Group", + "is_company": True, + "is_group": True, + } + ) + + self.delivery_product = self.env["product.product"].create( + { + "name": "Home Delivery Service", + "type": "service", + "list_price": 3.5, + } + ) + + self._base_vals = { + "name": "Delivery Test Order", + "group_ids": [(6, 0, [self.group.id])], + "type": "regular", + "period": "weekly", + "pickup_day": "3", + "cutoff_day": "1", + "start_date": fields.Date.today(), + "end_date": fields.Date.today() + timedelta(days=14), + } + + def _make_group_order(self, **extra): + vals = dict(self._base_vals, **extra) + return self.env["group.order"].create(vals) + + # --- compute correctness --- + + def test_home_delivery_true_when_product_set(self): + order = self._make_group_order(delivery_product_id=self.delivery_product.id) + self.assertTrue( + order.home_delivery, + "home_delivery should be True when delivery_product_id is set", + ) + + def test_home_delivery_false_without_product(self): + order = self._make_group_order() + self.assertFalse( + order.home_delivery, + "home_delivery should be False when no delivery_product_id", + ) + + def test_home_delivery_recomputes_when_product_set(self): + order = self._make_group_order() + self.assertFalse(order.home_delivery) + + order.delivery_product_id = self.delivery_product.id + self.assertTrue( + order.home_delivery, + "home_delivery should recompute to True after setting delivery_product_id", + ) + + def test_home_delivery_recomputes_when_product_cleared(self): + order = self._make_group_order(delivery_product_id=self.delivery_product.id) + self.assertTrue(order.home_delivery) + + order.delivery_product_id = False + self.assertFalse( + order.home_delivery, + "home_delivery should recompute to False after clearing delivery_product_id", + ) + + def test_direct_write_to_home_delivery_is_overridden_by_compute(self): + """Writing home_delivery directly must not stick — compute wins.""" + order = self._make_group_order() + # Attempt to set home_delivery=True without a product + order.home_delivery = True + order.invalidate_recordset() + self.assertFalse( + order.home_delivery, + "Computed field must override any direct write without a delivery product", + ) + + +class TestHomeDeliveryEffectiveContext(TransactionCase): + """Tests for _get_effective_delivery_context and sale.order.home_delivery.""" + + def setUp(self): + super().setUp() + + self.group = self.env["res.partner"].create( + { + "name": "Ctx Test Group", + "is_company": True, + "is_group": True, + } + ) + self.member_partner = self.env["res.partner"].create( + {"name": "Ctx Member", "email": "ctx@test.com"} + ) + self.group.member_ids = [(4, self.member_partner.id)] + + self.user = self.env["res.users"].create( + { + "name": "Ctx User", + "login": "ctx_user@test.com", + "email": "ctx_user@test.com", + "partner_id": self.member_partner.id, + } + ) + + self.delivery_product = self.env["product.product"].create( + {"name": "Delivery Svc", "type": "service", "list_price": 2.0} + ) + + today = fields.Date.today() + self.group_order_with_delivery = self.env["group.order"].create( + { + "name": "GO With Delivery", + "group_ids": [(6, 0, [self.group.id])], + "type": "regular", + "period": "weekly", + "pickup_day": "4", + "cutoff_day": "1", + "start_date": today, + "end_date": today + timedelta(days=14), + "delivery_product_id": self.delivery_product.id, + } + ) + self.group_order_with_delivery.action_open() + + self.group_order_no_delivery = self.env["group.order"].create( + { + "name": "GO No Delivery", + "group_ids": [(6, 0, [self.group.id])], + "type": "regular", + "period": "weekly", + "pickup_day": "4", + "cutoff_day": "1", + "start_date": today, + "end_date": today + timedelta(days=14), + } + ) + self.group_order_no_delivery.action_open() + + self.controller = AplicoopWebsiteSale() + + # --- _get_effective_delivery_context --- + + def test_delivery_enabled_when_product_configured_and_requested(self): + effective, _ = self.controller._get_effective_delivery_context( + self.group_order_with_delivery, is_delivery=True + ) + self.assertTrue( + effective, + "Delivery should be effective when group order has delivery product and user requests it", + ) + + def test_delivery_disabled_when_product_configured_but_not_requested(self): + effective, _ = self.controller._get_effective_delivery_context( + self.group_order_with_delivery, is_delivery=False + ) + self.assertFalse( + effective, + "Delivery should not be effective when user did not request it", + ) + + def test_delivery_disabled_when_no_product_even_if_requested(self): + effective, _ = self.controller._get_effective_delivery_context( + self.group_order_no_delivery, is_delivery=True + ) + self.assertFalse( + effective, + "Delivery should not be effective when group order has no delivery product", + ) + + # --- sale.order.home_delivery propagation --- + + def test_sale_order_home_delivery_set_when_delivery_requested(self): + """sale.order.home_delivery must be True when group order has product and user requests delivery.""" + order_vals = { + "partner_id": self.member_partner.id, + "group_order_id": self.group_order_with_delivery.id, + "pickup_day": self.group_order_with_delivery.pickup_day, + "pickup_date": self.group_order_with_delivery.pickup_date, + "home_delivery": True, + "order_line": [], + "state": "draft", + } + sale_order = self.env["sale.order"].sudo().create(order_vals) + self.assertTrue( + sale_order.home_delivery, + "sale.order.home_delivery should be True when group order has delivery enabled", + ) + + def test_sale_order_home_delivery_false_without_group_order_product(self): + """sale.order.home_delivery must be False when group order has no delivery product.""" + order_vals = { + "partner_id": self.member_partner.id, + "group_order_id": self.group_order_no_delivery.id, + "pickup_day": self.group_order_no_delivery.pickup_day, + "pickup_date": self.group_order_no_delivery.pickup_date, + "home_delivery": self.group_order_no_delivery.home_delivery, + "order_line": [], + "state": "draft", + } + sale_order = self.env["sale.order"].sudo().create(order_vals) + self.assertFalse( + sale_order.home_delivery, + "sale.order.home_delivery should be False when group order has no delivery product", + ) diff --git a/website_sale_aplicoop/tests/test_phase3_confirm_eskaera.py b/website_sale_aplicoop/tests/test_phase3_confirm_eskaera.py index 20fcb06..985602d 100644 --- a/website_sale_aplicoop/tests/test_phase3_confirm_eskaera.py +++ b/website_sale_aplicoop/tests/test_phase3_confirm_eskaera.py @@ -694,6 +694,16 @@ class TestConfirmEskaera_Integration(TransactionCase): } ) + # Delivery service product — home_delivery on group.order is computed + # from delivery_product_id, so we must set the product to enable delivery. + self.delivery_product = self.env["product.product"].create( + { + "name": "Integration Delivery Svc", + "type": "service", + "list_price": 0.0, + } + ) + # Create test group order self.group_order = self.env["group.order"].create( { @@ -704,7 +714,7 @@ class TestConfirmEskaera_Integration(TransactionCase): "end_date": date.today() + timedelta(days=7), "cutoff_day": "3", "pickup_day": "5", - "home_delivery": True, + "delivery_product_id": self.delivery_product.id, } ) diff --git a/website_sale_aplicoop/tests/test_save_order_endpoints.py b/website_sale_aplicoop/tests/test_save_order_endpoints.py index fe5cfbc..314ef9c 100644 --- a/website_sale_aplicoop/tests/test_save_order_endpoints.py +++ b/website_sale_aplicoop/tests/test_save_order_endpoints.py @@ -96,6 +96,15 @@ class TestSaveOrderEndpoints(TransactionCase): } ) + # Delivery service product (required to enable home_delivery on group.order) + self.delivery_product = self.env["product.product"].create( + { + "name": "Home Delivery Service", + "type": "service", + "list_price": 3.0, + } + ) + # Associate product with group order self.group_order.product_ids = [(4, self.product.id)] @@ -206,7 +215,8 @@ class TestSaveOrderEndpoints(TransactionCase): def test_save_eskaera_draft_propagates_home_delivery(self): """Test that save_eskaera_draft() propagates home_delivery correctly.""" - # Create a group order with home_delivery=True + # Create a group order with home delivery enabled via delivery_product_id + # (home_delivery is a computed field derived from delivery_product_id) group_order_home = self.env["group.order"].create( { "name": "Test Group Order with Home Delivery", @@ -217,7 +227,7 @@ class TestSaveOrderEndpoints(TransactionCase): "period": "weekly", "pickup_day": "3", "pickup_date": datetime.now().date() + timedelta(days=3), - "home_delivery": True, # Enable home delivery + "delivery_product_id": self.delivery_product.id, "cutoff_day": "0", } ) @@ -362,7 +372,7 @@ class TestSaveOrderEndpoints(TransactionCase): "period": "weekly", "pickup_day": "5", "pickup_date": datetime.now().date() + timedelta(days=12), - "home_delivery": True, + "delivery_product_id": self.delivery_product.id, "cutoff_day": "0", } ) diff --git a/website_sale_aplicoop/views/website_templates.xml b/website_sale_aplicoop/views/website_templates.xml index f34a93a..e393ed0 100644 --- a/website_sale_aplicoop/views/website_templates.xml +++ b/website_sale_aplicoop/views/website_templates.xml @@ -503,16 +503,17 @@
+
- +