addons-cm/website_sale_aplicoop/CHANGELOG.md
GitHub Copilot 23edee6154 [IMP] website_sale_aplicoop: readable slug in the group order URL
Group order pages were published under their database id (`/eskaera/1`), which
says nothing to the member opening the link. `group.order` gains a `slug` field
and the public pages move to `/eskaera/<slug>` (e.g. `/eskaera/escola-fructuos`).

The slug is generated from the order name on creation, is unique, and can be
edited under the name on the form; emptying it regenerates it from the current
name. Values that would make the order unreachable are rejected: a plain number
(the legacy numeric URLs win that match) and the static routes served under
`/eskaera/` (`labels`, `save-order`, `i18n`, ...).

`/eskaera/<id>` and `/eskaera/<id>/checkout` are kept as redirects to their slug
URL, so the links already shared with members keep working. The AJAX endpoints
(`load-page`, `save-order`, `confirm`, ...) stay numeric: they never show up in
the address bar. Consequently the frontend now reads the order id from the
`data-order-id` attribute only, as the URL no longer carries it.

The post-migration script fills the slug of the orders that already existed.

Renaming the `/eskaera` prefix itself (`/escolas` for a schools deployment)
needs no code: a *308 Redirect / Rewrite* rule per public route in Website >
Configuration > Redirects serves the pages on the new prefix, rewrites the
links in the templates and redirects the old URLs, per website. Documented in
`readme/CONFIGURE.rst`.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-11 16:49:54 +02:00

318 lines
15 KiB
Markdown

# Changelog - Website Sale Aplicoop
## [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 `aplicoopShop``groupOrderShop` 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.)