[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>
This commit is contained in:
parent
a67181ab42
commit
6ba554c91b
21 changed files with 1993 additions and 37 deletions
50
website_sale_aplicoop/models/payment_transaction.py
Normal file
50
website_sale_aplicoop/models/payment_transaction.py
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
# Copyright 2026 Criptomart
|
||||
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl)
|
||||
|
||||
import logging
|
||||
|
||||
from odoo import models
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class PaymentTransaction(models.Model):
|
||||
_inherit = "payment.transaction"
|
||||
|
||||
def _check_amount_and_confirm_order(self):
|
||||
"""Confirm group order sales the way the cutoff cron already does.
|
||||
|
||||
``group.order._confirm_linked_sale_orders`` confirms with
|
||||
``from_orderpoint=True`` on purpose: ``stock.move._action_confirm``
|
||||
forwards ``raise_user_error=not from_orderpoint`` to
|
||||
``procurement.group.run``, so a product with a broken replenishment
|
||||
route does not block the sale, and the missing moves are reported
|
||||
operationally instead.
|
||||
|
||||
The standard payment post-processing confirms without that context,
|
||||
and ``/payment/status/poll`` rolls back and re-raises anything
|
||||
``_post_process`` throws. Without this override, one misconfigured
|
||||
product turns a successful payment into an error page while the
|
||||
transaction is already ``done``, and the retry cron keeps failing on
|
||||
it. Orders born from a group order follow the cron's operational
|
||||
rules, so they get the cron's context.
|
||||
"""
|
||||
eskaera_txs = self.filtered(lambda tx: tx.sale_order_ids.group_order_id)
|
||||
if not eskaera_txs:
|
||||
return super()._check_amount_and_confirm_order()
|
||||
|
||||
confirmed_orders = super(
|
||||
PaymentTransaction, self - eskaera_txs
|
||||
)._check_amount_and_confirm_order()
|
||||
|
||||
_logger.info(
|
||||
"[PAYMENT] Confirming %d group order transaction(s) with "
|
||||
"from_orderpoint=True: %s",
|
||||
len(eskaera_txs),
|
||||
eskaera_txs.ids,
|
||||
)
|
||||
confirmed_orders |= super(
|
||||
PaymentTransaction, eskaera_txs.with_context(from_orderpoint=True)
|
||||
)._check_amount_and_confirm_order()
|
||||
|
||||
return confirmed_orders
|
||||
Loading…
Add table
Add a link
Reference in a new issue