Commit graph

43 commits

Author SHA1 Message Date
GitHub Copilot
ef1283be7c [REF] stock_picking_batch_collect: rename and drop the aplicoop dependency
"custom" said nothing about what the module does. It is really about collecting
goods into baskets: the extra detailed-operation columns, the is_collected flag,
the Product Summary tab, the per-company validation restrictions and the
Basket Assembly operator view all serve that one job. Rename it accordingly.

Invert the dependency while at it. A generic warehouse addon was dragging in an
entire eCommerce application, and the whole coupling was a single field:
stock.move.line.home_delivery, related to picking_id.home_delivery. Everything
else was already duck-typed. website_sale_aplicoop now depends on this module
and injects its own consumer group columns into these views.

This removes duplicated logic rather than relocating it: stock.picking
.batch_consumer_group_id re-derived from sale_id a value aplicoop already stored
as stock.picking.consumer_group_id, and the duplicate carried no @api.depends,
so it never recomputed reliably. The batch transfers list now shows the stored
field, which is sortable and groupable.

The two aplicoop tests that probed information_schema for the res_company
batch_* columns can drop that guard: a real dependency guarantees them.

Renaming an addon is not something a migrations/ script can do, since a renamed
addon is a brand new module to Odoo and its migration scripts never run. A
pre_init_hook does it instead: it fires on install after the Python is imported
but before registry.load(), which is the window where remapping ir_model_data
makes Odoo reuse the existing tables and columns. is_collected, the summary line
table and the company settings all survive untouched.

Two details the hook has to get right:

- ir_model_constraint.module and ir_model_relation.module are integer FKs with
  ON DELETE CASCADE, so they must be repointed before the old module row is
  deleted or the bookkeeping goes with it.
- Deleting an ir_model_data row does not cascade to the record it points at.
  Artifacts handed over to aplicoop only need the xmlid dropped, but artifacts
  that disappear need the record deleted too, or the field survives as an orphan
  manual field and the view as a custom view referencing it.

Verified against a restored copy of the dev database: 50 xmlids moved, 19 summary
lines and 5 collected move lines preserved, no orphans, both test suites green,
and the module installs cleanly on a database without website_sale_aplicoop.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-27 14:26:47 +02:00
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
GitHub Copilot
f81c1ca8e7 [IMP] website_sale_aplicoop: surface the tax display setting on the Eskaera page
Pricing now follows website_sale, which includes the website's `Display
Product Prices`. That setting defaults to tax-excluded in 18.0, so a website
nobody configured shows Eskaera prices without VAT -- correct behaviour, but
easy to read as a bug when the members are consumers and expect the price they
will pay.

The setting is a related field website_sale already exposes, so it now appears
in the Eskaera block too, right where the co-op switches the feature on, rather
than only under eCommerce.

Tests cover both values on the shop and the listing paths, so the tax-excluded
case is a decision on record instead of a surprise.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-17 19:13:57 +02:00
GitHub Copilot
5a2f5d120f [ADD] website_sale_aplicoop: serve Eskaera per website
A co-op can run the plain shop on one website and the group orders on another,
but routes are registered process-wide: every /eskaera page answered on every
website of the database, and Odoo had already copied the Eskaera menu to all of
them.

`website.eskaera_enabled` decides which websites serve it, on by default so
installing changes nothing. It reaches the settings screen through `website_id`,
so it follows the website selector there. Where it is off the routes raise
NotFound and the menu is hidden.

The menu is hidden rather than deleted, by extending `_compute_visible`. That
keeps the record and any manual rename or reordering, so switching the feature
back on restores it as it was.

Note that a JSON route reports the 404 inside the JSON-RPC payload and still
answers HTTP 200; that is the transport, not a hole in the guard, and a test
pins it so the next reader does not take it for one.

The three remaining settings (lazy loading, products per page, low stock
threshold) are still `config_parameter`, so they stay global to the database.
Two websites share their values.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-17 17:31:03 +02:00
GitHub Copilot
817ff31d39 [REF] website_sale_aplicoop: price through website_sale instead of privately
Eskaera carried its own pricing path: a `website_sale_aplicoop.pricelist_id`
setting resolved inside the Eskaera controllers, plus a local reimplementation
of the tax and discount maths. The pricelist part meant /shop and Eskaera could
quote different prices on the same website, and the maths part had drifted from
the original it was copied from.

Pricing is now the standard one. The setting is gone (a migration drops the
parameter, which would otherwise linger looking like live configuration) and
`_resolve_pricelist` is `request.website.pricelist_id`. Scoping a pricelist to
a website is already core's job through `product.pricelist.website_id`, so
running the plain shop on one website and the co-op on another needs no code
here.

Taxes are applied by `product.template._apply_taxes_to_price`, which fixes a
real defect: it calls `_get_tax_included_unit_price_from_price` first, and this
module did not. With a fiscal position remapping a tax-included tax, a product
at 121 (100 + 21%) was displayed at 121 instead of 110, because the price was
handed to the mapped tax as if it were already that tax's gross amount. The
helper is a no-op without a remapping, so ordinary pricing is untouched. It
also means the website's `show_line_subtotals_tax_selection` is respected
rather than overridden with a hardcoded tax-included display.

Listing a page now costs one `_compute_price_rule` call instead of one per
product, which matters on the lazy-loading path.

Two smaller things found on the way. `_get_product_price_rule` was being passed
`target_currency=`, which is not one of its arguments: it fell into **kwargs
and was ignored, so the conversion never happened. And `_compute_price_info`
resolved `product.product_variant_ids[0]`, but it receives variants, so a
multi-variant template was priced from its first variant rather than the one
asked for.

The delivery display price loses its hardcoded 5.74 fallback and reuses the
same helpers as everything else.

Kept local, because none of it is pricing: the /Kg and /L suffixes, the 0.1
quantity step for bulk goods, the base unit price and the supplier name.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-17 17:28:51 +02:00
GitHub Copilot
3e4bd5e5db [ADD] website_sale_aplicoop: restore the test coverage lost with the dead code
The dead-code cleanup dropped 11 test files that were never wired into
tests/__init__.py. Reviewing what they covered turned up real holes, so the
worthwhile ones come back, rewritten against the current schema.

Blacklists were the serious gap: product, supplier and category exclusions
have absolute priority over product discovery and nothing exercised them.
The old file had two separate defects. Its supplier fixtures wrote
`main_seller_id` directly, but product_main_seller computes that field from
`variant_seller_ids`, so the compute reset it to False and the blacklist had
nothing to exclude; they now create real supplierinfo records. Worse, four
whole classes asserted against `group_order.product_ids` -- the m2m *input* --
instead of the discovery result, so they set `category_ids` and then checked a
field they never touched. Those go through `_get_products_for_group_order`
now, and three tests that had no assertions at all got some.

The remaining three failures were test bugs too, all Odoo 17->18 leftovers:

* Date cases assumed `pickup_date` derives from `start_date`. The chain is
  cutoff -> pickup -> delivery, and a recurring order whose start date has
  passed rolls forward to the current cycle, so a 2024 order has no 2024
  pickup. The new file anchors on future dates and finds the next 29 February
  dynamically, with a class documenting the roll-forward itself.
* `/eskaera/labels` is `type="json"`; the old test hit it with a plain GET and
  read the resulting 400 as a bug. It is called over JSON-RPC now, and a test
  pins the 400 so nobody repeats it. Also `uom.uom.categ` -> `uom.category`.
* `price_include` is computed in 18.0, so fixtures must set
  `price_include_override`. On top of that `_get_price` filters taxes by
  company and defaults to `env.company`, not the fixture's, which left the
  tax list empty -- `tax_included` was False for the wrong reason.

Two of the portal tests were passing for the wrong reason as well: the access
guard bounced them to /eskaera, which also answers 200. Membership has to be
set from the member side with `is_group`, and a new test checks the final URL
rather than the status alone. Each fixture that can silently build the wrong
thing now carries a guard test.

Left out on purpose: three files were unimplemented placeholders, and
test_draft_persistence still deserves recovering (see the notes file).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-17 17:24:31 +02:00
GitHub Copilot
b3999e2283 [REM] website_sale_aplicoop: remove dead code and orphaned tests
Duplicate _translate_labels fallback, unreachable /eskaera/add-to-cart and
/eskaera/save-cart routes (the frontend cart is localStorage-only and uses
save-order), redundant pickup wrappers, unused pagination/count helpers and
fields, deprecated JS shims, and the already-empty checkout_summary.js and
i18n key/init leftovers.

Also drops 11 tests/*.py never wired into tests/__init__.py: three were
unimplemented placeholders (setUp with no assertions), and the other eight
had real assertions but were bit-rotted against the current schema (e.g.
res.partner.is_supplier no longer exists) — wiring them in surfaced 43
failures unrelated to this cleanup. Verified 0 failed/0 errors of 270 tests
both before and after.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-16 22:31:13 +02:00
GitHub Copilot
6ba554c91b [ADD] website_sale_aplicoop: online payment per group order
Members can now pay their eskaera at checkout, through the standard Odoo
payment machinery. Enabled per group order with a new `online_payment`
boolean, off by default: an order without it behaves exactly as before,
members save a draft and the cutoff cron confirms them in bulk.

The flow mirrors website_sale's: the checkout button becomes "Confirm and
pay", saving the cart redirects to a new /eskaera/<slug>/payment step that
renders `payment.form` from `sale`'s `_get_payment_values`, and the standard
/my/orders/<id>/transaction route takes it from there. This addon ships no
provider and configures none; the co-op publishes whichever it wants.

`website_sale`'s `_get_shop_payment_values` is deliberately not reused: it
runs `_get_shop_payment_errors`, which blocks on shippable products without a
delivery method — exactly an eskaera order, collected at the co-op with no
carrier. For the same reason the transaction route stays the portal one,
which does not call `_check_cart_is_ready_to_be_paid()`.

Payment confirms the order, which has three consequences handled here:

* `payment.transaction._check_amount_and_confirm_order` now confirms group
  orders with `from_orderpoint=True`, the way the cutoff cron already does.
  Without it a product with a broken replenishment route raises inside
  `_post_process`, and `/payment/status/poll` rolls back and re-raises: the
  member sees a payment error over a `done` transaction and the retry cron
  fails forever.
* `_confirm_linked_sale_orders` also sweeps the cycle's already confirmed
  orders into the picking batch, scoped by `pickup_date`. Its early return on
  "no drafts" ran before any batching, so a fully prepaid cycle produced no
  batch at all. `_cron_batch_paid_orders_of_closed_cycles` covers the same
  hole for cycles closed by hand.
* A duplicate-order guard answers 409 on save-order, add-to-cart and
  load-draft, and shows a notice on the shop, so a member whose order is
  already placed cannot build and pay for a second one.

The payment policy lives in the model rather than the controller: there are
three sale.order creation paths and two are live, so `_compute_require_payment`
and `_compute_prepayment_percent` are extended instead of patching five vals
dicts. Orders are also created under the group order's company, which is what
filters the payment providers.

Along the way: eskaera drafts were invisible in /my/orders. The portal rule is
`message_partner_ids child_of` and sale.order only subscribes the customer on
send or confirm, never on a draft create, so the `_prepare_orders_domain`
override that includes drafts never had any effect. Fixed with an explicit
`message_subscribe`.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-16 21:59:35 +02:00
GitHub Copilot
157b344d4b [FIX] website_sale_aplicoop: three defects in the group order slug
- create(): slugs were only checked against the database, so records created
  in the same batch (duplicating several orders from the list view, importing
  rows sharing a name) collided on group_order_slug_uniq. _generate_unique_slug
  now also skips the slugs already handed out in the batch.
- _redirect_to_slug_url(): `post` is passed as a dict instead of splatted, so a
  query parameter named `suffix` no longer binds to the keyword argument of the
  same name (HTTP 500 on /eskaera/<id>/checkout?suffix=x, and a corrupted path
  on the shop route).
- post-migrate: the backfill runs with tracking_disable, `slug` is tracked and
  the upgrade posted a chatter message on every pre-existing order.

Tests for the two reachable cases: batch create and query parameters kept
across the legacy redirect.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-11 22:53:09 +02:00
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
GitHub Copilot
aba22fd230 [IMP] website_sale_aplicoop: automate non-weekly group order cycles
One-time, biweekly and monthly group orders now follow the same cron
confirmation flow as weekly ones (confirm sale orders + batch pickings
when the cycle cutoff passes):

- Biweekly/monthly keep the cutoff_day/pickup_day weekday scheme on a
  recurrence grid anchored at start_date (creation date as fallback):
  cutoffs advance +14 days / +1 month snapped to cutoff_day, with
  catch-up after cron downtime. Previously they behaved as weekly.
- One-time orders (specials/promotions) are driven by end_date
  (cutoff_date = end_date); once passed, the cron confirms, batches
  and closes the group order.
- end_date keeps its "empty = permanent" meaning for recurring orders.
- Website draft-cart lookup window is now period-aware instead of
  assuming a 6-day weekly cycle.
- New cron tests for once/biweekly/monthly cycles; i18n es/eu updated.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-15 16:26:14 +02:00
GitHub Copilot
1d6747e703 [IMP] website_sale_aplicoop: filter and cap stock using forecasted net qty
Group orders are not confirmed until the cutoff date, so draft/sent
sale.order lines never generate stock.moves and are invisible to
virtual_available. This change makes the shop aware of that demand.

- group.order._compute_draft_sale_demand: queries sale.order.line in
  draft/sent state (mirroring sale_stock forecasted report logic) and
  returns pending demand per product.id in the product's own UoM.
- _get_products_for_group_order: delegates to new _apply_stock_filter_and_sort
  which excludes storable products whose forecasted net qty
  (virtual_available − draft demand) <= 0, unless allow_out_of_stock_order.
- _compute_stock_ribbons: reads draft_demand_by_product from ORM context
  so is_out_of_stock / is_low_stock / dynamic_ribbon_id reflect net qty.
- Controller: new _prepare_draft_stock_data helper calculates demand once
  per request, injects context, and builds product_max_qty dict. Applied
  in eskaera_shop, load_eskaera_page and load_products_ajax.
- Template: qty input gets max and data-max-qty from product_max_qty.
- JS: blocks add-to-cart if requested quantity exceeds data-max-qty.
- Fixes type check: type=='consu' → is_storable=True (Odoo 18 semantics).
- 21 new tests in test_forecasted_stock.py covering demand calculation,
  ribbon logic with context, and group order filtering.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-12 18:18:40 +02:00
GitHub Copilot
3b8cb7582b [FIX] website_sale_aplicoop: scope draft lookup to active cycle window
The /eskaera/load-draft endpoint was returning previous-cycle drafts when
group_order.cutoff_date was still in the future but group_order.pickup_date
had not yet been recomputed (its compute only depends on pickup_day and
start_date). Both the stale group_order.pickup_date and the old draft's
pickup_date held the same past value, so the exact-pickup-date filter in
_find_recent_draft_order matched the stale draft and the cart was
repopulated with old products immediately after being cleared.

Replace the pickup_date exact-match + current-week fallback with a single
cutoff-anchored window: create_date in [cutoff_date - 6 days, cutoff_date].
Drafts created outside that window belong to a previous cycle and must
not be reused. The change applies to all four callers (load-draft,
clear-cart, save-order, confirm) so the merge/confirm paths also stop
attaching to stale drafts.

Covered by a new regression test that mirrors the production setup
(matching pickup_date, draft create_date backdated 10 days).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-29 17:40:48 +02:00
GitHub Copilot
b01794c319 [FIX] website_sale_aplicoop: clear cart when group order cutoff has passed
Previously, when a user reopened a group order whose cutoff day had
already passed, the /eskaera/check-status response correctly triggered
the localStorage cart clear, but _autoLoadDraftOnInit immediately
re-fetched the previous cycle's draft sale.order from /eskaera/load-draft
(which only guarded on group_order.state, not cutoff_date) and the stale
items reappeared in the cart, confusing users.

Add a cutoff_date < today guard to load_draft_cart so the endpoint
returns the existing clear_cart unavailable response, and short-circuit
_autoLoadDraftOnInit on the frontend via a _skipDraftAutoLoad flag set
in _checkGroupOrderStatus to avoid the now-pointless XHR round trip.

Covered by a new regression test in tests/test_group_order_status_endpoint.py.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-28 13:51:37 +02:00
GitHub Copilot
a1899483c5 [TEST] website_sale_aplicoop: add tests for home_delivery computed field
- New test_home_delivery.py: verifies group.order.home_delivery derives
  from delivery_product_id (compute), and that sale.order.home_delivery
  is set correctly through _get_effective_delivery_context
- Fix test_save_order_endpoints: replace home_delivery=True (now ignored
  on computed field) with delivery_product_id to enable delivery
- Fix test_phase3_confirm_eskaera: same fix for integration test setUp

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-20 17:49:50 +02:00
GitHub Copilot
91cfb9e137 [FIX] website_sale_aplicoop: lint fixes (exception chaining, imports, remove unused vars) 2026-05-20 16:05:14 +02:00
snt
3ca90578ae [IMP] website_sale_aplicoop: Validar disponibilidad de productos al cargar órdenes históricas
- Backend: Agregar método _validate_items_for_group_order() para validar que los productos históricos sigan siendo disponibles en la orden de grupo actual
- Backend: Modificar load_order_from_history() para filtrar solo items disponibles antes de pasar al template
- Backend: Generar mensaje de aviso traducido cuando hay productos no disponibles
- Template: Pasar información de productos no disponibles y warnings al JavaScript
- Frontend: Mostrar notificación de advertencia si hubo productos excluidos durante la carga histórica
- Notas: Esto evita cargar productos que ya no existen en la orden actual debido a cambios en categorías, proveedores o listas negras
2026-05-20 16:05:14 +02:00
snt
828278573d Fix stock picking batch date and does not split batchs by consumer group 2026-05-20 16:05:14 +02:00
snt
ce393b6034 [FIX] TestConfirmEskaera_Integration: limpieza de decoradores @patch y corrección de bugs
- Convertir 4 tests de decorador @patch a context manager 'with patch(...)' para evitar RuntimeError en LocalProxy de Werkzeug
- Corregir patrón env(user=..., context=dict(...)) en Odoo 18 (sin .with_context())
- Agregar website real al mock para integración con helpers de pricing (_get_pricing_info)
- Añadir pickup_date en fixture de existing_order para que _find_recent_draft_order localice correctamente
- BUGFIX: Agregar (5,) a order_line para limpiar líneas previas al actualizar pedido existente

Resultado: 0 failed, 0 errors de 4 tests en Docker para TestConfirmEskaera_Integration

BREAKING: _create_or_update_sale_order ahora limpia las líneas anteriores con (5,) antes de asignar las nuevas cuando se actualiza un pedido existente. Comportamiento previo (duplicación de líneas) era un bug.
2026-04-08 17:26:57 +02:00
snt
6a748ca308 [FIX] website_sale_aplicoop: restringir acceso portal por grupo de consumo 2026-04-07 22:50:22 +02:00
snt
7d11a95344 [FIX] website_sale_aplicoop: freeze pickup dates on cron confirm 2026-03-31 20:07:50 +02:00
snt
331a2e8944 [FIX] website_sale_aplicoop: harden group order cron 2026-03-31 19:36:20 +02:00
snt
813c8071d9 [I18N] website_sale_aplicoop: remove legacy week draft strings 2026-03-31 19:07:43 +02:00
snt
c345699bf4 [FIX] website_sale_aplicoop: invalidate stale carts on closed group orders 2026-03-30 17:39:15 +02:00
snt
89c008441e [FIX] Resolver 3 fallos de tests en website_sale_aplicoop
- Fix: delivery product price now includes VAT (homepage/checkout)
  * Added _get_delivery_product_display_price() helper to use same pricing pipeline as regular products
  * Uses pricelist + tax calculations instead of bare list_price
  * Fallback chain: pricelist → bare list_price → default 5.74
  * Updated context in eskaera_shop() and eskaera_checkout()

- Test: test_constraint_cutoff_before_pickup_invalid
  * Constraint removed: now allows any combination of cutoff_day and pickup_day
  * Updated test to reflect this change (no ValidationError expected)

- Test: test_day_names_not_using_inline_underscore
  * Fixed to check sub-template eskaera_order_card_meta where day_names is actually used
  * eskaera_page calls this sub-template so day_names context is inherited

Results: 128 tests - 0 failed, 0 errors (100% pass rate)
2026-03-30 16:21:57 +02:00
snt
e2ced75ecd [ADD] website_sale_aplicoop: create picking batches after cutoff
- Add stock_picking_batch dependency to manifest
- Add cutoff date validation in _confirm_linked_sale_orders()
- Create _create_picking_batches_for_sale_orders() method
- Group pickings by consumer_group_id into separate batches
- Set batch scheduled_date from group order pickup_date
- Add test_cron_picking_batch.py with 7 tests covering:
  - Skip orders before cutoff
  - Confirm orders after cutoff
  - Separate batches per consumer group
  - Same group orders in same batch
  - Batch has scheduled_date
  - No duplicate batches on re-run
  - Closed group orders not processed
2026-03-06 15:45:12 +01:00
snt
80539f3e36 [IMP] website_sale_aplicoop: propagate consumer_group_id directly from group_order
- Add consumer_group_id to sale.order for tracking the consumer group
- Fix stock.picking consumer_group_id to use sale_id.consumer_group_id
- Add group_ids inverse relation in res.partner for bidirectional access
- Remove auto-calculation of consumer_group_id, data comes directly from group_order.group_ids[0]
- Add debug logging for consumer_group propagation
- commitment_date propagates directly from group_order (no recalculation)
2026-03-06 13:49:13 +01:00
snt
a9c1f1f609 [FIX] website_sale_aplicoop: align pricing and drafts 2026-02-27 19:39:25 +01:00
snt
539cd5cccd feat(website_sale_aplicoop): ordenar productos por website_sequence y nombre
- Ordena productos primero por website_sequence y luego alfabéticamente por nombre (case-insensitive)
- Fix test: cambiar tipo de producto de 'product' a 'consu' (Odoo 18 compatibility)
2026-02-25 19:34:49 +01:00
snt
c1226e720b [ADD] website_sale_aplicoop: Category blacklist with recursive exclusion
- Add comprehensive test suite for excluded_category_ids
- 9 tests covering: single category, recursive subcategories,
  parent exclusion, direct product override, unrelated categories,
  empty blacklist, multiple exclusions, combined blacklists,
  available_products_count validation
- Update UI to show excluded_category_ids in 'Productos Excluidos'
- Bump version to 18.0.1.6.0
- Update CHANGELOG with category blacklist documentation

Technical notes:
- Category blacklist was already implemented in model/logic
- This commit adds missing tests and documentation
- Recursive exclusion via get_all_excluded_descendants()
- Blacklist has absolute priority over all inclusion sources
2026-02-22 23:04:33 +01:00
snt
d90f2cdc61 [ADD] website_sale_aplicoop: Supplier blacklist feature for group orders
- Add excluded_supplier_ids field for supplier exclusion
- Filter products by main_seller_id (from product_main_seller addon)
- Blacklist has absolute priority over all inclusion sources
- Products with blacklisted main supplier never appear in orders
- Update _get_products_for_group_order() with supplier blacklist logic
- Add excluded_supplier_ids to 'Productos Excluidos' section in form view
- Add comprehensive test suite (TestSupplierBlacklist class with 9 tests):
  * Test exclusion by main_seller_id
  * Test multiple supplier exclusion
  * Test products without main seller not affected
  * Test blacklist with direct product inclusion
  * Test blacklist priority over supplier inclusion
  * Test combined product and supplier blacklist
  * Test available_products_count with supplier blacklist
- Add Spanish and Euskera translations
- Update available_products_count computation to include excluded_supplier_ids
- Version bump to 18.0.1.5.0

Use case: Exclude all products from specific supplier (e.g., temporary unavailability)
Example: Category with 100 products, exclude supplier X → all products from X excluded
Workflow: Bulk inclusion via categories + supplier-level exclusion + product-level exclusion
2026-02-22 21:35:40 +01:00
snt
75ebb7b907 [ADD] website_sale_aplicoop: Product blacklist feature for group orders
- Add excluded_product_ids field for explicit product exclusion
- Blacklist has absolute priority over all inclusion sources (product_ids, category_ids, supplier_ids)
- Update _get_products_for_group_order() with blacklist filter logic
- Rename 'Associations' section to 'Catálogo de Productos' with subsections:
  * Productos Incluidos (whitelist: suppliers, categories, direct products)
  * Productos Excluidos (blacklist: explicit exclusions)
- Add comprehensive test suite (TestProductBlacklist class with 7 tests)
- Add Spanish and Euskera translations
- Update available_products_count computation to include excluded_product_ids
- Version bump to 18.0.1.4.0

Use case: Bulk inclusion via categories/suppliers + fine-grained exclusion via blacklist
Example: Select a category with 100 products, exclude 5 unwanted → 95 available
2026-02-22 20:41:11 +01:00
snt
ed8c6acd92 [FIX] website_sale_aplicoop: Add portal user support for sale.order creation
Portal users don't have write/create permissions on sale.order by default.
This causes errors when trying to create orders during checkout or draft save.

Changes:
- Add _get_salesperson_for_order() helper to retrieve partner's salesperson
- Use sudo() for all sale.order create() operations
- Automatically assign user_id (salesperson) when creating orders
- Use sudo() for order updates and line modifications
- Add fallback to commercial_partner_id.user_id for salesperson

This ensures orders are created with proper permissions while maintaining
traceability through the assigned salesperson.

Test coverage:
- Add test_portal_sale_order_creation.py with 3 tests
- Test portal user creates sale.order
- Test salesperson fallback logic
- Test portal user updates order lines
2026-02-21 14:09:57 +01:00
snt
cf9ea887c1 [REF] Code quality improvements and structure fixes
- Add mypy.ini configuration to exclude migration scripts
- Rename migration files to proper snake_case (post-migration.py → post_migration.py)
- Add __init__.py to migration directories for proper Python package structure
- Add new portal access tests for website_sale_aplicoop
- Code formatting improvements (black, isort)
- Update copilot instructions and project configuration

Related to previous code quality refactoring work.
2026-02-21 13:51:25 +01:00
snt
dbf5bd38b4 [TEST FIX] Resolver errores de tests en addons custom
CAMBIOS PRINCIPALES:
- Agregar field 'default_supplier_id' a product_main_seller (related a main_seller_id)
- Actualizar product_price_category_supplier tests para usar seller_ids (supplierinfo)
- Cambiar product type de 'product' a 'consu' en tests de account_invoice_triple_discount_readonly
- Crear product.template en lugar de product.product directamente en tests
- Corregir parámetros de _compute_price: 'qty' -> 'quantity'
- Comentar test de company_dependent que no puede ejecutarse sin migración

RESULTADOS:
- 193 tests totales (fue 172)
- 0 error(s) (fueron 5 en setUpClass)
- 10 failed (lógica de descuentos en account_invoice_triple_discount_readonly)
- 183 tests PASANDO

ADDONS PASANDO COMPLETAMENTE:
 product_main_seller: 9 tests
 product_price_category_supplier: 12 tests
 product_sale_price_from_pricelist: 47 tests
 website_sale_aplicoop: 111 tests
 account_invoice_triple_discount_readonly: 36/46 tests
2026-02-18 18:17:55 +01:00
snt
6fbc7b9456 [FIX] website_sale_aplicoop: Remove redundant string= attributes and fix OCA linting warnings
- Remove redundant string= from 17 field definitions where name matches string value (W8113)
- Convert @staticmethod to instance methods in selection methods for proper self.env._() access
- Fix W8161 (prefer-env-translation) by using self.env._() instead of standalone _()
- Fix W8301/W8115 (translation-not-lazy) by proper placement of % interpolation outside self.env._()
- Remove unused imports of odoo._ from group_order.py and sale_order_extension.py
- All OCA linting warnings in website_sale_aplicoop main models are now resolved

Changes:
- website_sale_aplicoop/models/group_order.py: 21 field definitions cleaned
- website_sale_aplicoop/models/sale_order_extension.py: 5 field definitions cleaned + @staticmethod conversion
- Consistent with OCA standards for addon submission
2026-02-18 17:54:43 +01:00
snt
8b0a402ccf [FIX] website_sale_aplicoop: Critical date calculation fixes (v18.0.1.3.1)
- Fixed _compute_cutoff_date logic: Changed days_ahead <= 0 to days_ahead < 0 to allow cutoff_date same day as today
- Enabled store=True for delivery_date field to persist calculated values and enable database filtering
- Added constraint _check_cutoff_before_pickup to validate pickup_day >= cutoff_day in weekly orders
- Added @api.onchange methods for immediate UI feedback when changing cutoff_day or pickup_day
- Created daily cron job _cron_update_dates to automatically recalculate dates for active orders
- Added 'Calculated Dates' section in form view showing readonly cutoff_date, pickup_date, delivery_date
- Added 6 regression tests with @tagged('post_install', 'date_calculations')
- Updated documentation with comprehensive changelog

This is a more robust fix than v18.0.1.2.0, addressing edge cases in date calculations.
2026-02-18 17:45:45 +01:00
snt
9000e92324 [DOC] website_sale_aplicoop: Add lazy loading documentation and implement v18.0.1.3.0 feature
- Add LAZY_LOADING.md with complete technical documentation (600+ lines)
- Add LAZY_LOADING_QUICK_START.md for quick reference (5 min)
- Add LAZY_LOADING_DOCS_INDEX.md as navigation guide
- Add UPGRADE_INSTRUCTIONS_v18.0.1.3.0.md with step-by-step installation
- Create DOCUMENTATION.md as main documentation index
- Update README.md with lazy loading reference
- Update docs/README.md with new docs section
- Update website_sale_aplicoop/README.md with features and changelog
- Create website_sale_aplicoop/CHANGELOG.md with version history

Lazy Loading Implementation (v18.0.1.3.0):
- Reduces initial store load from 10-20s to 500-800ms (20x faster)
- Add pagination configuration to res_config_settings
- Add _get_products_paginated() method to group_order model
- Implement AJAX endpoint for product loading
- Create 'Load More' button in website templates
- Add JavaScript listener for lazy loading behavior
- Backward compatible: can be disabled in settings

Performance Improvements:
- Initial load: 500-800ms (vs 10-20s before)
- Subsequent pages: 200-400ms via AJAX
- DOM optimization: 20 products initial vs 1000+ before
- Configurable: enable/disable and items per page

Documentation Coverage:
- Technical architecture and design
- Installation and upgrade instructions
- Configuration options and best practices
- Troubleshooting and common issues
- Performance metrics and validation
- Rollback procedures
- Future improvements roadmap
2026-02-16 18:39:39 +01:00
snt
eb6b53db1a [ADD] website_sale_aplicoop: Phase 3 test suite implementation
Implementa test_phase3_confirm_eskaera.py con cobertura completa de los 3 helpers
creados en Phase 3 del refactoring de confirm_eskaera():

Helper Methods Tested:
- _validate_confirm_json(): Validación de request JSON
- _process_cart_items(): Procesamiento de items del carrito
- _build_confirmation_message(): Construcción de mensajes localizados

Test Coverage:
- 4 test classes
- 24 test methods
- 61 assertions

Test Breakdown:
1. TestValidateConfirmJson (5 tests):
   - Validación exitosa de datos JSON
   - Manejo de error: order_id faltante
   - Manejo de error: order no existe
   - Manejo de error: carrito vacío
   - Validación de flag is_delivery

2. TestProcessCartItems (5 tests):
   - Procesamiento exitoso de items
   - Fallback a list_price cuando price=0
   - Skip de productos inválidos
   - Error cuando no quedan items válidos
   - Traducción de nombres de productos

3. TestBuildConfirmationMessage (11 tests):
   - Mensaje de confirmación para pickup
   - Mensaje de confirmación para delivery
   - Manejo cuando no hay fechas
   - Formato de fecha DD/MM/YYYY
   - Soporte multi-idioma: ES, EU, CA, GL, PT, FR, IT

4. TestConfirmEskaera_Integration (3 tests):
   - Flujo completo para pickup order
   - Flujo completo para delivery order
   - Actualización de draft existente

Features Validated:
 Validación robusta de request JSON con mensajes de error claros
 Procesamiento de items con manejo de errores y fallbacks
 Construcción de mensajes con soporte para 7 idiomas
 Diferenciación pickup vs delivery con fechas correctas
 Integración completa end-to-end del flujo confirm_eskaera

Quality Checks:
 Sintaxis Python válida
 Pre-commit hooks: black, isort, flake8, pylint (all passed)
 671 líneas de código de tests
 29 docstrings explicativos

Total Test Suite (Phase 1 + 2 + 3):
- 53 test methods (18 + 11 + 24)
- 3 test files (test_helper_methods_phase1.py, test_phase2_eskaera_shop.py, test_phase3_confirm_eskaera.py)
- 1,311 líneas de código de tests

Este commit completa la implementación de tests para el refactoring completo de 3 fases,
proporcionando cobertura exhaustiva de todas las funcionalidades críticas del sistema
eskaera (pedidos de grupo cooperativos).

Files:
- website_sale_aplicoop/tests/test_phase3_confirm_eskaera.py (NEW, 671 lines)
2026-02-16 16:00:39 +01:00
snt
8b728b8b7c [IMP] website_sale_aplicoop: Phase 2 - Refactor eskaera_shop() and add_to_eskaera_cart()
Phase 2 of cyclomatic complexity reduction refactoring.

Code Quality Improvements:
- eskaera_shop(): 426 → 317 lines (-109 lines, 25.6% reduction)
- eskaera_shop(): C901 complexity 42 → 33 (-9 points, 21.4% improvement)
- add_to_eskaera_cart(): Refactored to use _resolve_pricelist()
- Eliminated duplicate pricelist resolution code (2 instances consolidated)

Status: Ready for Phase 3 (confirm_eskaera refactoring)
2026-02-16 15:47:15 +01:00
snt
23e156a13e [REFACTOR] Phase 1: Add 3 helper methods and tests (pre-commit skipped for C901)
Helper Methods:
- _resolve_pricelist(): 3-tier pricelist resolution with logging
- _validate_confirm_request(): Confirm endpoint validation
- _validate_draft_request(): Draft endpoint validation

Tests:
- 21 test cases covering all validation scenarios
- All tests passing quality checks (flake8 clean for new code)

Note: Existing C901 warnings on eskaera_shop(), confirm_eskaera(), etc.
are target for Phase 2/3 refactoring.
2026-02-16 15:41:03 +01:00
snt
4d23e98f7b Revertir cambio: eliminar cálculo duplicado de impuestos
El método _get_price() del addon OCA ya maneja correctamente los impuestos
según la configuración de Odoo. El cálculo adicional con compute_all() estaba
duplicando los impuestos cuando price_include estaba activado.

Cambios:
- Eliminado método _compute_price_with_taxes()
- Revertido eskaera_shop() para usar directamente _get_price()
- Revertido add_to_eskaera_cart() para usar directamente _get_price()

El precio mostrado ahora respeta la configuración de impuestos de Odoo
sin duplicación.
2026-02-11 19:54:28 +01:00
snt
7cff89e418 Aplicoop desde el repo de kidekoop 2026-02-11 15:32:11 +01:00