3.4 KiB
Stock Account AVCO Negative Fix
Corrige un bug del core de Odoo 16 en la valoración de coste promedio (AVCO)
que puede dejar el standard_price de un producto en un valor negativo (o
cero) cuando existe stock negativo (quantity_svl < 0) y se recibe una
compra que compensa solo parcialmente ese stock.
Descripción del problema
El método product_price_update_before_done() de
stock_account/models/stock_move.py calcula el nuevo coste medio ponderado
de un producto AVCO con la fórmula:
new_std_price = (amount_unit * qty_svl + price_unit_in * qty) / (qty_svl + qty)
Cuando (qty_svl + qty) es un valor positivo muy pequeño (el stock negativo
casi se compensa con la compra recibida, pero no del todo), el numerador
puede resultar negativo y el standard_price calculado se vuelve negativo,
corrompiendo la valoración de inventario en los recálculos posteriores.
Bug reportado en upstream sin corrección oficial: https://github.com/odoo/odoo/issues/187169
Solución
Este módulo no copia ni sobrescribe el algoritmo del core. En su lugar,
hereda stock.move y aplica una estrategia de post-procesado con super()
sobre _action_done(), basada en una precondición evaluada antes de que
nada más intervenga:
- Antes de llamar a
super(), para cada movimiento entrante AVCO relevante (move._is_in()ycost_method == 'average'), comprueba si el producto teníaquantity_svl <= 0antes de este movimiento (la precondición bajo la cual el core puede degenerar a un coste negativo o cero). Si se cumple, captura elprice_unitentrante (move._get_price_unit()). - Llama a
super()._action_done()para que el core ejecute su lógica completa sin modificaciones (media ponderada AVCO, rama FIFO y el_run_fifo_vacuum()final). - Después de
super(), para cada producto marcado como "en riesgo" en el paso 1, reescribe incondicionalmente sustandard_pricecon elprice_unitcapturado —sin comprobar el valor que haya quedado escrito—, replicando el mismo patrón de escritura del core (disable_auto_svl=True,sudo()) para no generar SVLs de revaluación espurios.
¿Por qué precondición incondicional y no comprobar el signo final? Una
primera versión de este fix comprobaba el signo de standard_price
después de super(). Se detectó que ese criterio es insuficiente: otros
módulos que reaccionan durante _action_done() (por ejemplo, el OCA
product_cost_price_avco_sync, que sincroniza el coste AVCO al reaccionar a
stock.valuation.layer.write() disparado por _run_fifo_vacuum()) pueden
reescribir standard_price a un valor positivo pero distinto del esperado,
haciendo que la comprobación <= 0 nunca se cumpla aunque la situación de
riesgo sí se haya dado. Al decidir antes de que empiece la cadena y
aplicar la corrección de forma incondicional, el fix es robusto frente a
cualquier módulo que reescriba standard_price durante el proceso, ahora o
en el futuro.
Este enfoque evita duplicar el algoritmo del core (resiliente a cambios de
upstream) y es compatible con cualquier otro módulo, presente o futuro, que
también herede product_price_update_before_done/_action_done sobre
stock.move, ya que la cadena de super() se respeta íntegramente.
Alcance
- Solo actúa sobre productos con
cost_method = 'average'. - Productos FIFO o de coste estándar no se ven afectados.
- No introduce modelos, vistas ni campos nuevos.
Créditos
Autores
- Criptomart