[ADD] website_sale_aplicoop: botón limpiar carrito en sidebar

Añade botón 'Clear Cart' (fa-trash) en el header y footer del sidebar
del carrito en la página de lista de productos.

Cambios:
- views/website_templates.xml: botón clear-cart-btn en card-header y
  clear-cart-btn-footer en card-footer del sidebar
- controllers/website_sale.py: nuevo endpoint POST /eskaera/clear-cart
  que cancela el sale.order borrador del usuario si existe
- static/src/js/website_sale.js: método _clearCart(), listeners para
  ambos botones (header + footer)
- models/js_translations.py: nuevas cadenas clear_cart, clear_cart_confirm,
  cart_cleared, draft_cancelled
- i18n/es.po, i18n/eu.po: traducciones ES y EU de los nuevos labels
This commit is contained in:
snt 2026-04-07 23:50:30 +02:00
parent 0eb7957a70
commit 135967019e
6 changed files with 244 additions and 7 deletions

View file

@ -268,6 +268,13 @@ class AplicoopWebsiteSale(WebsiteSale):
"items": env_lang._("items"),
"added_to_cart": env_lang._("added to cart"),
"out_of_stock": env_lang._("Out of stock"),
# ============ CLEAR CART ============
"clear_cart": env_lang._("Clear Cart"),
"clear_cart_confirm": env_lang._(
"Are you sure you want to clear the cart? This will also cancel any saved draft order."
),
"cart_cleared": env_lang._("Cart cleared"),
"draft_cancelled": env_lang._("draft order cancelled"),
}
return labels
@ -2415,6 +2422,107 @@ class AplicoopWebsiteSale(WebsiteSale):
status=500,
)
@http.route(
["/eskaera/clear-cart"],
type="http",
auth="user",
website=True,
methods=["POST"],
csrf=False,
)
def clear_cart(self, **post):
"""Clear the user's cart and cancel any existing draft sale.order.
Receives: JSON body with 'order_id'
Returns: JSON with success status and cancelled_order_id if applicable.
"""
try:
data = self._decode_json_body()
except ValueError as e:
return request.make_response(
json.dumps({"error": str(e)}),
[("Content-Type", "application/json")],
status=400,
)
order_id = data.get("order_id")
if not order_id:
return request.make_response(
json.dumps({"error": "order_id is required"}),
[("Content-Type", "application/json")],
status=400,
)
try:
order_id = int(order_id)
except (ValueError, TypeError):
return request.make_response(
json.dumps({"error": f"Invalid order_id format: {order_id}"}),
[("Content-Type", "application/json")],
status=400,
)
group_order = request.env["group.order"].sudo().browse(order_id)
if not group_order.exists():
return request.make_response(
json.dumps({"error": f"Order {order_id} not found"}),
[("Content-Type", "application/json")],
status=404,
)
current_user = request.env.user
if not current_user.partner_id:
return request.make_response(
json.dumps({"error": "User has no associated partner"}),
[("Content-Type", "application/json")],
status=400,
)
# Find and cancel any existing draft sale.order for this period
cancelled_order_id = None
try:
draft_orders = self._find_recent_draft_order(
current_user.partner_id.id, group_order
)
if draft_orders:
draft_order = draft_orders[0]
cancelled_order_id = draft_order.id
# Use action_cancel if available, otherwise unlink
if draft_order.state == "draft":
draft_order.sudo().action_cancel()
_logger.info(
"clear_cart: Cancelled draft sale.order %d for partner %d",
draft_order.id,
current_user.partner_id.id,
)
else:
_logger.info(
"clear_cart: Draft order %d already in state '%s', skipping cancel",
draft_order.id,
draft_order.state,
)
except Exception as e:
_logger.warning(
"clear_cart: Error cancelling draft order for partner %d: %s",
current_user.partner_id.id,
str(e),
)
response_data = {
"success": True,
"message": request.env._("Cart cleared"),
"cancelled_order_id": cancelled_order_id,
}
_logger.info(
"clear_cart: Cart cleared for partner %d, order %d (cancelled_sale_order: %s)",
current_user.partner_id.id,
order_id,
cancelled_order_id,
)
return request.make_response(
json.dumps(response_data),
[("Content-Type", "application/json")],
)
@http.route(
["/eskaera/save-order"],
type="http",