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

@ -1095,6 +1095,15 @@
/>
</div>
</t>
<!-- Stock Ribbon -->
<t t-if="product.dynamic_ribbon_id">
<t t-set="ribbon" t-value="product.dynamic_ribbon_id"/>
<span
t-attf-class="o_ribbon {{ ribbon._get_position_class() }} z-1"
t-attf-style="color: {{ ribbon.text_color }}; background-color: {{ ribbon.bg_color }};"
t-esc="ribbon.name"
/>
</t>
<div
class="card-body d-flex flex-column"
>
@ -1266,13 +1275,14 @@
/>
</button>
<button
class="add-to-cart-btn"
t-attf-class="add-to-cart-btn {{ 'btn-disabled' if product.is_out_of_stock else '' }}"
type="button"
t-attf-aria-label="Add {{ product.name }} to cart"
t-attf-title="Add {{ product.name }} to cart"
t-attf-disabled="{{ 'disabled' if product.is_out_of_stock else '' }}"
t-attf-aria-label="{{ 'Out of stock' if product.is_out_of_stock else 'Add %s to cart' % product.name }}"
t-attf-title="{{ 'Out of stock' if product.is_out_of_stock else 'Add %s to cart' % product.name }}"
>
<i
class="fa fa-shopping-cart"
t-attf-class="fa {{ 'fa-ban' if product.is_out_of_stock else 'fa-shopping-cart' }}"
aria-hidden="true"
/>
</button>