addons-cm/website_sale_aplicoop/CHANGELOG.md
GitHub Copilot e625b0c2f3 [IMP] website_sale_aplicoop: pay on the checkout page, not a step later
The separate /eskaera/<slug>/payment step is gone. Members review the
summary, choose home delivery and pick a payment method on the checkout,
in one screen; the old URL redirects there so bookmarks and sessions that
were mid-flow do not hit a 404.

The checkout now renders the member's draft sale.order instead of the
localStorage cart. That is what fixes the products appearing "out of
nowhere" between the two pages: the summary was a snapshot of localStorage
taken at page load, and `_autoLoadDraftOnInit` then pulled the draft back
into localStorage without re-rendering. Deleting a product in the shop
removed it from the cart but left the line on the draft, so the autoload
resurrected it, the confirm button sent it back, and it only became
visible one page later. The checkout no longer auto-loads the draft — it
renders it, and what it shows is what the payment form charges.

"Proceed to Checkout" pushes the cart to that draft before navigating.
Saving is idempotent: `_merge_or_replace_draft` reuses the cycle's draft
and, through the new `_draft_matches_lines`, rewrites `order_line` only
when the lines actually differ — replacing them unlinks and recreates
every one of them, which is pure churn when nothing changed.

The home delivery checkbox goes through the new /eskaera/set-home-delivery
so the delivery line moves on the order itself. Writing only to
localStorage would have changed the summary and left the amount alone,
which with online payment on is the amount being charged.

Also fixes the confirmation notice nobody ever saw: saving answered with
the payment step URL and the frontend followed it immediately, destroying
the toast in the same tick. Saving no longer navigates; the caller decides
whether it is staying or moving on.

Along the way: checkout_labels.js and the eskaera_checkout_summary /
eskaera_payment templates are removed, superseded by the server-rendered
summary and checkout, and the stale sessionStorage delivery preference no
longer overrides the checkbox the order just rendered.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-17 19:14:49 +02:00

16 KiB

Changelog - Website Sale Aplicoop

[18.0.1.16.0] - 2026-08-17

Changed

  • Payment moved into the checkout: the separate /eskaera/<slug>/payment step is gone. Members review the summary, pick home delivery and choose a payment method on /eskaera/<slug>/checkout, in one screen. The old URL redirects there so bookmarks and in-flight sessions keep working.
  • The checkout summary is server-side: it renders the draft sale.order instead of the localStorage cart, so what is shown is what gets charged. "Proceed to Checkout" pushes the cart to that draft before navigating, reusing the cycle's draft and rewriting its lines only when they differ.
  • The home delivery checkbox on the checkout writes to the order through /eskaera/set-home-delivery, so the total and the payment amount follow it.

Fixed

  • The confirmation notice after saving a draft was destroyed by the immediate redirect to the payment step, so members saw nothing at all. Saving no longer navigates.
  • Products the member had deleted reappeared between the checkout and the payment page. The checkout auto-loaded the draft into localStorage after the summary had already rendered, resurrecting deleted lines, which were then sent back on confirm. The checkout no longer auto-loads the draft — it renders it.

Removed

  • checkout_labels.js and the eskaera_checkout_summary / eskaera_payment templates, superseded by the server-rendered summary and checkout.

[18.0.1.13.0] - 2026-08-11

Added

  • Readable URLs for group orders: group.order gains a slug field and the public pages move from /eskaera/<id> to /eskaera/<slug> (e.g. /eskaera/escola-fructuos). The slug is generated from the order name on creation, is editable on the form (empty it to regenerate it from the name), is unique, and rejects values that would collide with the shop's own routes (labels, save-order, …) or with a plain number. A post-migration script fills the slug of the orders that already existed.

Changed

  • /eskaera/<id> and /eskaera/<id>/checkout are kept as redirects to their slug URL, so links already shared with members keep working. The AJAX endpoints (/eskaera/<id>/load-page, /eskaera/save-order, …) stay numeric.
  • The frontend reads the order id from the data-order-id attribute only; it can no longer be recovered from the URL, which now carries the slug.

[18.0.1.12.0] - 2026-08-06

Removed

  • Standard cart restriction extracted to website_sale_disable_cart: the cart UI removal (header cart link, add-to-cart buttons) and the /shop/cart* route redirects no longer live in this module, so installing Aplicoop leaves the standard website_sale shop untouched. Sites that want the previous behaviour must install website_sale_disable_cart and set its Cart Redirect URL to /eskaera. The new module also fixes the route paths (Odoo 18 uses /shop/cart/quantity, not /shop/cart_quantity), covers the 5 header styles that were missing (boxed, sidebar, sales two/three/four — the header cart link was still visible on those) and overrides the standard endpoints instead of registering duplicate ones. Upgrade order matters: update this module first (so its old templates are dropped) and install website_sale_disable_cart in a second Odoo run — both sets of templates use the same XPaths and obsolete records are only cleaned up at the end of a run, so a single combined run fails.

[18.0.1.11.0] - 2026-07-15

Added

  • Non-weekly cycle automation: one-time (special/promotional), biweekly and monthly group orders now follow the same cron confirmation flow as weekly orders (confirm linked sale orders + batch their pickings once the cycle cutoff passes).
  • Biweekly/monthly recurrence: these periods keep the weekday scheme (cutoff_day/pickup_day) but on a recurrence grid anchored at start_date (creation date as fallback): cutoffs advance every 14 days / 1 month (snapped to cutoff_day), with automatic catch-up if the cron was down. Previously they behaved as weekly.
  • One-time orders: their single cycle is driven by end_date (cutoff_date = end_date); once it passes, the cron confirms, batches and closes the group order automatically.

Changed

  • end_date keeps its "permanent if empty" meaning for all recurring periods; it only closes the cycle for one-time orders.
  • Draft-cart lookup window on the website is now period-aware (7/14 days, 1 month, or the full order lifetime for one-time orders) instead of always 6 days.

[18.0.1.9.0] - 2026-05-20

Added

  • Category sequence: New sequence field on product.category for controlling product display order on the web shop. Migration adds the column with default 10.
  • Stock availability check on history orders: When loading a historical order, products are validated against current stock (virtual_available). Out-of-stock items are flagged before adding to cart.

Changed

  • Cart disabled / redirected to /eskaera: Standard website_sale cart is fully hidden — header cart icon removed, add-to-cart buttons removed from standard shop, /cart and /shop routes redirect to /eskaera. Customers interact exclusively through the group order flow.
  • Product card UI overhaul: Improved responsive layout, placeholder image for products without photo, better accessibility markup, and professional styling for mobile.
  • Stock check uses virtual_available: Replaces qty_available so forecasted quantity (including incoming moves) is considered.

Fixed

  • Lint fixes: exception chaining (raise ... from exc), unused imports/vars, disable attribute-string-redundant annotations.

[18.0.1.8.0] - 2026-04-08

Added

  • Clear cart button: New button in the sidebar to empty the current group order cart in one click (eskaera_clear_cart route).

Changed

  • Portal access restricted to consumer group: Portal users can only see group orders belonging to their consumer group. Security rule rule_group_order_company_read updated with user.share guard.
  • Controller filtering by consumer group: eskaera_list and eskaera_shop routes filter orders by the logged-in user's consumer group, not just by company.

Fixed

  • Stale cart invalidation: carts linked to closed/cancelled group orders are cleared on next access.
  • Home delivery draft flow: fixed state transition for home delivery orders.
  • Auto-confirm sale.order on cutoff day: fixed off-by-one (> vs >=) in cutoff guard so orders on the cutoff day are correctly confirmed.
  • Pickup dates frozen after cron confirm: cron no longer overwrites pickup dates on already-confirmed orders.
  • Cron hardened against missing/invalid group order configurations.
  • Cart layout: moved cart sidebar before product grid.
  • i18n: added missing ES/EU translations for checkout, cart labels, Save Draft button, and weekday strings. Removed legacy week_draft translation keys.

[18.0.1.7.0] - 2026-03-06

Added

  • Automatic batch picking creation: After the cutoff cron confirms sale orders, picking batches are automatically created grouped by consumer group and pickup date.
  • Consumer group propagation: consumer_group_id is now propagated directly from the group.order to linked sale.order records, eliminating manual assignment.
  • Out-of-stock blocking: Add-to-cart button is hard-disabled for out-of-stock products. allow_out_of_stock_order flag on product is respected.

Changed

  • Cron auto-confirm sale orders: Daily cron (_cron_confirm_group_orders) now confirms linked sale orders on the cutoff date instead of requiring manual action.
  • Order card meta extracted: Order card metadata (dates, state badge) moved to a reusable _eskaera_order_card_meta template for cleaner rendering.
  • Delivery toggle (reparto/envío a domicilio) wired directly from the cart sidebar.
  • Pricing alignment: theoretical price and displayed price reconciled for orders with home delivery.

Fixed

  • Fixed broken aplicoopShopgroupOrderShop JS class reference.
  • Ensured add-to-cart event listeners are re-attached after infinite scroll loads new products.
  • i18n: translated weekday names (ES/EU).

[18.0.1.6.0] - 2026-02-22

Added

  • Category Blacklist Feature: Category-based exclusion system for group orders
    • New field: excluded_category_ids (Many2many to product.category)
    • Recursive exclusion: Excludes products in selected categories AND all subcategories
    • Blacklist has absolute priority over all inclusion sources
    • Helper method: get_all_excluded_descendants() for recursive category tree traversal
    • Comprehensive test suite in test_product_discovery.py (TestCategoryBlacklist class with 9 tests)

Changed

  • Product Discovery Logic: Extended to filter by category blacklist

    • _get_products_for_group_order() now applies excluded_category_ids filter recursively
    • Products in excluded categories and their subcategories are filtered out
    • _compute_available_products_count() now depends on excluded_category_ids
    • Detailed logging for excluded categories and affected products
  • UI Updates: "Productos Excluidos" section now includes three blacklist types:

    • excluded_supplier_ids: Blacklist suppliers
    • excluded_category_ids: Blacklist categories (recursive)
    • excluded_product_ids: Blacklist specific products

Technical Details

  • New M2M relation: group_order_excluded_category_rel
  • Recursive logic: Walks category tree to find all descendants
  • Filter logic: products.filtered(lambda p: p.categ_id not in all_excluded_categories)
  • Works in combination with product and supplier blacklists (all filters apply)

Use Case

  • Admin wants to exclude all products in a category and its subcategories
  • Example: Exclude "Fresh Produce" → automatically excludes "Fruits", "Vegetables", etc.
  • Add parent category to inclusion → add problematic subcategory to exclusion
  • Result: Fine-grained control over product catalog with minimal configuration

[18.0.1.5.0] - 2026-02-22

Added

  • Supplier Blacklist Feature: New exclusion system by supplier for group orders
    • New field: excluded_supplier_ids (Many2many to res.partner)
    • Filters products by main_seller_id (from product_main_seller addon)
    • Blacklist has absolute priority over all inclusion sources
    • Products whose main supplier is blacklisted never appear
    • Comprehensive test suite in test_product_discovery.py (TestSupplierBlacklist class with 9 tests)

Changed

  • Product Discovery Logic: Extended to filter by supplier blacklist

    • _get_products_for_group_order() now applies excluded_supplier_ids filter
    • Products with main_seller_id in excluded_supplier_ids are filtered out
    • _compute_available_products_count() now depends on excluded_supplier_ids
    • Detailed logging for excluded suppliers and affected products
  • UI Updates: "Productos Excluidos" section now includes both:

    • excluded_supplier_ids: Blacklist suppliers
    • excluded_product_ids: Blacklist specific products

Technical Details

  • New M2M relation: group_order_excluded_supplier_rel
  • Filter logic: products.filtered(lambda p: p.product_tmpl_id.main_seller_id not in excluded_supplier_ids)
  • Works in combination with product blacklist (both filters apply)
  • Uses main_seller_id from product_main_seller addon (NOT default_supplier_id)

Use Case

  • Admin wants to exclude all products from a specific supplier (e.g., temporary unavailability)
  • Add category with 100 products → add problematic supplier to excluded_supplier_ids
  • Result: All products from that supplier are excluded, even if directly included
  • Combined workflow: Category inclusion + supplier exclusion + individual product exclusion

[18.0.1.4.0] - 2026-02-22

Added

  • Product Blacklist Feature: New exclusion system for group orders
    • New field: excluded_product_ids (Many2many to product.product)
    • Blacklist has absolute priority over all inclusion sources (product_ids, category_ids, supplier_ids)
    • Model method: Updated _get_products_for_group_order() with blacklist filter
    • Comprehensive test suite in test_product_discovery.py (TestProductBlacklist class)

Changed

  • UI Improvements: Renamed "Associations" section to "Catálogo de Productos" for better user clarity

    • New subsection: "Productos Incluidos" (whitelist: suppliers, categories, direct products)
    • New subsection: "Productos Excluidos" (blacklist: explicit exclusions)
    • Updated help texts for all inclusion fields
    • Complete Spanish and Euskera translations
  • Product Discovery Logic:

    • _get_products_for_group_order() now applies excluded_product_ids filter at the end
    • Products in blacklist never appear, regardless of inclusion source
    • _compute_available_products_count() now depends on excluded_product_ids
    • Detailed logging for excluded product count

Technical Details

  • New M2M relation: group_order_excluded_product_rel (separate from whitelist relations)
  • Blacklist filter uses set subtraction: products = products - order.excluded_product_ids
  • All tests validate absolute priority: direct products, category products, supplier products, and multi-source products all respect blacklist

Use Case

  • Admin selects a category with 100 products → adds to category_ids
  • Admin identifies 5 unwanted products → adds to excluded_product_ids
  • Result: 95 products available in the order
  • Workflow: Bulk inclusion via categories/suppliers + fine-grained exclusion via blacklist

[18.0.1.3.0] - 2026-02-16

Added

  • Lazy Loading Feature: Configurable product pagination for significantly faster page loads

    • New Settings: Enable Lazy Loading, Products Per Page
    • New endpoint: GET /eskaera/<order_id>/load-page?page=N
    • JavaScript method: _attachLoadMoreListener()
    • Model method: group_order._get_products_paginated()
  • Configuration Parameters:

    • website_sale_aplicoop.lazy_loading_enabled (Boolean, default: True)
    • website_sale_aplicoop.products_per_page (Integer, default: 20)
  • Frontend Components:

    • New template: eskaera_shop_products (reusable for initial page + AJAX)
    • Load More button with pagination controls
    • Spinner during AJAX load ("Loading..." state)
    • Event listener re-attachment for dynamically loaded products
  • Documentation:

    • Complete lazy loading guide: docs/LAZY_LOADING.md
    • Configuration examples
    • Troubleshooting section
    • Performance metrics

Changed

  • Template eskaera_shop:

    • Products grid now has id="products-grid"
    • Calls reusable eskaera_shop_products template
    • Conditional "Load More" button display
  • JavaScript website_sale.js:

    • _attachEventListeners() now calls _attachLoadMoreListener()
    • Re-attaches listeners after AJAX loads new products
  • README.md:

    • Added lazy loading feature to features list
    • Added version 18.0.1.3.0 to changelog

Performance Impact

  • Initial page load: 10-20s → 500-800ms (20x faster)
  • Product DOM size: 1000 elements → 20 elements (initial)
  • Subsequent page loads: 200-400ms via AJAX
  • Price calculation: Only for visible products (reduced from 1000+ to 20)

Technical Details

  • Zero-impact if lazy loading disabled
  • Transparent pagination (no URL changes)
  • Maintains cart synchronization
  • Compatible with existing search/filter
  • No changes to pricing logic or validation

[18.0.1.2.0] - 2026-02-02

Added

  • Improved UI elements in cart and checkout

Fixed

  • Pickup date calculation (was adding extra week)
  • Delivery date display on order pages

Changed

  • Cart styling: 2x text size, larger icons
  • Checkout button: Enhanced visibility

[18.0.1.0.0] - 2024-12-20

Added

  • Initial release of Website Sale Aplicoop
  • Group order management system
  • Multi-language support (ES, PT, GL, CA, EU, FR, IT)
  • Member management and tracking
  • Order state machine (draft → confirmed → collected → invoiced → completed)
  • Separate shopping carts per group order
  • Cutoff and pickup date validation
  • Integration with OCA ecosystem (pricing, taxes, etc.)