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:
parent
5dc07a7b4a
commit
54382267ba
42
facho/fe/data/dian/codelist/TipoImpuesto-2.1.custom.gc
Normal file
42
facho/fe/data/dian/codelist/TipoImpuesto-2.1.custom.gc
Normal 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>
|
@ -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'))
|
.update(CodeList(path_for_codelist('TipoResponsabilidad-2.1.custom.gc'), 'code', 'name'))
|
||||||
TipoAmbiente = CodeList(path_for_codelist('TipoAmbiente-2.1.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')
|
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')
|
CodigoPrecioReferencia = CodeList(path_for_codelist('CodigoPrecioReferencia-2.1.gc'), 'code', 'name')
|
||||||
MediosPago = CodeList(path_for_codelist('MediosPago-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')
|
RegimenFiscal = CodeList(path_for_codelist('RegimenFiscal-2.1.custom.gc'), 'code', 'name')
|
||||||
|
@ -135,15 +135,17 @@ class DianXMLExtensionCUDFE(FachoXMLExtension):
|
|||||||
build_vars['ValorBruto'] = invoice.invoice_legal_monetary_total.line_extension_amount
|
build_vars['ValorBruto'] = invoice.invoice_legal_monetary_total.line_extension_amount
|
||||||
build_vars['ValorTotalPagar'] = invoice.invoice_legal_monetary_total.payable_amount
|
build_vars['ValorTotalPagar'] = invoice.invoice_legal_monetary_total.payable_amount
|
||||||
ValorImpuestoPara = defaultdict(lambda: form.Amount(0.0))
|
ValorImpuestoPara = defaultdict(lambda: form.Amount(0.0))
|
||||||
build_vars['CodImpuesto1'] = 1
|
build_vars['CodImpuesto1'] = '01'
|
||||||
build_vars['CodImpuesto2'] = 4
|
build_vars['CodImpuesto2'] = '04'
|
||||||
build_vars['CodImpuesto3'] = 3
|
build_vars['CodImpuesto3'] = '03'
|
||||||
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:
|
||||||
# TODO cual es la naturaleza de tax_scheme_ident?
|
# TODO cual es la naturaleza de tax_scheme_ident?
|
||||||
codigo_impuesto = int(subtotal.tax_scheme_ident)
|
codigo_impuesto = subtotal.scheme.code
|
||||||
ValorImpuestoPara.setdefault(codigo_impuesto, form.Amount(0.0))
|
ValorImpuestoPara.setdefault(codigo_impuesto, form.Amount(0.0))
|
||||||
ValorImpuestoPara[codigo_impuesto] += subtotal.tax_amount
|
ValorImpuestoPara[codigo_impuesto] += subtotal.tax_amount
|
||||||
|
|
||||||
build_vars['ValorImpuestoPara'] = ValorImpuestoPara
|
build_vars['ValorImpuestoPara'] = ValorImpuestoPara
|
||||||
build_vars['NitOFE'] = invoice.invoice_supplier.ident
|
build_vars['NitOFE'] = invoice.invoice_supplier.ident
|
||||||
build_vars['NumAdq'] = invoice.invoice_customer.ident
|
build_vars['NumAdq'] = invoice.invoice_customer.ident
|
||||||
@ -184,11 +186,11 @@ class DianXMLExtensionCUFE(DianXMLExtensionCUDFE):
|
|||||||
'%s' % build_vars['FecFac'],
|
'%s' % build_vars['FecFac'],
|
||||||
'%s' % build_vars['HoraFac'],
|
'%s' % build_vars['HoraFac'],
|
||||||
form.Amount(build_vars['ValorBruto']).format('%.02f'),
|
form.Amount(build_vars['ValorBruto']).format('%.02f'),
|
||||||
'%02d' % CodImpuesto1,
|
CodImpuesto1,
|
||||||
form.Amount(build_vars['ValorImpuestoPara'].get(CodImpuesto1, 0.0)).format('%.02f'),
|
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'),
|
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['ValorImpuestoPara'].get(CodImpuesto3, 0.0)).format('%.02f'),
|
||||||
form.Amount(build_vars['ValorTotalPagar']).format('%.02f'),
|
form.Amount(build_vars['ValorTotalPagar']).format('%.02f'),
|
||||||
'%s' % build_vars['NitOFE'],
|
'%s' % build_vars['NitOFE'],
|
||||||
@ -221,11 +223,11 @@ class DianXMLExtensionCUDE(DianXMLExtensionCUDFE):
|
|||||||
'%s' % build_vars['FecFac'],
|
'%s' % build_vars['FecFac'],
|
||||||
'%s' % build_vars['HoraFac'],
|
'%s' % build_vars['HoraFac'],
|
||||||
form.Amount(build_vars['ValorBruto']).format('%.02f'),
|
form.Amount(build_vars['ValorBruto']).format('%.02f'),
|
||||||
'%02d' % CodImpuesto1,
|
CodImpuesto1,
|
||||||
form.Amount(build_vars['ValorImpuestoPara'].get(CodImpuesto1, 0.0)).format('%.02f'),
|
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'),
|
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['ValorImpuestoPara'].get(CodImpuesto3, 0.0)).format('%.02f'),
|
||||||
form.Amount(build_vars['ValorTotalPagar']).format('%.02f'),
|
form.Amount(build_vars['ValorTotalPagar']).format('%.02f'),
|
||||||
'%s' % build_vars['NitOFE'],
|
'%s' % build_vars['NitOFE'],
|
||||||
|
@ -272,15 +272,28 @@ class Party:
|
|||||||
if self.organization_code not in codelist.TipoOrganizacion:
|
if self.organization_code not in codelist.TipoOrganizacion:
|
||||||
raise ValueError("organization_code not found")
|
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
|
@dataclass
|
||||||
class TaxSubTotal:
|
class TaxSubTotal:
|
||||||
percent: float
|
percent: float
|
||||||
tax_scheme_ident: str = '01'
|
scheme: typing.Optional[TaxScheme] = None
|
||||||
tax_scheme_name: str = 'IVA'
|
|
||||||
|
|
||||||
tax_amount: Amount = Amount(0.0)
|
tax_amount: Amount = Amount(0.0)
|
||||||
|
|
||||||
def calculate(self, invline):
|
def calculate(self, invline):
|
||||||
|
if self.percent is not None:
|
||||||
self.tax_amount = invline.total_amount * Amount(self.percent / 100)
|
self.tax_amount = invline.total_amount * Amount(self.percent / 100)
|
||||||
|
|
||||||
|
|
||||||
@ -298,6 +311,13 @@ class TaxTotal:
|
|||||||
self.tax_amount += subtax.tax_amount
|
self.tax_amount += subtax.tax_amount
|
||||||
|
|
||||||
|
|
||||||
|
class TaxTotalOmit(TaxTotal):
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__([])
|
||||||
|
|
||||||
|
def calculate(self, invline):
|
||||||
|
pass
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class Price:
|
class Price:
|
||||||
amount: Amount
|
amount: Amount
|
||||||
@ -377,7 +397,8 @@ class InvoiceLine:
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def total_tax_inclusive_amount(self):
|
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
|
@property
|
||||||
def total_tax_exclusive_amount(self):
|
def total_tax_exclusive_amount(self):
|
||||||
|
@ -424,8 +424,8 @@ class DIANInvoiceXML(fe.FeXML):
|
|||||||
percent_for = defaultdict(lambda: None)
|
percent_for = defaultdict(lambda: None)
|
||||||
|
|
||||||
#requeridos para CUFE
|
#requeridos para CUFE
|
||||||
tax_amount_for['01']['tax_amount'] = Amount(0.0)
|
#tax_amount_for['01']['tax_amount'] = Amount(0.0)
|
||||||
tax_amount_for['01']['taxable_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
|
#DIAN 1.7.-2020: FAS07 => Se debe construir estrategia para su manejo
|
||||||
#tax_amount_for['04']['tax_amount'] = 0.0
|
#tax_amount_for['04']['tax_amount'] = 0.0
|
||||||
#tax_amount_for['04']['taxable_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)
|
total_tax_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:
|
||||||
tax_amount_for[subtotal.tax_scheme_ident]['tax_amount'] += subtotal.tax_amount
|
if subtotal.scheme is not None:
|
||||||
tax_amount_for[subtotal.tax_scheme_ident]['taxable_amount'] += invoice_line.taxable_amount
|
tax_amount_for[subtotal.scheme.code]['tax_amount'] += subtotal.tax_amount
|
||||||
total_tax_amount += subtotal.tax_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.tax_scheme_ident] = subtotal.percent
|
percent_for[subtotal.scheme.code] = subtotal.percent
|
||||||
|
|
||||||
|
total_tax_amount += subtotal.tax_amount
|
||||||
|
|
||||||
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',
|
||||||
@ -491,17 +493,7 @@ class DIANInvoiceXML(fe.FeXML):
|
|||||||
def tag_document_concilied(fexml):
|
def tag_document_concilied(fexml):
|
||||||
return 'Invoiced'
|
return 'Invoiced'
|
||||||
|
|
||||||
def set_invoice_lines(fexml, invoice):
|
def set_invoice_line_tax(fexml, line, invoice_line):
|
||||||
next_append = False
|
|
||||||
for index, invoice_line in enumerate(invoice.invoice_lines):
|
|
||||||
line = fexml.fragment('./cac:%sLine' % (fexml.tag_document()), append=next_append)
|
|
||||||
next_append = True
|
|
||||||
|
|
||||||
line.set_element('./cbc:ID', index + 1)
|
|
||||||
line.set_element('./cbc:%sQuantity' % (fexml.tag_document_concilied()), invoice_line.quantity, unitCode = 'NAR')
|
|
||||||
fexml.set_element_amount_for(line,
|
|
||||||
'./cbc:LineExtensionAmount',
|
|
||||||
invoice_line.total_amount)
|
|
||||||
fexml.set_element_amount_for(line,
|
fexml.set_element_amount_for(line,
|
||||||
'./cac:TaxTotal/cbc:TaxAmount',
|
'./cac:TaxTotal/cbc:TaxAmount',
|
||||||
invoice_line.tax_amount)
|
invoice_line.tax_amount)
|
||||||
@ -513,9 +505,30 @@ class DIANInvoiceXML(fe.FeXML):
|
|||||||
|
|
||||||
for subtotal in invoice_line.tax.subtotals:
|
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/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)
|
if subtotal.percent is not None:
|
||||||
line.set_element('./cac:TaxTotal/cac:TaxSubtotal/cac:TaxCategory/cac:TaxScheme/cbc:Name', subtotal.tax_scheme_name)
|
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):
|
||||||
|
line = fexml.fragment('./cac:%sLine' % (fexml.tag_document()), append=next_append)
|
||||||
|
next_append = True
|
||||||
|
|
||||||
|
line.set_element('./cbc:ID', index + 1)
|
||||||
|
line.set_element('./cbc:%sQuantity' % (fexml.tag_document_concilied()), invoice_line.quantity, unitCode = 'NAR')
|
||||||
|
fexml.set_element_amount_for(line,
|
||||||
|
'./cbc:LineExtensionAmount',
|
||||||
|
invoice_line.total_amount)
|
||||||
|
|
||||||
|
if not isinstance(invoice_line.tax, TaxTotalOmit):
|
||||||
|
fexml.set_invoice_line_tax(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',
|
||||||
|
Loading…
Reference in New Issue
Block a user