feat(website_sale_aplicoop): sistema de ribbons basado en stock

- Añadidos ribbons 'Sin Stock' (rojo) y 'Pocas Existencias' (amarillo)
- Nuevo campo configurable: umbral de stock bajo (default: 5.0)
- Campos computed en product.product:
  * is_out_of_stock: True cuando qty_available <= 0
  * is_low_stock: True cuando 0 < qty_available <= threshold
  * dynamic_ribbon_id: ribbon automático según nivel de stock
- Ordenamiento mejorado: productos con stock primero, sin stock al final
- Template actualizado:
  * Muestra ribbon de stock en tarjeta de producto
  * Deshabilita botón add-to-cart cuando producto sin stock
  * Cambia icono a 'fa-ban' en productos sin stock
- Vista de configuración: campo 'Low Stock Threshold' en Settings > Shop Performance
- Solo aplica a productos type='consu' (almacenables)
- Tests: 112 pasados, 0 fallos
This commit is contained in:
snt 2026-02-25 19:48:39 +01:00
parent 539cd5cccd
commit 2a005a9d5a
7 changed files with 132 additions and 6 deletions

View file

@ -477,9 +477,14 @@ class GroupOrder(models.Model):
len(products),
)
# Sort products by website_sequence (primary) and name (secondary, case-insensitive)
# Sort products: in-stock first, then out-of-stock, maintaining sequence+name within each group
# is_out_of_stock is Boolean: False (in stock) comes first, True (out of stock) comes last
return products.sorted(
lambda p: (p.product_tmpl_id.website_sequence, (p.name or "").lower())
lambda p: (
p.is_out_of_stock, # Boolean: False < True, so in-stock products first
p.product_tmpl_id.website_sequence,
(p.name or "").lower(),
)
)
def _get_products_paginated(self, order_id, page=1, per_page=20):