Se adiciona xml de AllowanceCharge para documento
FossilOrigin-Name: 93cd9530bb2f63a2803b8a32a5aceeeda015eff2d26c96e95a13dbb9193cad82
This commit is contained in:
		| @@ -396,6 +396,43 @@ class InvoiceDocumentReference(BillingReference): | |||||||
|     date: fecha de emision de la nota credito relacionada |     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 | @dataclass | ||||||
| class InvoiceLine: | class InvoiceLine: | ||||||
|     # RESOLUCION 0004: pagina 155 |     # RESOLUCION 0004: pagina 155 | ||||||
| @@ -409,6 +446,13 @@ class InvoiceLine: | |||||||
|     # de subtotal |     # de subtotal | ||||||
|     tax: typing.Optional[TaxTotal] |     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 |     @property | ||||||
|     def total_amount(self): |     def total_amount(self): | ||||||
|         return self.quantity * self.price.amount |         return self.quantity * self.price.amount | ||||||
| @@ -459,42 +503,6 @@ class LegalMonetaryTotal: | |||||||
|             - self.prepaid_amount |             - 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): | class NationalSalesInvoiceDocumentType(str): | ||||||
|     def __str__(self): |     def __str__(self): | ||||||
| @@ -593,7 +601,7 @@ class Invoice: | |||||||
|  |  | ||||||
|         self.invoice_operation_type = operation |         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) |         self.invoice_allowance_charge.append(charge) | ||||||
|  |  | ||||||
|     def add_invoice_line(self, line: InvoiceLine): |     def add_invoice_line(self, line: InvoiceLine): | ||||||
|   | |||||||
| @@ -543,7 +543,23 @@ class DIANInvoiceXML(fe.FeXML): | |||||||
|                              invoice_line.price.quantity, |                              invoice_line.price.quantity, | ||||||
|                              unitCode=invoice_line.quantity.code) |                              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): |     def attach_invoice(fexml, invoice): | ||||||
|         """adiciona etiquetas a FEXML y retorna FEXML |         """adiciona etiquetas a FEXML y retorna FEXML | ||||||
|         en caso de fallar validacion retorna None""" |         en caso de fallar validacion retorna None""" | ||||||
| @@ -579,6 +595,7 @@ class DIANInvoiceXML(fe.FeXML): | |||||||
|         fexml.set_invoice_totals(invoice) |         fexml.set_invoice_totals(invoice) | ||||||
|         fexml.set_invoice_lines(invoice) |         fexml.set_invoice_lines(invoice) | ||||||
|         fexml.set_payment_mean(invoice) |         fexml.set_payment_mean(invoice) | ||||||
|  |         fexml.set_allowance_charge(invoice) | ||||||
|         fexml.set_billing_reference(invoice) |         fexml.set_billing_reference(invoice) | ||||||
|  |  | ||||||
|         return fexml |         return fexml | ||||||
|   | |||||||
| @@ -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() |     inv.calculate() | ||||||
|     assert inv.invoice_legal_monetary_total.line_extension_amount == form.Amount(100.0) |     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.add_prepaid_payment(form.PrePaidPayment(paid_amount = form.Amount(50.0))) | ||||||
|     inv.calculate() |     inv.calculate() | ||||||
|  |  | ||||||
|   | |||||||
| @@ -6,9 +6,13 @@ | |||||||
| """Tests for `facho` package.""" | """Tests for `facho` package.""" | ||||||
|  |  | ||||||
| import pytest | import pytest | ||||||
|  | from datetime import datetime | ||||||
|  |  | ||||||
|  | from facho.fe import form | ||||||
| from facho.fe import form_xml | from facho.fe import form_xml | ||||||
|  |  | ||||||
|  | from fixtures import * | ||||||
|  |  | ||||||
| def test_import_DIANInvoiceXML(): | def test_import_DIANInvoiceXML(): | ||||||
|     try: |     try: | ||||||
|         form_xml.DIANInvoiceXML |         form_xml.DIANInvoiceXML | ||||||
| @@ -27,3 +31,29 @@ def test_import_DIANCreditNoteXML(): | |||||||
|         form_xml.DIANCreditNoteXML |         form_xml.DIANCreditNoteXML | ||||||
|     except AttributeError: |     except AttributeError: | ||||||
|         pytest.fail("unexpected not found") |         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' | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user