diff --git a/account.py b/account.py index cdce2cf..8271e39 100644 --- a/account.py +++ b/account.py @@ -56,6 +56,7 @@ class TaxTemplate(metaclass=PoolMeta): return res + class Tax(metaclass=PoolMeta): __name__ = 'account.tax' diff --git a/invoice.py b/invoice.py index be09611..8a7466f 100644 --- a/invoice.py +++ b/invoice.py @@ -1,17 +1,21 @@ # -*- coding: utf-8 -*- # The COPYRIGHT file at the top level of this repository contains the full # copyright notices and license terms. -import glob import logging import os import re +import base64 +import random +import xmlsig +import hashlib +import datetime from decimal import Decimal from jinja2 import Environment, FileSystemLoader from lxml import etree from operator import attrgetter -from subprocess import Popen, PIPE from tempfile import NamedTemporaryFile - +from cryptography.hazmat.primitives import serialization +from cryptography.hazmat.primitives.serialization import pkcs12 from trytond.model import ModelView, fields from trytond.pool import Pool, PoolMeta from trytond.pyson import Bool, Eval @@ -151,6 +155,16 @@ class Invoice(metaclass=PoolMeta): } }) + @classmethod + def copy(cls, invoices, default=None): + if default is None: + default = {} + else: + default = default.copy() + default.setdefault('invoice_facturae', None) + default.setdefault('rectificative_reason_code', None) + return super(Invoice, cls).copy(invoices, default=default) + def get_credited_invoices(self, name): pool = Pool() InvoiceLine = pool.get('account.invoice.line') @@ -308,7 +322,7 @@ class Invoice(metaclass=PoolMeta): 'account_invoice_facturae.invoice_address_fields', invoice=self.rec_name)) - euro, = Currency.search([('code', '=', 'EUR')]) + euro, = Currency.search([('code', '=', 'EUR')], limit=1) if self.currency != euro: assert (euro.rate == Decimal(1) or self.currency.rate == Decimal(1)), ( @@ -436,52 +450,184 @@ class Invoice(metaclass=PoolMeta): cert_file.write(self.company.facturae_certificate) cert_file.close() - signed_file = NamedTemporaryFile(suffix='.xsig', delete=False) + def _sign_file(cert, password, request): + ( + private_key, + certificate, + additional_certificates, + ) = pkcs12.load_key_and_certificates(cert, password) + # key = private_key.private_bytes( + # encoding=serialization.Encoding.PEM, + # format=serialization.PrivateFormat.PKCS8, + # encryption_algorithm=serialization.NoEncryption(), + # ) + # DER is an ASN.1 encoding type + crt = certificate.public_bytes(serialization.Encoding.DER) - env = {} - env.update(os.environ) - libs = os.path.join(module_path(), 'java', 'lib', '*.jar') - env['CLASSPATH'] = ':'.join(glob.glob(libs)) + rand_min = 1 + rand_max = 99999 + signature_id = "Signature%05d" % random.randint(rand_min, rand_max) + signed_properties_id = ( + signature_id + + "-SignedProperties%05d" % random.randint(rand_min, rand_max) + ) + key_info_id = "KeyInfo%05d" % random.randint(rand_min, rand_max) + reference_id = "Reference%05d" % random.randint(rand_min, rand_max) + object_id = "Object%05d" % random.randint(rand_min, rand_max) + etsi = "http://uri.etsi.org/01903/v1.3.2#" + sig_policy_identifier = ( + "http://www.facturae.es/" + "politica_de_firma_formato_facturae/" + "politica_de_firma_formato_facturae_v3_1" + ".pdf" + ) + sig_policy_hash_value = "Ohixl6upD6av8N7pEvDABhEL6hM=" + root = etree.fromstring(request) + sign = xmlsig.template.create( + c14n_method=xmlsig.constants.TransformInclC14N, + sign_method=xmlsig.constants.TransformRsaSha1, + name=signature_id, + ns="ds", + ) + key_info = xmlsig.template.ensure_key_info(sign, name=key_info_id) + x509_data = xmlsig.template.add_x509_data(key_info) + xmlsig.template.x509_data_add_certificate(x509_data) + xmlsig.template.add_key_value(key_info) - # TODO: implement Signer with python - # http://www.pyopenssl.org/en/stable/api/crypto.html#OpenSSL.crypto.load_pkcs12 - signature_command = [ - 'java', - '-Djava.awt.headless=true', - 'com.nantic.facturae.Signer', - '0', - unsigned_file.name, - signed_file.name, - 'facturae31', - cert_file.name, - certificate_password - ] - signature_process = Popen(signature_command, - stdout=PIPE, - stderr=PIPE, - env=env, - cwd=os.path.join(module_path(), 'java')) - output, err = signature_process.communicate() - rc = signature_process.returncode - if rc != 0: - logger.warning('Error %s signing invoice "%s" with command ' - '"%s ": %s %s', rc, self.id, - signature_command[:-1], output, err) - raise UserError(gettext( - 'account_invoice_factura.error_signing', - invoice=self.rec_name, - process_output=output)) + xmlsig.template.add_reference( + sign, + xmlsig.constants.TransformSha1, + uri="#" + signed_properties_id, + uri_type="http://uri.etsi.org/01903#SignedProperties", + ) + xmlsig.template.add_reference( + sign, xmlsig.constants.TransformSha1, uri="#" + key_info_id + ) + ref = xmlsig.template.add_reference( + sign, xmlsig.constants.TransformSha1, name=reference_id, uri="" + ) + xmlsig.template.add_transform(ref, xmlsig.constants.TransformEnveloped) + object_node = etree.SubElement( + sign, + etree.QName(xmlsig.constants.DSigNs, "Object"), + nsmap={"etsi": etsi}, + attrib={xmlsig.constants.ID_ATTR: object_id}, + ) + qualifying_properties = etree.SubElement( + object_node, + etree.QName(etsi, "QualifyingProperties"), + attrib={"Target": "#" + signature_id}, + ) + signed_properties = etree.SubElement( + qualifying_properties, + etree.QName(etsi, "SignedProperties"), + attrib={xmlsig.constants.ID_ATTR: signed_properties_id}, + ) + signed_signature_properties = etree.SubElement( + signed_properties, etree.QName(etsi, "SignedSignatureProperties") + ) + now = datetime.datetime.now() + etree.SubElement( + signed_signature_properties, etree.QName(etsi, "SigningTime") + ).text = now.isoformat() + signing_certificate = etree.SubElement( + signed_signature_properties, etree.QName(etsi, "SigningCertificate") + ) + signing_certificate_cert = etree.SubElement( + signing_certificate, etree.QName(etsi, "Cert") + ) + cert_digest = etree.SubElement( + signing_certificate_cert, etree.QName(etsi, "CertDigest") + ) + etree.SubElement( + cert_digest, + etree.QName(xmlsig.constants.DSigNs, "DigestMethod"), + attrib={"Algorithm": "http://www.w3.org/2000/09/xmldsig#sha1"}, + ) + + hash_cert = hashlib.sha1(crt) + etree.SubElement( + cert_digest, etree.QName(xmlsig.constants.DSigNs, "DigestValue") + ).text = base64.b64encode(hash_cert.digest()) + + issuer_serial = etree.SubElement( + signing_certificate_cert, etree.QName(etsi, "IssuerSerial") + ) + etree.SubElement( + issuer_serial, etree.QName(xmlsig.constants.DSigNs, "X509IssuerName") + ).text = xmlsig.utils.get_rdns_name(certificate.issuer.rdns) + etree.SubElement( + issuer_serial, etree.QName(xmlsig.constants.DSigNs, "X509SerialNumber") + ).text = str(certificate.serial_number) + + signature_policy_identifier = etree.SubElement( + signed_signature_properties, + etree.QName(etsi, "SignaturePolicyIdentifier"), + ) + signature_policy_id = etree.SubElement( + signature_policy_identifier, etree.QName(etsi, "SignaturePolicyId") + ) + sig_policy_id = etree.SubElement( + signature_policy_id, etree.QName(etsi, "SigPolicyId") + ) + etree.SubElement( + sig_policy_id, etree.QName(etsi, "Identifier") + ).text = sig_policy_identifier + etree.SubElement( + sig_policy_id, etree.QName(etsi, "Description") + ).text = "PolĂ­tica de Firma FacturaE v3.1" + sig_policy_hash = etree.SubElement( + signature_policy_id, etree.QName(etsi, "SigPolicyHash") + ) + etree.SubElement( + sig_policy_hash, + etree.QName(xmlsig.constants.DSigNs, "DigestMethod"), + attrib={"Algorithm": "http://www.w3.org/2000/09/xmldsig#sha1"}, + ) + hash_value = sig_policy_hash_value + etree.SubElement( + sig_policy_hash, etree.QName(xmlsig.constants.DSigNs, "DigestValue") + ).text = hash_value + signer_role = etree.SubElement( + signed_signature_properties, etree.QName(etsi, "SignerRole") + ) + claimed_roles = etree.SubElement( + signer_role, etree.QName(etsi, "ClaimedRoles") + ) + etree.SubElement( + claimed_roles, etree.QName(etsi, "ClaimedRole") + ).text = "supplier" + signed_data_object_properties = etree.SubElement( + signed_properties, etree.QName(etsi, "SignedDataObjectProperties") + ) + data_object_format = etree.SubElement( + signed_data_object_properties, + etree.QName(etsi, "DataObjectFormat"), + attrib={"ObjectReference": "#" + reference_id}, + ) + etree.SubElement( + data_object_format, etree.QName(etsi, "Description") + ).text = "Factura" + etree.SubElement( + data_object_format, etree.QName(etsi, "MimeType") + ).text = "text/xml" + ctx = xmlsig.SignatureContext() + ctx.x509 = certificate + ctx.public_key = certificate.public_key() + ctx.private_key = private_key + root.append(sign) + ctx.sign(sign) + return etree.tostring(root, xml_declaration=True, encoding="UTF-8") + + signed_file_content = _sign_file( + self.company.facturae_certificate, + certificate_password.encode(), + xml_string, + ) logger.info("Factura-e for invoice %s (%s) generated and signed", self.rec_name, self.id) - signed_file_content = signed_file.read() - signed_file.close() - - os.unlink(unsigned_file.name) - os.unlink(cert_file.name) - os.unlink(signed_file.name) - return signed_file_content diff --git a/java/com/nantic/facturae/Signer.java b/java/com/nantic/facturae/Signer.java deleted file mode 100644 index 7cd085e..0000000 --- a/java/com/nantic/facturae/Signer.java +++ /dev/null @@ -1,51 +0,0 @@ -package com.nantic.facturae; - -import com.nantic.facturae.xmlsign.Sign; - -public class Signer { - - public static void main (String [] args) { - if (args.length < 1) { - System.out.println("Missing operation param"); - System.exit(-1); - } - int op = Integer.parseInt(args[0]); - switch (op) { - case 0: { - if (args.length < 6) { - System.out.println("Missing expected params: 0 " + - " " + - " "); - System.exit(-1); - } - Sign s = new Sign(); - s.FirmaXADES_EPES(args[1], args[2], args[3], args[4], args[5]); - break; - } - // case 1: { - // Verify v = new Verify(); - // if (v.validarFichero(args[1])) break; - // System.out.println("La firma no es valida."); - // System.exit(2001); - // break; - // } - // case 2: { - // PDFSign pdfsign = new PDFSign(); - // float[] dim = new float[]{Float.parseFloat(args[8]), Float.parseFloat(args[9]), Float.parseFloat(args[10]), Float.parseFloat(args[11])}; - // try { - // pdfsign.FirmaPDF(args[1], args[2], args[3], args[4], args[5], args[6], args[7], dim[0], dim[1], dim[2], dim[3]); - // } - // catch (Exception e) { - // e.printStackTrace(); - // System.exit(3001); - // } - // break; - // } - default: { - System.out.println("Unexpected operation"); - System.exit(9001); - } - } - System.exit(0); - } -} diff --git a/java/com/nantic/facturae/xmlsign/PassStoreKS.java b/java/com/nantic/facturae/xmlsign/PassStoreKS.java deleted file mode 100644 index 0eb0520..0000000 --- a/java/com/nantic/facturae/xmlsign/PassStoreKS.java +++ /dev/null @@ -1,18 +0,0 @@ -package com.nantic.facturae.xmlsign; - -import java.security.cert.X509Certificate; - -import es.mityc.javasign.pkstore.IPassStoreKS; - -public class PassStoreKS implements IPassStoreKS { - private transient String password; - - public PassStoreKS(String pass) { - this.password = new String(pass); - } - - public char[] getPassword(X509Certificate certificate, String alias) { - return this.password.toCharArray(); - } -} - diff --git a/java/com/nantic/facturae/xmlsign/Sign.java b/java/com/nantic/facturae/xmlsign/Sign.java deleted file mode 100644 index b799a4e..0000000 --- a/java/com/nantic/facturae/xmlsign/Sign.java +++ /dev/null @@ -1,151 +0,0 @@ -package com.nantic.facturae.xmlsign; - -import java.io.BufferedInputStream; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.io.PrintStream; -import java.net.URI; -import java.security.KeyStore; -import java.security.KeyStoreException; -import java.security.NoSuchAlgorithmException; -import java.security.PrivateKey; -import java.security.Provider; -import java.security.cert.CertificateException; -import java.security.cert.X509Certificate; -import java.util.List; -import javax.xml.parsers.DocumentBuilder; -import javax.xml.parsers.DocumentBuilderFactory; -import javax.xml.parsers.ParserConfigurationException; -import org.w3c.dom.Document; -import org.xml.sax.SAXException; - -import es.mityc.firmaJava.libreria.utilidades.UtilidadTratarNodo; -import es.mityc.firmaJava.libreria.xades.DataToSign; -import es.mityc.javasign.EnumFormatoFirma; -import es.mityc.firmaJava.libreria.xades.FirmaXML; -import es.mityc.firmaJava.libreria.xades.XAdESSchemas; -import es.mityc.firmaJava.libreria.xades.elementos.xades.ObjectIdentifier; -import es.mityc.javasign.pkstore.CertStoreException; -import es.mityc.javasign.pkstore.IPassStoreKS; -import es.mityc.javasign.pkstore.keystore.KSStore; -import es.mityc.javasign.xml.refs.AbstractObjectToSign; -import es.mityc.javasign.xml.refs.AllXMLToSign; -import es.mityc.javasign.xml.refs.ObjectToSign; - -import com.nantic.facturae.xmlsign.PassStoreKS; - -public class Sign extends FirmaXML { - public void FirmaXADES_EPES(String xmlOrigen, String xmlDestino, String policy, String certificado, String password) { - KSStore storeManager = null; - try { - KeyStore ks = KeyStore.getInstance("PKCS12"); - ks.load(new FileInputStream(certificado), password.toCharArray()); - storeManager = new KSStore(ks, (IPassStoreKS)new PassStoreKS(password)); - } - catch (KeyStoreException ex) { - System.out.println("Error KeyStoreException..."); - System.exit(1001); - } - catch (NoSuchAlgorithmException ex) { - System.out.println("Error NoSuchAlgorithmException..."); - System.exit(1002); - } - catch (CertificateException ex) { - System.out.println("Error CertificateException..."); - System.exit(1003); - } - catch (IOException ex) { - System.out.println("Error IOException... Provably the supplied password is incorrect"); - System.exit(1004); - } - - List certs = null; - try { - certs = storeManager.getSignCertificates(); - } - catch (CertStoreException ex) { - System.out.println("Error al obtener los certificados"); - System.exit(1005); - } - if (certs == null || certs.size() == 0) { - System.out.println("No hay certificados"); - System.exit(1006); - } - X509Certificate certificate = (X509Certificate)certs.get(0); - PrivateKey privateKey = null; - try { - privateKey = storeManager.getPrivateKey(certificate); - } - catch (CertStoreException e) { - System.out.println("Error al acceder al almac\u00e9n"); - System.exit(1007); - } - Provider provider = storeManager.getProvider(certificate); - DataToSign dataToSign = this.createDataToSign(xmlOrigen, policy); - Object[] res = null; - try { - res = this.signFile(certificate, dataToSign, privateKey, provider); - } - catch (Exception ex) { - System.out.println("Error!!!"); - System.exit(1008); - } - try { - UtilidadTratarNodo.saveDocumentToOutputStream((Document)((Document)res[0]), (OutputStream)new FileOutputStream(xmlDestino), (boolean)true); - } - catch (FileNotFoundException e) { - System.out.println("Error!"); - System.exit(1009); - } - System.out.println("\u00a1XML de origen firmado correctamente!."); - } - - private DataToSign createDataToSign(String xmlOrigen, String policy) { - DataToSign dataToSign = new DataToSign(); - dataToSign.setXadesFormat(EnumFormatoFirma.XAdES_BES); - dataToSign.setEsquema(XAdESSchemas.XAdES_132); - dataToSign.setPolicyKey(policy); - dataToSign.setAddPolicy(true); - dataToSign.setXMLEncoding("UTF-8"); - dataToSign.setEnveloped(true); - dataToSign.addObject(new ObjectToSign((AbstractObjectToSign)new AllXMLToSign(), "Documento de ejemplo", null, "text/xml", null)); - Document docToSign = this.getDocument(xmlOrigen); - dataToSign.setDocument(docToSign); - return dataToSign; - } - - private Document getDocument(String resource) { - Document doc = null; - DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); - dbf.setNamespaceAware(true); - try { - doc = dbf.newDocumentBuilder().parse(new BufferedInputStream(new FileInputStream(resource))); - } - catch (ParserConfigurationException ex) { - System.err.println("Error al parsear el documento"); - ex.printStackTrace(); - System.exit(-1); - } - catch (SAXException ex) { - System.err.println("Error al parsear el documento"); - ex.printStackTrace(); - System.exit(-1); - } - catch (IOException ex) { - System.err.println("Error al parsear el documento"); - ex.printStackTrace(); - System.exit(-1); - } - catch (IllegalArgumentException ex) { - System.err.println("Error al parsear el documento"); - ex.printStackTrace(); - System.exit(-1); - } - return doc; - } -} - diff --git a/java/compile.sh b/java/compile.sh deleted file mode 100755 index ad489ad..0000000 --- a/java/compile.sh +++ /dev/null @@ -1,41 +0,0 @@ -#!/bin/bash - -GENERATED_JAR="nantic-facturae-0.1.jar" - -rm lib/$GENERATED_JAR - -if [ -z "$JAVA_HOME" ]; then - directories="/usr/lib/jvm/java-7-openjdk-amd64/bin /usr/lib/j2sdk1.6-sun /usr/lib/j2sdk1.5-sun" - for d in $directories; do - if [ -d "$d" ]; then - export JAVA_HOME="$d" - fi - done -fi - -# echo "JAVA_HOME=$JAVA_HOME" -# export PATH="$JAVA_HOME"/bin:/bin:/usr/bin -export CLASSPATH=$(ls -1 lib/* | grep jar$ | awk '{printf "%s:", $1}') - -# echo "Class-Path: $(ls -1 lib/* | grep jar$ | awk '{printf "%s:", $1}')" > Manifest.txt - -FILES=$(find com -iname "*.java") - -echo "Compiling com.nantic.facturae" -javac -Xlint:deprecation $FILES || exit - -# echo "Main-Class: com.nantic.facturae.Signer" > Manifest.txt -# echo "Class-Path:" >> Manifest.txt -# for jarfile in `ls -1 lib/*.jar` -# do -# echo " $jarfile" >> Manifest.txt -# done -# echo "" >> Manifest.txt -# -# jar cvfm nantic-facturae-0.1.jar Manifest.txt com -jar cvf $GENERATED_JAR com -mv $GENERATED_JAR lib/$GENERATED_JAR - -# echo "Executing java com.nantic.facturae.Sign" -export CLASSPATH="lib/$GENERATED_JAR"":$CLASSPATH" -java com.nantic.facturae.Signer diff --git a/java/lib/MITyCLibAPI-1.1.8.jar b/java/lib/MITyCLibAPI-1.1.8.jar deleted file mode 100644 index 7b9c628..0000000 Binary files a/java/lib/MITyCLibAPI-1.1.8.jar and /dev/null differ diff --git a/java/lib/MITyCLibCert-1.1.8.jar b/java/lib/MITyCLibCert-1.1.8.jar deleted file mode 100644 index 42b2cca..0000000 Binary files a/java/lib/MITyCLibCert-1.1.8.jar and /dev/null differ diff --git a/java/lib/MITyCLibCrypt-1.1.8.jar b/java/lib/MITyCLibCrypt-1.1.8.jar deleted file mode 100644 index 073ab12..0000000 Binary files a/java/lib/MITyCLibCrypt-1.1.8.jar and /dev/null differ diff --git a/java/lib/MITyCLibOCSP-1.1.8.jar b/java/lib/MITyCLibOCSP-1.1.8.jar deleted file mode 100644 index 8d42b30..0000000 Binary files a/java/lib/MITyCLibOCSP-1.1.8.jar and /dev/null differ diff --git a/java/lib/MITyCLibPolicy-1.1.8.jar b/java/lib/MITyCLibPolicy-1.1.8.jar deleted file mode 100644 index 53d547d..0000000 Binary files a/java/lib/MITyCLibPolicy-1.1.8.jar and /dev/null differ diff --git a/java/lib/MITyCLibTSA-1.1.8.jar b/java/lib/MITyCLibTSA-1.1.8.jar deleted file mode 100644 index 7d93db3..0000000 Binary files a/java/lib/MITyCLibTSA-1.1.8.jar and /dev/null differ diff --git a/java/lib/MITyCLibTrust-1.1.8.jar b/java/lib/MITyCLibTrust-1.1.8.jar deleted file mode 100644 index 680b818..0000000 Binary files a/java/lib/MITyCLibTrust-1.1.8.jar and /dev/null differ diff --git a/java/lib/MITyCLibXADES-1.1.8.jar b/java/lib/MITyCLibXADES-1.1.8.jar deleted file mode 100644 index 7a1d4c5..0000000 Binary files a/java/lib/MITyCLibXADES-1.1.8.jar and /dev/null differ diff --git a/java/lib/bcmail-jdk16-1.45.jar b/java/lib/bcmail-jdk16-1.45.jar deleted file mode 100644 index 2e9d7ed..0000000 Binary files a/java/lib/bcmail-jdk16-1.45.jar and /dev/null differ diff --git a/java/lib/bcprov-jdk16-1.45.jar b/java/lib/bcprov-jdk16-1.45.jar deleted file mode 100644 index 38685d5..0000000 Binary files a/java/lib/bcprov-jdk16-1.45.jar and /dev/null differ diff --git a/java/lib/bctsp-jdk16-1.45.jar b/java/lib/bctsp-jdk16-1.45.jar deleted file mode 100644 index cebdc1a..0000000 Binary files a/java/lib/bctsp-jdk16-1.45.jar and /dev/null differ diff --git a/java/lib/commons-codec-1.3.jar b/java/lib/commons-codec-1.3.jar deleted file mode 100644 index 957b675..0000000 Binary files a/java/lib/commons-codec-1.3.jar and /dev/null differ diff --git a/java/lib/commons-httpclient-3.0.1.jar b/java/lib/commons-httpclient-3.0.1.jar deleted file mode 100644 index cfc777c..0000000 Binary files a/java/lib/commons-httpclient-3.0.1.jar and /dev/null differ diff --git a/java/lib/commons-lang-2.4.jar b/java/lib/commons-lang-2.4.jar deleted file mode 100644 index 532939e..0000000 Binary files a/java/lib/commons-lang-2.4.jar and /dev/null differ diff --git a/java/lib/commons-logging-1.1.1.jar b/java/lib/commons-logging-1.1.1.jar deleted file mode 100644 index 1deef14..0000000 Binary files a/java/lib/commons-logging-1.1.1.jar and /dev/null differ diff --git a/java/lib/itext-2.1.3.jar b/java/lib/itext-2.1.3.jar deleted file mode 100644 index 6278a56..0000000 Binary files a/java/lib/itext-2.1.3.jar and /dev/null differ diff --git a/java/lib/jss-4.2.5.jar b/java/lib/jss-4.2.5.jar deleted file mode 100644 index 4214821..0000000 Binary files a/java/lib/jss-4.2.5.jar and /dev/null differ diff --git a/java/lib/log4j-1.2.14.jar b/java/lib/log4j-1.2.14.jar deleted file mode 100644 index 6251307..0000000 Binary files a/java/lib/log4j-1.2.14.jar and /dev/null differ diff --git a/java/lib/mail-1.4.1.jar b/java/lib/mail-1.4.1.jar deleted file mode 100644 index 1d15e79..0000000 Binary files a/java/lib/mail-1.4.1.jar and /dev/null differ diff --git a/java/lib/nantic-facturae-0.1.jar b/java/lib/nantic-facturae-0.1.jar deleted file mode 100644 index e91afb6..0000000 Binary files a/java/lib/nantic-facturae-0.1.jar and /dev/null differ diff --git a/java/lib/serializer-2.7.1.jar b/java/lib/serializer-2.7.1.jar deleted file mode 100644 index 99f98db..0000000 Binary files a/java/lib/serializer-2.7.1.jar and /dev/null differ diff --git a/java/lib/sunpkcs11-1.0.jar b/java/lib/sunpkcs11-1.0.jar deleted file mode 100644 index 45f7242..0000000 Binary files a/java/lib/sunpkcs11-1.0.jar and /dev/null differ diff --git a/java/lib/xml-apis-1.3.04.jar b/java/lib/xml-apis-1.3.04.jar deleted file mode 100644 index d42c0ea..0000000 Binary files a/java/lib/xml-apis-1.3.04.jar and /dev/null differ diff --git a/java/lib/xmlsec-1.4.2-ADSI-1.1.jar b/java/lib/xmlsec-1.4.2-ADSI-1.1.jar deleted file mode 100644 index b60d359..0000000 Binary files a/java/lib/xmlsec-1.4.2-ADSI-1.1.jar and /dev/null differ diff --git a/template_facturae_3.2.1.xml b/template_facturae_3.2.1.xml index 0c3c626..d5f0b13 100644 --- a/template_facturae_3.2.1.xml +++ b/template_facturae_3.2.1.xml @@ -10,21 +10,21 @@ {{ ('%s%s' % (invoice.company.party.tax_identifier.code, invoice.number))[:70] }} 1 - {{ invoice.total_amount }} + {{ Currency.compute(invoice.currency, invoice.total_amount, euro) }} {% if invoice.currency != euro %} {{ Currency.compute(invoice.currency, invoice.total_amount, euro) }} {% endif %} {# TODO: it must to get amount_to_pay? #} - {{ invoice.total_amount }} + {{ Currency.compute(invoice.currency, invoice.total_amount, euro) }} {% if invoice.currency != euro %} {{ Currency.compute(invoice.currency, invoice.total_amount, euro) }} {% endif %} {# TODO: it must to get amount_to_pay? #} - {{ invoice.total_amount }} + {{ Currency.compute(invoice.currency, invoice.total_amount, euro) }} {% if invoice.currency != euro %} {{ Currency.compute(invoice.currency, invoice.total_amount, euro) }} {% endif %} @@ -161,7 +161,7 @@ {{ invoice.currency.code.upper() }} {% if invoice.currency != euro %} - {{ exchange_rate }} + {{ Currency.compute(invoice.currency, exchange_rate, euro) }} {{ exchange_rate_date }} {% endif %} @@ -172,15 +172,15 @@ {% for invoice_tax in invoice.taxes_outputs %} {{ invoice_tax.tax.report_type }} - {{ invoice_tax.tax.rate * 100 }} + {{ Currency.compute(invoice.currency, invoice_tax.tax.rate * 100, euro) }} - {{ invoice_tax.base }} + {{ Currency.compute(invoice.currency, invoice_tax.base, euro) }} {% if invoice.currency != euro %} {{ Currency.compute(invoice.currency, invoice_tax.base, euro) }} {% endif %} - {{ invoice_tax.amount }} + {{ Currency.compute(invoice.currency, invoice_tax.amount, euro) }} {% if invoice.currency != euro %} {{ Currency.compute(invoice.currency, invoice_tax.amount, euro) }} {% endif %} @@ -193,7 +193,7 @@ {# TODO: EquivalenceSurchace must to have its own Tax entry or it must to go to the IVA line? TaxRate == EquivalenceSurcharge and TaxAmount == EquivalenceSurchargeAmount? #} {{ (invoice_tax.tax.rate * 100).quantize(Decimal('0.01')) }} - {{ invoice_tax.amount }} + {{ Currency.compute(invoice.currency, invoice_tax.amount, euro) }} {% if invoice.currency != euro %} {{ Currency.compute(invoice.currency, invoice_tax.amount, euro) }} {% endif %} @@ -207,15 +207,15 @@ {% for invoice_tax in invoice.taxes_withheld %} {{ invoice_tax.tax.report_type }} - {{ invoice_tax.tax.rate * 100 }} + {{ Currency.compute(invoice.currency, invoice_tax.tax.rate * 100, euro) }} - {{ invoice_tax.base }} + {{ Currency.compute(invoice.currency, invoice_tax.base, euro) }} {% if invoice.currency != euro %} {{ Currency.compute(invoice.currency, invoice_tax.base, euro) }} {% endif %} - {{ invoice_tax.amount }} + {{ Currency.compute(invoice.currency, invoice_tax.amount, euro) }} {% if invoice.currency != euro %} {{ Currency.compute(invoice.currency, invoice_tax.amount, euro) }} {% endif %} @@ -225,13 +225,13 @@ {% endif %} - {{ invoice.untaxed_amount }} + {{ Currency.compute(invoice.currency, invoice.untaxed_amount, euro) }} {# TODO: GeneralDiscounts and TotalGeneralDiscounts (account_invoice_discount_global) not supported #} {# TODO: GeneralSurcharges and TotalGeneralSurcharges not supported #} - {{ invoice.untaxed_amount }} - {{ invoice.taxes_outputs | sum(attribute='amount', start=Decimal(0)) }} - {{ invoice.taxes_withheld | sum(attribute='amount', start=Decimal(0)) }} - {{ invoice.total_amount }} + {{ Currency.compute(invoice.currency, invoice.untaxed_amount, euro) }} + {{ Currency.compute(invoice.currency, invoice.taxes_outputs | sum(attribute='amount', start=Decimal(0)), euro) }} + {{ Currency.compute(invoice.currency, invoice.taxes_withheld | sum(attribute='amount', start=Decimal(0)), euro) }} + {{ Currency.compute(invoice.currency, invoice.total_amount, euro) }} {# TODO: optional, not supported - Subsidies - PaymentsOnAccount, TotalPaymentsOnAccount @@ -239,8 +239,8 @@ - TotalFinancialExpenses (account_payment_type_cost?) - AmountsWithheld #} - {{ invoice.total_amount }} - {{ invoice.total_amount }} + {{ Currency.compute(invoice.currency, invoice.total_amount, euro) }} + {{ Currency.compute(invoice.currency, invoice.total_amount, euro) }} {% for line in invoice.lines if line.type == 'line' %} @@ -258,27 +258,27 @@ {{ line.description and line.description[:2500] or '' }} {{ line.quantity }} {{ UOM_CODE2TYPE.get(line.unit.symbol, '05') if line.unit else '05' }} - {{ line.unit_price }} - {{ line.amount }} + {{ Currency.compute(invoice.currency, line.unit_price, euro) }} + {{ Currency.compute(invoice.currency, line.amount, euro) }} {# TODO: optional, not supported - DiscountsAndRebates (account_invoice_discount) - Charges #} - {{ line.amount }} + {{ Currency.compute(invoice.currency, line.amount, euro) }} {% if line.taxes_withheld %} {% for line_tax in invoice.taxes_withheld %} {{ line_tax.tax.report_type }} - {{ line_tax.tax.rate * 100 }} + {{ Currency.compute(invoice.currency, line_tax.tax.rate * 100, euro) }} - {{ line.amount }} + {{ Currency.compute(invoice.currency, line.amount, euro) }} {% if invoice.currency != euro %} {{ Currency.compute(invoice.currency, line.amount, euro) }} {% endif %} - {{ line.amount * line_tax.tax.rate }} + {{ Currency.compute(invoice.currency, line.amount * line_tax.tax.rate, euro) }} {% if invoice.currency != euro %} {{ Currency.compute(invoice.currency, line.amount * line_tax.tax.rate, euro) }} {% endif %} @@ -291,15 +291,15 @@ {% for line_tax in line.taxes_outputs %} {{ line_tax.tax.report_type }} - {{ line_tax.tax.rate * 100 }} + {{ Currency.compute(invoice.currency, line_tax.tax.rate * 100, euro) }} - {{ line.amount }} + {{ Currency.compute(invoice.currency, line.amount, euro) }} {% if invoice.currency != euro %} {{ Currency.compute(invoice.currency, line.amount, euro) }} {% endif %} - {{ line.amount * line_tax.tax.rate }} + {{ Currency.compute(invoice.currency, line.amount * line_tax.tax.rate, euro) }} {% if invoice.currency != euro %} {{ Currency.compute(invoice.currency, line.amount * line_tax.tax.rate, euro) }} {% endif %} @@ -312,7 +312,7 @@ {# TODO: EquivalenceSurchace must to have its own Tax entry or it must to go to the IVA line? TaxRate == EquivalenceSurcharge and TaxAmount == EquivalenceSurchargeAmount? #} {{ (line_tax.tax.rate * 100).quantize(Decimal('0.01')) }} - {{ line.amount * line_tax.tax.rate }} + {{ Currency.compute(invoice.currency, line.amount * line_tax.tax.rate, euro) }} {% if invoice.currency != euro %} {{ Currency.compute(invoice.currency, line.amount * line_tax.tax.rate, euro) }} {% endif %} @@ -340,7 +340,7 @@ {% for move_line in invoice.payment_details %} {{ move_line.maturity_date.isoformat() }} - {{ ((move_line.debit - move_line.credit) | abs).quantize(Decimal('0.01')) }} + {{ Currency.compute(invoice.currency, ((move_line.debit - move_line.credit) | abs).quantize(Decimal('0.01')), euro) }} {{ move_line.payment_type.facturae_type }} {% if move_line.payment_type.facturae_type == '04' %} @@ -376,7 +376,7 @@ - RelatedDocuments - Extensions #} - Factura generada con Tryton (http://www.tryton.org) + Factura generada con Tryton (https://www.tryton.org) diff --git a/tests/test_account_invoice_facturae.py b/tests/test_account_invoice_facturae.py index 225dad1..b3f997c 100644 --- a/tests/test_account_invoice_facturae.py +++ b/tests/test_account_invoice_facturae.py @@ -67,30 +67,6 @@ class TestAccountInvoiceFacturaeCase(ModuleTestCase): CURRENT_PATH, 'certificate.pfx'), 'rb') as cert_file: company.facturae_certificate = cert_file.read() - payment_term, = PaymentTerm.create([{ - 'name': '20 days, 40 days', - 'lines': [ - ('create', [{ - 'sequence': 0, - 'type': 'percent', - 'divisor': 2, - 'ratio': Decimal('.5'), - 'relativedeltas': [('create', [{ - 'days': 20, - }, - ]), - ], - }, { - 'sequence': 1, - 'type': 'remainder', - 'relativedeltas': [('create', [{ - 'days': 40, - }, - ]), - ], - }])] - }]) - with set_company(company): create_chart(company, tax=True) @@ -225,7 +201,8 @@ class TestAccountInvoiceFacturaeCase(ModuleTestCase): Invoice.post([invoice]) Invoice.generate_facturae_default([invoice], 'privatepassword') - + self.assertNotEqual(invoice.invoice_facturae, None) + self.assertEqual(invoice.invoice_facturae_filename, 'facturae-1.xsig') def suite(): suite = trytond.tests.test_tryton.suite() diff --git a/view/tax_form.xml b/view/tax_form.xml index 6a0b388..f76f3fa 100644 --- a/view/tax_form.xml +++ b/view/tax_form.xml @@ -2,10 +2,10 @@ - - + + - \ No newline at end of file + diff --git a/view/template_tax_form.xml b/view/template_tax_form.xml index 6a0b388..f76f3fa 100644 --- a/view/template_tax_form.xml +++ b/view/template_tax_form.xml @@ -2,10 +2,10 @@ - - + + - \ No newline at end of file +