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:
parent
539cd5cccd
commit
2a005a9d5a
7 changed files with 132 additions and 6 deletions
|
|
@ -21,6 +21,71 @@ class ProductProduct(models.Model):
|
|||
help="Group orders where this product is available",
|
||||
)
|
||||
|
||||
is_out_of_stock = fields.Boolean(
|
||||
string="Is Out of Stock",
|
||||
compute="_compute_stock_ribbons",
|
||||
store=False,
|
||||
help="True if qty_available <= 0",
|
||||
)
|
||||
|
||||
is_low_stock = fields.Boolean(
|
||||
string="Is Low Stock",
|
||||
compute="_compute_stock_ribbons",
|
||||
store=False,
|
||||
help="True if 0 < qty_available <= threshold",
|
||||
)
|
||||
|
||||
dynamic_ribbon_id = fields.Many2one(
|
||||
"product.ribbon",
|
||||
string="Dynamic Stock Ribbon",
|
||||
compute="_compute_stock_ribbons",
|
||||
store=False,
|
||||
help="Auto-assigned ribbon based on stock levels",
|
||||
)
|
||||
|
||||
@api.depends("qty_available", "type")
|
||||
def _compute_stock_ribbons(self):
|
||||
"""Compute stock-based ribbons dynamically."""
|
||||
# Obtener ribbons (usar sudo para evitar permisos)
|
||||
out_of_stock_ribbon = self.env.ref(
|
||||
"website_sale_aplicoop.out_of_stock_ribbon", raise_if_not_found=False
|
||||
)
|
||||
low_stock_ribbon = self.env.ref(
|
||||
"website_sale_aplicoop.low_stock_ribbon", raise_if_not_found=False
|
||||
)
|
||||
|
||||
# Obtener threshold de configuración
|
||||
threshold = float(
|
||||
self.env["ir.config_parameter"]
|
||||
.sudo()
|
||||
.get_param("website_sale_aplicoop.low_stock_threshold", default="5.0")
|
||||
)
|
||||
|
||||
for product in self:
|
||||
# Solo para productos almacenables (type='consu' o 'product' en algunos casos)
|
||||
# En Odoo 18: 'consu' = Goods (almacenable), 'service' = Service, 'combo' = Combo
|
||||
if product.type != "consu":
|
||||
product.is_out_of_stock = False
|
||||
product.is_low_stock = False
|
||||
product.dynamic_ribbon_id = False
|
||||
continue
|
||||
|
||||
# Lógica de stock
|
||||
qty = product.qty_available
|
||||
|
||||
if qty <= 0:
|
||||
product.is_out_of_stock = True
|
||||
product.is_low_stock = False
|
||||
product.dynamic_ribbon_id = out_of_stock_ribbon
|
||||
elif qty <= threshold:
|
||||
product.is_out_of_stock = False
|
||||
product.is_low_stock = True
|
||||
product.dynamic_ribbon_id = low_stock_ribbon
|
||||
else:
|
||||
product.is_out_of_stock = False
|
||||
product.is_low_stock = False
|
||||
product.dynamic_ribbon_id = False # No ribbon
|
||||
|
||||
@api.model
|
||||
def _get_products_for_group_order(self, order_id):
|
||||
"""Backward-compatible delegation to `group.order` discovery.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue