Fix mapper instantiation, vat_code format, identifier_type party selection

This commit is contained in:
Sergio Morillo 2017-06-08 08:36:48 +02:00
parent e584e3ddc1
commit bad19c9136
15 changed files with 113 additions and 90 deletions

68
aeat.py
View file

@ -247,7 +247,7 @@ class SIIReport(Workflow, ModelSQL, ModelView):
'readonly': Eval('state') != 'draft', 'readonly': Eval('state') != 'draft',
}, depends=['state']) }, depends=['state'])
currency = fields.Function(fields.Many2One('currency.currency', currency = fields.Function(fields.Many2One('currency.currency',
'Currency'), 'get_currency') 'Currency'), 'on_change_with_currency')
fiscalyear = fields.Many2One('account.fiscalyear', 'Fiscal Year', fiscalyear = fields.Many2One('account.fiscalyear', 'Fiscal Year',
required=True, states={ required=True, states={
'readonly': Eval('state') != 'draft', 'readonly': Eval('state') != 'draft',
@ -261,7 +261,7 @@ class SIIReport(Workflow, ModelSQL, ModelView):
domain=[('fiscalyear', '=', Eval('fiscalyear'))], domain=[('fiscalyear', '=', Eval('fiscalyear'))],
states={ states={
'readonly': Eval('state') != 'draft', 'readonly': Eval('state') != 'draft',
}, depends=['state']) }, depends=['state', 'fiscalyear'])
operation_type = fields.Selection(COMMUNICATION_TYPE, 'Operation Type', operation_type = fields.Selection(COMMUNICATION_TYPE, 'Operation Type',
required=True, required=True,
@ -335,14 +335,14 @@ class SIIReport(Workflow, ModelSQL, ModelView):
('cancelled', 'draft'), ('cancelled', 'draft'),
)) ))
@staticmethod @staticmethod
def default_company(): def default_company():
return Transaction().context.get('company') return Transaction().context.get('company')
def get_currency(self, name): @fields.depends('company')
return self.company.currency.id def on_change_with_currency(self, name):
if self.company:
return self.company.currency.id
@staticmethod @staticmethod
def default_fiscalyear(): def default_fiscalyear():
@ -356,12 +356,21 @@ class SIIReport(Workflow, ModelSQL, ModelView):
@staticmethod @staticmethod
def default_version(): def default_version():
return '0.6' return '0.7'
@fields.depends('company') @fields.depends('company')
def on_change_with_company_vat(self): def on_change_with_company_vat(self):
if self.company: if self.company:
return self.company.party.vat_code return self.company.party.sii_vat_code
@classmethod
def copy(cls, records, default=None):
if default is None:
default = {}
else:
default = default.copy()
default['communication_state'] = None
return super(SIIReport, cls).copy(records, default=default)
@classmethod @classmethod
@ModelView.button @ModelView.button
@ -440,10 +449,10 @@ class SIIReport(Workflow, ModelSQL, ModelView):
_logger.info('Sending report %s to AEAT SII', self.id) _logger.info('Sending report %s to AEAT SII', self.id)
headers = mapping.get_headers( headers = mapping.get_headers(
name=self.company.party.name, name=self.company.party.name,
vat=self.company.party.vat_code, vat=self.company_vat,
comm_kind=self.operation_type) comm_kind=self.operation_type)
pool = Pool() pool = Pool()
mapper = pool.get('aeat.sii.issued.invoice.mapper')(pool=pool) mapper = IssuedTrytonInvoiceMapper(pool=pool)
res = None res = None
with self.company.tmp_ssl_credentials() as (crt, key): with self.company.tmp_ssl_credentials() as (crt, key):
srv = service.bind_issued_invoices_service(crt, key, test=True) srv = service.bind_issued_invoices_service(crt, key, test=True)
@ -467,10 +476,10 @@ class SIIReport(Workflow, ModelSQL, ModelView):
def delete_issued_invoices(self): def delete_issued_invoices(self):
headers = mapping.get_headers( headers = mapping.get_headers(
name=self.company.party.name, name=self.company.party.name,
vat=self.company.party.vat_code, vat=self.company_vat,
comm_kind=self.operation_type) comm_kind=self.operation_type)
pool = Pool() pool = Pool()
mapper = pool.get('aeat.sii.issued.invoice.mapper')(pool=pool) mapper = IssuedTrytonInvoiceMapper(pool=pool)
res = None res = None
with self.company.tmp_ssl_credentials() as (crt, key): with self.company.tmp_ssl_credentials() as (crt, key):
srv = service.bind_issued_invoices_service(crt, key, test=True) srv = service.bind_issued_invoices_service(crt, key, test=True)
@ -497,7 +506,7 @@ class SIIReport(Workflow, ModelSQL, ModelView):
Invoice = pool.get('account.invoice') Invoice = pool.get('account.invoice')
headers = mapping.get_headers( headers = mapping.get_headers(
name=self.company.party.name, name=self.company.party.name,
vat=self.company.party.vat_code, vat=self.company_vat,
comm_kind=self.operation_type) comm_kind=self.operation_type)
with self.company.tmp_ssl_credentials() as (crt, key): with self.company.tmp_ssl_credentials() as (crt, key):
@ -544,10 +553,10 @@ class SIIReport(Workflow, ModelSQL, ModelView):
_logger.info('Sending report %s to AEAT SII', self.id) _logger.info('Sending report %s to AEAT SII', self.id)
headers = mapping.get_headers( headers = mapping.get_headers(
name=self.company.party.name, name=self.company.party.name,
vat=self.company.party.vat_code, vat=self.company_vat,
comm_kind=self.operation_type) comm_kind=self.operation_type)
pool = Pool() pool = Pool()
mapper = pool.get('aeat.sii.recieved.invoice.mapper')(pool=pool) mapper = RecievedTrytonInvoiceMapper(pool=pool)
res = None res = None
with self.company.tmp_ssl_credentials() as (crt, key): with self.company.tmp_ssl_credentials() as (crt, key):
srv = service.bind_recieved_invoices_service(crt, key, test=True) srv = service.bind_recieved_invoices_service(crt, key, test=True)
@ -571,10 +580,10 @@ class SIIReport(Workflow, ModelSQL, ModelView):
def delete_recieved_invoices(self): def delete_recieved_invoices(self):
headers = mapping.get_headers( headers = mapping.get_headers(
name=self.company.party.name, name=self.company.party.name,
vat=self.company.party.vat_code, vat=self.company_vat,
comm_kind=self.operation_type) comm_kind=self.operation_type)
pool = Pool() pool = Pool()
mapper = pool.get('aeat.sii.recieved.invoice.mapper')(pool=pool) mapper = RecievedTrytonInvoiceMapper(pool=pool)
res = None res = None
with self.company.tmp_ssl_credentials() as (crt, key): with self.company.tmp_ssl_credentials() as (crt, key):
srv = service.bind_recieved_invoices_service(crt, key, test=True) srv = service.bind_recieved_invoices_service(crt, key, test=True)
@ -601,7 +610,7 @@ class SIIReport(Workflow, ModelSQL, ModelView):
Invoice = pool.get('account.invoice') Invoice = pool.get('account.invoice')
headers = mapping.get_headers( headers = mapping.get_headers(
name=self.company.party.name, name=self.company.party.name,
vat=self.company.party.vat_code, vat=self.company_vat,
comm_kind=self.operation_type) comm_kind=self.operation_type)
with self.company.tmp_ssl_credentials() as (crt, key): with self.company.tmp_ssl_credentials() as (crt, key):
@ -646,23 +655,23 @@ class SIIReport(Workflow, ModelSQL, ModelView):
}) })
class BaseTrytonInvoiceMapper(Model): class BaseTrytonInvoiceMapper(object):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super(BaseTrytonInvoiceMapper, self).__init__(*args, **kwargs) super(BaseTrytonInvoiceMapper, self).__init__()
self.pool = Pool() self.pool = Pool()
year = attrgetter('move.period.fiscalyear.name') year = attrgetter('move.period.fiscalyear.name')
period = attrgetter('move.period.start_date.month') period = attrgetter('move.period.start_date.month')
nif = attrgetter('company.party.vat_code') nif = attrgetter('company.party.sii_vat_code')
issue_date = attrgetter('invoice_date') issue_date = attrgetter('invoice_date')
invoice_kind = attrgetter('sii_operation_key') invoice_kind = attrgetter('sii_operation_key')
rectified_invoice_kind = callback_utils.fixed_value('I') rectified_invoice_kind = callback_utils.fixed_value('I')
not_exempt_kind = attrgetter('sii_subjected') not_exempt_kind = attrgetter('sii_subjected')
counterpart_name = attrgetter('party.name') counterpart_name = attrgetter('party.name')
counterpart_nif = attrgetter('party.vat_code') counterpart_nif = attrgetter('party.sii_vat_code')
counterpart_id_type = attrgetter('party.identifier_type') counterpart_id_type = attrgetter('party.identifier_type')
counterpart_country = attrgetter('party.vat_country') counterpart_country = attrgetter('party.sii_vat_country')
counterpart_id = counterpart_nif counterpart_id = counterpart_nif
taxes = attrgetter('taxes') taxes = attrgetter('taxes')
tax_rate = attrgetter('tax.rate') tax_rate = attrgetter('tax.rate')
@ -697,7 +706,6 @@ class IssuedTrytonInvoiceMapper(
""" """
Tryton Issued Invoice to AEAT mapper Tryton Issued Invoice to AEAT mapper
""" """
__name__ = 'aeat.sii.issued.invoice.mapper'
serial_number = attrgetter('number') serial_number = attrgetter('number')
specialkey_or_trascendence = attrgetter('sii_issued_key') specialkey_or_trascendence = attrgetter('sii_issued_key')
@ -708,7 +716,6 @@ class RecievedTrytonInvoiceMapper(
""" """
Tryton Recieved Invoice to AEAT mapper Tryton Recieved Invoice to AEAT mapper
""" """
__name__ = 'aeat.sii.recieved.invoice.mapper'
serial_number = attrgetter('reference') serial_number = attrgetter('reference')
specialkey_or_trascendence = attrgetter('sii_received_key') specialkey_or_trascendence = attrgetter('sii_received_key')
move_date = attrgetter('move.date') move_date = attrgetter('move.date')
@ -735,3 +742,14 @@ class SIIReportLine(ModelSQL, ModelView):
@staticmethod @staticmethod
def default_company(): def default_company():
return Transaction().context.get('company') return Transaction().context.get('company')
@classmethod
def copy(cls, records, default=None):
if default is None:
default = {}
else:
default = default.copy()
default['state'] = None
default['communication_code'] = None
default['communication_msg'] = None
return super(SIIReportLine, cls).copy(records, default=default)

View file

@ -1,4 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<!-- The COPYRIGHT file at the top level of this repository contains the full
copyright notices and license terms. -->
<tryton> <tryton>
<data> <data>

View file

@ -1,6 +1,5 @@
#This file is part party_comment module for Tryton. # The COPYRIGHT file at the top level of this repository contains
#The COPYRIGHT file at the top level of this repository contains # the full copyright notices and license terms.
#the full copyright notices and license terms.
from logging import getLogger from logging import getLogger
from contextlib import contextmanager from contextlib import contextmanager
@ -27,6 +26,7 @@ class Company:
cls._error_messages.update({ cls._error_messages.update({
'missing_fernet_key': "Missing Fernet key configuration", 'missing_fernet_key': "Missing Fernet key configuration",
'missing_pem_cert': "Missing PEM certificate"
}) })
pem_certificate = fields.Binary( pem_certificate = fields.Binary(
@ -90,6 +90,8 @@ class Company:
@contextmanager @contextmanager
def tmp_ssl_credentials(self): def tmp_ssl_credentials(self):
if not self.pem_certificate:
self.raise_user_error('missing_pem_cert')
with NamedTemporaryFile(suffix='.crt') as crt: with NamedTemporaryFile(suffix='.crt') as crt:
with NamedTemporaryFile(suffix='.pem') as key: with NamedTemporaryFile(suffix='.pem') as key:
crt.write(self.pem_certificate) crt.write(self.pem_certificate)

View file

@ -1,4 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0"?>
<!-- The COPYRIGHT file at the top level of this repository contains the full
copyright notices and license terms. -->
<tryton> <tryton>
<data> <data>

View file

@ -61,7 +61,7 @@ class Invoice:
table = SIILines.__table__() table = SIILines.__table__()
cursor = Transaction().connection.cursor() cursor = Transaction().cursor
cursor.execute(*table.select(Max(table.id), table.invoice, cursor.execute(*table.select(Max(table.id), table.invoice,
group_by=table.invoice)) group_by=table.invoice))
@ -86,10 +86,10 @@ class Invoice:
SIILines = pool.get('aeat.sii.report.lines') SIILines = pool.get('aeat.sii.report.lines')
result = {} result = {}
for name in names: for name in names:
result[name] = dict((i.id, '') for i in invoices) result[name] = dict((i.id, None) for i in invoices)
table = SIILines.__table__() table = SIILines.__table__()
cursor = Transaction().connection.cursor() cursor = Transaction().cursor
cursor.execute(*table.select(Max(table.id), table.invoice, cursor.execute(*table.select(Max(table.id), table.invoice,
where=table.invoice.in_([x.id for x in invoices]), where=table.invoice.in_([x.id for x in invoices]),
group_by=table.invoice)) group_by=table.invoice))
@ -102,7 +102,6 @@ class Invoice:
for state, inv in cursor.fetchall(): for state, inv in cursor.fetchall():
result['sii_state'][inv] = state result['sii_state'][inv] = state
return result return result
def _credit(self): def _credit(self):

View file

@ -1,4 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<!-- The COPYRIGHT file at the top level of this repository contains the full
copyright notices and license terms. -->
<tryton> <tryton>
<data> <data>
<record model="ir.ui.view" id="aeat_sii_invoice_form_view"> <record model="ir.ui.view" id="aeat_sii_invoice_form_view">

View file

@ -1,21 +1,23 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<!-- The COPYRIGHT file at the top level of this repository contains the full
copyright notices and license terms. -->
<tryton> <tryton>
<data> <data>
<record model="ir.ui.view" id="load_pkcs12_start_view"> <record model="ir.ui.view" id="load_pkcs12_start_view">
<field name="model">aeat.sii.load_pkcs12.start</field> <field name="model">aeat.sii.load_pkcs12.start</field>
<field name="type">form</field> <field name="type">form</field>
<field name="name">load_pkcs12_start</field> <field name="name">load_pkcs12_start</field>
</record> </record>
<record model="ir.action.wizard" id="act_aeat_sii_load_pkcs12"> <record model="ir.action.wizard" id="act_aeat_sii_load_pkcs12">
<field name="name">Load PKCS12</field> <field name="name">Load PKCS12</field>
<field name="wiz_name">aeat.sii.load_pkcs12</field> <field name="wiz_name">aeat.sii.load_pkcs12</field>
</record> </record>
<record model="ir.action.keyword" id="act_aeat_sii_load_pkcs12_keyword1"> <record model="ir.action.keyword" id="act_aeat_sii_load_pkcs12_keyword1">
<field name="action" ref="act_aeat_sii_load_pkcs12"/> <field name="action" ref="act_aeat_sii_load_pkcs12"/>
<field name="keyword">form_action</field> <field name="keyword">form_action</field>
<field name="model">company.company,-1</field> <field name="model">company.company,-1</field>
</record> </record>
</data> </data>
</tryton> </tryton>

View file

@ -8,7 +8,11 @@ msgstr "No se ha podido cargar PKCS12: %s"
msgctxt "error:company.company:" msgctxt "error:company.company:"
msgid "Missing Fernet key configuration" msgid "Missing Fernet key configuration"
msgstr "Falta clave Fernet a la configuración" msgstr "Falta clave Fernet en la configuración"
msgctxt "error:company.company:"
msgid "Missing PEM certificate"
msgstr "Falta certificado PEM"
msgctxt "field:account.invoice,sii_book_key:" msgctxt "field:account.invoice,sii_book_key:"
msgid "SII Book Key" msgid "SII Book Key"
@ -122,7 +126,6 @@ msgctxt "field:aeat.sii.report,rec_name:"
msgid "Name" msgid "Name"
msgstr "Nombre" msgstr "Nombre"
#, fuzzy
msgctxt "field:aeat.sii.report,state:" msgctxt "field:aeat.sii.report,state:"
msgid "State" msgid "State"
msgstr "Estado" msgstr "Estado"
@ -131,7 +134,6 @@ msgctxt "field:aeat.sii.report,version:"
msgid "Version" msgid "Version"
msgstr "Versión" msgstr "Versión"
#, fuzzy
msgctxt "field:aeat.sii.report,write_date:" msgctxt "field:aeat.sii.report,write_date:"
msgid "Write Date" msgid "Write Date"
msgstr "Fecha de modificación" msgstr "Fecha de modificación"
@ -184,7 +186,6 @@ msgctxt "field:aeat.sii.report.lines,write_date:"
msgid "Write Date" msgid "Write Date"
msgstr "Fecha de modificación" msgstr "Fecha de modificación"
#, fuzzy
msgctxt "field:aeat.sii.report.lines,write_uid:" msgctxt "field:aeat.sii.report.lines,write_uid:"
msgid "Write User" msgid "Write User"
msgstr "Usuario modificación" msgstr "Usuario modificación"
@ -251,7 +252,7 @@ msgstr "Informe SII"
msgctxt "model:ir.action,name:act_aeat_sii_report_line" msgctxt "model:ir.action,name:act_aeat_sii_report_line"
msgid "AEAT SII Report Lines" msgid "AEAT SII Report Lines"
msgstr "Línias informe SII" msgstr "Líneas informe SII"
msgctxt "model:ir.action,name:act_aeat_sii_report_lines" msgctxt "model:ir.action,name:act_aeat_sii_report_lines"
msgid "SII Report Lines" msgid "SII Report Lines"
@ -802,14 +803,13 @@ msgctxt "view:aeat.sii.report:"
msgid "Load Invoices" msgid "Load Invoices"
msgstr "Cargar facturas" msgstr "Cargar facturas"
#, fuzzy
msgctxt "view:aeat.sii.report:" msgctxt "view:aeat.sii.report:"
msgid "Send" msgid "Send"
msgstr "Enviar" msgstr "Enviar"
msgctxt "view:company.company:" msgctxt "view:company.company:"
msgid "Certificate" msgid "Certificate"
msgstr "" msgstr "Certificado"
msgctxt "wizard_button:aeat.sii.load_pkcs12,start,end:" msgctxt "wizard_button:aeat.sii.load_pkcs12,start,end:"
msgid "Cancel" msgid "Cancel"

View file

@ -1,16 +1,27 @@
#This file is part party_comment module for Tryton. # The COPYRIGHT file at the top level of this repository contains
#The COPYRIGHT file at the top level of this repository contains # the full copyright notices and license terms.
#the full copyright notices and license terms.
from trytond.model import fields from trytond.model import fields
from trytond.pool import PoolMeta from trytond.pool import PoolMeta
from . import aeat from . import aeat
__all__ = ['Party'] __all__ = ['Party']
class Party: class Party:
__name__ = 'party.party' __name__ = 'party.party'
__metaclass__ = PoolMeta __metaclass__ = PoolMeta
# TODO: v4 change to party.identifier module # TODO: v4 change to party.identifier module
identifier_type = fields.Selection([('', '')] + aeat.PARTY_IDENTIFIER_TYPE, identifier_type = fields.Selection(aeat.PARTY_IDENTIFIER_TYPE,
'Identifier Type', ) 'Identifier Type')
sii_vat_code = fields.Function(fields.Char('VAT', size=9),
'get_sii_vat_data')
sii_vat_country = fields.Function(fields.Char('VAT', size=2),
'get_sii_vat_data')
def get_sii_vat_data(self, name=None):
if self.vat_code:
if name == 'sii_vat_code':
return self.vat_code[-9:]
elif name == 'sii_vat_country':
return self.vat_code[:2]

View file

@ -1,6 +1,5 @@
<?xml version="1.0"?> <?xml version="1.0"?>
<!-- This file is part party_comment module for Tryton. <!-- The COPYRIGHT file at the top level of this repository contains the full copyright notices and license terms. -->
The COPYRIGHT file at the top level of this repository contains the full copyright notices and license terms. -->
<tryton> <tryton>
<data> <data>
<record model="ir.ui.view" id="party_view_form"> <record model="ir.ui.view" id="party_view_form">

View file

@ -3,29 +3,20 @@
import unittest import unittest
# import doctest # import doctest
import trytond.tests.test_tryton import trytond.tests.test_tryton
from trytond.tests.test_tryton import test_view, test_depends from trytond.tests.test_tryton import ModuleTestCase
# TODO: Remove if no sceneario needed. # TODO: Remove if no sceneario needed.
# from trytond.tests.test_tryton import doctest_setup, doctest_teardown # from trytond.tests.test_tryton import doctest_setup, doctest_teardown
class TestCase(unittest.TestCase): class AeatSIITestCase(ModuleTestCase):
'Test module' 'Test AEAT SII module'
module = 'aeat_sii'
def setUp(self):
trytond.tests.test_tryton.install_module('aeat_sii')
def test0005views(self):
'Test views'
test_view('aeat_sii')
def test0006depends(self):
'Test depends'
test_depends()
def suite(): def suite():
suite = trytond.tests.test_tryton.suite() suite = trytond.tests.test_tryton.suite()
suite.addTests(unittest.TestLoader().loadTestsFromTestCase(TestCase)) suite.addTests(unittest.TestLoader().loadTestsFromTestCase(
AeatSIITestCase))
# TODO: remove if no scenario needed. # TODO: remove if no scenario needed.
#suite.addTests(doctest.DocFileSuite('scenario_aeat_sii.rst', #suite.addTests(doctest.DocFileSuite('scenario_aeat_sii.rst',
# setUp=doctest_setup, tearDown=doctest_teardown, encoding='utf-8', # setUp=doctest_setup, tearDown=doctest_teardown, encoding='utf-8',

View file

@ -1,8 +1,6 @@
<?xml version="1.0"?> <?xml version="1.0"?>
<!-- This file is part of party_comment module for Tryton. <!-- The COPYRIGHT file at the top level of this repository contains the full
The COPYRIGHT file at the top level of this repository contains the full
copyright notices and license types. --> copyright notices and license types. -->
<data> <data>
<xpath expr="/form/notebook/page[@id='reports']" position="after"> <xpath expr="/form/notebook/page[@id='reports']" position="after">
<page string="Certificate" col="2" id="certificate"> <page string="Certificate" col="2" id="certificate">

View file

@ -1,8 +1,6 @@
<?xml version="1.0"?> <?xml version="1.0"?>
<!-- This file is part of party_comment module for Tryton. <!-- The COPYRIGHT file at the top level of this repository contains the full
The COPYRIGHT file at the top level of this repository contains the full
copyright notices and license types. --> copyright notices and license types. -->
<form string="Load PKCS12" col="2"> <form string="Load PKCS12" col="2">
<label name="pfx"/> <label name="pfx"/>
<field name="pfx"/> <field name="pfx"/>

View file

@ -1,12 +1,11 @@
<?xml version="1.0"?> <?xml version="1.0"?>
<!-- This file is part of party_comment module for Tryton. <!-- The COPYRIGHT file at the top level of this repository contains the full
The COPYRIGHT file at the top level of this repository contains the full
copyright notices and license types. --> copyright notices and license types. -->
<data> <data>
<xpath expr="/form/notebook/page[@id='accounting']/field[@name='vat_number']" <xpath expr="/form/notebook/page[@name='identifiers']/field[@name='identifiers']"
position="after"> position="before">
<label name="identifier_type"/> <label name="identifier_type"/>
<field name="identifier_type"/> <field name="identifier_type"/>
<newline/>
</xpath> </xpath>
</data> </data>

View file

@ -1,5 +1,5 @@
<?xml version="1.0"?> <?xml version="1.0"?>
<!-- This file is part of Tryton. The COPYRIGHT file at the top level of <!-- The COPYRIGHT file at the top level of
this repository contains the full copyright notices and license terms. --> this repository contains the full copyright notices and license terms. -->
<form string="AEAT SII Data Reassigment Done" col="2"> <form string="AEAT SII Data Reassigment Done" col="2">
<image name="tryton-dialog-information" xexpand="0" xfill="0"/> <image name="tryton-dialog-information" xexpand="0" xfill="0"/>