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:
		
							
								
								
									
										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')) | ||||
| 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') | ||||
|   | ||||
| @@ -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'], | ||||
|   | ||||
| @@ -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): | ||||
|   | ||||
| @@ -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', | ||||
|   | ||||
		Reference in New Issue
	
	Block a user