diff --git a/__init__.py b/__init__.py
index 9085ba3..85e7df6 100644
--- a/__init__.py
+++ b/__init__.py
@@ -1,18 +1,21 @@
# The COPYRIGHT file at the top level of this repository contains the full
# copyright notices and license terms.
from trytond.pool import Pool
-from . import company
+from . import account
from . import invoice
from . import party
from . import payment_type
-from . import account
+from .invoice import FACTURAE_SCHEMA_VERSION
+
+__all__ = [FACTURAE_SCHEMA_VERSION]
def register():
Pool.register(
+ account.Configuration,
+ account.ConfigurationFacturae,
account.TaxTemplate,
account.Tax,
- company.Company,
invoice.Invoice,
invoice.InvoiceLine,
invoice.CreditInvoiceStart,
diff --git a/account.py b/account.py
index 8271e39..a0698e4 100644
--- a/account.py
+++ b/account.py
@@ -1,10 +1,8 @@
# The COPYRIGHT file at the top level of this repository contains the full
# copyright notices and license terms.
-from trytond.model import fields
-from trytond.pool import PoolMeta
-
-__all__ = ['TaxTemplate', 'Tax']
-
+from trytond.model import ModelSQL, fields
+from trytond.pool import Pool, PoolMeta
+from trytond.modules.company.model import CompanyValueMixin
REPORT_TYPES = [
(None, ""),
@@ -43,6 +41,50 @@ REPORT_TYPES = [
]
+class Configuration(metaclass=PoolMeta):
+ __name__ = 'account.configuration'
+ facturae_certificate = fields.MultiValue(
+ fields.Many2One('certificate', "Factura-e Certificate",
+ help='Certificate to sign Factura-e'))
+ facturae_service = fields.MultiValue(
+ fields.Selection('get_facturae_service', "Factura-e Service",
+ help='Service to be used when post the invoice'))
+ invoice_facturae_after = fields.TimeDelta("Send Factura-e after",
+ help="Grace period after which the invoice will be sent to the facturae "
+ "service. Applies only if a worker queue is activated.")
+
+ @classmethod
+ def default_facturae_service(cls, **pattern):
+ return cls.multivalue_model('facturae_service').default_facturae_service()
+
+ @classmethod
+ def get_facturae_service(cls):
+ pool = Pool()
+ ConfigurationFacturae = pool.get('account.configuration.facturae')
+ return ConfigurationFacturae.fields_get(['facturae_service'])['facturae_service']['selection']
+
+ @classmethod
+ def multivalue_model(cls, field):
+ pool = Pool()
+ if field in {'facturae_certificate', 'facturae_service'}:
+ return pool.get('account.configuration.facturae')
+ return super().multivalue_model(field)
+
+
+class ConfigurationFacturae(ModelSQL, CompanyValueMixin):
+ "Account Configuration Factura-e"
+ __name__ = 'account.configuration.facturae'
+ facturae_certificate = fields.Many2One('certificate', "Factura-e Certificate",
+ help='Certificate to sign Factura-e')
+ facturae_service = fields.Selection([
+ (None, ''),
+ ], "Factura-e Service")
+
+ @staticmethod
+ def default_facturae_service():
+ return None
+
+
class TaxTemplate(metaclass=PoolMeta):
__name__ = 'account.tax.template'
diff --git a/account.xml b/account.xml
new file mode 100644
index 0000000..7826297
--- /dev/null
+++ b/account.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+ account.configuration
+
+ configuration_form
+
+
+
diff --git a/company.py b/company.py
deleted file mode 100644
index 87ed499..0000000
--- a/company.py
+++ /dev/null
@@ -1,14 +0,0 @@
-# The COPYRIGHT file at the top level of this repository contains the full
-# copyright notices and license terms.
-from trytond.model import fields
-from trytond.pool import PoolMeta
-
-__all__ = ['Company']
-
-
-class Company(metaclass=PoolMeta):
- __name__ = 'company.company'
- facturae_certificate = fields.Binary('Factura-e Certificate',
- help='The certificate to generate the XAdES electronic firm for '
- 'invoices.')
-
diff --git a/company.xml b/company.xml
deleted file mode 100644
index 89ab3fa..0000000
--- a/company.xml
+++ /dev/null
@@ -1,12 +0,0 @@
-
-
-
-
-
- company.company
-
- company_form
-
-
-
diff --git a/invoice.py b/invoice.py
index 242dd76..8620b1d 100644
--- a/invoice.py
+++ b/invoice.py
@@ -25,6 +25,8 @@ from trytond import backend
from trytond.i18n import gettext
from trytond.exceptions import UserError
+FACTURAE_SCHEMA_VERSION = '3.2.2'
+
# Get from XSD scheme of Facturae 3.2.2
# http://www.facturae.gob.es/formato/Versiones/Facturaev3_2_2.xml
RECTIFICATIVE_REASON_CODES = [
@@ -142,16 +144,17 @@ class Invoice(metaclass=PoolMeta):
filename='invoice_facturae_filename')
invoice_facturae_filename = fields.Function(fields.Char(
'Factura-e filename'), 'get_invoice_facturae_filename')
+ invoice_facturae_sent = fields.Boolean('Factura-e Sent')
@classmethod
def __setup__(cls):
super(Invoice, cls).__setup__()
- cls._check_modify_exclude.add('invoice_facturae')
+ cls._check_modify_exclude |= {'invoice_facturae', 'invoice_facturae_sent'}
cls._buttons.update({
'generate_facturae_wizard': {
'invisible': ((Eval('type') != 'out')
| ~Eval('state').in_(['posted', 'paid'])),
- 'readonly': Bool(Eval('invoice_facturae')),
+ 'readonly': Bool(Eval('invoice_facturae_sent')),
}
})
@@ -163,6 +166,7 @@ class Invoice(metaclass=PoolMeta):
default = default.copy()
default.setdefault('invoice_facturae', None)
default.setdefault('rectificative_reason_code', None)
+ default.setdefault('invoice_facturae_sent', None)
return super(Invoice, cls).copy(invoices, default=default)
def get_credited_invoices(self, name):
@@ -207,6 +211,28 @@ class Invoice(metaclass=PoolMeta):
if ml.account.type.receivable],
key=attrgetter('maturity_date'))
+ @classmethod
+ def draft(cls, invoices):
+ invoice_facturae_sends = [invoice for invoice in invoices if invoice.invoice_facturae_sent]
+ if invoice_facturae_sends:
+ names = ', '.join(m.rec_name for m in invoice_facturae_sends[:5])
+ if len(invoice_facturae_sends) > 5:
+ names += '...'
+ raise UserError(gettext('account_invoice_facturae.msg_draft_invoice_facturae_sent',
+ invoices=names))
+
+ cls.write(invoices, {
+ 'invoice_facturae': None,
+ })
+ super(Invoice, cls).draft(invoices)
+
+ @classmethod
+ def post(cls, invoices):
+ super(Invoice, cls).post(invoices)
+
+ for invoice in invoices:
+ cls.__queue__.generate_facturae(invoice)
+
def _credit(self, **values):
credit = super(Invoice, self)._credit(**values)
rectificative_reason_code = Transaction().context.get(
@@ -221,23 +247,38 @@ class Invoice(metaclass=PoolMeta):
def generate_facturae_wizard(cls, invoices):
pass
- @classmethod
- def generate_facturae_default(cls, invoices, certificate_password):
- to_write = ([],)
- for invoice in invoices:
- if invoice.invoice_facturae:
- continue
- facturae_content = invoice.get_facturae()
- invoice._validate_facturae(facturae_content)
+ def generate_facturae(self, certificate=None, service=None):
+ pool = Pool()
+ Configuration = pool.get('account.configuration')
+ Invoice = pool.get('account.invoice')
+
+ config = Configuration(1)
+ transaction = Transaction()
+
+ if not self.invoice_facturae:
+ facturae_content = self.get_facturae()
+ self._validate_facturae(facturae_content)
if backend.name != 'sqlite':
- invoice_facturae = invoice._sign_facturae(
- facturae_content, certificate_password)
+ invoice_facturae = self._sign_facturae(facturae_content,
+ 'default', certificate)
else:
invoice_facturae = facturae_content
- to_write[0].append(invoice)
- to_write += ({'invoice_facturae': invoice_facturae},)
- if to_write[0]:
- cls.write(*to_write)
+ self.invoice_facturae = invoice_facturae
+ self.save()
+
+ # send facturae to service
+ if not service and config.facturae_service:
+ service = config.facturae_service
+ if self.invoice_facturae and service:
+ with transaction.set_context(
+ queue_scheduled_at=config.invoice_facturae_after):
+ Invoice.__queue__.send_facturae(self, service)
+
+ def send_facturae(self, service):
+ Invoice = Pool().get('account.invoice')
+
+ method = 'send_facturae_%s' % service
+ getattr(Invoice, method)(self)
def get_facturae(self):
jinja_env = Environment(
@@ -427,32 +468,41 @@ class Invoice(metaclass=PoolMeta):
invoice=self.rec_name, message=e))
return True
- def _sign_facturae(self, xml_string, certificate_password):
+ def _sign_facturae(self, xml_string, service='default', certificate=None):
"""
Inspired by https://github.com/pedrobaeza/l10n-spain/blob/d01d049934db55130471e284012be7c860d987eb/l10n_es_facturae/wizard/create_facturae.py
"""
- if not self.company.facturae_certificate:
- raise UserError(gettext(
- 'account_invoice_facturae.missing_certificate',
- company=self.company.rec_name))
+ Configuration = Pool().get('account.configuration')
+
+ if not certificate:
+ certificate = Configuration(1).facturae_certificate
+ if not certificate:
+ raise UserError(gettext(
+ 'account_invoice_facturae.msg_missing_certificate'))
+
+ certificate_facturae = certificate.pem_certificate
+ certificate_password = certificate.certificate_password
logger = logging.getLogger('account_invoice_facturae')
- unsigned_file = NamedTemporaryFile(suffix='.xml', delete=False)
- unsigned_file.write(xml_string)
- unsigned_file.close()
+ with NamedTemporaryFile(suffix='.xml', delete=False) as unsigned_file:
+ unsigned_file.write(xml_string)
- cert_file = NamedTemporaryFile(suffix='.pfx', delete=False)
- cert_file.write(self.company.facturae_certificate)
- cert_file.close()
+ with NamedTemporaryFile(suffix='.pfx', delete=False) as cert_file:
+ cert_file.write(certificate_facturae)
def _sign_file(cert, password, request):
# get key and certificates from PCK12 file
- (
- private_key,
- certificate,
- additional_certificates,
- ) = pkcs12.load_key_and_certificates(cert, password)
+ try:
+ (
+ private_key,
+ certificate,
+ additional_certificates,
+ ) = pkcs12.load_key_and_certificates(cert, password)
+ except ValueError as e:
+ logger.warning("Error load_key_and_certificates file",
+ exc_info=True)
+ raise UserError(str(e))
# DER is an ASN.1 encoding type
crt = certificate.public_bytes(serialization.Encoding.DER)
@@ -656,7 +706,7 @@ class Invoice(metaclass=PoolMeta):
return etree.tostring(root, xml_declaration=True, encoding="UTF-8")
signed_file_content = _sign_file(
- self.company.facturae_certificate,
+ certificate_facturae,
certificate_password.encode(),
xml_string,
)
@@ -670,6 +720,23 @@ class Invoice(metaclass=PoolMeta):
class InvoiceLine(metaclass=PoolMeta):
__name__ = 'account.invoice.line'
+ @property
+ def facturae_article_code(self):
+ return self.product and self.product.code or ''
+
+ @property
+ def facturae_item_description(self):
+ return (
+ (self.description and self.description[:2500])
+ or (self.product and self.product.rec_name[:2500])
+ or '#'+str(self.id)
+ )
+
+ @property
+ def facturae_receiver_transaction_reference(self):
+ # TODO Issuer/ReceiverTransactionDate (sale, contract...)
+ return ''
+
@property
def taxes_outputs(self):
"""Return list of 'impuestos repecutidos'"""
@@ -723,17 +790,17 @@ class GenerateFacturaeStart(ModelView):
'Generate Factura-e file - Start'
__name__ = 'account.invoice.generate_facturae.start'
service = fields.Selection([
- ('default', 'Default'),
- ], 'Service', required=True)
- certificate_password = fields.Char('Certificate Password',
+ (None, ''),
+ ], 'Service')
+ certificate_facturae = fields.Many2One('certificate',
+ 'Certificate Factura-e',
states={
- 'required': Eval('service') == 'default',
- 'invisible': Eval('service') != 'default',
+ 'invisible': ~Bool(Eval('service')),
}, depends=['service'])
@staticmethod
def default_service():
- return 'default'
+ return None
class GenerateFacturae(Wizard):
@@ -750,6 +817,7 @@ class GenerateFacturae(Wizard):
Invoice = Pool().get('account.invoice')
invoices = Invoice.browse(Transaction().context['active_ids'])
- service = 'generate_facturae_%s' % self.start.service
- getattr(Invoice, service)(invoices, self.start.certificate_password)
+ for invoice in invoices:
+ invoice.generate_facturae(certificate=self.start.certificate_facturae,
+ service=self.start.service)
return 'end'
diff --git a/locale/ca.po b/locale/ca.po
index 074bbaa..1142d6e 100644
--- a/locale/ca.po
+++ b/locale/ca.po
@@ -2,6 +2,22 @@
msgid ""
msgstr "Content-Type: text/plain; charset=utf-8\n"
+msgctxt "field:account.configuration,certificate_service_facturae:"
+msgid "Certificate Service Post Factura-e"
+msgstr "Servei certificat comptabilitar Factura-e"
+
+msgctxt "field:account.configuration,invoice_facturae_after:"
+msgid "Send Factura-e after"
+msgstr "Envia "
+
+msgctxt "field:account.configuration.facturae,certificate_service_facturae:"
+msgid "Certificate Service Post Factura-e"
+msgstr "Servei certificat comptabilitar Factura-e"
+
+msgctxt "field:account.configuration.facturae,company:"
+msgid "Company"
+msgstr "Empresa"
+
msgctxt "field:account.invoice,credited_invoices:"
msgid "Credited Invoices"
msgstr "Factures abonades"
@@ -22,6 +38,10 @@ msgctxt "field:account.invoice.credit.start,rectificative_reason_code:"
msgid "Rectificative Reason Code"
msgstr "Codi motiu rectificació"
+msgctxt "field:account.invoice.generate_facturae.start,certificate_facturae:"
+msgid "Certificate Factura-e"
+msgstr "Certificat"
+
msgctxt "field:account.invoice.generate_facturae.start,certificate_password:"
msgid "Certificate Password"
msgstr "Contrasenya certificat"
@@ -42,10 +62,6 @@ msgctxt "field:account.tax.template,report_type:"
msgid "Report Type"
msgstr "Tipus informe"
-msgctxt "field:company.company,facturae_certificate:"
-msgid "Factura-e Certificate"
-msgstr "Certificat Factura-e"
-
msgctxt "field:party.party,facturae_person_type:"
msgid "Person Type"
msgstr "Tipus persona"
@@ -70,9 +86,13 @@ msgctxt "field:party.party,unidad_tramitadora:"
msgid "Unidad tramitadora"
msgstr "Unidad tramitadora"
-msgctxt "help:company.company,facturae_certificate:"
-msgid "The certificate to generate the XAdES electronic firm for invoices."
-msgstr "El certificat per generar la firma electrònica XAdES de les factures."
+msgctxt "help:account.configuration,certificate_service_facturae:"
+msgid "Service to be used when post the invoice"
+msgstr "El servei que s'utilitzarà quan es comptabilitzi una factura"
+
+msgctxt "model:account.configuration.facturae,name:"
+msgid "Account Configuration Factura-e"
+msgstr "Configuració comptable Factura-e"
msgctxt "model:account.invoice.generate_facturae.start,name:"
msgid "Generate Factura-e file - Start"
@@ -84,8 +104,8 @@ msgstr "Genera Factura-e"
msgctxt "model:ir.message,text:company_address_fields"
msgid ""
-"Missing Street, Postal Code, City, Subdivision or Country in default address of "
-"company\\'s Party \"%(party)s\"."
+"Missing Street, Postal Code, City, Subdivision or Country in default address"
+" of company\\'s Party \"%(party)s\"."
msgstr ""
"Falta el carrer, codi postal, ciutat, província o país de la direcció per "
"defecte del tercer de l'empresa \"%(party)s\"."
@@ -100,42 +120,40 @@ msgstr ""
msgctxt "model:ir.message,text:error_signing"
msgid ""
-"Error signing invoice \"%(invoice)s\".\\nMessage returned by signing "
-"process: %(process_output)s"
+"Error signing invoice \"%(invoice)s\".\n"
+"Message returned by signing process: %(process_output)s"
msgstr ""
"Error firmant la factura \"%(invoice)s\".\n"
"Missatge retornat pel procés de firma: %(process_output)s"
msgctxt "model:ir.message,text:incompatible_facturae_type_account_bank"
msgid ""
-"The Factura-e Type and Account Bank Kind of Payment Type \"%(payment_type)s\""
-" are not compatible."
+"The Factura-e Type and Account Bank Kind of Payment Type "
+"\"%(payment_type)s\" are not compatible."
msgstr ""
"El Tipus Fatura-e i el Tipus compte bancari del Tipus de pagament "
"\"%(payment_type)s\" no són compatibles."
msgctxt "model:ir.message,text:invalid_factura_xml_file"
msgid ""
-"The Factura-e file (XML) generated for invoice \"%(invoice)s\" is not valid "
-"against the oficial XML Schema Definition:\\n%(message)s"
+"The Factura-e file (XML) generated for invoice \"%(invoice)s\" is not valid against the oficial XML Schema Definition:\n"
+"%(message)s"
msgstr ""
"El fitxer Factura-e (XML) generat per la factura \"%(invoice)s\" no és vàlid segons el XML Schema Definition oficial:\n"
"%(message)s"
msgctxt "model:ir.message,text:invoice_address_fields"
msgid ""
-"Missing Street, Postal Code, City, Subdivision or Country in Invoice Address of "
-"invoice \"%(invoice)s\"."
+"Missing Street, Postal Code, City, Subdivision or Country in Invoice Address"
+" of invoice \"%(invoice)s\"."
msgstr ""
"Falta el carrer, codi postal, ciutat, subdivisió o país a l'adreça de la "
"factura \"%(invoice)s\"."
msgctxt "model:ir.message,text:missing_account_bank_module"
msgid ""
-"You must to install \"account_bank\" module to inform the Bank Account in "
-"invoices.\\nThe payment type \"%(payment_type)s\" used in invoice "
-"\"%(invoice)s\" is configured as \"Direct Debit\" or \"Credit Transfer\" and"
-" it requires the company/party bank account."
+"You must to install \"account_bank\" module to inform the Bank Account in invoices.\n"
+"The payment type \"%(payment_type)s\" used in invoice \"%(invoice)s\" is configured as \"Direct Debit\" or \"Credit Transfer\" and it requires the company/party bank account."
msgstr ""
"S'ha d'instal·lar el mòdul \"account_bank\" per poder indicar el compte bancari a les factures.\n"
"El tipus de pagament \"%(payment_type)s\" utilitzat a la factura \"%(invoice)s\" està configurat com \"Rebut domiciliat\" o \"Transferència\" i es requereix el copmte bancari de l'empresa o del tercer."
@@ -145,10 +163,6 @@ msgid "The Bank Account is missing in some move lines of invoice \"%(invoice)s\"
msgstr ""
"Falta el compte bancari en algun dels apunts de la factura \"%(invoice)s\"."
-msgctxt "model:ir.message,text:missing_certificate"
-msgid "Missing Factura-e Certificate in company \"%(company)s\"."
-msgstr "Falta el Certificat Factura-e a l'empresa \"%(company)s\"."
-
msgctxt "model:ir.message,text:missing_iban"
msgid ""
"The Bank Account \"%(bank_account)s\" used in invoice \"%(invoice)s\" "
@@ -172,6 +186,15 @@ msgstr ""
"Falta el Tipus Factura-e al tipus de pagament \"%(payment_type)s\" utilitzat"
" a la factura \"%(invoice)s\"."
+msgctxt "model:ir.message,text:msg_missing_certificate"
+msgid "Missing default configuration Factura-e Certificate."
+msgstr "Falta la configuració del Certificat Factura-e"
+
+msgctxt "model:ir.message,text:msg_missing_password_certificate"
+msgid "Missing Password Factura-e Certificate in company \"%(company)s\"."
+msgstr ""
+"Falta la contrasenya del certificat Factura-e a l'empresa \"%(company)s\"."
+
msgctxt "model:ir.message,text:no_rate"
msgid "No rate found for currency \"%(currency)s\" on \"%(date)s\""
msgstr ""
@@ -188,9 +211,8 @@ msgstr ""
msgctxt "model:ir.message,text:party_name_surname"
msgid ""
-"The name of \"%(party)s\" of invoice \"%(invoice)s\" doesn\\'t contain the "
-"Name and the, at least, First Surname.\\n They must to be separated by one "
-"space: Name Surname."
+"The name of \"%(party)s\" of invoice \"%(invoice)s\" doesn\\'t contain the Name and the, at least, First Surname.\n"
+"They must to be separated by one space: Name Surname."
msgstr ""
"El nom del tercer \"%(party)s\" de la factura \"%(invoice)s\" no conté el nom i, al menys, el primer cognom.\n"
"Aquests han d'anar separats per un espai: Nom Cognom."
@@ -205,7 +227,7 @@ msgstr ""
msgctxt "model:ir.model.button,string:generate_facturae_button"
msgid "Generate Facturae"
-msgstr "Genera Facturae"
+msgstr "Crea Facturae"
msgctxt "selection:account.invoice,rectificative_reason_code:"
msgid ""
@@ -755,6 +777,10 @@ msgctxt "selection:party.party,facturae_residence_type:"
msgid "Resident in other EU country"
msgstr "Resident a la Unió Europea (excepte Espanya)"
+msgctxt "view:account.configuration:"
+msgid "Factura-e"
+msgstr "Factura-e "
+
msgctxt "view:account.invoice.generate_facturae.start:"
msgid ""
"It will generate a Factura-e file for selected invoices, will sign them with"
@@ -769,6 +795,14 @@ msgctxt "view:account.invoice:"
msgid "Generate Factura-e"
msgstr "Genera Factura-e"
+msgctxt "view:account.tax.template:"
+msgid "Factura-e"
+msgstr "Factura-e"
+
+msgctxt "view:account.tax:"
+msgid "Factura-e"
+msgstr "Factura-e"
+
msgctxt "view:party.party:"
msgid "Factura-e"
msgstr "Factura-e"
diff --git a/locale/es.po b/locale/es.po
index 8cbafc7..2d776c1 100644
--- a/locale/es.po
+++ b/locale/es.po
@@ -2,6 +2,22 @@
msgid ""
msgstr "Content-Type: text/plain; charset=utf-8\n"
+msgctxt "field:account.configuration,certificate_service_facturae:"
+msgid "Certificate Service Post Factura-e"
+msgstr "Servicio certificado contabilizar Factura-e"
+
+msgctxt "field:account.configuration,invoice_facturae_after:"
+msgid "Send Factura-e after"
+msgstr "Enviar Factura-e después de"
+
+msgctxt "field:account.configuration.facturae,certificate_service_facturae:"
+msgid "Certificate Service Post Factura-e"
+msgstr "Servicio certificado contabilizar Factura-e"
+
+msgctxt "field:account.configuration.facturae,company:"
+msgid "Company"
+msgstr "Empresa"
+
msgctxt "field:account.invoice,credited_invoices:"
msgid "Credited Invoices"
msgstr "Facturas abonadas"
@@ -22,6 +38,10 @@ msgctxt "field:account.invoice.credit.start,rectificative_reason_code:"
msgid "Rectificative Reason Code"
msgstr "Código motivo rectificación"
+msgctxt "field:account.invoice.generate_facturae.start,certificate_facturae:"
+msgid "Certificate Factura-e"
+msgstr "Certificado Factura-e"
+
msgctxt "field:account.invoice.generate_facturae.start,certificate_password:"
msgid "Certificate Password"
msgstr "Contraseña certificado"
@@ -42,10 +62,6 @@ msgctxt "field:account.tax.template,report_type:"
msgid "Report Type"
msgstr "Tipo informe"
-msgctxt "field:company.company,facturae_certificate:"
-msgid "Factura-e Certificate"
-msgstr "Certificado Factura-e"
-
msgctxt "field:party.party,facturae_person_type:"
msgid "Person Type"
msgstr "Tipo persona"
@@ -70,10 +86,13 @@ msgctxt "field:party.party,unidad_tramitadora:"
msgid "Unidad tramitadora"
msgstr "Unidad tramitadora"
-msgctxt "help:company.company,facturae_certificate:"
-msgid "The certificate to generate the XAdES electronic firm for invoices."
-msgstr ""
-"El certificado para generar la firma electrónica XAdES de las facturas."
+msgctxt "help:account.configuration,certificate_service_facturae:"
+msgid "Service to be used when post the invoice"
+msgstr "El servicio que se utilitzará cuando se contabilize una factura"
+
+msgctxt "model:account.configuration.facturae,name:"
+msgid "Account Configuration Factura-e"
+msgstr "Configuración contable Factura-e"
msgctxt "model:account.invoice.generate_facturae.start,name:"
msgid "Generate Factura-e file - Start"
@@ -85,8 +104,8 @@ msgstr "Generar Factura-e"
msgctxt "model:ir.message,text:company_address_fields"
msgid ""
-"Missing Street, Postal Code, City, Subdivision or Country in default address of "
-"company\\'s Party \"%(party)s\"."
+"Missing Street, Postal Code, City, Subdivision or Country in default address"
+" of company\\'s Party \"%(party)s\"."
msgstr ""
"Falta la calle, código postal, ciudad, provincia o país de la dirección por "
"defecto del tercero de la empresa \"%(party)s\"."
@@ -109,8 +128,8 @@ msgstr ""
msgctxt "model:ir.message,text:incompatible_facturae_type_account_bank"
msgid ""
-"The Factura-e Type and Account Bank Kind of Payment Type \"%(payment_type)s\""
-" are not compatible."
+"The Factura-e Type and Account Bank Kind of Payment Type "
+"\"%(payment_type)s\" are not compatible."
msgstr ""
"El Tipo Factura-e y el Tipo cuenta bancaria del Tipo de pago "
"\"%(payment_type)s\" no son compatibles."
@@ -125,8 +144,8 @@ msgstr ""
msgctxt "model:ir.message,text:invoice_address_fields"
msgid ""
-"Missing Street, Postal Code, City, Subdivision or Country in Invoice Address of "
-"invoice \"%(invoice)s\"."
+"Missing Street, Postal Code, City, Subdivision or Country in Invoice Address"
+" of invoice \"%(invoice)s\"."
msgstr ""
"Falta la calle, código postal, ciudad, província o país de la dirección de "
"la factura \"%(invoice)s\"."
@@ -145,10 +164,6 @@ msgstr ""
"Falta la cuenta bancaria en alguno de los apuntes de la factura "
"\"%(invoice)s\"."
-msgctxt "model:ir.message,text:missing_certificate"
-msgid "Missing Factura-e Certificate in company \"%(company)s\"."
-msgstr "Falta el Certificado Factura-e en la empresa \"%(company)s\"."
-
msgctxt "model:ir.message,text:missing_iban"
msgid ""
"The Bank Account \"%(bank_account)s\" used in invoice \"%(invoice)s\" "
@@ -170,6 +185,21 @@ msgstr ""
"Falta el Tipo Factura-e en el tipo de pago \"%(payment_type)s\" usado en la "
"factura \"%(invoice)s\""
+msgctxt "model:ir.message,text:msg_missing_certificate"
+msgid "Missing default configuration Factura-e Certificate."
+msgstr "Falta la configuració del Certificat Factura-e"
+
+msgctxt "model:ir.message,text:msg_missing_certificate_service"
+msgid "Missing default \"%(service)s\" Factura-e Certificate."
+msgstr ""
+"Falta el por defecto del servicio \"%(service)s\" del certificado Factura-e."
+
+msgctxt "model:ir.message,text:msg_missing_password_certificate"
+msgid "Missing Password Factura-e Certificate in company \"%(company)s\"."
+msgstr ""
+"Falta la contraseña del certificado Factura-e Certificate en la empresa "
+"\"%(company)s\"."
+
msgctxt "model:ir.message,text:no_rate"
msgid "No rate found for currency \"%(currency)s\" on \"%(date)s\""
msgstr ""
@@ -202,7 +232,7 @@ msgstr ""
msgctxt "model:ir.model.button,string:generate_facturae_button"
msgid "Generate Facturae"
-msgstr ""
+msgstr "Crear Facturae"
msgctxt "selection:account.invoice,rectificative_reason_code:"
msgid "Applicable Date/Period"
diff --git a/message.xml b/message.xml
index 8e8a4e0..5222b29 100644
--- a/message.xml
+++ b/message.xml
@@ -3,8 +3,11 @@
this repository contains the full copyright notices and license terms. -->
-
- Missing Factura-e Certificate in company "%(company)s".
+
+ Missing default configuration Factura-e Certificate.
+
+
+ Missing Password Factura-e Certificate in company "%(company)s".
Missing VAT Identifier in company\'s Party "%(party)s", or its length is not between 3 and 30.
@@ -55,6 +58,8 @@ Message returned by signing process: %(process_output)s
The Factura-e Type and Account Bank Kind of Payment Type "%(payment_type)s" are not compatible.
-
+
+ Can not draft "%(invoices)s" that have been sent to Factura-e.
+
diff --git a/template_facturae_3.2.2.xml b/template_facturae_3.2.2.xml
index 494f8a5..c969731 100644
--- a/template_facturae_3.2.2.xml
+++ b/template_facturae_3.2.2.xml
@@ -141,7 +141,7 @@
{{ invoice.credited_invoices[0].move.period.start_date.isoformat() }}
{{ invoice.credited_invoices[0].move.period.end_date.isoformat() }}
- {# TODO: Methods not supported:
+ {# TODO: Methods not supported:
- 02 (solo se anotan los detalles ya rectificados)
- 03 (Rectificación por descuento por volumen de operaciones durante un periodo)
- 04 (Autorizadas por la Agencia Tributaria)
@@ -167,6 +167,7 @@
{% endif %}
EUR
{{ invoice.party_lang[:2] if invoice.party_lang else 'es' }}
+ {{ invoice.reference and invoice.reference[:20] or '' }}
{% for invoice_tax in invoice.taxes_outputs %}
@@ -247,14 +248,14 @@
{# TODO: optional, not supported
- Issuer/ReceiverContractReference, Issuer/ReceiverContractDate (contract)
- - Issuer/ReceiverTransactionReference, Issuer/ReceiverTransactionDate (sale, contract...)
- FileReference, FileDate
- SequenceNumber
- DeliveryNotesReferences (account_invoice_stock)
- TransactionDate
- Extensions
#}
- {{ (line.description and line.description[:2500]) or (line.product and line.product.rec_name[:2500]) or '#'+line.id|string }}
+ {{ line.facturae_receiver_transaction_reference }}
+ {{ line.facturae_item_description }}
{{ line.quantity }}
{{ UOM_CODE2TYPE.get(line.unit.symbol, '05') if line.unit else '05' }}
{{ Currency.compute(invoice.currency, line.unit_price, euro) }}
@@ -339,7 +340,7 @@
{% endif %}
{# TODO: SpecialTaxableEvent not supported #}
{% if line.product and line.product.code %}
- {{ line.product.code[:20] }}
+ {{ line.facturae_article_code[:20] }}
{% endif %}
{% endfor %}
diff --git a/tests/test_module.py b/tests/test_module.py
index fd45944..fd85cf7 100644
--- a/tests/test_module.py
+++ b/tests/test_module.py
@@ -23,6 +23,8 @@ class AccountInvoiceFacturaeTestCase(CompanyTestMixin, ModuleTestCase):
'Test invoice generation'
pool = Pool()
+ Configuration = pool.get('account.configuration')
+ Certificate = pool.get('certificate')
Account = pool.get('account.account')
FiscalYear = pool.get('account.fiscalyear')
Invoice = pool.get('account.invoice')
@@ -61,12 +63,15 @@ class AccountInvoiceFacturaeTestCase(CompanyTestMixin, ModuleTestCase):
company.party.save()
company.save()
- # Save certificate into company
- with open(os.path.join(
- CURRENT_PATH, 'certificate.pfx'), 'rb') as cert_file:
- company.facturae_certificate = cert_file.read()
-
with set_company(company):
+ certificate = Certificate()
+ certificate.name = 'dummy Certificate'
+ # Save certificate
+ with open(os.path.join(
+ CURRENT_PATH, 'certificate.pfx'), 'rb') as cert_file:
+ certificate.pem_certificate = cert_file.read()
+ certificate.save()
+
create_chart(company, tax=True)
fiscalyear = set_invoice_sequences(get_fiscalyear(company))
@@ -199,7 +204,7 @@ class AccountInvoiceFacturaeTestCase(CompanyTestMixin, ModuleTestCase):
invoice.save()
Invoice.post([invoice])
- Invoice.generate_facturae_default([invoice], 'privatepassword')
+ invoice.generate_facturae()
self.assertNotEqual(invoice.invoice_facturae, None)
self.assertEqual(invoice.invoice_facturae_filename, 'facturae-1.xsig')
diff --git a/tryton.cfg b/tryton.cfg
index 9587a71..c229770 100644
--- a/tryton.cfg
+++ b/tryton.cfg
@@ -4,11 +4,12 @@ depends:
account
account_invoice
account_payment_type
+ certificate_manager
extras_depend:
account_bank
account_es
xml:
- company.xml
+ account.xml
invoice.xml
party.xml
payment_type.xml
diff --git a/view/company_form.xml b/view/company_form.xml
deleted file mode 100644
index 3ef4fd6..0000000
--- a/view/company_form.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
-
-
-
-
-
-
-
diff --git a/view/configuration_form.xml b/view/configuration_form.xml
new file mode 100644
index 0000000..328a5fa
--- /dev/null
+++ b/view/configuration_form.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/view/generate_facturae_start_form.xml b/view/generate_facturae_start_form.xml
index ad10464..c0696d4 100644
--- a/view/generate_facturae_start_form.xml
+++ b/view/generate_facturae_start_form.xml
@@ -10,7 +10,7 @@
yalign="0.0" xalign="0.0" xexpand="1"/>
-
-
+
+
diff --git a/view/invoice_form.xml b/view/invoice_form.xml
index e452194..39e0621 100644
--- a/view/invoice_form.xml
+++ b/view/invoice_form.xml
@@ -14,6 +14,11 @@
+
+
+
+