InvoiceLine.tax y TaxTotal.subtotals se hacen opcionales.

Nuevo tipo form.TaxTotalOmit() para excluir de impuesto la linea.

FossilOrigin-Name: 84fce4487c15e25724992b14663c2205e592380f2dd1ba99c10ebbc0acb909f6
This commit is contained in:
bit4bit@riseup.net 2020-11-05 02:16:30 +00:00
parent 5dc07a7b4a
commit 54382267ba
5 changed files with 117 additions and 38 deletions

View File

@ -0,0 +1,42 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- 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/">
<Identification>
<ShortName>TipoImpuesto</ShortName>
<LongName xml:lang="es">Tipo de Tributos</LongName>
<Version>1</Version>
<CanonicalUri>urn:dian:names:especificacion:ubl:listacodigos:gc:TipoImpuesto</CanonicalUri>
<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>
<Agency>
<LongName xml:lang="es">DIAN (Dirección de Impuestos y Aduanas Nacionales)</LongName>
<Identifier>195</Identifier>
</Agency>
</Identification>
<ColumnSet>
<Column Id="code" Use="required">
<ShortName>Code</ShortName>
<LongName xml:lang="es">Codigo Comun</LongName>
<Data Type="normalizedString"/>
</Column>
<Column Id="name" Use="required">
<ShortName>Name</ShortName>
<LongName xml:lang="es">Nombre</LongName>
<Data Type="string"/>
</Column>
<Key Id="codeKey">
<ShortName>CodeKey</ShortName>
<ColumnRef Ref="code"/>
</Key>
</ColumnSet>
<SimpleCodeList>
<Row>
<Value ColumnRef="code">
<SimpleValue>ZY</SimpleValue>
</Value>
<Value ColumnRef="name">
<SimpleValue>No causa</SimpleValue>
</Value>
</Row>
</SimpleCodeList>
</gc:CodeList>

View File

@ -86,7 +86,8 @@ TipoResponsabilidad = CodeList(path_for_codelist('TipoResponsabilidad-2.1.gc'),
.update(CodeList(path_for_codelist('TipoResponsabilidad-2.1.custom.gc'), 'code', 'name'))
TipoAmbiente = CodeList(path_for_codelist('TipoAmbiente-2.1.gc'), 'code', 'name')
TipoDocumento = CodeList(path_for_codelist('TipoDocumento-2.1.gc'), 'code', 'name')
TipoImpuesto = CodeList(path_for_codelist('TipoImpuesto-2.1.gc'), 'code', 'name')
TipoImpuesto = CodeList(path_for_codelist('TipoImpuesto-2.1.gc'), 'code', 'name')\
.update(CodeList(path_for_codelist('TipoImpuesto-2.1.custom.gc'), 'code', 'name'))
CodigoPrecioReferencia = CodeList(path_for_codelist('CodigoPrecioReferencia-2.1.gc'), 'code', 'name')
MediosPago = CodeList(path_for_codelist('MediosPago-2.1.gc'), 'code', 'name')
RegimenFiscal = CodeList(path_for_codelist('RegimenFiscal-2.1.custom.gc'), 'code', 'name')

View File

@ -135,15 +135,17 @@ class DianXMLExtensionCUDFE(FachoXMLExtension):
build_vars['ValorBruto'] = invoice.invoice_legal_monetary_total.line_extension_amount
build_vars['ValorTotalPagar'] = invoice.invoice_legal_monetary_total.payable_amount
ValorImpuestoPara = defaultdict(lambda: form.Amount(0.0))
build_vars['CodImpuesto1'] = 1
build_vars['CodImpuesto2'] = 4
build_vars['CodImpuesto3'] = 3
build_vars['CodImpuesto1'] = '01'
build_vars['CodImpuesto2'] = '04'
build_vars['CodImpuesto3'] = '03'
for invoice_line in invoice.invoice_lines:
for subtotal in invoice_line.tax.subtotals:
# TODO cual es la naturaleza de tax_scheme_ident?
codigo_impuesto = int(subtotal.tax_scheme_ident)
ValorImpuestoPara.setdefault(codigo_impuesto, form.Amount(0.0))
ValorImpuestoPara[codigo_impuesto] += subtotal.tax_amount
if subtotal.scheme is not None:
# TODO cual es la naturaleza de tax_scheme_ident?
codigo_impuesto = subtotal.scheme.code
ValorImpuestoPara.setdefault(codigo_impuesto, form.Amount(0.0))
ValorImpuestoPara[codigo_impuesto] += subtotal.tax_amount
build_vars['ValorImpuestoPara'] = ValorImpuestoPara
build_vars['NitOFE'] = invoice.invoice_supplier.ident
build_vars['NumAdq'] = invoice.invoice_customer.ident
@ -184,11 +186,11 @@ class DianXMLExtensionCUFE(DianXMLExtensionCUDFE):
'%s' % build_vars['FecFac'],
'%s' % build_vars['HoraFac'],
form.Amount(build_vars['ValorBruto']).format('%.02f'),
'%02d' % CodImpuesto1,
CodImpuesto1,
form.Amount(build_vars['ValorImpuestoPara'].get(CodImpuesto1, 0.0)).format('%.02f'),
'%02d' % CodImpuesto2,
CodImpuesto2,
form.Amount(build_vars['ValorImpuestoPara'].get(CodImpuesto2, 0.0)).format('%.02f'),
'%02d' % CodImpuesto3,
CodImpuesto3,
form.Amount(build_vars['ValorImpuestoPara'].get(CodImpuesto3, 0.0)).format('%.02f'),
form.Amount(build_vars['ValorTotalPagar']).format('%.02f'),
'%s' % build_vars['NitOFE'],
@ -221,11 +223,11 @@ class DianXMLExtensionCUDE(DianXMLExtensionCUDFE):
'%s' % build_vars['FecFac'],
'%s' % build_vars['HoraFac'],
form.Amount(build_vars['ValorBruto']).format('%.02f'),
'%02d' % CodImpuesto1,
CodImpuesto1,
form.Amount(build_vars['ValorImpuestoPara'].get(CodImpuesto1, 0.0)).format('%.02f'),
'%02d' % CodImpuesto2,
CodImpuesto2,
form.Amount(build_vars['ValorImpuestoPara'].get(CodImpuesto2, 0.0)).format('%.02f'),
'%02d' % CodImpuesto3,
CodImpuesto3,
form.Amount(build_vars['ValorImpuestoPara'].get(CodImpuesto3, 0.0)).format('%.02f'),
form.Amount(build_vars['ValorTotalPagar']).format('%.02f'),
'%s' % build_vars['NitOFE'],

View File

@ -272,16 +272,29 @@ class Party:
if self.organization_code not in codelist.TipoOrganizacion:
raise ValueError("organization_code not found")
@dataclass
class TaxScheme:
code: str = '01'
@property
def name(self):
return codelist.TipoImpuesto[self.code]['name']
def __post_init__(self):
if self.code not in codelist.TipoImpuesto:
raise ValueError("code [%s] not found" % (self.code))
@dataclass
class TaxSubTotal:
percent: float
tax_scheme_ident: str = '01'
tax_scheme_name: str = 'IVA'
scheme: typing.Optional[TaxScheme] = None
tax_amount: Amount = Amount(0.0)
def calculate(self, invline):
self.tax_amount = invline.total_amount * Amount(self.percent / 100)
if self.percent is not None:
self.tax_amount = invline.total_amount * Amount(self.percent / 100)
@dataclass
@ -298,6 +311,13 @@ class TaxTotal:
self.tax_amount += subtax.tax_amount
class TaxTotalOmit(TaxTotal):
def __init__(self):
super().__init__([])
def calculate(self, invline):
pass
@dataclass
class Price:
amount: Amount
@ -377,7 +397,8 @@ class InvoiceLine:
@property
def total_tax_inclusive_amount(self):
return self.tax.taxable_amount + self.tax.tax_amount
# FAU06
return self.total_amount + self.tax.tax_amount
@property
def total_tax_exclusive_amount(self):

View File

@ -424,8 +424,8 @@ class DIANInvoiceXML(fe.FeXML):
percent_for = defaultdict(lambda: None)
#requeridos para CUFE
tax_amount_for['01']['tax_amount'] = Amount(0.0)
tax_amount_for['01']['taxable_amount'] = Amount(0.0)
#tax_amount_for['01']['tax_amount'] = Amount(0.0)
#tax_amount_for['01']['taxable_amount'] = Amount(0.0)
#DIAN 1.7.-2020: FAS07 => Se debe construir estrategia para su manejo
#tax_amount_for['04']['tax_amount'] = 0.0
#tax_amount_for['04']['taxable_amount'] = 0.0
@ -435,13 +435,15 @@ class DIANInvoiceXML(fe.FeXML):
total_tax_amount = Amount(0.0)
for invoice_line in invoice.invoice_lines:
for subtotal in invoice_line.tax.subtotals:
tax_amount_for[subtotal.tax_scheme_ident]['tax_amount'] += subtotal.tax_amount
tax_amount_for[subtotal.tax_scheme_ident]['taxable_amount'] += invoice_line.taxable_amount
if subtotal.scheme is not None:
tax_amount_for[subtotal.scheme.code]['tax_amount'] += subtotal.tax_amount
tax_amount_for[subtotal.scheme.code]['taxable_amount'] += invoice_line.taxable_amount
# MACHETE ojo InvoiceLine.tax pasar a Invoice
percent_for[subtotal.scheme.code] = subtotal.percent
total_tax_amount += subtotal.tax_amount
# MACHETE ojo InvoiceLine.tax pasar a Invoice
percent_for[subtotal.tax_scheme_ident] = subtotal.percent
fexml.placeholder_for('./cac:TaxTotal')
fexml.set_element_amount('./cac:TaxTotal/cbc:TaxAmount',
@ -491,6 +493,27 @@ class DIANInvoiceXML(fe.FeXML):
def tag_document_concilied(fexml):
return 'Invoiced'
def set_invoice_line_tax(fexml, line, invoice_line):
fexml.set_element_amount_for(line,
'./cac:TaxTotal/cbc:TaxAmount',
invoice_line.tax_amount)
#DIAN 1.7.-2020: FAX05
fexml.set_element_amount_for(line,
'./cac:TaxTotal/cac:TaxSubtotal/cbc:TaxableAmount',
invoice_line.taxable_amount)
for subtotal in invoice_line.tax.subtotals:
line.set_element('./cac:TaxTotal/cac:TaxSubtotal/cbc:TaxAmount', subtotal.tax_amount, currencyID='COP')
if subtotal.percent is not None:
line.set_element('./cac:TaxTotal/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: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)
def set_invoice_lines(fexml, invoice):
next_append = False
for index, invoice_line in enumerate(invoice.invoice_lines):
@ -502,20 +525,10 @@ class DIANInvoiceXML(fe.FeXML):
fexml.set_element_amount_for(line,
'./cbc:LineExtensionAmount',
invoice_line.total_amount)
fexml.set_element_amount_for(line,
'./cac:TaxTotal/cbc:TaxAmount',
invoice_line.tax_amount)
#DIAN 1.7.-2020: FAX05
fexml.set_element_amount_for(line,
'./cac:TaxTotal/cac:TaxSubtotal/cbc:TaxableAmount',
invoice_line.taxable_amount)
if not isinstance(invoice_line.tax, TaxTotalOmit):
fexml.set_invoice_line_tax(line, invoice_line)
for subtotal in invoice_line.tax.subtotals:
line.set_element('./cac:TaxTotal/cac:TaxSubtotal/cbc:TaxAmount', subtotal.tax_amount, currencyID='COP')
line.set_element('./cac:TaxTotal/cac:TaxSubtotal/cac:TaxCategory/cbc:Percent', subtotal.percent)
line.set_element('./cac:TaxTotal/cac:TaxSubtotal/cac:TaxCategory/cac:TaxScheme/cbc:ID', subtotal.tax_scheme_ident)
line.set_element('./cac:TaxTotal/cac:TaxSubtotal/cac:TaxCategory/cac:TaxScheme/cbc:Name', subtotal.tax_scheme_name)
line.set_element('./cac:Item/cbc:Description', invoice_line.item.description)
line.set_element('./cac:Item/cac:StandardItemIdentification/cbc:ID',