2017-06-16 09:32:47 +02:00
|
|
|
# The COPYRIGHT file at the top level of this repository contains the full
|
|
|
|
# copyright notices and license terms.
|
2019-11-15 10:03:26 +01:00
|
|
|
import hashlib
|
2020-04-05 09:16:17 +02:00
|
|
|
from ast import literal_eval
|
2017-07-17 10:40:33 +02:00
|
|
|
from decimal import Decimal
|
2017-07-04 11:44:44 +02:00
|
|
|
from trytond.model import ModelView, fields
|
2017-06-16 09:32:47 +02:00
|
|
|
from trytond.pool import Pool, PoolMeta
|
2018-01-18 17:08:54 +01:00
|
|
|
from trytond.pyson import Eval, Bool
|
2017-06-16 09:32:47 +02:00
|
|
|
from trytond.transaction import Transaction
|
2019-03-26 12:23:35 +01:00
|
|
|
from trytond.i18n import gettext
|
2019-05-10 15:01:23 +02:00
|
|
|
from trytond.exceptions import UserError, UserWarning
|
2020-03-18 08:08:01 +01:00
|
|
|
from trytond.wizard import Wizard, StateView, StateTransition, Button
|
2018-01-18 17:08:54 +01:00
|
|
|
from sql import Null
|
2017-06-16 09:32:47 +02:00
|
|
|
from sql.aggregate import Max
|
2019-04-17 13:18:48 +02:00
|
|
|
from trytond.tools import grouped_slice
|
2017-06-16 09:32:47 +02:00
|
|
|
from .aeat import (
|
|
|
|
OPERATION_KEY, BOOK_KEY, SEND_SPECIAL_REGIME_KEY,
|
|
|
|
RECEIVE_SPECIAL_REGIME_KEY, AEAT_INVOICE_STATE, IVA_SUBJECTED,
|
|
|
|
EXCEMPTION_CAUSE, INTRACOMUNITARY_TYPE, COMMUNICATION_TYPE)
|
|
|
|
|
|
|
|
|
2020-03-18 08:08:01 +01:00
|
|
|
__all__ = ['Invoice', 'ResetSIIKeysStart', 'ResetSIIKeys', 'ResetSIIKeysEnd']
|
2017-06-16 09:32:47 +02:00
|
|
|
|
2020-03-18 08:08:01 +01:00
|
|
|
_SII_INVOICE_KEYS = ['sii_book_key', 'sii_operation_key', 'sii_issued_key',
|
|
|
|
'sii_received_key', 'sii_subjected_key', 'sii_excemption_key',
|
2017-06-22 23:36:46 +02:00
|
|
|
'sii_intracomunity_key']
|
|
|
|
|
2019-04-17 13:18:48 +02:00
|
|
|
MAX_SII_LINES = 300
|
|
|
|
|
2017-06-16 09:32:47 +02:00
|
|
|
|
2018-10-11 15:32:09 +02:00
|
|
|
class Invoice(metaclass=PoolMeta):
|
2017-06-16 09:32:47 +02:00
|
|
|
__name__ = 'account.invoice'
|
|
|
|
|
|
|
|
sii_book_key = fields.Selection(BOOK_KEY, 'SII Book Key')
|
|
|
|
sii_operation_key = fields.Selection(OPERATION_KEY, 'SII Operation Key')
|
|
|
|
sii_issued_key = fields.Selection(SEND_SPECIAL_REGIME_KEY,
|
|
|
|
'SII Issued Key',
|
|
|
|
states={
|
|
|
|
'invisible': ~Eval('sii_book_key').in_(['E']),
|
2017-06-23 00:03:18 +02:00
|
|
|
}, depends=['sii_book_key'])
|
2017-06-16 09:32:47 +02:00
|
|
|
sii_received_key = fields.Selection(RECEIVE_SPECIAL_REGIME_KEY,
|
|
|
|
'SII Recived Key',
|
|
|
|
states={
|
2018-01-18 17:08:54 +01:00
|
|
|
'invisible': ~Eval('sii_book_key').in_(['R']),
|
2017-06-23 00:03:18 +02:00
|
|
|
}, depends=['sii_book_key'])
|
2017-06-16 09:32:47 +02:00
|
|
|
sii_subjected_key = fields.Selection(IVA_SUBJECTED, 'Subjected')
|
|
|
|
sii_excemption_key = fields.Selection(EXCEMPTION_CAUSE,
|
|
|
|
'Excemption Cause')
|
|
|
|
sii_intracomunity_key = fields.Selection(INTRACOMUNITARY_TYPE,
|
|
|
|
'SII Intracommunity Key',
|
|
|
|
states={
|
|
|
|
'invisible': ~Eval('sii_book_key').in_(['U']),
|
2017-06-23 00:03:18 +02:00
|
|
|
}, depends=['sii_book_key'])
|
2017-06-16 09:32:47 +02:00
|
|
|
sii_records = fields.One2Many('aeat.sii.report.lines', 'invoice',
|
2017-06-23 00:03:18 +02:00
|
|
|
"SII Report Lines")
|
2019-03-25 17:17:13 +01:00
|
|
|
sii_state = fields.Selection(AEAT_INVOICE_STATE,
|
|
|
|
'SII State', readonly=True)
|
|
|
|
sii_communication_type = fields.Selection(
|
|
|
|
COMMUNICATION_TYPE, 'SII Communication Type', readonly=True)
|
|
|
|
sii_pending_sending = fields.Boolean('SII Pending Sending Pending',
|
|
|
|
readonly=True)
|
|
|
|
sii_header = fields.Text('Header')
|
2017-06-16 09:32:47 +02:00
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def __setup__(cls):
|
|
|
|
super(Invoice, cls).__setup__()
|
2017-07-05 09:29:23 +02:00
|
|
|
sii_fields = ['sii_book_key', 'sii_operation_key',
|
2017-06-16 09:32:47 +02:00
|
|
|
'sii_received_key', 'sii_issued_key', 'sii_subjected_key',
|
2019-03-25 17:17:13 +01:00
|
|
|
'sii_excemption_key', 'sii_intracomunity_key','sii_pending_sending',
|
|
|
|
'sii_communication_type', 'sii_state', 'sii_header']
|
2017-07-05 09:29:23 +02:00
|
|
|
cls._check_modify_exclude += sii_fields
|
|
|
|
if hasattr(cls, '_intercompany_excluded_fields'):
|
|
|
|
cls._intercompany_excluded_fields += sii_fields
|
|
|
|
cls._intercompany_excluded_fields += ['sii_records']
|
2017-06-16 09:32:47 +02:00
|
|
|
|
2019-03-25 17:17:13 +01:00
|
|
|
@staticmethod
|
|
|
|
def default_sii_pending_sending():
|
|
|
|
return False
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def get_issued_sii_reports(cls):
|
|
|
|
pool = Pool()
|
|
|
|
Invoice = pool.get('account.invoice')
|
2019-11-12 22:27:09 +01:00
|
|
|
SIIReportLine = pool.get('aeat.sii.report.lines')
|
2019-03-25 17:17:13 +01:00
|
|
|
|
|
|
|
issued_invoices = {
|
|
|
|
'A0': {}, # 'A0', 'Registration of invoices/records'
|
|
|
|
'A1': {}, # 'A1', 'Amendment of invoices/records (registration errors)'
|
|
|
|
'D0': {}, # 'D0', 'Delete Invoices'
|
|
|
|
}
|
|
|
|
|
2019-04-17 13:18:48 +02:00
|
|
|
issued_invs = Invoice.search([
|
2019-03-25 17:17:13 +01:00
|
|
|
('sii_pending_sending', '=', True),
|
|
|
|
('sii_state', '=', 'Correcto'),
|
2019-04-02 10:56:04 +02:00
|
|
|
('sii_header', '!=', None),
|
2019-06-20 10:29:37 +02:00
|
|
|
('type', 'in', ['out']),
|
|
|
|
])
|
2019-03-25 17:17:13 +01:00
|
|
|
|
2019-04-17 13:18:48 +02:00
|
|
|
# search issued invoices [delete]
|
|
|
|
delete_issued_invoices = []
|
|
|
|
# search issued invoices [modify]
|
|
|
|
modify_issued_invoices = []
|
|
|
|
for issued_inv in issued_invs:
|
|
|
|
if not issued_inv.sii_records:
|
|
|
|
continue
|
|
|
|
sii_record_id = max([s.id for s in issued_inv.sii_records])
|
|
|
|
sii_record = SIIReportLine(sii_record_id)
|
|
|
|
if issued_inv.sii_header:
|
2020-04-05 09:16:17 +02:00
|
|
|
if (literal_eval(issued_inv.sii_header) ==
|
|
|
|
literal_eval(sii_record.sii_header)):
|
2019-04-17 13:18:48 +02:00
|
|
|
modify_issued_invoices.append(issued_inv)
|
2020-04-05 09:16:17 +02:00
|
|
|
else:
|
|
|
|
delete_issued_invoices.append(issued_inv)
|
2019-04-17 13:18:48 +02:00
|
|
|
|
2019-06-20 10:29:37 +02:00
|
|
|
periods = {}
|
2019-03-25 17:17:13 +01:00
|
|
|
for invoice in delete_issued_invoices:
|
|
|
|
period = invoice.move.period
|
2019-06-20 10:29:37 +02:00
|
|
|
if period in periods:
|
|
|
|
periods[period].append(invoice,)
|
2019-04-17 13:18:48 +02:00
|
|
|
else:
|
2019-06-20 10:29:37 +02:00
|
|
|
periods[period] = [invoice]
|
|
|
|
issued_invoices['D0'] = periods
|
2019-04-17 13:18:48 +02:00
|
|
|
|
2019-06-20 10:29:37 +02:00
|
|
|
periods2 = {}
|
2019-04-17 13:18:48 +02:00
|
|
|
for invoice in modify_issued_invoices:
|
|
|
|
period = invoice.move.period
|
2019-06-20 10:29:37 +02:00
|
|
|
if period in periods2:
|
|
|
|
periods2[period].append(invoice,)
|
2019-03-25 17:17:13 +01:00
|
|
|
else:
|
2019-06-20 10:29:37 +02:00
|
|
|
periods2[period] = [invoice]
|
|
|
|
issued_invoices['A1'] = periods2
|
2019-03-25 17:17:13 +01:00
|
|
|
|
|
|
|
# search issued invoices [new]
|
|
|
|
new_issued_invoices = Invoice.search([
|
|
|
|
('sii_state', 'in', (None, 'Incorrecto')),
|
|
|
|
('sii_pending_sending', '=', True),
|
|
|
|
('type', '=', 'out'),
|
|
|
|
])
|
|
|
|
|
2019-04-17 13:18:48 +02:00
|
|
|
# search possible deleted invoices in SII and not uploaded again
|
|
|
|
new_issued_invoices += Invoice.search([
|
|
|
|
('sii_state', '=', 'Anulada'),
|
2019-03-25 17:17:13 +01:00
|
|
|
('sii_pending_sending', '=', True),
|
|
|
|
('type', '=', 'out'),
|
2019-04-17 13:18:48 +02:00
|
|
|
('state', 'in', ['paid', 'posted']),
|
2019-03-25 17:17:13 +01:00
|
|
|
])
|
|
|
|
|
2019-04-17 13:18:48 +02:00
|
|
|
new_issued_invoices += delete_issued_invoices
|
|
|
|
|
2019-06-20 10:29:37 +02:00
|
|
|
periods1 = {}
|
2019-04-17 13:18:48 +02:00
|
|
|
for invoice in new_issued_invoices:
|
2019-03-25 17:17:13 +01:00
|
|
|
period = invoice.move.period
|
2019-06-20 10:29:37 +02:00
|
|
|
if period in periods1:
|
|
|
|
periods1[period].append(invoice,)
|
2019-03-25 17:17:13 +01:00
|
|
|
else:
|
2019-06-20 10:29:37 +02:00
|
|
|
periods1[period] = [invoice]
|
|
|
|
issued_invoices['A0'] = periods1
|
2019-04-17 13:18:48 +02:00
|
|
|
|
|
|
|
book_type = 'E' # Issued
|
|
|
|
return cls.create_sii_book(issued_invoices, book_type)
|
2019-03-25 17:17:13 +01:00
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def get_received_sii_reports(cls):
|
|
|
|
pool = Pool()
|
|
|
|
Invoice = pool.get('account.invoice')
|
2019-04-17 13:18:48 +02:00
|
|
|
SIIReportLine = pool.get('aeat.sii.report.lines')
|
2019-03-25 17:17:13 +01:00
|
|
|
|
|
|
|
received_invoices = {
|
|
|
|
'A0': {}, # 'A0', 'Registration of invoices/records'
|
|
|
|
'A1': {}, # 'A1', 'Amendment of invoices/records (registration errors)'
|
|
|
|
'D0': {}, # 'D0', 'Delete Invoices'
|
|
|
|
}
|
|
|
|
|
2019-04-17 13:18:48 +02:00
|
|
|
received_invs = Invoice.search([
|
2019-03-25 17:17:13 +01:00
|
|
|
('sii_pending_sending', '=', True),
|
|
|
|
('sii_state', '=', 'Correcto'),
|
2019-04-02 10:56:04 +02:00
|
|
|
('sii_header', '!=', None),
|
|
|
|
('type', '=', 'in'),
|
2019-03-25 17:17:13 +01:00
|
|
|
])
|
2019-04-17 13:18:48 +02:00
|
|
|
# search received invoices [delete]
|
|
|
|
delete_received_invoices = []
|
|
|
|
# search received invoices [modify]
|
|
|
|
modify_received_invoices = []
|
|
|
|
for received_inv in received_invs:
|
|
|
|
if not received_inv.sii_records:
|
|
|
|
continue
|
|
|
|
sii_record_id = max([s.id for s in received_inv.sii_records])
|
|
|
|
sii_record = SIIReportLine(sii_record_id)
|
|
|
|
if received_inv.sii_header:
|
2020-04-05 09:16:17 +02:00
|
|
|
if (literal_eval(received_inv.sii_header) ==
|
|
|
|
literal_eval(sii_record.sii_header)):
|
2019-04-17 13:18:48 +02:00
|
|
|
modify_received_invoices.append(received_inv)
|
2020-04-05 09:16:17 +02:00
|
|
|
else:
|
|
|
|
delete_received_invoices.append(received_inv)
|
2019-04-17 13:18:48 +02:00
|
|
|
|
2019-06-20 10:29:37 +02:00
|
|
|
periods2 = {}
|
|
|
|
for invoice in modify_received_invoices:
|
2019-03-25 17:17:13 +01:00
|
|
|
period = invoice.move.period
|
2019-06-20 10:29:37 +02:00
|
|
|
if period in periods2:
|
|
|
|
periods2[period].append(invoice,)
|
2019-04-17 13:18:48 +02:00
|
|
|
else:
|
2019-06-20 10:29:37 +02:00
|
|
|
periods2[period] = [invoice]
|
|
|
|
received_invoices['A1'] = periods2
|
2019-04-17 13:18:48 +02:00
|
|
|
|
2019-06-20 10:29:37 +02:00
|
|
|
periods = {}
|
|
|
|
for invoice in delete_received_invoices:
|
2019-04-17 13:18:48 +02:00
|
|
|
period = invoice.move.period
|
2019-06-20 10:29:37 +02:00
|
|
|
if period in periods:
|
|
|
|
periods[period].append(invoice,)
|
2019-03-25 17:17:13 +01:00
|
|
|
else:
|
2019-06-20 10:29:37 +02:00
|
|
|
periods[period] = [invoice]
|
|
|
|
received_invoices['D0'] = periods
|
2019-03-25 17:17:13 +01:00
|
|
|
|
|
|
|
# search received invoices [new]
|
|
|
|
new_received_invoices = Invoice.search([
|
|
|
|
('sii_state', 'in', (None, 'Incorrecto')),
|
|
|
|
('sii_pending_sending', '=', True),
|
|
|
|
('type', '=', 'in'),
|
|
|
|
])
|
|
|
|
|
2019-04-17 13:18:48 +02:00
|
|
|
# search possible deleted invoices in SII and not uploaded again
|
|
|
|
new_received_invoices += Invoice.search([
|
|
|
|
('sii_state', '=', 'Anulada'),
|
2019-04-02 10:56:04 +02:00
|
|
|
('sii_pending_sending', '=', True),
|
|
|
|
('type', '=', 'in'),
|
2019-04-17 13:18:48 +02:00
|
|
|
('state', 'in', ['paid', 'posted']),
|
2019-04-02 10:56:04 +02:00
|
|
|
])
|
2019-03-25 17:17:13 +01:00
|
|
|
|
2019-04-17 13:18:48 +02:00
|
|
|
new_received_invoices += delete_received_invoices
|
|
|
|
|
2019-06-20 10:29:37 +02:00
|
|
|
periods1 = {}
|
2019-04-17 13:18:48 +02:00
|
|
|
for invoice in new_received_invoices:
|
2019-03-25 17:17:13 +01:00
|
|
|
period = invoice.move.period
|
2019-06-20 10:29:37 +02:00
|
|
|
if period in periods1:
|
|
|
|
periods1[period].append(invoice,)
|
2019-03-25 17:17:13 +01:00
|
|
|
else:
|
2019-06-20 10:29:37 +02:00
|
|
|
periods1[period] = [invoice]
|
|
|
|
received_invoices['A0'] = periods1
|
2019-03-25 17:17:13 +01:00
|
|
|
|
|
|
|
book_type = 'R' # Received
|
|
|
|
return cls.create_sii_book(received_invoices, book_type)
|
|
|
|
|
|
|
|
@classmethod
|
2019-04-17 13:18:48 +02:00
|
|
|
def create_sii_book(cls, book_invoices, book):
|
2019-03-25 17:17:13 +01:00
|
|
|
pool = Pool()
|
|
|
|
SIIReport = pool.get('aeat.sii.report')
|
|
|
|
SIIReportLine = pool.get('aeat.sii.report.lines')
|
|
|
|
Company = Pool().get('company.company')
|
|
|
|
|
|
|
|
company = Transaction().context.get('company')
|
|
|
|
company = Company(company)
|
|
|
|
company_vat = company.party.sii_vat_code
|
|
|
|
|
2019-04-17 13:18:48 +02:00
|
|
|
cursor = Transaction().connection.cursor()
|
|
|
|
report_line_table = SIIReportLine.__table__()
|
|
|
|
|
|
|
|
reports = []
|
|
|
|
for operation in ['D0', 'A1', 'A0']:
|
|
|
|
values = book_invoices[operation]
|
|
|
|
delete = True if operation == 'D0' else False
|
2019-04-27 06:15:34 +02:00
|
|
|
for period, invoices in values.items():
|
2019-04-17 13:18:48 +02:00
|
|
|
for invs in grouped_slice(invoices, MAX_SII_LINES):
|
|
|
|
report = SIIReport()
|
|
|
|
report.company = company
|
|
|
|
report.company_vat = company_vat
|
|
|
|
report.fiscalyear = period.fiscalyear
|
|
|
|
report.period = period
|
|
|
|
report.operation_type = operation
|
|
|
|
report.book = book
|
|
|
|
report.save()
|
|
|
|
reports.append(report)
|
|
|
|
|
|
|
|
values = []
|
|
|
|
for inv in invs:
|
|
|
|
sii_header = str(inv.get_sii_header(inv, delete))
|
|
|
|
values.append([report.id, inv.id, sii_header, company.id])
|
|
|
|
|
|
|
|
cursor.execute(*report_line_table.insert(
|
|
|
|
columns=[report_line_table.report,
|
|
|
|
report_line_table.invoice,
|
|
|
|
report_line_table.sii_header,
|
|
|
|
report_line_table.company],
|
|
|
|
values=values
|
|
|
|
))
|
|
|
|
|
|
|
|
return reports
|
2019-03-25 17:17:13 +01:00
|
|
|
|
2017-06-16 09:32:47 +02:00
|
|
|
@classmethod
|
|
|
|
def search_sii_state(cls, name, clause):
|
|
|
|
pool = Pool()
|
|
|
|
SIILines = pool.get('aeat.sii.report.lines')
|
|
|
|
|
|
|
|
table = SIILines.__table__()
|
|
|
|
|
|
|
|
cursor = Transaction().connection.cursor()
|
|
|
|
cursor.execute(*table.select(Max(table.id), table.invoice,
|
|
|
|
group_by=table.invoice))
|
|
|
|
|
|
|
|
invoices = []
|
|
|
|
lines = []
|
|
|
|
for id_, invoice in cursor.fetchall():
|
|
|
|
invoices.append(invoice)
|
|
|
|
lines.append(id_)
|
|
|
|
|
2017-06-29 11:10:15 +02:00
|
|
|
is_none = False
|
|
|
|
c = clause[-1]
|
|
|
|
if isinstance(clause[-1], list):
|
|
|
|
if None in clause[-1]:
|
|
|
|
is_none = True
|
|
|
|
c.remove(None)
|
|
|
|
|
|
|
|
c0 = []
|
2019-06-20 10:29:37 +02:00
|
|
|
if clause[-1] == None or is_none:
|
2017-06-29 11:10:15 +02:00
|
|
|
c0 = [('id', 'not in', invoices)]
|
2017-06-16 09:32:47 +02:00
|
|
|
|
2019-06-20 10:29:37 +02:00
|
|
|
clause2 = [tuple(('state',)) + tuple(clause[1:])] + \
|
|
|
|
[('id', 'in', lines)]
|
2017-06-16 09:32:47 +02:00
|
|
|
|
|
|
|
res_lines = SIILines.search(clause2)
|
2017-06-29 11:10:15 +02:00
|
|
|
|
|
|
|
if is_none:
|
2019-04-17 13:18:48 +02:00
|
|
|
return ['OR', c0, [('id', 'in', [x.invoice.id for x in res_lines])]]
|
2017-06-29 11:10:15 +02:00
|
|
|
else:
|
|
|
|
return [('id', 'in', [x.invoice.id for x in res_lines])]
|
2017-06-16 09:32:47 +02:00
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def get_sii_state(cls, invoices, names):
|
|
|
|
pool = Pool()
|
|
|
|
SIILines = pool.get('aeat.sii.report.lines')
|
|
|
|
SIIReport = pool.get('aeat.sii.report')
|
|
|
|
|
|
|
|
result = {}
|
2017-07-05 13:25:22 +02:00
|
|
|
|
2017-06-16 09:32:47 +02:00
|
|
|
for name in names:
|
|
|
|
result[name] = dict((i.id, None) for i in invoices)
|
|
|
|
|
|
|
|
table = SIILines.__table__()
|
|
|
|
report = SIIReport.__table__()
|
|
|
|
cursor = Transaction().connection.cursor()
|
|
|
|
join = table.join(report, condition=table.report == report.id)
|
|
|
|
|
|
|
|
cursor.execute(*table.select(Max(table.id), table.invoice,
|
|
|
|
where=(table.invoice.in_([x.id for x in invoices]) &
|
2018-01-18 17:08:54 +01:00
|
|
|
(table.state != Null)),
|
2017-06-16 09:32:47 +02:00
|
|
|
group_by=table.invoice))
|
|
|
|
|
|
|
|
lines = [a[0] for a in cursor.fetchall()]
|
|
|
|
|
|
|
|
if lines:
|
|
|
|
cursor.execute(*join.select(table.state, report.operation_type,
|
2017-07-05 13:25:22 +02:00
|
|
|
table.invoice,
|
2018-01-18 17:08:54 +01:00
|
|
|
where=((table.id.in_(lines)) & (table.state != Null) &
|
2017-07-05 13:25:22 +02:00
|
|
|
(table.company == report.company))))
|
2017-06-16 09:32:47 +02:00
|
|
|
|
|
|
|
for state, op, inv in cursor.fetchall():
|
|
|
|
if 'sii_state' in names:
|
|
|
|
result['sii_state'][inv] = state
|
|
|
|
if 'sii_communication_type' in names:
|
|
|
|
result['sii_communication_type'][inv] = op
|
|
|
|
|
|
|
|
return result
|
|
|
|
|
|
|
|
def _credit(self):
|
2018-07-04 15:48:40 +02:00
|
|
|
credit = super(Invoice, self)._credit()
|
2017-06-22 23:36:46 +02:00
|
|
|
for field in _SII_INVOICE_KEYS:
|
2018-07-04 15:48:40 +02:00
|
|
|
setattr(credit, field, getattr(self, field))
|
2017-06-16 09:32:47 +02:00
|
|
|
|
2019-07-11 17:16:20 +02:00
|
|
|
credit.sii_operation_key = 'R1'
|
2018-07-04 15:48:40 +02:00
|
|
|
return credit
|
2017-06-16 09:32:47 +02:00
|
|
|
|
2017-07-04 11:44:44 +02:00
|
|
|
def _set_sii_keys(self):
|
2018-07-08 23:01:54 +02:00
|
|
|
tax = None
|
|
|
|
for t in self.taxes:
|
2019-01-03 15:58:16 +01:00
|
|
|
if t.tax.sii_book_key:
|
2018-07-08 23:09:34 +02:00
|
|
|
tax = t.tax
|
2018-07-08 23:01:54 +02:00
|
|
|
break
|
2017-07-04 11:44:44 +02:00
|
|
|
if not tax:
|
|
|
|
return
|
|
|
|
for field in _SII_INVOICE_KEYS:
|
2018-07-08 23:09:34 +02:00
|
|
|
setattr(self, field, getattr(tax, field))
|
2017-07-04 11:44:44 +02:00
|
|
|
|
2017-06-22 23:36:46 +02:00
|
|
|
@fields.depends(*_SII_INVOICE_KEYS)
|
2017-06-16 09:32:47 +02:00
|
|
|
def _on_change_lines_taxes(self):
|
|
|
|
super(Invoice, self)._on_change_lines_taxes()
|
2017-06-22 23:36:46 +02:00
|
|
|
for field in _SII_INVOICE_KEYS:
|
2017-06-16 09:32:47 +02:00
|
|
|
if getattr(self, field):
|
|
|
|
return
|
2017-07-04 11:44:44 +02:00
|
|
|
self._set_sii_keys()
|
2017-06-23 10:01:47 +02:00
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def copy(cls, records, default=None):
|
|
|
|
if default is None:
|
|
|
|
default = {}
|
|
|
|
default = default.copy()
|
2019-06-20 17:30:27 +02:00
|
|
|
default.setdefault('sii_records')
|
|
|
|
default.setdefault('sii_state')
|
|
|
|
default.setdefault('sii_communication_type')
|
|
|
|
default.setdefault('sii_operation_key')
|
|
|
|
default.setdefault('sii_pending_sending')
|
|
|
|
default.setdefault('sii_header')
|
2017-06-23 10:01:47 +02:00
|
|
|
return super(Invoice, cls).copy(records, default=default)
|
2017-06-29 10:35:26 +02:00
|
|
|
|
2019-11-12 22:16:18 +01:00
|
|
|
def _get_sii_operation_key(self):
|
|
|
|
return 'R1' if self.untaxed_amount < Decimal('0.0') else 'F1'
|
|
|
|
|
2017-07-04 11:44:44 +02:00
|
|
|
@classmethod
|
2020-03-18 08:08:01 +01:00
|
|
|
def reset_sii_keys(cls, invoices):
|
2017-07-04 11:44:44 +02:00
|
|
|
to_write = []
|
2020-03-18 08:08:01 +01:00
|
|
|
for invoice in invoices:
|
2022-02-15 16:09:53 +01:00
|
|
|
if invoice.sii_state in ('Correcto', 'Correcta'):
|
2020-03-18 08:08:01 +01:00
|
|
|
continue
|
|
|
|
for field in _SII_INVOICE_KEYS:
|
|
|
|
setattr(invoice, field, None)
|
|
|
|
invoice._set_sii_keys()
|
|
|
|
if not invoice.sii_operation_key:
|
|
|
|
invoice.sii_operation_key = invoice._get_sii_operation_key()
|
|
|
|
to_write.extend(([invoice], invoice._save_values))
|
2017-07-04 11:44:44 +02:00
|
|
|
|
|
|
|
if to_write:
|
|
|
|
cls.write(*to_write)
|
|
|
|
|
2018-06-19 16:20:35 +02:00
|
|
|
@classmethod
|
2019-01-23 12:17:44 +01:00
|
|
|
def process(cls, invoices):
|
|
|
|
super(Invoice, cls).process(invoices)
|
2018-06-19 16:20:35 +02:00
|
|
|
invoices_sii = ''
|
|
|
|
for invoice in invoices:
|
2019-01-23 12:17:44 +01:00
|
|
|
if invoice.state != 'draft':
|
|
|
|
continue
|
2018-06-19 16:20:35 +02:00
|
|
|
if invoice.sii_state:
|
|
|
|
invoices_sii += '\n%s: %s' % (invoice.number, invoice.sii_state)
|
|
|
|
if invoices_sii:
|
2019-03-26 12:23:35 +01:00
|
|
|
raise UserError(gettext('aeat_sii.msg_invoices_sii',
|
|
|
|
invoices=invoices_sii))
|
2018-06-19 16:20:35 +02:00
|
|
|
|
2019-03-25 17:17:13 +01:00
|
|
|
@classmethod
|
|
|
|
def draft(cls, invoices):
|
2019-05-10 15:01:23 +02:00
|
|
|
pool = Pool()
|
2019-05-23 12:25:16 +02:00
|
|
|
Warning = pool.get('res.user.warning')
|
2019-03-25 17:17:13 +01:00
|
|
|
super(Invoice, cls).draft(invoices)
|
2019-11-15 10:03:26 +01:00
|
|
|
invoices_sii = []
|
2019-03-25 17:17:13 +01:00
|
|
|
to_write = []
|
|
|
|
for invoice in invoices:
|
|
|
|
to_write.extend(([invoice], {'sii_pending_sending': False}))
|
|
|
|
|
|
|
|
if invoice.sii_state:
|
2019-11-15 10:03:26 +01:00
|
|
|
invoices_sii.append('%s: %s' % (
|
|
|
|
invoice.number, invoice.sii_state))
|
2019-03-25 17:17:13 +01:00
|
|
|
for record in invoice.sii_records:
|
|
|
|
if record.report.state == 'draft':
|
2019-03-26 12:23:35 +01:00
|
|
|
raise UserError(gettext('aeat_sii.invoices_sii_pending'))
|
2019-03-25 17:17:13 +01:00
|
|
|
|
|
|
|
if invoices_sii:
|
2019-11-15 10:03:26 +01:00
|
|
|
warning_name = 'invoices_sii.' + hashlib.md5(
|
|
|
|
''.join(invoices_sii).encode('utf-8')).hexdigest()
|
2019-05-10 15:01:23 +02:00
|
|
|
if Warning.check(warning_name):
|
2019-11-15 10:03:26 +01:00
|
|
|
raise UserWarning(warning_name,
|
|
|
|
gettext('aeat_sii.msg_invoices_sii',
|
|
|
|
invoices='\n'.join(invoices_sii)))
|
2019-03-25 17:17:13 +01:00
|
|
|
|
|
|
|
if to_write:
|
|
|
|
cls.write(*to_write)
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def post(cls, invoices):
|
|
|
|
to_write = []
|
2020-04-06 11:58:45 +02:00
|
|
|
|
|
|
|
invoices2checksii = []
|
|
|
|
for invoice in invoices:
|
|
|
|
if not invoice.move or invoice.move.state == 'draft':
|
|
|
|
invoices2checksii.append(invoice)
|
|
|
|
|
2019-03-25 17:17:13 +01:00
|
|
|
super(Invoice, cls).post(invoices)
|
|
|
|
|
2020-04-05 09:41:59 +02:00
|
|
|
for invoice in invoices2checksii:
|
2019-03-25 17:17:13 +01:00
|
|
|
values = {}
|
|
|
|
if invoice.sii_book_key:
|
2020-03-18 08:08:01 +01:00
|
|
|
if not invoice.sii_operation_key:
|
|
|
|
values['sii_operation_key'] =\
|
|
|
|
invoice._get_sii_operation_key()
|
2019-03-25 17:17:13 +01:00
|
|
|
values['sii_pending_sending'] = True
|
|
|
|
values['sii_header'] = str(cls.get_sii_header(invoice, False))
|
|
|
|
to_write.extend(([invoice], values))
|
|
|
|
if to_write:
|
|
|
|
cls.write(*to_write)
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def cancel(cls, invoices):
|
|
|
|
cls.write(invoices, {'sii_pending_sending': False})
|
|
|
|
return super(Invoice, cls).cancel(invoices)
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def get_sii_header(cls, invoice, delete):
|
|
|
|
pool = Pool()
|
2019-11-18 10:29:03 +01:00
|
|
|
IssuedMapper = pool.get('aeat.sii.issued.invoice.mapper')
|
|
|
|
ReceivedMapper = pool.get('aeat.sii.recieved.invoice.mapper')
|
2019-03-25 17:17:13 +01:00
|
|
|
|
|
|
|
if delete:
|
2019-06-20 10:29:37 +02:00
|
|
|
rline = [x for x in invoice.sii_records if x.state == 'Correcto'
|
|
|
|
and x.sii_header != None]
|
2019-03-25 17:17:13 +01:00
|
|
|
if rline:
|
|
|
|
return rline[0].sii_header
|
|
|
|
if invoice.type == 'out':
|
2019-11-18 10:29:03 +01:00
|
|
|
mapper = IssuedMapper()
|
|
|
|
header = mapper.build_delete_request(invoice)
|
2019-03-25 17:17:13 +01:00
|
|
|
else:
|
2019-11-18 10:29:03 +01:00
|
|
|
mapper = ReceivedMapper()
|
|
|
|
header = mapper.build_delete_request(invoice)
|
2019-03-25 17:17:13 +01:00
|
|
|
return header
|
2020-03-18 08:08:01 +01:00
|
|
|
|
|
|
|
|
|
|
|
class ResetSIIKeysStart(ModelView):
|
|
|
|
"""
|
|
|
|
Reset to default SII Keys Start
|
|
|
|
"""
|
|
|
|
__name__ = "aeat.sii.reset.keys.start"
|
|
|
|
|
|
|
|
|
|
|
|
class ResetSIIKeysEnd(ModelView):
|
|
|
|
"""
|
|
|
|
Reset to default SII Keys End
|
|
|
|
"""
|
|
|
|
__name__ = "aeat.sii.reset.keys.end"
|
|
|
|
|
|
|
|
|
|
|
|
class ResetSIIKeys(Wizard):
|
|
|
|
"""
|
|
|
|
Reset to default SII Keys
|
|
|
|
"""
|
|
|
|
__name__ = "aeat.sii.reset.keys"
|
|
|
|
|
|
|
|
start = StateView('aeat.sii.reset.keys.start',
|
|
|
|
'aeat_sii.aeat_sii_reset_keys_start_view', [
|
|
|
|
Button('Cancel', 'end', 'tryton-cancel'),
|
|
|
|
Button('Reset', 'reset', 'tryton-ok', default=True),
|
|
|
|
])
|
|
|
|
reset = StateTransition()
|
|
|
|
done = StateView('aeat.sii.reset.keys.end',
|
|
|
|
'aeat_sii.aeat_sii_reset_keys_end_view', [
|
|
|
|
Button('Ok', 'end', 'tryton-ok', default=True),
|
|
|
|
])
|
|
|
|
|
|
|
|
def transition_reset(self):
|
|
|
|
pool = Pool()
|
|
|
|
Invoice = pool.get('account.invoice')
|
|
|
|
invoices = Invoice.browse(Transaction().context['active_ids'])
|
|
|
|
Invoice.reset_sii_keys(invoices)
|
|
|
|
return 'done'
|