Integrate fiscal cancellation reasons
This commit is contained in:
+101
-4
@@ -7,9 +7,9 @@ from sql.aggregate import Sum
|
||||
from sql.conditionals import Coalesce
|
||||
from sql.functions import Round
|
||||
from trytond import backend
|
||||
from trytond.model import fields
|
||||
from trytond.model import ModelView, Workflow, fields
|
||||
from trytond.pool import Pool, PoolMeta
|
||||
from trytond.pyson import Eval, If, And
|
||||
from trytond.pyson import Eval, If, And, Bool
|
||||
from trytond.transaction import Transaction
|
||||
from trytond.exceptions import UserError
|
||||
from trytond.i18n import gettext
|
||||
@@ -293,6 +293,43 @@ class Invoice(metaclass=PoolMeta):
|
||||
isr_withholding_type_string = isr_withholding_type.translated(
|
||||
'isr_withholding_type')
|
||||
|
||||
ncf_cancellation_type = fields.Selection(
|
||||
_CANCELLATION_TYPE, 'NCF Cancellation Type',
|
||||
states={
|
||||
'required': (
|
||||
(Eval('state') == 'cancelled')
|
||||
& (Eval('type') == 'out')
|
||||
& Bool(Eval('ncf_number'))
|
||||
& ~Eval('context', {}).get('invoice_force_cancel', False)),
|
||||
'readonly': Eval('state') == 'cancelled',
|
||||
'invisible': Eval('type') == 'in',
|
||||
},
|
||||
depends=['state', 'type', 'ncf_number'])
|
||||
|
||||
ncf_cancellation_type_string = ncf_cancellation_type.translated(
|
||||
'ncf_cancellation_type')
|
||||
|
||||
ncf_cancellation_reason = fields.Text(
|
||||
'NCF Cancellation Reason',
|
||||
states={
|
||||
'required': (
|
||||
(Eval('state') == 'cancelled')
|
||||
& (Eval('type') == 'out')
|
||||
& Bool(Eval('ncf_number'))
|
||||
& ~Eval('context', {}).get('invoice_force_cancel', False)),
|
||||
'readonly': Eval('state') == 'cancelled',
|
||||
'invisible': Eval('type') == 'in',
|
||||
},
|
||||
depends=['state', 'type', 'ncf_number'])
|
||||
|
||||
@classmethod
|
||||
def __setup__(cls):
|
||||
super().__setup__()
|
||||
cls._check_modify_exclude.update({
|
||||
'ncf_cancellation_type',
|
||||
'ncf_cancellation_reason',
|
||||
})
|
||||
|
||||
@classmethod
|
||||
def __register__(cls, module_name):
|
||||
"""
|
||||
@@ -308,6 +345,7 @@ class Invoice(metaclass=PoolMeta):
|
||||
cls._ncf_prepare_legacy_invoice_ncf_type_column()
|
||||
super().__register__(module_name)
|
||||
cls._ncf_migrate_invoice_ncf_type_from_legacy_column()
|
||||
cls._ncf_migrate_legacy_cancellation_fields()
|
||||
|
||||
@classmethod
|
||||
def _ncf_column_exists(cls, cursor, table_name, column_name):
|
||||
@@ -447,6 +485,35 @@ class Invoice(metaclass=PoolMeta):
|
||||
unmatched,
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def _ncf_migrate_legacy_cancellation_fields(cls):
|
||||
"""
|
||||
Copy data from the former account_invoice_cancel_reason module.
|
||||
|
||||
The old module used generic cancellation_type/cancellation_reason
|
||||
columns. ncf_manager stores those values as fiscal DGII cancellation
|
||||
data so they can later feed the NCF annulment report.
|
||||
"""
|
||||
cursor = Transaction().connection.cursor()
|
||||
table_name = 'account_invoice'
|
||||
field_pairs = (
|
||||
('cancellation_type', 'ncf_cancellation_type'),
|
||||
('cancellation_reason', 'ncf_cancellation_reason'),
|
||||
)
|
||||
|
||||
for old_column, new_column in field_pairs:
|
||||
if not cls._ncf_column_exists(cursor, table_name, old_column):
|
||||
continue
|
||||
if not cls._ncf_column_exists(cursor, table_name, new_column):
|
||||
continue
|
||||
cursor.execute(f"""
|
||||
UPDATE {table_name}
|
||||
SET {new_column} = {old_column}
|
||||
WHERE {old_column} IS NOT NULL
|
||||
AND {old_column} <> ''
|
||||
AND ({new_column} IS NULL OR {new_column} = '')
|
||||
""")
|
||||
|
||||
@classmethod
|
||||
def search_rec_name(cls, name, clause):
|
||||
domain = super().search_rec_name(name, clause)
|
||||
@@ -1645,6 +1712,25 @@ class Invoice(metaclass=PoolMeta):
|
||||
raise UserError(gettext(
|
||||
'ncf_manager.msg_incorrect_lenght_ncf_number_modified'))
|
||||
|
||||
@staticmethod
|
||||
def _ncf_is_fiscal_cancellation_required(invoice):
|
||||
return (
|
||||
invoice.type == 'out'
|
||||
and bool(getattr(invoice, 'ncf_number', None)))
|
||||
|
||||
@classmethod
|
||||
def _ncf_check_cancellation_information(cls, invoice):
|
||||
if not cls._ncf_is_fiscal_cancellation_required(invoice):
|
||||
return
|
||||
if not invoice.ncf_cancellation_type:
|
||||
raise UserError(gettext(
|
||||
'ncf_manager.msg_missing_ncf_cancellation_type',
|
||||
invoice=invoice.rec_name))
|
||||
if not invoice.ncf_cancellation_reason:
|
||||
raise UserError(gettext(
|
||||
'ncf_manager.msg_missing_ncf_cancellation_reason',
|
||||
invoice=invoice.rec_name))
|
||||
|
||||
@classmethod
|
||||
def copy(cls, invoices, default=None):
|
||||
if default is None:
|
||||
@@ -1658,6 +1744,8 @@ class Invoice(metaclass=PoolMeta):
|
||||
default.setdefault('ncf_number_modified', None)
|
||||
default.setdefault('expiration_date', None)
|
||||
default.setdefault('security_code', None)
|
||||
default.setdefault('ncf_cancellation_type', None)
|
||||
default.setdefault('ncf_cancellation_reason', None)
|
||||
|
||||
return super(Invoice, cls).copy(invoices, default=default)
|
||||
|
||||
@@ -1674,11 +1762,20 @@ class Invoice(metaclass=PoolMeta):
|
||||
('/form/notebook/page[@id="tax_information"]/separator[@id="expense"]', 'states', {
|
||||
'invisible': Eval('type') == 'out',
|
||||
}),
|
||||
('/form/notebook/page[@id="cancel_reason"]', 'states', {
|
||||
'invisible': Eval('type') == 'in',
|
||||
('/form/notebook/page[@id="ncf_cancellation"]', 'states', {
|
||||
'invisible': (
|
||||
(Eval('type') == 'in') | ~Bool(Eval('ncf_number'))),
|
||||
}),
|
||||
]
|
||||
|
||||
@classmethod
|
||||
@ModelView.button
|
||||
@Workflow.transition('cancelled')
|
||||
def cancel(cls, invoices):
|
||||
if not Transaction().context.get('invoice_force_cancel'):
|
||||
for invoice in invoices:
|
||||
cls._ncf_check_cancellation_information(invoice)
|
||||
super().cancel(invoices)
|
||||
|
||||
@classmethod
|
||||
def _post(cls, invoices):
|
||||
|
||||
Reference in New Issue
Block a user