From a7eb945962d8a92233b5e2b1386c78665052d3ee Mon Sep 17 00:00:00 2001 From: "bit4bit@riseup.net" Date: Fri, 30 Oct 2020 03:27:11 +0000 Subject: [PATCH] tests/test_fe_form.py: se implementa prueba cude. se implementa prueba para cude de nota debito segun ejemplo en pagina 614, pero el sha384 de la composicion difiere del generado tanto de facho como el sitio web que dan para generarlo. FossilOrigin-Name: 51b29990f6eaf4fc4f85dc51873957f41d9ba047889e4e4fe2ea4f0b1285e88c --- facho/fe/fe.py | 10 ++-- facho/fe/form.py | 17 +++++++ facho/fe/form_xml/debit_note.py | 12 ++--- facho/fe/form_xml/invoice.py | 2 +- tests/test_fe_form.py | 85 ++++++++++++++++++++++++++++++++- 5 files changed, 113 insertions(+), 13 deletions(-) diff --git a/facho/fe/fe.py b/facho/fe/fe.py index 64e05b7..b523e80 100644 --- a/facho/fe/fe.py +++ b/facho/fe/fe.py @@ -13,6 +13,7 @@ import hashlib from contextlib import contextmanager from .data.dian import codelist from . import form +from collections import defaultdict AMBIENTE_PRUEBAS = codelist.TipoAmbiente.by_name('Pruebas')['code'] AMBIENTE_PRODUCCION = codelist.TipoAmbiente.by_name('Producción')['code'] @@ -97,7 +98,7 @@ class DianXMLExtensionCUDFE(FachoXMLExtension): raise NotImplementedError() def build(self, fachoxml): - cufe = self._generate_cufe(fachoxml) + cufe = self._generate_cufe() fachoxml.set_element('./cbc:UUID', cufe, schemeID=self.tipo_ambiente, schemeName=self.schemeName()) @@ -122,7 +123,7 @@ class DianXMLExtensionCUDFE(FachoXMLExtension): # PAG 601 build_vars['ValorBruto'] = invoice.invoice_legal_monetary_total.line_extension_amount build_vars['ValorTotalPagar'] = invoice.invoice_legal_monetary_total.payable_amount - ValorImpuestoPara = {} + ValorImpuestoPara = defaultdict(lambda: form.Amount(0.0)) build_vars['CodImpuesto1'] = 1 build_vars['CodImpuesto2'] = 4 build_vars['CodImpuesto3'] = 3 @@ -139,9 +140,8 @@ class DianXMLExtensionCUDFE(FachoXMLExtension): return build_vars - def _generate_cufe(self, fachoxml): - formatVars = self.formatVars() - cufe = "".join(formatVars) + def _generate_cufe(self): + cufe = "".join(self.formatVars()) # crear hash... h = hashlib.sha384() diff --git a/facho/fe/form.py b/facho/fe/form.py index dab1fda..b5960f4 100644 --- a/facho/fe/form.py +++ b/facho/fe/form.py @@ -91,6 +91,7 @@ class Amount: def is_same_currency(self, other): return self.currency == other.currency + @dataclass class Item: description: str @@ -348,12 +349,19 @@ class AllowanceCharge: class NationalSalesInvoiceDocumentType(str): def __str__(self): + # 6.1.3 return '01' class CreditNoteDocumentType(str): def __str__(self): + # 6.1.3 return '91' +class DebitNoteDocumentType(str): + def __str__(self): + # 6.1.3 + return '92' + class Invoice: def __init__(self, type_code: str): if str(type_code) not in codelist.TipoDocumento: @@ -475,3 +483,12 @@ class CreditNote(Invoice): if not isinstance(invoice_document_reference, BillingReference): raise TypeError('invoice_document_reference invalid type') self.invoice_billing_reference = invoice_document_reference + + +class DebitNote(Invoice): + def __init__(self, invoice_document_reference: BillingReference): + super().__init__(DebitNoteDocumentType()) + + if not isinstance(invoice_document_reference, BillingReference): + raise TypeError('invoice_document_reference invalid type') + self.invoice_billing_reference = invoice_document_reference diff --git a/facho/fe/form_xml/debit_note.py b/facho/fe/form_xml/debit_note.py index 3bf367a..c537f2a 100644 --- a/facho/fe/form_xml/debit_note.py +++ b/facho/fe/form_xml/debit_note.py @@ -17,7 +17,7 @@ class DIANDebitNoteXML(fe.FeXML): ublextension = self.fragment('/fe:DebitNote/ext:UBLExtensions/ext:UBLExtension', append=True) extcontent = ublextension.find_or_create_element('/ext:UBLExtension/ext:ExtensionContent') self.attach_invoice(invoice) - + def set_supplier(fexml, invoice): fexml.placeholder_for('/fe:DebitNote/cac:AccountingSupplierParty') #DIAN 1.7.-2020: DAJ02 @@ -101,7 +101,7 @@ class DIANDebitNoteXML(fe.FeXML): invoice.invoice_customer.tax_scheme.code) #DIAN 1.7.-2020: DAJ41 fexml.set_element('/fe:DebitNote/cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme/cac:TaxScheme/cbc:Name', - invoice.invoice_customer.tax_scheme.name) + invoice.invoice_customer.tax_scheme.name) #DIAN 1.7.-2020: DAJ42 fexml.placeholder_for('/fe:DebitNote/cac:AccountingSupplierParty/cac:Party/cac:PartyLegalEntity') #DIAN 1.7.-2020: DAJ43 @@ -229,7 +229,7 @@ class DIANDebitNoteXML(fe.FeXML): #DIAN 1.7.-2020: CAK51, CAK55 fexml.set_element('/fe:DebitNote/cac:AccountingCustomerParty/cac:Party/cac:Contact/cbc:ElectronicMail', invoice.invoice_customer.email) - + def set_payment_mean(fexml, invoice): payment_mean = invoice.invoice_payment_mean @@ -275,10 +275,10 @@ class DIANDebitNoteXML(fe.FeXML): schemeName='CUFE-SHA384') fexml.set_element('/fe:DebitNote/cac:BillingReference/cac:InvoiceDocumentReference/cbc:IssueDate', invoice.invoice_billing_reference.date) - - + + def set_invoice_totals(fexml, invoice): - tax_amount_for = defaultdict(lambda: defaultdict(lambda: 0.0)) + tax_amount_for = defaultdict(lambda: defaultdict(lambda: Amount(0.0))) percent_for = defaultdict(lambda: None) #requeridos para CUDE diff --git a/facho/fe/form_xml/invoice.py b/facho/fe/form_xml/invoice.py index 3fb05db..63c4fd3 100644 --- a/facho/fe/form_xml/invoice.py +++ b/facho/fe/form_xml/invoice.py @@ -416,7 +416,7 @@ class DIANInvoiceXML(fe.FeXML): return fexml._set_invoice_document_reference(reference) def set_invoice_totals(fexml, invoice): - tax_amount_for = defaultdict(lambda: defaultdict(lambda: 0.0)) + tax_amount_for = defaultdict(lambda: defaultdict(lambda: Amount(0.0))) percent_for = defaultdict(lambda: None) #requeridos para CUFE diff --git a/tests/test_fe_form.py b/tests/test_fe_form.py index 060ced3..15a6c13 100644 --- a/tests/test_fe_form.py +++ b/tests/test_fe_form.py @@ -12,7 +12,39 @@ import zipfile import facho.fe.form as form from facho import fe -from facho.fe.form_xml import DIANInvoiceXML, DIANCreditNoteXML +from facho.fe.form_xml import DIANInvoiceXML, DIANCreditNoteXML, DIANDebitNoteXML + +@pytest.fixture +def simple_debit_note_without_lines(): + inv = form.DebitNote(form.InvoiceDocumentReference('1234', 'xx', datetime.now())) + inv.set_period(datetime.now(), datetime.now()) + inv.set_issue(datetime.now()) + inv.set_ident('ABC123') + inv.set_operation_type('10') + inv.set_payment_mean(form.PaymentMean(form.PaymentMean.DEBIT, '41', datetime.now(), '1234')) + inv.set_supplier(form.Party( + name = 'facho-supplier', + ident = form.PartyIdentification('123','', '31'), + responsability_code = form.Responsability(['O-07']), + responsability_regime_code = '48', + organization_code = '1', + address = form.Address( + '', '', form.City('05001', 'Medellín'), + form.Country('CO', 'Colombia'), + form.CountrySubentity('05', 'Antioquia')) + )) + inv.set_customer(form.Party( + name = 'facho-customer', + ident = form.PartyIdentification('321', '', '31'), + responsability_code = form.Responsability(['O-07']), + responsability_regime_code = '48', + organization_code = '1', + address = form.Address( + '', '', form.City('05001', 'Medellín'), + form.Country('CO', 'Colombia'), + form.CountrySubentity('05', 'Antioquia')) + )) + return inv @pytest.fixture def simple_credit_note_without_lines(): @@ -312,3 +344,54 @@ def test_credit_note_cude(simple_credit_note_without_lines): cude = xml_invoice.get_element_text('/fe:CreditNote/cbc:UUID') # pag 612 assert cude == '907e4444decc9e59c160a2fb3b6659b33dc5b632a5008922b9a62f83f757b1c448e47f5867f2b50dbdb96f48c7681168' + + +# pag 614 +def test_debit_note_cude(simple_debit_note_without_lines): + simple_invoice = simple_debit_note_without_lines + simple_invoice.invoice_ident = 'ND1001' + simple_invoice.invoice_issue = datetime.strptime('2019-01-18 10:58:00-05:00', '%Y-%m-%d %H:%M:%S%z') + simple_invoice.invoice_supplier.ident = form.PartyIdentification('900197264', '5', '31') + simple_invoice.invoice_customer.ident = form.PartyIdentification('10254102', '5', '31') + simple_invoice.add_invoice_line(form.InvoiceLine( + quantity = 1, + description = 'producto', + item = form.StandardItem('test', 111), + price = form.Price(form.Amount(30_000), '01', ''), + tax = form.TaxTotal( + subtotals = [ + form.TaxSubTotal( + tax_scheme_ident = '04', + percent = 8.0 + )]) + )) + + simple_invoice.calculate() + xml_invoice = DIANDebitNoteXML(simple_invoice) + + cude_extension = fe.DianXMLExtensionCUDE( + simple_invoice, + '10201', + tipo_ambiente = fe.AMBIENTE_PRUEBAS, + ) + build_vars = cude_extension.buildVars() + assert build_vars['NumFac'] == 'ND1001' + assert build_vars['FecFac'] == '2019-01-18' + assert build_vars['HoraFac'] == '10:58:00-05:00' + assert build_vars['ValorBruto'] == form.Amount(30_000) + assert build_vars['NitOFE'] == '900197264' + assert build_vars['NumAdq'] == '10254102' + assert build_vars['ValorImpuestoPara'][1] == form.Amount(0) + assert build_vars['ValorImpuestoPara'][4] == form.Amount(2400) + assert build_vars['ValorImpuestoPara'][3] == form.Amount(0) + assert build_vars['ValorTotalPagar'] == form.Amount(32400) + assert build_vars['Software-PIN'] == '10201' + assert build_vars['TipoAmb'] == 2 + + + cude_composicion = "".join(cude_extension.formatVars()) + assert cude_composicion == 'ND10012019-01-1810:58:00-05:0030000.00010.00042400.00030.0032400.0090019726410254102102012' + + xml_invoice.add_extension(cude_extension) + cude = xml_invoice.get_element_text('/fe:DebitNote/cbc:UUID') + assert cude == 'b9483dc2a17167feedf37b6bd67c4204e7b601933e0e389cffbd545e4d0ec370b403cbb41ff656776cb6cb5d8348ecd4'