144 lines
6.9 KiB
Python
144 lines
6.9 KiB
Python
# This file is part of ncf_manager module for Tryton. The COPYRIGHT file at the top level of
|
|
# this repository contains the full copyright notices and license terms.
|
|
from trytond.exceptions import UserError
|
|
from trytond.model import fields
|
|
from trytond.pyson import Eval
|
|
from trytond.pool import PoolMeta
|
|
from trytond.i18n import gettext
|
|
|
|
# Maps sequence_type.name → (prefix, padding)
|
|
_NCF_TYPE_MAP = {
|
|
'Factura de Crédito Fiscal': ('B01', 8),
|
|
'Factura de Consumo': ('B02', 8),
|
|
'Nota de Débito': ('B03', 8),
|
|
'Nota de Crédito': ('B04', 8),
|
|
'Comprobante de Compras': ('B11', 8),
|
|
'Registro Único de Ingreso': ('B12', 8),
|
|
'Comprobante para Gasto Menor': ('B13', 8),
|
|
'Comprobante de Regímen Especial': ('B14', 8),
|
|
'Comprobante Gubernamental': ('B15', 8),
|
|
'Comprobante para exportaciones': ('B16', 8),
|
|
'Comprobante para Pagos al Exterior': ('B17', 8),
|
|
'Factura de Crédito Fiscal Electrónica': ('E31', 10),
|
|
'Factura de Consumo Electrónica': ('E32', 10),
|
|
'Nota de Débito Electrónica': ('E33', 10),
|
|
'Nota de Crédito Electrónica': ('E34', 10),
|
|
'Compras Electrónico': ('E41', 10),
|
|
'Gastos Menores Electrónico': ('E43', 10),
|
|
'Regímenes Especiales Electrónico': ('E44', 10),
|
|
'Gubernamental Electrónico': ('E45', 10),
|
|
'Exportación Electrónico': ('E46', 10),
|
|
'Pagos al Exterior Electrónico': ('E47', 10),
|
|
}
|
|
NCF_PREFIXES = tuple(prefix for prefix, _padding in _NCF_TYPE_MAP.values())
|
|
|
|
|
|
class Sequence(metaclass=PoolMeta):
|
|
__name__ = 'ir.sequence'
|
|
|
|
from_number = fields.Integer('From Number',
|
|
states={
|
|
'required': Eval('prefix').in_(['B01', 'B02', 'B03', 'B04',
|
|
'B11', 'B12', 'B13', 'B14', 'B15', 'B16', 'B17', 'E31',
|
|
'E32', 'E33', 'E34', 'E41', 'E43', 'E44', 'E45', 'E46', 'E47']),
|
|
'invisible': ~Eval('prefix').in_(['B01', 'B02', 'B03', 'B04',
|
|
'B11', 'B12', 'B13', 'B14', 'B15', 'B16', 'B17', 'E31',
|
|
'E32', 'E33', 'E34', 'E41', 'E43', 'E44', 'E45', 'E46', 'E47'])
|
|
}, depends=['prefix'])
|
|
|
|
until_number = fields.Integer('Until Number',
|
|
states={
|
|
'required': Eval('prefix').in_(['B01', 'B02', 'B03', 'B04',
|
|
'B11', 'B12', 'B13', 'B14', 'B15', 'B16', 'B17', 'E31',
|
|
'E32', 'E33', 'E34', 'E41', 'E43', 'E44', 'E45', 'E46', 'E47']),
|
|
'invisible': ~Eval('prefix').in_(['B01', 'B02', 'B03', 'B04',
|
|
'B11', 'B12', 'B13', 'B14', 'B15', 'B16', 'B17', 'E31',
|
|
'E32', 'E33', 'E34', 'E41', 'E43', 'E44', 'E45', 'E46', 'E47'])
|
|
}, depends=['prefix'])
|
|
|
|
approved_qty = fields.Function(fields.Integer('Approved Quantity',
|
|
states={
|
|
'invisible': ~Eval('prefix').in_(['B01', 'B02', 'B03', 'B04',
|
|
'B11', 'B12', 'B13', 'B14', 'B15', 'B16', 'B17', 'E31',
|
|
'E32', 'E33', 'E34', 'E41', 'E43', 'E44', 'E45', 'E46', 'E47'])
|
|
}), 'on_change_with_approved_qty')
|
|
|
|
expiration_date = fields.Date('Expiration Date',
|
|
states={
|
|
'required': Eval('prefix').in_(['B01', 'B03',
|
|
'B11', 'B12', 'B13', 'B14', 'B15', 'B16', 'B17', 'E31',
|
|
'E33', 'E41', 'E43', 'E44', 'E45', 'E46', 'E47']),
|
|
'invisible': ~Eval('prefix').in_(['B01', 'B02', 'B03', 'B04',
|
|
'B11', 'B12', 'B13', 'B14', 'B15', 'B16', 'B17', 'E31',
|
|
'E32', 'E33', 'E34', 'E41', 'E43', 'E44', 'E45', 'E46', 'E47'])
|
|
}, depends=['prefix'])
|
|
|
|
remaining_qty = fields.Function(fields.Integer('Remaining Quantity',
|
|
states={
|
|
'invisible': ~Eval('prefix').in_(['B01', 'B02', 'B03', 'B04',
|
|
'B11', 'B12', 'B13', 'B14', 'B15', 'B16', 'B17', 'E31',
|
|
'E32', 'E33', 'E34', 'E41', 'E43', 'E44', 'E45', 'E46', 'E47'])
|
|
}), 'on_change_with_remaining_qty')
|
|
|
|
@fields.depends('sequence_type')
|
|
def on_change_with_name(self, name=None):
|
|
if self.sequence_type:
|
|
return self.sequence_type.name
|
|
return None
|
|
|
|
@fields.depends('sequence_type')
|
|
def on_change_with_prefix(self, name=None):
|
|
if not self.sequence_type or not self.sequence_type.name:
|
|
return None
|
|
entry = _NCF_TYPE_MAP.get(self.sequence_type.name)
|
|
return entry[0] if entry else None
|
|
|
|
@fields.depends('sequence_type')
|
|
def on_change_with_padding(self, name=None):
|
|
if not self.sequence_type or not self.sequence_type.name:
|
|
return 0
|
|
entry = _NCF_TYPE_MAP.get(self.sequence_type.name)
|
|
return entry[1] if entry else 0
|
|
|
|
@fields.depends('from_number', 'until_number')
|
|
def on_change_with_approved_qty(self, name=None):
|
|
if self.from_number and self.until_number:
|
|
return self.until_number - self.from_number + 1
|
|
|
|
@fields.depends('number_next', 'until_number')
|
|
def on_change_with_remaining_qty(self, name=None):
|
|
if self.until_number is not None and self.number_next is not None:
|
|
return self.until_number - self.number_next + 1
|
|
|
|
@classmethod
|
|
def validate_fields(cls, records, field_names):
|
|
super().validate_fields(records, field_names)
|
|
if field_names & {
|
|
'prefix', 'padding', 'from_number', 'until_number',
|
|
'number_next'}:
|
|
for record in records:
|
|
record._validate_ncf_range()
|
|
|
|
def _validate_ncf_range(self):
|
|
if self.prefix not in NCF_PREFIXES:
|
|
return
|
|
expected_padding = 10 if self.prefix.startswith('E') else 8
|
|
if self.padding != expected_padding:
|
|
raise UserError(gettext(
|
|
'ncf_manager.msg_ncf_invalid_padding',
|
|
padding=expected_padding))
|
|
if self.from_number is None or self.until_number is None:
|
|
return
|
|
if self.from_number < 1 or self.until_number < self.from_number:
|
|
raise UserError(gettext('ncf_manager.msg_ncf_invalid_range'))
|
|
if (self.number_next is not None
|
|
and self.number_next < self.from_number):
|
|
raise UserError(gettext('ncf_manager.msg_ncf_next_before_range'))
|
|
|
|
class SequenceStrict(Sequence):
|
|
"Sequence Strict"
|
|
__name__ = 'ir.sequence.strict'
|
|
_table = None # Needed to reset Sequence._table
|
|
_strict = True
|
|
|