add RTE to invoice

FossilOrigin-Name: 8d9aa1694a61e0e358e4cc8c5ecb1b3544d18e353535491f5b8a576c5b5fe446
This commit is contained in:
sinergia 2023-02-19 22:32:36 +00:00
parent 1abf34d4f0
commit d26cc2bef7
4 changed files with 291 additions and 180 deletions

View File

@ -1,170 +1,171 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<!-- DIAN Genericode listas de valores:: Ultima modificación 18-02-2019 - evb--> <!-- DIAN Genericode listas de valores:: Ultima modificación 18-02-2019 - evb-->
<gc:CodeList xmlns:gc="http://docs.oasis-open.org/codelist/ns/genericode/1.0/"> <gc:CodeList xmlns:gc="http://docs.oasis-open.org/codelist/ns/genericode/1.0/">
<Identification> <Identification>
<ShortName>TipoImpuesto</ShortName> <ShortName>TipoImpuesto</ShortName>
<LongName xml:lang="es">Tipo de Tributos</LongName> <LongName xml:lang="es">Tipo de Tributos</LongName>
<Version>1</Version> <Version>1</Version>
<CanonicalUri>urn:dian:names:especificacion:ubl:listacodigos:gc:TipoImpuesto</CanonicalUri> <CanonicalUri>urn:dian:names:especificacion:ubl:listacodigos:gc:TipoImpuesto</CanonicalUri>
<CanonicalVersionUri>urn:dian:names:especificacion:ubl:listacodigos:gc:TipoImpuesto-2.1</CanonicalVersionUri> <CanonicalVersionUri>urn:dian:names:especificacion:ubl:listacodigos:gc:TipoImpuesto-2.1</CanonicalVersionUri>
<LocationUri>http://dian.gov.co/ubl/os-ubl-2.0/cl/gc/default/TipoImpuesto-2.1.gc</LocationUri> <LocationUri>http://dian.gov.co/ubl/os-ubl-2.0/cl/gc/default/TipoImpuesto-2.1.gc</LocationUri>
<Agency> <Agency>
<LongName xml:lang="es">DIAN (Dirección de Impuestos y Aduanas Nacionales)</LongName> <LongName xml:lang="es">DIAN (Dirección de Impuestos y Aduanas Nacionales)</LongName>
<Identifier>195</Identifier> <Identifier>195</Identifier>
</Agency> </Agency>
</Identification> </Identification>
<ColumnSet> <ColumnSet>
<Column Id="code" Use="required"> <Column Id="code" Use="required">
<ShortName>Code</ShortName> <ShortName>Code</ShortName>
<LongName xml:lang="es">Codigo Comun</LongName> <LongName xml:lang="es">Codigo Comun</LongName>
<Data Type="normalizedString"/> <Data Type="normalizedString"/>
</Column> </Column>
<Column Id="name" Use="required"> <Column Id="name" Use="required">
<ShortName>Name</ShortName> <ShortName>Name</ShortName>
<LongName xml:lang="es">Nombre</LongName> <LongName xml:lang="es">Nombre</LongName>
<Data Type="string"/> <Data Type="string"/>
</Column> </Column>
<Key Id="codeKey"> <Key Id="codeKey">
<ShortName>CodeKey</ShortName> <ShortName>CodeKey</ShortName>
<ColumnRef Ref="code"/> <ColumnRef Ref="code"/>
</Key> </Key>
</ColumnSet> </ColumnSet>
<SimpleCodeList> <SimpleCodeList>
<Row> <Row>
<Value ColumnRef="code"> <Value ColumnRef="code">
<SimpleValue>01</SimpleValue> <SimpleValue>01</SimpleValue>
</Value> </Value>
<Value ColumnRef="name"> <Value ColumnRef="name">
<SimpleValue>IVA</SimpleValue> <SimpleValue>IVA</SimpleValue>
</Value> </Value>
</Row> </Row>
<Row> <Row>
<Value ColumnRef="code"> <Value ColumnRef="code">
<SimpleValue>02</SimpleValue> <SimpleValue>02</SimpleValue>
</Value> </Value>
<Value ColumnRef="name"> <Value ColumnRef="name">
<SimpleValue>IC</SimpleValue> <SimpleValue>IC</SimpleValue>
</Value> </Value>
</Row> </Row>
<Row> <Row>
<Value ColumnRef="code"> <Value ColumnRef="code">
<SimpleValue>03</SimpleValue> <SimpleValue>03</SimpleValue>
</Value> </Value>
<Value ColumnRef="name"> <Value ColumnRef="name">
<SimpleValue>ICA</SimpleValue> <SimpleValue>ICA</SimpleValue>
</Value> </Value>
</Row> </Row>
<Row> <Row>
<Value ColumnRef="code"> <Value ColumnRef="code">
<SimpleValue>04</SimpleValue> <SimpleValue>04</SimpleValue>
</Value> </Value>
<Value ColumnRef="name"> <Value ColumnRef="name">
<SimpleValue>INC</SimpleValue> <SimpleValue>INC</SimpleValue>
</Value> </Value>
</Row> </Row>
<Row> <Row>
<Value ColumnRef="code"> <Value ColumnRef="code">
<SimpleValue>05</SimpleValue> <SimpleValue>05</SimpleValue>
</Value> </Value>
<Value ColumnRef="name"> <Value ColumnRef="name">
<SimpleValue>ReteIVA</SimpleValue> <SimpleValue>ReteIVA</SimpleValue>
</Value> </Value>
</Row> </Row>
<Row> <Row>
<Value ColumnRef="code"> <Value ColumnRef="code">
<SimpleValue>06</SimpleValue> <SimpleValue>06</SimpleValue>
</Value> </Value>
<Value ColumnRef="name"> <Value ColumnRef="name">
<SimpleValue>ReteFuente</SimpleValue> <SimpleValue>ReteRenta</SimpleValue>
</Value> </Value>
</Row> </Row>
<Row> <Row>
<Value ColumnRef="code"> <Value ColumnRef="code">
<SimpleValue>07</SimpleValue> <SimpleValue>07</SimpleValue>
</Value> </Value>
<Value ColumnRef="name"> <Value ColumnRef="name">
<SimpleValue>ReteICA</SimpleValue> <SimpleValue>ReteICA</SimpleValue>
</Value> </Value>
</Row> </Row>
<Row> <Row>
<Value ColumnRef="code"> <Value ColumnRef="code">
<SimpleValue>08</SimpleValue> <SimpleValue>08</SimpleValue>
</Value> </Value>
<Value ColumnRef="name"> <Value ColumnRef="name">
<SimpleValue>ReteCREE</SimpleValue> <SimpleValue>ReteCREE</SimpleValue>
</Value> </Value>
</Row> </Row>
<Row> <Row>
<Value ColumnRef="code"> <Value ColumnRef="code">
<SimpleValue>20</SimpleValue> <SimpleValue>20</SimpleValue>
</Value> </Value>
<Value ColumnRef="name"> <Value ColumnRef="name">
<SimpleValue>FtoHorticultura</SimpleValue> <SimpleValue>FtoHorticultura</SimpleValue>
</Value> </Value>
</Row> </Row>
<Row> <Row>
<Value ColumnRef="code"> <Value ColumnRef="code">
<SimpleValue>21</SimpleValue> <SimpleValue>21</SimpleValue>
</Value> </Value>
<Value ColumnRef="name"> <Value ColumnRef="name">
<SimpleValue>Timbre</SimpleValue> <SimpleValue>Timbre</SimpleValue>
</Value> </Value>
</Row> </Row>
<Row> <Row>
<Value ColumnRef="code"> <Value ColumnRef="code">
<SimpleValue>22</SimpleValue> <SimpleValue>22</SimpleValue>
</Value> </Value>
<Value ColumnRef="name"> <Value ColumnRef="name">
<SimpleValue>Bolsas</SimpleValue> <SimpleValue>Bolsas</SimpleValue>
</Value> </Value>
</Row> </Row>
<Row> <Row>
<Value ColumnRef="code"> <Value ColumnRef="code">
<SimpleValue>23</SimpleValue> <SimpleValue>23</SimpleValue>
</Value> </Value>
<Value ColumnRef="name"> <Value ColumnRef="name">
<SimpleValue>INCarbono</SimpleValue> <SimpleValue>INCarbono</SimpleValue>
</Value> </Value>
</Row> </Row>
<Row> <Row>
<Value ColumnRef="code"> <Value ColumnRef="code">
<SimpleValue>24</SimpleValue> <SimpleValue>24</SimpleValue>
</Value> </Value>
<Value ColumnRef="name"> <Value ColumnRef="name">
<SimpleValue>INCombustibles</SimpleValue> <SimpleValue>INCombustibles</SimpleValue>
</Value> </Value>
</Row> </Row>
<Row> <Row>
<Value ColumnRef="code"> <Value ColumnRef="code">
<SimpleValue>25</SimpleValue> <SimpleValue>25</SimpleValue>
</Value> </Value>
<Value ColumnRef="name"> <Value ColumnRef="name">
<SimpleValue>Sobretasa Combustibles</SimpleValue> <SimpleValue>Sobretasa Combustibles</SimpleValue>
</Value> </Value>
</Row> </Row>
<Row> <Row>
<Value ColumnRef="code"> <Value ColumnRef="code">
<SimpleValue>26</SimpleValue> <SimpleValue>26</SimpleValue>
</Value> </Value>
<Value ColumnRef="name"> <Value ColumnRef="name">
<SimpleValue>Sordicom</SimpleValue> <SimpleValue>Sordicom</SimpleValue>
</Value> </Value>
</Row> </Row>
<Row> <Row>
<Value ColumnRef="code"> <Value ColumnRef="code">
<SimpleValue>30</SimpleValue> <SimpleValue>30</SimpleValue>
</Value> </Value>
<Value ColumnRef="name"> <Value ColumnRef="name">
<SimpleValue>Impuesto al Consumo de Datos</SimpleValue> <SimpleValue>Impuesto al Consumo de Datos</SimpleValue>
</Value> </Value>
</Row> </Row>
<Row>
<Value ColumnRef="code"> <Row>
<SimpleValue>ZZ</SimpleValue> <Value ColumnRef="code">
</Value> <SimpleValue>ZZ</SimpleValue>
<Value ColumnRef="name"> </Value>
<SimpleValue>Nombre de la figura tributaria</SimpleValue> <Value ColumnRef="name">
</Value> <SimpleValue>Nombre de la figura tributaria</SimpleValue>
</Row> </Value>
</SimpleCodeList> </Row>
</SimpleCodeList>
</gc:CodeList> </gc:CodeList>

View File

@ -1,6 +1,5 @@
# This file is part of facho. The COPYRIGHT file at the top level of # This file is part of facho. The COPYRIGHT file at the top level of
# this repository contains the full copyright notices and license terms. # this repository contains the full copyright notices and license terms.
import hashlib import hashlib
from functools import reduce from functools import reduce
import copy import copy
@ -11,7 +10,6 @@ from collections import defaultdict
import decimal import decimal
from decimal import Decimal from decimal import Decimal
import typing import typing
from ..data.dian import codelist from ..data.dian import codelist
DECIMAL_PRECISION = 6 DECIMAL_PRECISION = 6
@ -266,7 +264,6 @@ class TaxScheme:
code: str code: str
name: str = '' name: str = ''
def __post_init__(self): def __post_init__(self):
if self.code not in codelist.TipoImpuesto: if self.code not in codelist.TipoImpuesto:
raise ValueError("code not found") raise ValueError("code not found")
@ -325,7 +322,6 @@ class TaxTotal:
def calculate(self, invline): def calculate(self, invline):
self.taxable_amount = invline.total_amount self.taxable_amount = invline.total_amount
for subtax in self.subtotals: for subtax in self.subtotals:
subtax.calculate(invline) subtax.calculate(invline)
self.tax_amount += subtax.tax_amount self.tax_amount += subtax.tax_amount
@ -338,6 +334,36 @@ class TaxTotalOmit(TaxTotal):
def calculate(self, invline): def calculate(self, invline):
pass pass
@dataclass
class WithholdingTaxSubTotal:
percent: float
scheme: typing.Optional[TaxScheme] = None
tax_amount: Amount = Amount(0.0)
def calculate(self, invline):
if self.percent is not None:
self.tax_amount = invline.total_amount * Amount(self.percent / 100)
@dataclass
class WithholdingTaxTotal:
subtotals: list
tax_amount: Amount = Amount(0.0)
taxable_amount: Amount = Amount(0.0)
def calculate(self, invline):
self.taxable_amount = invline.total_amount
for subtax in self.subtotals:
subtax.calculate(invline)
self.tax_amount += subtax.tax_amount
class WithholdingTaxTotalOmit(WithholdingTaxTotal):
def __init__(self):
super().__init__([])
def calculate(self, invline):
pass
@dataclass @dataclass
class Price: class Price:
amount: Amount amount: Amount
@ -474,7 +500,7 @@ class InvoiceLine:
# la factura y el percent es unico por type_code # la factura y el percent es unico por type_code
# de subtotal # de subtotal
tax: typing.Optional[TaxTotal] tax: typing.Optional[TaxTotal]
withholding: typing.Optional[WithholdingTaxTotal]
allowance_charge: typing.List[AllowanceCharge] = dataclasses.field(default_factory=list) allowance_charge: typing.List[AllowanceCharge] = dataclasses.field(default_factory=list)
def add_allowance_charge(self, charge): def add_allowance_charge(self, charge):
@ -518,8 +544,17 @@ class InvoiceLine:
def taxable_amount(self): def taxable_amount(self):
return self.tax.taxable_amount return self.tax.taxable_amount
@property
def withholding_amount(self):
return self.withholding.tax_amount
@property
def withholding_taxable_amount(self):
return self.withholding.taxable_amount
def calculate(self): def calculate(self):
self.tax.calculate(self) self.tax.calculate(self)
self.withholding.calculate(self)
def __post_init__(self): def __post_init__(self):
if not isinstance(self.quantity, Quantity): if not isinstance(self.quantity, Quantity):
@ -527,6 +562,9 @@ class InvoiceLine:
if self.tax is None: if self.tax is None:
self.tax = TaxTotalOmit() self.tax = TaxTotalOmit()
if self.withholding is None:
self.withholding = WithholdingTaxTotalOmit()
@dataclass @dataclass
class LegalMonetaryTotal: class LegalMonetaryTotal:

View File

@ -147,7 +147,6 @@ class DIANInvoiceXML(fe.FeXML):
fexml.set_element('./cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme/cac:TaxScheme/cbc:ID', fexml.set_element('./cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme/cac:TaxScheme/cbc:ID',
invoice.invoice_customer.tax_scheme.code) invoice.invoice_customer.tax_scheme.code)
#DIAN 1.7.-2020: CAJ41 #DIAN 1.7.-2020: CAJ41
fexml.set_element('./cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme/cac:TaxScheme/cbc:Name', fexml.set_element('./cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme/cac:TaxScheme/cbc:Name',
invoice.invoice_customer.tax_scheme.name) invoice.invoice_customer.tax_scheme.name)
@ -421,6 +420,7 @@ class DIANInvoiceXML(fe.FeXML):
def set_invoice_totals(fexml, invoice): def set_invoice_totals(fexml, invoice):
tax_amount_for = defaultdict(lambda: defaultdict(lambda: Amount(0.0))) tax_amount_for = defaultdict(lambda: defaultdict(lambda: Amount(0.0)))
withholding_amount_for = defaultdict(lambda: defaultdict(lambda: Amount(0.0)))
percent_for = defaultdict(lambda: None) percent_for = defaultdict(lambda: None)
#requeridos para CUFE #requeridos para CUFE
@ -433,23 +433,34 @@ class DIANInvoiceXML(fe.FeXML):
#tax_amount_for['03']['taxable_amount'] += 0.0 #tax_amount_for['03']['taxable_amount'] += 0.0
total_tax_amount = Amount(0.0) total_tax_amount = Amount(0.0)
total_withholding_amount = Amount(0.0)
for invoice_line in invoice.invoice_lines: for invoice_line in invoice.invoice_lines:
for subtotal in invoice_line.tax.subtotals: for subtotal in invoice_line.tax.subtotals:
if subtotal.scheme is not None: if subtotal.scheme is not None:
tax_amount_for[subtotal.scheme.code]['tax_amount'] += subtotal.tax_amount tax_amount_for[subtotal.scheme.code]['tax_amount'] += subtotal.tax_amount
tax_amount_for[subtotal.scheme.code]['taxable_amount'] += invoice_line.taxable_amount tax_amount_for[subtotal.scheme.code]['taxable_amount'] += invoice_line.taxable_amount
# MACHETE ojo InvoiceLine.tax pasar a Invoice # MACHETE ojo InvoiceLine.tax pasar a Invoice
percent_for[subtotal.scheme.code] = subtotal.percent percent_for[subtotal.scheme.code] = subtotal.percent
total_tax_amount += subtotal.tax_amount total_tax_amount += subtotal.tax_amount
for subtotal_withholding in invoice_line.withholding.subtotals:
if subtotal_withholding.scheme is not None:
withholding_amount_for[subtotal_withholding.scheme.code]['tax_amount'] += subtotal_withholding.tax_amount
withholding_amount_for[subtotal_withholding.scheme.code]['taxable_amount'] += invoice_line.withholding_taxable_amount
# MACHETE ojo InvoiceLine.tax pasar a Invoice
percent_for[subtotal_withholding.scheme.code] = subtotal_withholding.percent
total_withholding_amount += subtotal_withholding.tax_amount
if total_tax_amount != Amount(0.0): if total_tax_amount != Amount(0.0):
fexml.placeholder_for('./cac:TaxTotal') fexml.placeholder_for('./cac:TaxTotal')
fexml.set_element_amount('./cac:TaxTotal/cbc:TaxAmount', fexml.set_element_amount('./cac:TaxTotal/cbc:TaxAmount',
total_tax_amount) total_tax_amount)
for index, item in enumerate(tax_amount_for.items()): for index, item in enumerate(tax_amount_for.items()):
cod_impuesto, amount_of = item cod_impuesto, amount_of = item
@ -487,7 +498,44 @@ class DIANInvoiceXML(fe.FeXML):
cod_impuesto) cod_impuesto)
line.set_element('/cac:TaxTotal/cac:TaxSubtotal/cac:TaxCategory/cac:TaxScheme/cbc:Name', line.set_element('/cac:TaxTotal/cac:TaxSubtotal/cac:TaxCategory/cac:TaxScheme/cbc:Name',
'IVA') 'IVA')
for index, item in enumerate(withholding_amount_for.items()):
cod_impuesto, amount_of = item
next_append = index > 0
#DIAN 1.7.-2020: FAS01
line = fexml.fragment('./cac:WithholdingTaxTotal', append=next_append)
#DIAN 1.7.-2020: FAU06
tax_amount = amount_of['tax_amount']
fexml.set_element_amount_for(line,
'/cac:WithholdingTaxTotal/cbc:TaxAmount',
tax_amount)
#DIAN 1.7.-2020: FAS05
fexml.set_element_amount_for(line,
'/cac:WithholdingTaxTotal/cac:TaxSubtotal/cbc:TaxableAmount',
amount_of['taxable_amount'])
#DIAN 1.7.-2020: FAU06
fexml.set_element_amount_for(line,
'/cac:WithholdingTaxTotal/cac:TaxSubtotal/cbc:TaxAmount',
amount_of['tax_amount'])
#DIAN 1.7.-2020: FAS07
if percent_for[cod_impuesto]:
line.set_element('/cac:WithholdingTaxTotal/cac:TaxSubtotal/cbc:Percent',
percent_for[cod_impuesto])
if percent_for[cod_impuesto]:
line.set_element('/cac:WithholdingTaxTotal/cac:TaxSubtotal/cac:TaxCategory/cbc:Percent',
percent_for[cod_impuesto])
line.set_element('/cac:WithholdingTaxTotal/cac:TaxSubtotal/cac:TaxCategory/cac:TaxScheme/cbc:ID',
cod_impuesto)
line.set_element('/cac:WithholdingTaxTotal/cac:TaxSubtotal/cac:TaxCategory/cac:TaxScheme/cbc:Name',
'ReteRenta')
# abstract method # abstract method
def tag_document(fexml): def tag_document(fexml):
return 'Invoice' return 'Invoice'
@ -515,7 +563,29 @@ class DIANInvoiceXML(fe.FeXML):
#DIAN 1.7.-2020: FAX15 #DIAN 1.7.-2020: FAX15
line.set_element('./cac:TaxTotal/cac:TaxSubtotal/cac:TaxCategory/cac:TaxScheme/cbc:ID', subtotal.scheme.code) line.set_element('./cac:TaxTotal/cac:TaxSubtotal/cac:TaxCategory/cac:TaxScheme/cbc:ID', subtotal.scheme.code)
line.set_element('./cac:TaxTotal/cac:TaxSubtotal/cac:TaxCategory/cac:TaxScheme/cbc:Name', subtotal.scheme.name) line.set_element('./cac:TaxTotal/cac:TaxSubtotal/cac:TaxCategory/cac:TaxScheme/cbc:Name', subtotal.scheme.name)
def set_invoice_line_withholding(fexml, line, invoice_line):
fexml.set_element_amount_for(line,
'./cac:WithholdingTaxTotal/cbc:TaxAmount',
invoice_line.withholding_amount)
#DIAN 1.7.-2020: FAX05
fexml.set_element_amount_for(line,
'./cac:WithholdingTaxTotal/cac:TaxSubtotal/cbc:TaxableAmount',
invoice_line.withholding_taxable_amount)
for subtotal in invoice_line.withholding.subtotals:
line.set_element('./cac:WithholdingTaxTotal/cac:TaxSubtotal/cbc:TaxAmount', subtotal.tax_amount, currencyID='COP')
if subtotal.percent is not None:
line.set_element('./cac:WithholdingTaxTotal/cac:TaxSubtotal/cac:TaxCategory/cbc:Percent', '%0.2f' % round(subtotal.percent, 2))
if subtotal.scheme is not None:
#DIAN 1.7.-2020: FAX15
line.set_element('./cac:WithholdingTaxTotal/cac:TaxSubtotal/cac:TaxCategory/cac:TaxScheme/cbc:ID', subtotal.scheme.code)
line.set_element('./cac:WithholdingTaxTotal/cac:TaxSubtotal/cac:TaxCategory/cac:TaxScheme/cbc:Name', subtotal.scheme.name)
def set_invoice_lines(fexml, invoice): def set_invoice_lines(fexml, invoice):
next_append = False next_append = False
for index, invoice_line in enumerate(invoice.invoice_lines): for index, invoice_line in enumerate(invoice.invoice_lines):
@ -531,6 +601,9 @@ class DIANInvoiceXML(fe.FeXML):
if not isinstance(invoice_line.tax, TaxTotalOmit): if not isinstance(invoice_line.tax, TaxTotalOmit):
fexml.set_invoice_line_tax(line, invoice_line) fexml.set_invoice_line_tax(line, invoice_line)
if not isinstance(invoice_line.withholding, WithholdingTaxTotalOmit):
fexml.set_invoice_line_withholding(line, invoice_line)
line.set_element('./cac:Item/cbc:Description', invoice_line.item.description) line.set_element('./cac:Item/cbc:Description', invoice_line.item.description)
line.set_element('./cac:Item/cac:StandardItemIdentification/cbc:ID', line.set_element('./cac:Item/cac:StandardItemIdentification/cbc:ID',

View File

@ -499,6 +499,5 @@ class DIANSupportDocumentXML(fe.FeXML):
return fexml return fexml
def customize(fexml, invoice): def customize(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"""