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:
2026-10-11 12:17:23 +00:00
parent ee8a844d96
commit a916753ebe
38 changed files with 4201 additions and 30 deletions

View File

@@ -0,0 +1,69 @@
# -*- coding: utf-8 -*-
"""Proveedores sobre `res.partner`.
No se duplica el modelo de persona: un proveedor es un `res.partner` marcado con
`skeen_es_proveedor` más un puñado de campos propios de negocio (días de crédito,
contacto, datos de pago y notas).
Nota de compatibilidad: el `supplier_rank` estándar de Odoo lo define el módulo
`account`, que NO está instalado en todas las instancias (p. ej. la de pruebas).
Por eso la fuente de verdad es el flag propio `skeen_es_proveedor`; si la
instancia sí tiene `supplier_rank`, se sincroniza para no perder la integración
con el estándar. El flag `skeen_proveedor_activo` permite "desactivar" un
proveedor con historial sin borrar los datos ni romper las compras pasadas.
"""
from odoo import models, fields, api
from odoo.exceptions import ValidationError
class ResPartner(models.Model):
_inherit = 'res.partner'
skeen_es_proveedor = fields.Boolean(
string='Es proveedor', default=False, index=True,
help='Marca propia de proveedor. Se sincroniza con supplier_rank si la '
'instancia tiene el módulo account instalado.')
skeen_proveedor_activo = fields.Boolean(
string='Proveedor activo', default=True,
help='Se apaga al "eliminar" un proveedor con compras registradas.')
skeen_dias_credito = fields.Integer(
string='Días de crédito', default=0)
skeen_contacto = fields.Char(
string='Contacto de compras')
skeen_banco = fields.Char(string='Banco')
skeen_cuenta_pago = fields.Char(string='Cuenta / CLABE de pago')
skeen_notas_proveedor = fields.Text(string='Notas de proveedor')
skeen_compra_ids = fields.One2many(
'skeen.compra', 'partner_id', string='Compras')
skeen_compras_count = fields.Integer(
string='Número de compras', compute='_compute_skeen_compras_count')
skeen_saldo = fields.Float(
string='Saldo por pagar', compute='_compute_skeen_saldo',
help='Suma de los saldos de las compras no canceladas.')
@api.depends('skeen_compra_ids')
def _compute_skeen_compras_count(self):
for p in self:
p.skeen_compras_count = len(p.skeen_compra_ids)
@api.depends('skeen_compra_ids.saldo', 'skeen_compra_ids.estado')
def _compute_skeen_saldo(self):
for p in self:
p.skeen_saldo = sum(
c.saldo for c in p.skeen_compra_ids
if c.estado not in ('cancelada',))
@api.constrains('skeen_dias_credito')
def _check_dias_credito(self):
for p in self:
if p.skeen_dias_credito is not None and p.skeen_dias_credito < 0:
raise ValidationError('Los días de crédito no pueden ser negativos.')
def action_marcar_proveedor(self):
"""Marca al partner como proveedor (flag propio + supplier_rank si existe)."""
for p in self:
p.skeen_es_proveedor = True
if 'supplier_rank' in p._fields and (p.supplier_rank or 0) <= 0:
p.supplier_rank = 1
p.skeen_proveedor_activo = True