From 1143b269883e6801861532aacc438f26ce7c872f Mon Sep 17 00:00:00 2001 From: bit4bit Date: Wed, 2 Dec 2020 18:46:18 +0000 Subject: [PATCH] Se adiciona xml de AllowanceCharge para documento FossilOrigin-Name: 93cd9530bb2f63a2803b8a32a5aceeeda015eff2d26c96e95a13dbb9193cad82 --- facho/fe/form/__init__.py | 82 ++++++++++++++++++++---------------- facho/fe/form_xml/invoice.py | 17 ++++++++ tests/test_form.py | 4 +- tests/test_form_xml.py | 30 +++++++++++++ 4 files changed, 94 insertions(+), 39 deletions(-) diff --git a/facho/fe/form/__init__.py b/facho/fe/form/__init__.py index c05a338..f49a1a0 100644 --- a/facho/fe/form/__init__.py +++ b/facho/fe/form/__init__.py @@ -396,6 +396,43 @@ class InvoiceDocumentReference(BillingReference): date: fecha de emision de la nota credito relacionada """ +@dataclass +class AllowanceChargeReason: + code: str + reason: str + + def __post_init__(self): + if self.code not in codelist.CodigoDescuento: + raise ValueError("code [%s] not found" % (self.code)) + + +@dataclass +class AllowanceCharge: + #DIAN 1.7.-2020: FAQ03 + charge_indicator: bool = True + amount: Amount = Amount(0.0) + reason: AllowanceChargeReason = None + + def isCharge(self): + return self.charge_indicator == True + + def isDiscount(self): + return self.charge_indicator == False + + def asCharge(self): + self.charge_indicator = True + + def asDiscount(self): + self.charge_indicator = False + + def hasReason(self): + return self.reason is not None + +class AllowanceChargeAsDiscount(AllowanceCharge): + def __init__(self, amount: Amount = Amount(0.0)): + self.charge_indicator = False + self.amount = amount + @dataclass class InvoiceLine: # RESOLUCION 0004: pagina 155 @@ -409,6 +446,13 @@ class InvoiceLine: # de subtotal tax: typing.Optional[TaxTotal] + allowance_charge = [] + + def add_allowance_charge(charge): + if not isinstance(charge, AllowanceCharge): + raise TypeError('charge invalid type expected AllowanceCharge') + self.allowance_charge.add(charge) + @property def total_amount(self): return self.quantity * self.price.amount @@ -459,42 +503,6 @@ class LegalMonetaryTotal: - self.prepaid_amount -@dataclass -class AllowanceChargeReason: - code: str - reason: str - - def __post_init__(self): - if self.code not in codelist.CodigoDescuento: - raise ValueError("code [%s] not found" % (self.code)) - - -@dataclass -class AllowanceCharge: - #DIAN 1.7.-2020: FAQ03 - charge_indicator: bool = True - amount: Amount = Amount(0.0) - reason: AllowanceChargeReason = None - - def isCharge(self): - return self.charge_indicator == True - - def isDiscount(self): - return self.charge_indicator == False - - def asCharge(self): - self.charge_indicator = True - - def asDiscount(self): - self.charge_indicator = False - - def hasReason(self): - return self.reason is not None - -class AllowanceChargeAsDiscount(AllowanceCharge): - def __init__(self, amount: Amount = Amount(0.0)): - self.charge_indicator = False - self.amount = amount class NationalSalesInvoiceDocumentType(str): def __str__(self): @@ -593,7 +601,7 @@ class Invoice: self.invoice_operation_type = operation - def add_allownace_charge(self, charge: AllowanceCharge): + def add_allowance_charge(self, charge: AllowanceCharge): self.invoice_allowance_charge.append(charge) def add_invoice_line(self, line: InvoiceLine): diff --git a/facho/fe/form_xml/invoice.py b/facho/fe/form_xml/invoice.py index 279fcfb..b9d4708 100644 --- a/facho/fe/form_xml/invoice.py +++ b/facho/fe/form_xml/invoice.py @@ -543,7 +543,23 @@ class DIANInvoiceXML(fe.FeXML): invoice_line.price.quantity, unitCode=invoice_line.quantity.code) + def set_allowance_charge(fexml, invoice): + for idx, charge in enumerate(invoice.invoice_allowance_charge): + next_append = idx > 0 + fexml.append_allowance_charge(fexml, idx + 1, charge, append=next_append) + def append_allowance_charge(fexml, parent, idx, charge, append=False): + line = fexml.fragment('./cac:AllowanceCharge', append=append) + #DIAN 1.7.-2020: FAQ02 + line.set_element('./cbc:ID', idx) + #DIAN 1.7.-2020: FAQ03 + line.set_element('./cbc:ChargeIndicator', str(charge.charge_indicator).lower()) + if charge.reason: + line.set_element('./cbc:AllowanceChargeReasonCode', charge.reason.code) + line.set_element('./cbc:allowanceChargeReason', charge.reason.reason) + fexml.set_element_amount_for(line, './cbc:Amount', charge.amount) + + def attach_invoice(fexml, invoice): """adiciona etiquetas a FEXML y retorna FEXML en caso de fallar validacion retorna None""" @@ -579,6 +595,7 @@ class DIANInvoiceXML(fe.FeXML): fexml.set_invoice_totals(invoice) fexml.set_invoice_lines(invoice) fexml.set_payment_mean(invoice) + fexml.set_allowance_charge(invoice) fexml.set_billing_reference(invoice) return fexml diff --git a/tests/test_form.py b/tests/test_form.py index 5089580..0476db8 100644 --- a/tests/test_form.py +++ b/tests/test_form.py @@ -61,7 +61,7 @@ def test_FAU10(): ] ) )) - inv.add_allownace_charge(form.AllowanceCharge(amount=form.Amount(19.0))) + inv.add_allowance_charge(form.AllowanceCharge(amount=form.Amount(19.0))) inv.calculate() assert inv.invoice_legal_monetary_total.line_extension_amount == form.Amount(100.0) @@ -89,7 +89,7 @@ def test_FAU14(): ] ) )) - inv.add_allownace_charge(form.AllowanceCharge(amount=form.Amount(19.0))) + inv.add_allowance_charge(form.AllowanceCharge(amount=form.Amount(19.0))) inv.add_prepaid_payment(form.PrePaidPayment(paid_amount = form.Amount(50.0))) inv.calculate() diff --git a/tests/test_form_xml.py b/tests/test_form_xml.py index 7e43f56..b4a01dd 100644 --- a/tests/test_form_xml.py +++ b/tests/test_form_xml.py @@ -6,9 +6,13 @@ """Tests for `facho` package.""" import pytest +from datetime import datetime +from facho.fe import form from facho.fe import form_xml +from fixtures import * + def test_import_DIANInvoiceXML(): try: form_xml.DIANInvoiceXML @@ -27,3 +31,29 @@ def test_import_DIANCreditNoteXML(): form_xml.DIANCreditNoteXML except AttributeError: pytest.fail("unexpected not found") + +def test_FAU10(simple_invoice_without_lines): + inv = simple_invoice_without_lines + inv.add_invoice_line(form.InvoiceLine( + quantity = form.Quantity(1, '94'), + description = 'producto facho', + item = form.StandardItem(9999), + price = form.Price( + amount = form.Amount(100.0), + type_code = '01', + type = 'x' + ), + tax = form.TaxTotal( + subtotals = [ + form.TaxSubTotal( + percent = 19.0, + ) + ] + ) + )) + inv.add_allowance_charge(form.AllowanceCharge(amount=form.Amount(19.0))) + + xml = form_xml.DIANInvoiceXML(inv) + assert xml.get_element_text('./cac:AllowanceCharge/cbc:ID') == '1' + assert xml.get_element_text('./cac:AllowanceCharge/cbc:ChargeIndicator') == 'true' + assert xml.get_element_text('./cac:AllowanceCharge/cbc:Amount') == '19.0'