fase 3.2: compras y proveedores (modulo skeen_compras: proveedores, ordenes de compra con recepcion total/parcial, costo promedio ponderado con lote y caducidad, pagos a proveedor, cuentas por pagar con antiguedad, reportes de compras y margen por articulo); apagado por defecto
This commit is contained in:
92
odoo-addons/skeen_compras/models/inventario.py
Normal file
92
odoo-addons/skeen_compras/models/inventario.py
Normal file
@@ -0,0 +1,92 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""Campos de costo/margen que Compras agrega al artículo de inventario.
|
||||
|
||||
`costo_promedio` y `ultimo_costo` son NUEVOS: `cost` (Costo unitario) se queda
|
||||
como estaba porque lo usan el valor de inventario y los reportes existentes, de
|
||||
modo que una instancia con `compras.activo=false` conserva sus cifras idénticas.
|
||||
El margen se calcula contra `costo_promedio` (con respaldo en `cost`) y el
|
||||
precio de venta ya sin impuesto.
|
||||
"""
|
||||
|
||||
from odoo import models, fields, api
|
||||
|
||||
|
||||
class SkeenInventarioItem(models.Model):
|
||||
_inherit = 'skeen.inventario.item'
|
||||
|
||||
costo_promedio = fields.Float(
|
||||
string='Costo promedio', default=0.0,
|
||||
help='Costo promedio ponderado de las compras recibidas. Editable por '
|
||||
'administración como ajuste de inventario.')
|
||||
ultimo_costo = fields.Float(
|
||||
string='Último costo', default=0.0,
|
||||
help='Costo unitario de la última recepción de compra.')
|
||||
margen = fields.Float(
|
||||
string='Margen', compute='_compute_margen',
|
||||
help='Precio de venta sin impuesto menos costo promedio.')
|
||||
margen_pct = fields.Float(
|
||||
string='Margen (%)', compute='_compute_margen')
|
||||
|
||||
@api.depends('price', 'cost', 'costo_promedio', 'tax_rate',
|
||||
'tax_use_default')
|
||||
def _compute_margen(self):
|
||||
from odoo.addons.skeen_citas.clinic_settings import get_impuestos
|
||||
try:
|
||||
imp = get_impuestos(self.env)
|
||||
except Exception:
|
||||
imp = {'activo': False, 'tasa_default': 0.0,
|
||||
'precios_incluyen_impuesto': False}
|
||||
for it in self:
|
||||
precio = it.price or 0.0
|
||||
if imp.get('activo'):
|
||||
tasa = (imp.get('tasa_default') or 0.0) if it.tax_use_default \
|
||||
else (it.tax_rate or 0.0)
|
||||
if imp.get('precios_incluyen_impuesto') and tasa:
|
||||
precio = precio / (1.0 + tasa / 100.0)
|
||||
costo = it.costo_promedio or it.cost or 0.0
|
||||
it.margen = precio - costo
|
||||
it.margen_pct = (it.margen / precio * 100.0) if precio else 0.0
|
||||
|
||||
def _actualizar_costo_recepcion(self, cantidad, costo_unitario,
|
||||
actualizar_promedio=True):
|
||||
"""Aplica el costo de una recepción sobre el artículo.
|
||||
|
||||
Fórmula ponderada: (stock_actual*costo_actual + cantidad*costo_recibido)
|
||||
/ (stock_actual + cantidad). Si el stock actual es <= 0 (cero o negativo)
|
||||
el promedio se reinicia al costo recibido, porque mezclar un costo viejo
|
||||
con existencias negativas daría un número sin sentido.
|
||||
|
||||
Debe llamarse ANTES de crear el movimiento de entrada, para que
|
||||
`stock_actual` sea la existencia previa a la recepción.
|
||||
"""
|
||||
self.ensure_one()
|
||||
stock_actual = self.qty or 0.0
|
||||
cantidad = float(cantidad or 0.0)
|
||||
costo_unitario = float(costo_unitario or 0.0)
|
||||
vals = {'ultimo_costo': costo_unitario}
|
||||
if actualizar_promedio and cantidad > 0:
|
||||
if stock_actual > 0:
|
||||
# Sin historial de compras el promedio arranca del costo unitario
|
||||
# ya cargado (`cost`); si no, mezclar contra 0 daría un promedio
|
||||
# artificialmente bajo.
|
||||
costo_actual = self.costo_promedio or self.cost or 0.0
|
||||
nuevo = ((stock_actual * costo_actual)
|
||||
+ (cantidad * costo_unitario)) / (stock_actual + cantidad)
|
||||
else:
|
||||
nuevo = costo_unitario
|
||||
vals['costo_promedio'] = nuevo
|
||||
self.write(vals)
|
||||
return self.costo_promedio
|
||||
|
||||
|
||||
class SkeenInventarioMove(models.Model):
|
||||
"""Lote y caducidad de la mercancía recibida.
|
||||
|
||||
Columnas nuevas y opcionales: el kardex actual no cambia. Las recepciones de
|
||||
compra las llenan; los demás movimientos las dejan vacías.
|
||||
"""
|
||||
_inherit = 'skeen.inventario.move'
|
||||
|
||||
lote = fields.Char(string='Lote')
|
||||
fecha_caducidad = fields.Date(string='Caducidad')
|
||||
|
||||
Reference in New Issue
Block a user