# 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: 1. Antes de llamar a `super()`, para cada movimiento entrante AVCO relevante (`move._is_in()` y `cost_method == 'average'`), comprueba si el producto tenía `quantity_svl <= 0` **antes** de este movimiento (la precondición bajo la cual el core puede degenerar a un coste negativo o cero). Si se cumple, captura el `price_unit` entrante (`move._get_price_unit()`). 2. 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). 3. Después de `super()`, para cada producto marcado como "en riesgo" en el paso 1, reescribe **incondicionalmente** su `standard_price` con el `price_unit` capturado —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