diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst
index 4200623..2190357 100644
--- a/CONTRIBUTING.rst
+++ b/CONTRIBUTING.rst
@@ -94,14 +94,6 @@ Ready to contribute? Here's how to set up `facho` for local development.
7. Submit a pull request through the GitHub website.
-Using docker
-------------
-
-1. make -f Makefile.dev build
-2. make -f Makefile.dev dev-shell
-3. make -f Makefile.dev python3.8 setup.py develop
-4. make -f Makefile.dev python3.8 setup.py test
-
Pull Request Guidelines
-----------------------
diff --git a/Dockerfile b/Dockerfile
index 7894872..ee5d265 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,24 +1,17 @@
# DERIVADO DE https://alextereshenkov.github.io/run-python-tests-with-tox-in-docker.html
-FROM ubuntu:24.04
+FROM ubuntu:18.04
RUN apt-get -qq update
-
-RUN apt install software-properties-common -y \
- && add-apt-repository ppa:deadsnakes/ppa
-
RUN apt-get install -y --no-install-recommends \
- python3.9 python3.9-distutils python3.9-dev \
- python3.10 python3.10-distutils python3.10-dev \
- python3.11 python3.11-distutils python3.11-dev \
- python3.12 python3-setuptools python3.12-dev \
+ python3.7 python3.7-distutils python3.7-dev \
+ python3.8 python3.8-distutils python3.8-dev \
wget \
ca-certificates
RUN wget https://bootstrap.pypa.io/get-pip.py \
- && python3.9 get-pip.py pip==23.2.1 --break-system-packages \
- && python3.10 get-pip.py pip==23.2.1 --break-system-packages \
- && python3.11 get-pip.py pip==23.2.1 --break-system-packages \
- && python3.12 get-pip.py pip==23.2.1 --break-system-packages \
+ && python3 get-pip.py pip==21.3 \
+ && python3.7 get-pip.py pip==21.3 \
+ && python3.8 get-pip.py pip==21.3 \
&& rm get-pip.py
RUN apt-get install -y --no-install-recommends \
@@ -27,14 +20,12 @@ RUN apt-get install -y --no-install-recommends \
build-essential \
zip
-RUN python3.9 --version
-RUN python3.10 --version
-RUN python3.11 --version
-RUN python3.12 --version
+RUN python3.6 --version
+RUN python3.7 --version
+RUN python3.8 --version
-RUN pip3.9 install setuptools setuptools-rust
-RUN pip3.10 install setuptools setuptools-rust
-RUN pip3.11 install setuptools setuptools-rust --break-system-packages
-RUN pip3.12 install setuptools setuptools-rust --break-system-packages
+RUN pip3.6 install setuptools setuptools-rust
+RUN pip3.7 install setuptools setuptools-rust
+RUN pip3.8 install setuptools setuptools-rust
-RUN pip3 install tox pytest --break-system-packages
+RUN pip3 install tox pytest
diff --git a/Makefile.dev b/Makefile.dev
index ae90ab9..f2aaeaf 100644
--- a/Makefile.dev
+++ b/Makefile.dev
@@ -11,11 +11,14 @@
dev-setup:
docker build -t facho .
+py-develop:
+ docker run -t -v $(PWD):/app -w /app facho sh -c 'python3.7 setup.py develop --user'
+
dev-shell:
docker run --rm -ti -v "$(PWD):/app" -w /app --name facho-cli facho bash
test:
- docker run -t -v $(PWD):/app -w /app facho sh -c 'cd /app; python3.12 setup.py test'
+ docker run -t -v $(PWD):/app -w /app facho sh -c 'cd /app; python3.7 setup.py test'
tox:
docker run -it -v $(PWD)/:/app -w /app facho tox
diff --git a/examples/generate-invoice-from-cli.py b/examples/generate-invoice-from-cli.py
index a2d6351..edb6cdd 100644
--- a/examples/generate-invoice-from-cli.py
+++ b/examples/generate-invoice-from-cli.py
@@ -14,7 +14,7 @@ from facho.fe import fe
from datetime import datetime, date
# Datos del fomulario del SET de pruebas
-INVOICE_AUTHORIZATION = '181360000001' # Número suministrado por la Dian en el momento de la creación del SET de Pruebas
+INVOICE_AUTHORIZATION = '181360000001' #Número suministrado por la Dian en el momento de la creación del SET de Pruebas
ID_SOFTWARE = '57bcb6d1-c591-5a90-b80a-cb030ec91440' #Id suministrado por la Dian en el momento de la creación del SET de Pruebas
PIN = '19642' #Número creado por la empresa para poder crear el SET de pruebas
CLAVE_TECNICA = 'fc9eac422eba16e21ffd8c5f94b3f30a6e38162d' ##Id suministrado por la Dian en el momento de la creación del SET de Pruebas
@@ -36,7 +36,6 @@ def extensions(inv):
'SETP', 990000000, 995000000)#del SET de pruebas
return [security_code, authorization_provider, cufe, software_provider, inv_authorization]
-
def invoice():
# factura de venta nacional
inv = form.Invoice('01')
@@ -50,17 +49,16 @@ def invoice():
inv.set_operation_type('10')
inv.set_supplier(form.Party(
legal_name = 'Nombre registrado de la empresa',
- name='Nombre comercial o él mismo nombre registrado',
- ident=form.PartyIdentification(
- 'nit_empresa', 'digito_verificación', '31'),
+ name = 'Nombre comercial o él mismo nombre registrado',
+ ident = form.PartyIdentification('nit_empresa', 'digito_verificación', '31'),
# obligaciones del contribuyente ver DIAN:FAK26
- responsability_code=form.Responsability(['ZZ', 'O-14', 'O-48']),
+ responsability_code = form.Responsability(['O-07', 'O-14', 'O-48']),
# ver DIAN:FAJ28
- responsability_regime_code='48',
+ responsability_regime_code = '48',
# tipo de organizacion juridica ver DIAN:6.2.3
- organization_code='1',
- email="correoempresa@correoempresa.correo",
- address=form.Address(
+ organization_code = '1',
+ email = "correoempresa@correoempresa.correo",
+ address = form.Address(
'', '', form.City('05001', 'Medellín'),
form.Country('CO', 'Colombia'),
form.CountrySubentity('05', 'Antioquia')),
@@ -78,43 +76,42 @@ def invoice():
'', '', form.City('05001', 'Medellín'),
form.Country('CO', 'Colombia'),
form.CountrySubentity('05', 'Antioquia')),
- # tax_scheme = form.TaxScheme('01', 'IVA')
+ #tax_scheme = form.TaxScheme('01', 'IVA')
))
# asignar metodo de pago
inv.set_payment_mean(form.PaymentMean(
# metodo de pago ver DIAN:3.4.1
- id='1',
- # codigocorrespondientealmediodepagoverDIAN:3.4.2
- code='20',
- # fechadevencimientodelafactura
- due_at=datetime.now(),
- # identificadornumerico
- payment_id='2'
+ id = '1',
+ # codigo correspondiente al medio de pago ver DIAN:3.4.2
+ code = '20',
+ # fecha de vencimiento de la factura
+ due_at = datetime.now(),
+ # identificador numerico
+ payment_id = '2'
))
# adicionar una linea al documento
- inv.add_invoice_line(
- form.InvoiceLine(
- quantity=form.Quantity(int(20.5), '94'),
- # item general de codigo 999
- description='productO3',
- sitem=form.StandardItem('test', 9999),
- price=form.Price(
- # precio base del item (sin iva)
- amount=form.Amount(200.00),
- # ver DIAN:6.3.5.1
- type_code='01',
- type='x'
- ),
- tax=form.TaxTotal(
- subtotals=[
- form.TaxSubTotal(
- percent=19.00,
- scheme=form.TaxScheme('01')
- )]
- )
- ))
+ inv.add_invoice_line(form.InvoiceLine(
+ quantity = form.Quantity(int(20.5), '94'),
+ # item general de codigo 999
+ description = 'productO3',
+ item = form.StandardItem('test', 9999),
+ price = form.Price(
+ # precio base del item (sin iva)
+ amount = form.Amount(200.00),
+ # ver DIAN:6.3.5.1
+ type_code = '01',
+ type = 'x'
+ ),
+ tax = form.TaxTotal(
+ subtotals = [
+ form.TaxSubTotal(
+ percent = 19.00,
+ scheme=form.TaxScheme('01')
+ )
+ ]
+ )
+ ))
return inv
-
def document_xml():
return form_xml.DIANInvoiceXML
diff --git a/examples/use-as-lib.py b/examples/use-as-lib.py
index 0215f62..96dbbaa 100644
--- a/examples/use-as-lib.py
+++ b/examples/use-as-lib.py
@@ -1,127 +1,109 @@
# importar libreria de modelos
-from facho import fe, form_xml
import facho.fe.form as form
-import datetime
-
-PRIVATE_KEY_PATH = 'ruta a mi llave privada'
-PRIVATE_PASSPHRASE = 'clave de la llave privada'
+import facho.fe.form_xml
+PRIVATE_KEY_PATH='ruta a mi llave privada'
+PRIVATE_PASSPHRASE='clave de la llave privada'
# consultar las extensiones necesarias
def extensions(inv):
- security_code = fe.DianXMLExtensionSoftwareSecurityCode(
- 'id software', 'pin', inv.invoice_ident)
+ security_code = fe.DianXMLExtensionSoftwareSecurityCode('id software', 'pin', inv.invoice_ident)
authorization_provider = fe.DianXMLExtensionAuthorizationProvider()
- cufe = fe.DianXMLExtensionCUFE(
- inv, fe.DianXMLExtensionCUFE.AMBIENTE_PRUEBAS,
- 'clave tecnica')
+ cufe = fe.DianXMLExtensionCUFE(inv, fe.DianXMLExtensionCUFE.AMBIENTE_PRUEBAS,
+ 'clave tecnica')
nit = form.PartyIdentification('nit', '5', '31')
- software_provider = fe.DianXMLExtensionSoftwareProvider(
- nit, nit.dv, 'id software')
- inv_authorization = fe.DianXMLExtensionInvoiceAuthorization(
- 'invoice autorization',
- datetime(2019, 1, 19),
- datetime(2030, 1, 19),
- 'SETP', 990000001, 995000000)
- return [
- security_code,
- authorization_provider,
- cufe, software_provider,
- inv_authorization
- ]
-
+ software_provider = fe.DianXMLExtensionSoftwareProvider(nit, nit.dv, 'id software')
+ inv_authorization = fe.DianXMLExtensionInvoiceAuthorization('invoice autorization',
+ datetime(2019, 1, 19),
+ datetime(2030, 1, 19),
+ 'SETP', 990000001, 995000000)
+ return [security_code, authorization_provider, cufe, software_provider, inv_authorization]
# generar documento desde modelo a ruta indicada
def generate_document(invoice, filepath):
xml = form_xml.DIANInvoiceXML(invoice)
for extension in extensions(invoice):
xml.add_extension(extension)
- form_xml.utils.DIANWriteSigned(
- xml, filepath, PRIVATE_KEY_PATH, PRIVATE_PASSPHRASE, True)
-
+ form_xml.utils.DIANWriteSigned(xml, filepath, PRIVATE_KEY_PATH, PRIVATE_PASSPHRASE, True)
# Modelars las facturas
# ...
# factura de venta nacional
inv = form.NationalSalesInvoice()
-
# asignar periodo de facturacion
inv.set_period(datetime.now(), datetime.now())
-
# asignar fecha de emision de la factura
-
inv.set_issue(datetime.now())
# asignar prefijo y numero del documento
-
inv.set_ident('SETP990003033')
# asignar tipo de operacion ver DIAN:6.1.5
inv.set_operation_type('10')
-
# asignar proveedor
inv.set_supplier(form.Party(
- legal_name='FACHOSOS',
- name='FACHOSOS',
- ident=form.PartyIdentification('900579212', '5', '31'),
+ legal_name = 'FACHO SOS',
+ name = 'FACHO SOS',
+ ident = form.PartyIdentification('900579212', '5', '31'),
# obligaciones del contribuyente ver DIAN:FAK26
- responsability_code=form.Responsability(['ZZ', 'O-09', 'O-14', 'O-48']),
+ responsability_code = form.Responsability(['O-07', 'O-09', 'O-14', 'O-48']),
# ver DIAN:FAJ28
- responsability_regime_code='48',
+ responsability_regime_code = '48',
# tipo de organizacion juridica ver DIAN:6.2.3
- organization_code='1',
- email="sdds@sd.com",
- address=form.Address(
- name='',
- street='',
- city=form.City('05001', 'Medellín'),
- country=form.Country('CO', 'Colombia'),
- countrysubentity=form.CountrySubentity('05', 'Antioquia'))
+ organization_code = '1',
+ email = "sdds@sd.com",
+ address = form.Address(
+ name = '',
+ street = '',
+ city = form.City('05001', 'Medellín'),
+ country = form.Country('CO', 'Colombia'),
+ countrysubentity = form.CountrySubentity('05', 'Antioquia'))
))
-
inv.set_customer(form.Party(
- legal_name='facho-customer',
- name='facho-customer',
- ident=form.PartyIdentification('999999999', '', '13'),
- responsability_code=form.Responsability(['R-99-PN']),
- responsability_regime_code='49',
- organization_code='2',
- email="sdds@sd.com",
- address=form.Address(
- name='',
- street='',
- city=form.City('05001', 'Medellín'),
- country=form.Country('CO', 'Colombia'),
- countrysubentity=form.CountrySubentity('05', 'Antioquia'))
+ legal_name = 'facho-customer',
+ name = 'facho-customer',
+ ident = form.PartyIdentification('999999999', '', '13'),
+ responsability_code = form.Responsability(['R-99-PN']),
+ responsability_regime_code = '49',
+ organization_code = '2',
+ email = "sdds@sd.com",
+ address = form.Address(
+ name = '',
+ street = '',
+ city = form.City('05001', 'Medellín'),
+ country = form.Country('CO', 'Colombia'),
+ countrysubentity = form.CountrySubentity('05', 'Antioquia'))
))
# asignar metodo de pago
inv.set_payment_mean(form.PaymentMean(
# metodo de pago ver DIAN:3.4.1
- id='1',
+ id = '1',
# codigo correspondiente al medio de pago ver DIAN:3.4.2
- code='10',
+ code = '10',
# fecha de vencimiento de la factura
- due_at=datetime.now(),
+ due_at = datetime.now(),
+
# identificador numerico
- payment_id='1'
+ payment_id = '1'
))
# adicionar una linea al documento
inv.add_invoice_line(form.InvoiceLine(
- quantity=form.Quantity(1, '94'),
- description='producto facho',
+ quantity = form.Quantity(1, '94'),
+ description = 'producto facho',
# item general de codigo 999
- item=form.StandardItem('test', 9999),
- price=form.Price(
+ item = form.StandardItem('test', 9999),
+ price = form.Price(
# precio base del tiem
- amount=form.Amount(100.00),
+ amount = form.Amount(100.00),
# ver DIAN:6.3.5.1
- type_code='01',
- type='x'
+ type_code = '01',
+ type = 'x'
),
- tax=form.TaxTotal(
- subtotals=[
+ tax = form.TaxTotal(
+ subtotals = [
form.TaxSubTotal(
- percent=19.00,
- )]
+ percent = 19.00,
+ )
+ ]
)
))
diff --git a/experimental/facho-signer/src/xmlusigned.xml b/experimental/facho-signer/src/xmlusigned.xml
index 35e11a5..23ead39 100644
--- a/experimental/facho-signer/src/xmlusigned.xml
+++ b/experimental/facho-signer/src/xmlusigned.xml
@@ -76,7 +76,7 @@
NEUROTEC TECNOLOGIA S.A.S
900579212
- ZZ;O-09;O-14;O-48
+ O-07;O-09;O-14;O-48
diff --git a/facho/cli.py b/facho/cli.py
index 9ef1fec..8db2710 100644
--- a/facho/cli.py
+++ b/facho/cli.py
@@ -259,14 +259,14 @@ def generate_invoice(private_key, passphrase, scriptname, generate=False, ssl=Tr
spec.loader.exec_module(module)
import facho.fe.form as form
- from facho.fe.form_xml import DIANInvoiceXML, DIANWriteSigned, DIANWrite, DIANSupportDocumentXML
+ from facho.fe.form_xml import DIANInvoiceXML, DIANWriteSigned,DIANWrite
from facho import fe
try:
invoice_xml = module.document_xml()
except AttributeError:
- #invoice_xml = DIANInvoiceXML
- invoice_xml = DIANSupportDocumentXML
+ invoice_xml = DIANInvoiceXML
+
print("Using document xml:", invoice_xml)
invoice = module.invoice()
invoice.calculate()
diff --git a/facho/facho.py b/facho/facho.py
index 0970c2c..0a1a243 100644
--- a/facho/facho.py
+++ b/facho/facho.py
@@ -1,11 +1,12 @@
# This file is part of facho. The COPYRIGHT file at the top level of
# this repository contains the full copyright notices and license terms.
+
from lxml import etree
-from lxml.etree import Element, tostring
+from lxml.etree import Element, SubElement, tostring
import re
from collections import defaultdict
from copy import deepcopy
-
+from pprint import pprint
class FachoValueInvalid(Exception):
def __init__(self, xpath):
@@ -31,10 +32,7 @@ class LXMLBuilder:
def __init__(self, nsmap):
self.nsmap = nsmap
- self._re_node_expr = \
- re.compile(
- r'^(?P((?P\w+):)?(?P[a-zA-Z0-9_-]+))'
- r'(?P\[.+\])?')
+ self._re_node_expr = re.compile(r'^(?P((?P\w+):)?(?P[a-zA-Z0-9_-]+))(?P\[.+\])?')
self._re_attrs = re.compile(r'(\w+)\s*=\s*\"?(\w+)\"?')
def match_expression(self, node_expr):
@@ -123,7 +121,7 @@ class LXMLBuilder:
elem.attrib[key] = value
@classmethod
- def remove_attributes(cls, elem, keys, exclude=[]):
+ def remove_attributes(cls, elem, keys, exclude = []):
for key in keys:
if key in exclude:
continue
@@ -145,8 +143,7 @@ class LXMLBuilder:
self.remove_attributes(el, keys, exclude=['facho_optional'])
is_optional = el.get('facho_optional', 'False') == 'True'
- if is_optional and el.getchildren() == [] and el.keys() == [
- 'facho_optional']:
+ if is_optional and el.getchildren() == [] and el.keys() == ['facho_optional']:
el.getparent().remove(el)
return tostring(elem, **attrs).decode('utf-8')
@@ -156,15 +153,14 @@ class FachoXML:
"""
Decora XML con funciones de consulta XPATH de un solo elemento
"""
- def __init__(self, root, builder=None, nsmap=None, fragment_prefix='',
- fragment_root_element=None):
+ def __init__(self, root, builder=None, nsmap=None, fragment_prefix='',fragment_root_element=None):
if builder is None:
self.builder = LXMLBuilder(nsmap)
else:
self.builder = builder
self.nsmap = nsmap
-
+
if isinstance(root, str):
self.root = self.builder.build_element_from_string(root, nsmap)
else:
@@ -181,22 +177,16 @@ class FachoXML:
xml = LXMLBuilder.from_string(document)
return FachoXML(xml, nsmap=namespaces)
- def root_namespace(self):
- return etree.QName(self.root).namespace
-
- def root_localname(self):
- return etree.QName(self.root).localname
-
def append_element(self, elem, new_elem):
- # elem = self.find_or_create_element(xpath, append=append)
- # self.builder.append(elem, new_elem)
+ #elem = self.find_or_create_element(xpath, append=append)
+ #self.builder.append(elem, new_elem)
self.builder.append(elem, new_elem)
def add_extension(self, extension):
extension.build(self)
- def fragment(
- self, xpath, append=False, append_not_exists=False):
+
+ def fragment(self, xpath, append=False, append_not_exists=False):
nodes = xpath.split('/')
nodes.pop()
root_prefix = '/'.join(nodes)
@@ -206,9 +196,7 @@ class FachoXML:
if parent is None:
parent = self.find_or_create_element(xpath, append=append)
- return FachoXML(
- parent, nsmap=self.nsmap, fragment_prefix=root_prefix,
- fragment_root_element=self.root)
+ return FachoXML(parent, nsmap=self.nsmap, fragment_prefix=root_prefix, fragment_root_element=self.root)
def register_alias_xpath(self, alias, xpath):
self.xpath_for[alias] = xpath
@@ -244,8 +232,7 @@ class FachoXML:
"""
xpath = self._path_xpath_for(xpath)
node_paths = xpath.split('/')
- # remove empty /
- node_paths.pop(0)
+ node_paths.pop(0) #remove empty /
root_tag = node_paths.pop(0)
root_node = self.builder.build_from_expression(root_tag)
@@ -253,10 +240,10 @@ class FachoXML:
# restaurar ya que no es la raiz y asignar actual como raiz
node_paths.insert(0, root_tag)
root_node = self.root
-
+
if not self.builder.same_tag(root_node.tag, self.root.tag):
- raise ValueError('xpath %s must be absolute to /%s' % (
- xpath, self.root.tag))
+
+ raise ValueError('xpath %s must be absolute to /%s' % (xpath, self.root.tag))
# crea jerarquia segun xpath indicado
parent = None
@@ -266,8 +253,8 @@ class FachoXML:
for node_path in node_paths:
node_expr = self.builder.match_expression(node_path)
node = self.builder.build_from_expression(node_path)
- child = self.builder.find_relative(
- current_elem, node_expr['path'], self.nsmap)
+
+ child = self.builder.find_relative(current_elem, node_expr['path'], self.nsmap)
parent = current_elem
if child is not None:
@@ -278,12 +265,11 @@ class FachoXML:
node_expr = self.builder.match_expression(node_tag)
node = self.builder.build_from_expression(node_tag)
- child = self.builder.find_relative(
- current_elem, node_expr['path'], self.nsmap)
+ child = self.builder.find_relative(current_elem, node_expr['path'], self.nsmap)
parent = current_elem
if child is not None:
current_elem = child
-
+
if parent == current_elem:
self.builder.append(parent, node)
return node
@@ -300,10 +286,9 @@ class FachoXML:
self.builder.append(parent, node)
return node
- if self.builder.is_attribute(
- last_slibing, 'facho_placeholder', 'True'):
+ if self.builder.is_attribute(last_slibing, 'facho_placeholder', 'True'):
self._remove_facho_attributes(last_slibing)
- return last_slibing
+ return last_slibing
self.builder.append_next(last_slibing, node)
return node
@@ -314,8 +299,7 @@ class FachoXML:
self._remove_facho_attributes(current_elem)
return current_elem
- def set_element_validator(
- self, xpath, validator=False):
+ def set_element_validator(self, xpath, validator = False):
"""
validador al asignar contenido a xpath indicado
@@ -328,9 +312,8 @@ class FachoXML:
self._validators[key] = lambda v, attrs: True
else:
self._validators[key] = validator
-
- def set_element(
- self, xpath, content, **attrs):
+
+ def set_element(self, xpath, content, **attrs):
"""
asigna contenido ubicado por ruta tipo XPATH.
@param xpath ruta tipo XPATH
@@ -372,8 +355,7 @@ class FachoXML:
self.builder.set_attribute(elem, k, str(v))
return self
- def get_element_attribute(
- self, xpath, attribute, multiple=False):
+ def get_element_attribute(self, xpath, attribute, multiple=False):
elem = self.get_element(xpath, multiple=multiple)
if elem is None:
@@ -410,16 +392,14 @@ class FachoXML:
return None
return format_(text)
- def get_element_text_or_attribute(
- self, xpath, default=None, multiple=False, raise_on_fail=False):
+ def get_element_text_or_attribute(self, xpath, default=None, multiple=False, raise_on_fail=False):
parts = xpath.split('/')
- is_attribute = parts[-1].startswith('@')
+ is_attribute = parts[-1].startswith('@')
if is_attribute:
attribute_name = parts.pop(-1).lstrip('@')
element_path = "/".join(parts)
try:
- val = self.get_element_attribute(
- element_path, attribute_name, multiple=multiple)
+ val = self.get_element_attribute(element_path, attribute_name, multiple=multiple)
if val is None:
return default
return val
@@ -452,8 +432,7 @@ class FachoXML:
if isinstance(xpath, tuple):
val = xpath[0]
else:
- val = self.get_element_text_or_attribute(
- xpath, raise_on_fail=raise_on_fail)
+ val = self.get_element_text_or_attribute(xpath, raise_on_fail=raise_on_fail)
vals.append(val)
return vals
@@ -475,8 +454,7 @@ class FachoXML:
return True
def _remove_facho_attributes(self, elem):
- self.builder.remove_attributes(
- elem, ['facho_optional', 'facho_placeholder'])
+ self.builder.remove_attributes(elem, ['facho_optional', 'facho_placeholder'])
def tostring(self, **kw):
return self.builder.tostring(self.root, **kw)
@@ -488,17 +466,15 @@ class FachoXML:
root = self.root
if self.fragment_root_element is not None:
root = self.fragment_root_element
-
+
if isinstance(self.nsmap, dict):
nsmap = dict(map(reversed, self.nsmap.items()))
ns = nsmap[etree.QName(root).namespace] + ':'
if self.fragment_root_element is not None:
- new_xpath = '/' + ns + etree.QName(root).localname + '/' + \
- etree.QName(self.root).localname + '/' + xpath.lstrip('/')
+ new_xpath = '/' + ns + etree.QName(root).localname + '/' + etree.QName(self.root).localname + '/' + xpath.lstrip('/')
else:
- new_xpath = '/' + ns + etree.QName(root).localname + '/' + \
- xpath.lstrip('/')
+ new_xpath = '/' + ns + etree.QName(root).localname + '/' + xpath.lstrip('/')
return new_xpath
def __str__(self):
diff --git a/facho/fe/__init__.py b/facho/fe/__init__.py
index fdfaedc..11adce3 100644
--- a/facho/fe/__init__.py
+++ b/facho/fe/__init__.py
@@ -5,7 +5,6 @@ from .fe import DianXMLExtensionSigner
from .fe import DianXMLExtensionSoftwareSecurityCode
from .fe import DianXMLExtensionCUFE
from .fe import DianXMLExtensionCUDE
-from .fe import DianXMLExtensionCUDS
from .fe import DianXMLExtensionInvoiceAuthorization
from .fe import DianXMLExtensionSoftwareProvider
from .fe import DianXMLExtensionAuthorizationProvider
diff --git a/facho/fe/client/wsse/signature.py b/facho/fe/client/wsse/signature.py
index 7295742..cc42a3d 100644
--- a/facho/fe/client/wsse/signature.py
+++ b/facho/fe/client/wsse/signature.py
@@ -234,10 +234,9 @@ def _append_timestamp(security, expires_dt=None):
if expires_dt is None:
expires_dt = timedelta(seconds=6000)
- timestamp = datetime.now()
etimestamp = utils.WSU.Timestamp({'{http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd}Id': utils.get_unique_id()})
- etimestamp.append(utils.WSU.Created(get_timestamp(timestamp=timestamp)))
- etimestamp.append(utils.WSU.Expires(get_timestamp(timestamp=timestamp, delta=expires_dt)))
+ etimestamp.append(utils.WSU.Created(get_timestamp()))
+ etimestamp.append(utils.WSU.Expires(get_timestamp(delta=expires_dt)))
security.insert(0, etimestamp)
if etree.LXML_VERSION[:2] >= (3, 5):
etree.cleanup_namespaces(security,
diff --git a/facho/fe/data/dian/codelist/TarifaImpuestoReteIVA-2.1.gc b/facho/fe/data/dian/codelist/TarifaImpuestoReteIVA-2.1.gc
index cd62b2d..2720b0e 100644
--- a/facho/fe/data/dian/codelist/TarifaImpuestoReteIVA-2.1.gc
+++ b/facho/fe/data/dian/codelist/TarifaImpuestoReteIVA-2.1.gc
@@ -1,61 +1,50 @@
-
-
-
-
- TarifaImpuestos
- Tarifas por Impuesto
- 1
- urn:dian:names:especificacion:ubl:listacodigos:gc:TarifaImpuestos
- urn:dian:names:especificacion:ubl:listacodigos:gc:TarifaImpuestos-2.1
- http://dian.gov.co/ubl/os-ubl-2.0/cl/gc/default/TarifaImpuestos-2.1.gc
-
- DIAN (Dirección de Impuestos y Aduanas Nacionales)
- 195
-
-
-
-
- Code
- Codigo Comun
-
-
-
- Name
- Nombre
-
-
-
- Description
- Descripcion
-
-
-
- CodeKey
-
-
-
-
-
-
- 15.00
-
-
- ReteIVA
-
-
- ReteIVA
-
-
-
-
- 100.00
-
-
- ReteIVA
-
-
- ReteIVA
-
-
-
-
+
+
+
+
+ TarifaImpuestos
+ Tarifas por Impuesto
+ 1
+ urn:dian:names:especificacion:ubl:listacodigos:gc:TarifaImpuestos
+ urn:dian:names:especificacion:ubl:listacodigos:gc:TarifaImpuestos-2.1
+ http://dian.gov.co/ubl/os-ubl-2.0/cl/gc/default/TarifaImpuestos-2.1.gc
+
+ DIAN (Dirección de Impuestos y Aduanas Nacionales)
+ 195
+
+
+
+
+ Code
+ Codigo Comun
+
+
+
+ Name
+ Nombre
+
+
+
+ Description
+ Descripcion
+
+
+
+ CodeKey
+
+
+
+
+
+
+ 15.00
+
+
+ ReteIVA
+
+
+ ReteIVA
+
+
+
+
diff --git a/facho/fe/data/dian/codelist/TarifaImpuestoReteRenta-2.1.gc b/facho/fe/data/dian/codelist/TarifaImpuestoReteRenta-2.1.gc
deleted file mode 100644
index 2ff8d6f..0000000
--- a/facho/fe/data/dian/codelist/TarifaImpuestoReteRenta-2.1.gc
+++ /dev/null
@@ -1,479 +0,0 @@
-
-
-
-
- TarifaImpuestoReteFuente
- Tarifas por Impuesto
- 1
- urn:dian:names:especificacion:ubl:listacodigos:gc:TarifaImpuestos
- urn:dian:names:especificacion:ubl:listacodigos:gc:TarifaImpuestos-2.1
- http://dian.gov.co/ubl/os-ubl-2.0/cl/gc/default/TarifaImpuestos-2.1.gc
-
- DIAN (Dirección de Impuestos y Aduanas Nacionales)
- 195
-
-
-
-
- Code
- Codigo Comun
-
-
-
- Name
- Nombre
-
-
-
- Description
- Descripcion
-
-
-
- CodeKey
-
-
-
-
-
-
- 2.50
-
-
- ReteFuente
-
-
- Compras generales (declarantes)
-
-
-
-
- 3.50
-
-
- ReteFuente
-
-
- Compras generales (no declarantes)
-
-
-
-
- 1.50
-
-
- ReteFuente
-
-
- Compras con tarjeta débito o crédito
-
-
-
-
- 1.50
-
-
- ReteFuente
-
-
- Compras de bienes o productos agrícolas o pecuarios sin procesamiento industrial
-
-
-
-
- 2.50
-
-
- ReteFuente
-
-
- Compras de bienes o productos agrícolas o pecuarios con procesamiento industrial (declarantes)
-
-
-
-
- 3.50
-
-
- ReteFuente
-
-
- Compras de bienes o productos agrícolas o pecuarios con procesamiento industrial declarantes (no declarantes)
-
-
-
-
- 0.50
-
-
- ReteFuente
-
-
- Compras de café pergamino o cereza
-
-
-
-
- 0.10
-
-
- ReteFuente
-
-
- Compras de combustibles derivados del petróleo
-
-
-
-
- 1.00
-
-
- ReteFuente
-
-
- Enajenación de activos fijos de personas naturales (notarías y tránsito son agentes retenedores)
-
-
-
-
- 1.00
-
-
- ReteFuente
-
-
- Compras de vehículos
-
-
-
-
- 1.00
-
-
- ReteFuente
-
-
- Compras de bienes raíces cuya destinación y uso sea vivienda de habitación (por las primeras 20.000 UVT, es decir hasta $637.780.000)
-
-
-
-
- 2.50
-
-
- ReteFuente
-
-
- Compras de bienes raíces cuya destinación y uso sea vivienda de habitación (exceso de las primeras 20.000 UVT, es decir superior a $637.780.000)
-
-
-
-
- 2.50
-
-
- ReteFuente
-
-
- Compras de bienes raíces cuya destinación y uso sea distinto a vivienda de habitación
-
-
-
-
- 4.00
-
-
- ReteFuente
-
-
- Servicios generales (declarantes)
-
-
-
-
- 6.00
-
-
- ReteFuente
-
-
- Servicios generales (no declarantes)
-
-
-
-
- 4.00
-
-
- ReteFuente
-
-
- Por emolumentos eclesiásticos (declarantes)
-
-
-
-
- 3.50
-
-
- ReteFuente
-
-
- Por emolumentos eclesiásticos (no declarantes)
-
-
-
-
- 1.00
-
-
- ReteFuente
-
-
- Servicios de transporte de carga
-
-
-
-
- 3.50
-
-
- ReteFuente
-
-
- Servicios de transporte nacional de pasajeros por vía terrestre (declarantes)
-
-
-
-
- 3.50
-
-
- ReteFuente
-
-
- Servicios de transporte nacional de pasajeros por vía terrestre (no declarantes)
-
-
-
-
- 1.00
-
-
- ReteFuente
-
-
- Servicios de transporte nacional de pasajeros por vía aérea o marítima
-
-
-
-
- 1.00
-
-
- ReteFuente
-
-
- Servicios prestados por empresas de servicios temporales (sobre AIU)
-
-
-
-
- 2.00
-
-
- ReteFuente
-
-
- Servicios prestados por empresas de vigilancia y aseo (sobre AIU)
-
-
-
-
- 2.00
-
-
- ReteFuente
-
-
- Servicios integrales de salud prestados por IPS
-
-
-
-
- 3.50
-
-
- ReteFuente
-
-
- Servicios de hoteles y restaurantes (declarantes)
-
-
-
-
- 3.50
-
-
- ReteFuente
-
-
- Servicios de hoteles y restaurantes (no declarantes)
-
-
-
-
- 4.00
-
-
- ReteFuente
-
-
- Arrendamiento de bienes muebles
-
-
-
-
- 3.50
-
-
- ReteFuente
-
-
- Arrendamiento de bienes inmuebles (declarantes)
-
-
-
-
- 3.50
-
-
- ReteFuente
-
-
- Arrendamiento de bienes inmuebles (no declarantes)
-
-
-
-
- 2.50
-
-
- ReteFuente
-
-
- Otros ingresos tributarios (declarantes)
-
-
-
-
- 3.50
-
-
- ReteFuente
-
-
- Otros ingresos tributarios (no declarantes)
-
-
-
-
- 11.00
-
-
- ReteFuente
-
-
- Honorarios y comisiones (personas jurídicas)
-
-
-
-
- 11.00
-
-
- ReteFuente
-
-
- Honorarios y comisiones personas naturales que suscriban contrato o cuya sumatoria de los pagos o abonos en cuenta superen las 3.300 UVT ($105.135.000)
-
-
-
-
- 10.00
-
-
- ReteFuente
-
-
- Honorarios y comisiones (no declarantes)
-
-
-
-
- 3.50
-
-
- ReteFuente
-
-
- Servicios de licenciamiento o derecho de uso de software
-
-
-
-
- 7.00
-
-
- ReteFuente
-
-
- Intereses o rendimientos financieros
-
-
-
-
- 4.00
-
-
- ReteFuente
-
-
- Rendimientos financieros provenientes de títulos de renta fija
-
-
-
-
- 20.00
-
-
- ReteFuente
-
-
- Loterías, rifas, apuestas y similares
-
-
-
-
- 3.00
-
-
- ReteFuente
-
-
- Retención en colocación independiente de juegos de suerte y azar
-
-
-
-
- 2.00
-
-
- ReteFuente
-
-
- Contratos de construcción y urbanización
-
-
-
-
diff --git a/facho/fe/data/dian/codelist/TipoDocumento-2.1.gc b/facho/fe/data/dian/codelist/TipoDocumento-2.1.gc
index ee0b9ef..80c7b96 100644
--- a/facho/fe/data/dian/codelist/TipoDocumento-2.1.gc
+++ b/facho/fe/data/dian/codelist/TipoDocumento-2.1.gc
@@ -1,100 +1,74 @@
-
-
-
-
- TipoDocumento
- Tipo de Documento
- 1
- urn:dian:names:especificacion:ubl:listacodigos:gc:TipoDocumento
- urn:dian:names:especificacion:ubl:listacodigos:gc:TipoDocumento-2.1
- http://dian.gov.co/ubl/os-ubl-2.0/cl/gc/default/TipoDocumento-2.1.gc
-
- DIAN (Dirección de Impuestos y Aduanas Nacionales)
- 195
-
-
-
-
- Code
- Codigo Comun
-
-
-
- Name
- Nombre
-
-
-
- CodeKey
-
-
-
-
-
-
- 01
-
-
- Factura electrónica de Venta
-
-
- Tipos de factura
-
-
-
-
- 02
-
-
- Factura electrónica de venta con propósito de exportación
-
-
- Tipos de factura
-
-
-
-
- 03
-
-
- Factura de talonario o papel con numeración de contingencia.
-
-
- Tipos de factura
-
-
-
-
- 04
-
-
- Factura electrónica de Venta por Contingencia DIAN
-
-
- Tipos de factura
-
-
-
-
- 91
-
-
- Nota Crédito
-
-
- Exclusivo en referencias a documentos (elementos DocumentReference)
-
-
-
-
- 92
-
-
- Nota Débito
-
-
- Exclusivo en referencias a documentos (elementos DocumentReference)
-
-
-
+
+
+
+
+ TipoDocumento
+ Tipo de Documento
+ 1
+ urn:dian:names:especificacion:ubl:listacodigos:gc:TipoDocumento
+ urn:dian:names:especificacion:ubl:listacodigos:gc:TipoDocumento-2.1
+ http://dian.gov.co/ubl/os-ubl-2.0/cl/gc/default/TipoDocumento-2.1.gc
+
+ DIAN (Dirección de Impuestos y Aduanas Nacionales)
+ 195
+
+
+
+
+ Code
+ Codigo Comun
+
+
+
+ Name
+ Nombre
+
+
+
+ CodeKey
+
+
+
+
+
+
+ 01
+
+
+ Factura de Venta Nacional
+
+
+
+
+ 02
+
+
+ Factura de Exportación
+
+
+
+
+ 03
+
+
+ Factura de Contingencia
+
+
+
+
+ 91
+
+
+ Nota Crédito
+
+
+
+
+ 92
+
+
+ Nota Débito
+
+
+
diff --git a/facho/fe/data/dian/codelist/TipoImpuesto-2.1.gc b/facho/fe/data/dian/codelist/TipoImpuesto-2.1.gc
index c70c793..4d1bf8b 100644
--- a/facho/fe/data/dian/codelist/TipoImpuesto-2.1.gc
+++ b/facho/fe/data/dian/codelist/TipoImpuesto-2.1.gc
@@ -1,171 +1,162 @@
-
-
-
-
- TipoImpuesto
- Tipo de Tributos
- 1
- urn:dian:names:especificacion:ubl:listacodigos:gc:TipoImpuesto
- urn:dian:names:especificacion:ubl:listacodigos:gc:TipoImpuesto-2.1
- http://dian.gov.co/ubl/os-ubl-2.0/cl/gc/default/TipoImpuesto-2.1.gc
-
- DIAN (Dirección de Impuestos y Aduanas Nacionales)
- 195
-
-
-
-
- Code
- Codigo Comun
-
-
-
- Name
- Nombre
-
-
-
- CodeKey
-
-
-
-
-
-
- 01
-
-
- IVA
-
-
-
-
- 02
-
-
- IC
-
-
-
-
- 03
-
-
- ICA
-
-
-
-
- 04
-
-
- INC
-
-
-
-
- 05
-
-
- ReteIVA
-
-
-
-
- 06
-
-
- ReteRenta
-
-
-
-
- 07
-
-
- ReteICA
-
-
-
-
- 08
-
-
- ReteCREE
-
-
-
-
- 20
-
-
- FtoHorticultura
-
-
-
-
- 21
-
-
- Timbre
-
-
-
-
- 22
-
-
- Bolsas
-
-
-
-
- 23
-
-
- INCarbono
-
-
-
-
- 24
-
-
- INCombustibles
-
-
-
-
- 25
-
-
- Sobretasa Combustibles
-
-
-
-
- 26
-
-
- Sordicom
-
-
-
-
- 30
-
-
- Impuesto al Consumo de Datos
-
-
-
-
-
- ZZ
-
-
- Nombre de la figura tributaria
-
-
-
+
+
+
+
+ TipoImpuesto
+ Tipo de Tributos
+ 1
+ urn:dian:names:especificacion:ubl:listacodigos:gc:TipoImpuesto
+ urn:dian:names:especificacion:ubl:listacodigos:gc:TipoImpuesto-2.1
+ http://dian.gov.co/ubl/os-ubl-2.0/cl/gc/default/TipoImpuesto-2.1.gc
+
+ DIAN (Dirección de Impuestos y Aduanas Nacionales)
+ 195
+
+
+
+
+ Code
+ Codigo Comun
+
+
+
+ Name
+ Nombre
+
+
+
+ CodeKey
+
+
+
+
+
+
+ 01
+
+
+ IVA
+
+
+
+
+ 02
+
+
+ IC
+
+
+
+
+ 03
+
+
+ ICA
+
+
+
+
+ 04
+
+
+ INC
+
+
+
+
+ 05
+
+
+ ReteIVA
+
+
+
+
+ 06
+
+
+ ReteFuente
+
+
+
+
+ 07
+
+
+ ReteICA
+
+
+
+
+ 08
+
+
+ ReteCREE
+
+
+
+
+ 20
+
+
+ FtoHorticultura
+
+
+
+
+ 21
+
+
+ Timbre
+
+
+
+
+ 22
+
+
+ Bolsas
+
+
+
+
+ 23
+
+
+ INCarbono
+
+
+
+
+ 24
+
+
+ INCombustibles
+
+
+
+
+ 25
+
+
+ Sobretasa Combustibles
+
+
+
+
+ 26
+
+
+ Sordicom
+
+
+
+
+ ZZ
+
+
+ Nombre de la figura tributaria
+
+
+
diff --git a/facho/fe/data/dian/codelist/TipoOperacionNCDS-2.1.gc b/facho/fe/data/dian/codelist/TipoOperacionNCDS-2.1.gc
deleted file mode 100644
index 33002a4..0000000
--- a/facho/fe/data/dian/codelist/TipoOperacionNCDS-2.1.gc
+++ /dev/null
@@ -1,47 +0,0 @@
-
-
-
- TipoOperacion
- Tipo de operacion
- 1
- urn:dian:names:especificacion:ubl:listacodigos:gc:TipoOperacion
- urn:dian:names:especificacion:ubl:listacodigos:gc:TipoOperacion-2.1
- http://dian.gov.co/ubl/os-ubl-2.0/cl/gc/default/TipoOperacion-2.1.gc
-
- DIAN (Dirección de Impuestos y Aduanas Nacionales)
- 195
-
-
-
-
- Code
-
-
-
- Nombre
-
-
-
- CodeKey
-
-
-
-
-
-
- 10
-
-
- Residente
-
-
-
-
- 11
-
-
- No Residente
-
-
-
-
diff --git a/facho/fe/data/dian/codelist/TipoResponsabilidad-2.1.custom.gc b/facho/fe/data/dian/codelist/TipoResponsabilidad-2.1.custom.gc
index 9c1ddd6..74ee5d8 100644
--- a/facho/fe/data/dian/codelist/TipoResponsabilidad-2.1.custom.gc
+++ b/facho/fe/data/dian/codelist/TipoResponsabilidad-2.1.custom.gc
@@ -30,6 +30,46 @@
+
+
+ O-99
+
+
+ Otro tipo de obligado
+
+
+
+
+ O-06
+
+
+ Ingresos y patrimonio
+
+
+
+
+ O-07
+
+
+ Retención en la fuente a título de renta
+
+
+
+
+ O-08
+
+
+ Retención timbre nacional
+
+
+
+
+ O-09
+
+
+ Retención en la fuente en el impuesto sobre las ventas
+
+
O-13
@@ -38,6 +78,14 @@
Gran contribuyente
+
+
+ O-14
+
+
+ Informante de exógena
+
+
O-15
@@ -46,6 +94,38 @@
Autorretenedor
+
+
+ O-16
+
+
+ Obligación de facturar por ingresos de bienes y/o servicios excluidos
+
+
+
+
+ O-17
+
+
+ Profesionales de compra y venta de divisas
+
+
+
+
+ O-19
+
+
+ Productor y/o exportador de bienes exentos
+
+
+
+
+ O-22
+
+
+ Obligado a cumplir deberes formales a nombre de terceros
+
+
O-23
@@ -54,6 +134,62 @@
Agente de retención en el impuesto sobre las ventas
+
+
+ O-32
+
+
+ Impuesto Nacional a la Gasolina y al ACPM
+
+
+
+
+ O-33
+
+
+ Impuesto Nacional al consumo
+
+
+
+
+ O-34
+
+
+ Régimen simplificado impuesto nacional consumo rest y bares
+
+
+
+
+ O-36
+
+
+ Establecimiento Permanente
+
+
+
+
+ O-37
+
+
+ Obligado a Facturar Electrónicamente Modelo 2242
+
+
+
+
+ O-38
+
+
+ Facturación Electrónica Voluntaria Modelo 2242
+
+
+
+
+ O-39
+
+
+ Proveedor de Servicios Tecnológicos PST Modelo 2242
+
+
O-47
@@ -78,6 +214,782 @@
No responsable de IVA
+
+
+ O-52
+
+
+ Facturador electrónico
+
+
+
+
+ O-99
+
+
+ Otro tipo de obligado
+
+
+
+
+ R-00-PN
+
+
+ Clientes del Exterior
+
+
+
+
+ R-12-PN
+
+
+ Factor PN
+
+
+
+
+ R-16-PN
+
+
+ Mandatario
+
+
+
+
+ R-25-PN
+
+
+ Agente Interventor
+
+
+
+
+ R-99-PN
+
+
+ No responsable
+
+
+
+
+ R-06-PJ
+
+
+ Apoderado especial
+
+
+
+
+ R-07-PJ
+
+
+ Apoderado general
+
+
+
+
+ R-12-PJ
+
+
+ Factor
+
+
+
+
+ R-16-PJ
+
+
+ Mandatario
+
+
+
+
+ R-99-PJ
+
+
+ Otro tipo de responsable
+
+
+
+
+ A-01
+
+
+ Agente de carga internacional
+
+
+
+
+ A-02
+
+
+ Agente marítimo
+
+
+
+
+ A-03
+
+
+ Almacén general de depósito
+
+
+
+
+ A-04
+
+
+ Comercializadora internacional (C.I.)
+
+
+
+
+ A-05
+
+
+ Comerciante de la zona aduanera especial de Inírida, Puerto Carreño, Cumaribo y Primavera
+
+
+
+
+ A-06
+
+
+ Comerciantes de la zona de régimen aduanero especial de Leticia
+
+
+
+
+ A-07
+
+
+ Comerciantes de la zona de régimen aduanero especial de Maicao, Uribia y Manaure
+
+
+
+
+ A-08
+
+
+ Comerciantes de la zona de régimen aduanero especial de Urabá, Tumaco y Guapí
+
+
+
+
+ A-09
+
+
+ Comerciantes del puerto libre de San Andrés, Providencia y Santa Catalina
+
+
+
+
+ A-10
+
+
+ Depósito público de apoyo logístico internacional
+
+
+
+
+ A-11
+
+
+ Depósito privado para procesamiento industrial
+
+
+
+
+ A-12
+
+
+ Depósito privado de transformación o ensamble
+
+
+
+
+ A-13
+
+
+ Depósito franco
+
+
+
+
+ A-14
+
+
+ Depósito privado aeronáutico
+
+
+
+
+ A-15
+
+
+ Depósito privado para distribución internacional
+
+
+
+
+ A-16
+
+
+ Depósito privado de provisiones de a bordo para consumo y para llevar
+
+
+
+
+ A-17
+
+
+ Depósito privado para envíos urgentes
+
+
+
+
+ A-18
+
+
+ Depósito privado
+
+
+
+
+ A-19
+
+
+ Depósito público
+
+
+
+
+ A-20
+
+
+ Depósito público para distribución internacional
+
+
+
+
+ A-21
+
+
+ Exportador de café
+
+
+
+
+ A-22
+
+
+ Exportador
+
+
+
+
+ A-23
+
+
+ Importador
+
+
+
+
+ A-24
+
+
+ Intermediario de tráfico postal y envíos urgentes
+
+
+
+
+ A-25
+
+
+ Operador de transporte multimodal
+
+
+
+
+ A-26
+
+
+ Sociedad de intermediación aduanera
+
+
+
+
+ A-27
+
+
+ Titular de puertos y muelles de servicio público o privado
+
+
+
+
+ A-28
+
+
+ Transportador 263nfor régimen de importación y/o exportación
+
+
+
+
+ A-29
+
+
+ Transportista nacional para operaciones del régimen de tránsito aduanero
+
+
+
+
+ A-30
+
+
+ Usuario comercial zona franca
+
+
+
+
+ A-32
+
+
+ Usuario industrial de bienes zona franca
+
+
+
+
+ A-34
+
+
+ Usuario industrial de servicios zona franca
+
+
+
+
+ A-36
+
+
+ Usuario operador de zona franca
+
+
+
+
+ A-37
+
+
+ Usuario aduanero permanente
+
+
+
+
+ A-38
+
+
+ Usuario altamente exportador
+
+
+
+
+ A-39
+
+
+ Usuario de zonas económicas especiales de exportación
+
+
+
+
+ A-40
+
+
+ Deposito privado de instalaciones industriales
+
+
+
+
+ A-41
+
+
+ Beneficiarios de programas especiales de exportación PEX
+
+
+
+
+ A-42
+
+
+ Depósitos privados para mercancías en tránsito San Andrés
+
+
+
+
+ A-43
+
+
+ Observadores de las operaciones de importación
+
+
+
+
+ A-44
+
+
+ Usuarios sistemas especiales Importación exportación
+
+
+
+
+ A-46
+
+
+ Transportador 263nformac régimen de importación y/o exportación
+
+
+
+
+ A-47
+
+
+ Transportador terrestre régimen de importación y/o exportación
+
+
+
+
+ A-48
+
+
+ Aeropuerto de servicio publico o privado
+
+
+
+
+ A-49
+
+
+ Transportador fluvial régimen de importación
+
+
+
+
+ A-50
+
+
+ Usuario industrial zona franca especial
+
+
+
+
+ A-53
+
+
+ Agencias de aduanas 1
+
+
+
+
+ A-54
+
+
+ Usuario Operador Zona Franca Especial
+
+
+
+
+ A-55
+
+
+ Agencias de aduanas 2
+
+
+
+
+ A-56
+
+
+ Agencias de aduanas 3
+
+
+
+
+ A-57
+
+
+ Agencias de aduanas 4
+
+
+
+
+ A-58
+
+
+ Transportador aéreo nacional
+
+
+
+
+ A-60
+
+
+ Transportador aéreo, marítimo o fluvial modalidad Cabotaje
+
+
+
+
+ A-61
+
+
+ Importador de alimentos de consumo humano y animal
+
+
+
+
+ A-62
+
+
+ Importador Ocasional
+
+
+
+
+ A-63
+
+
+ Importador de maquinaría y sus partes Decreto 2261 de 2012
+
+
+
+
+ A-64
+
+
+ Beneficiario Programa de Fomento Industria Automotriz-PROFIA
+
+
+
+
+ A-99
+
+
+ Otro tipo de agente aduanero
+
+
+
+
+ E-01
+
+
+ Agencia
+
+
+
+
+ E-02
+
+
+ Establecimiento de comercio
+
+
+
+
+ E-03
+
+
+ Centro de explotación agrícola
+
+
+
+
+ E-04
+
+
+ Centro de explotación animal
+
+
+
+
+ E-05
+
+
+ Centro de explotación minera
+
+
+
+
+ E-06
+
+
+ Centro de explotación de transformación
+
+
+
+
+ E-07
+
+
+ Centro de explotación de servicios
+
+
+
+
+ E-08
+
+
+ Oficina
+
+
+
+
+ E-09
+
+
+ Sede
+
+
+
+
+ E-10
+
+
+ Sucursal
+
+
+
+
+ E-11
+
+
+ Consultorio
+
+
+
+
+ E-12
+
+
+ Administraciones
+
+
+
+
+ E-13
+
+
+ Seccionales
+
+
+
+
+ E-14
+
+
+ Regionales
+
+
+
+
+ E-15
+
+
+ Intendencias
+
+
+
+
+ E-16
+
+
+ Local o negocio
+
+
+
+
+ E-17
+
+
+ Punto de venta
+
+
+
+
+ E-18
+
+
+ Fábrica
+
+
+
+
+ E-19
+
+
+ Taller
+
+
+
+
+ E-20
+
+
+ Cantera
+
+
+
+
+ E-21
+
+
+ Pozo de Petróleo y Gas
+
+
+
+
+ E-22
+
+
+ Otro lug de tipo de extrac explotación de recursos naturales
+
+
+
+
+ E-99
+
+
+ Otro tipo de establecimiento
+
+
+
+
+ O-13
+
+
+ Gran contribuyente
+
+
+
+
+ O-15
+
+
+ Autorretenedor
+
+
+
+
+ O-23
+
+
+ Agente de retención IVA
+
+
+
+
+ O-47
+
+
+ Régimen simple de tributación
+
+
R-99-PN
diff --git a/facho/fe/data/dian/codelist/TipoResponsabilidad-2.1.gc b/facho/fe/data/dian/codelist/TipoResponsabilidad-2.1.gc
index 36bd0d3..9ba4739 100644
--- a/facho/fe/data/dian/codelist/TipoResponsabilidad-2.1.gc
+++ b/facho/fe/data/dian/codelist/TipoResponsabilidad-2.1.gc
@@ -62,13 +62,5 @@
Régimen simple de tributación
-
-
- ZZ
-
-
- No aplica
-
-
diff --git a/facho/fe/data/dian/codelist/__init__.py b/facho/fe/data/dian/codelist/__init__.py
index 837009d..feeaa45 100644
--- a/facho/fe/data/dian/codelist/__init__.py
+++ b/facho/fe/data/dian/codelist/__init__.py
@@ -82,16 +82,11 @@ 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')\
.update(CodeList(path_for_codelist('TipoImpuesto-2.1.custom.gc'), 'code', 'name'))
-TarifaImpuesto = CodeList(path_for_codelist('TarifaImpuestoINC-2.1.gc'), 'code', 'name')\
- .update(CodeList(path_for_codelist('TarifaImpuestoIVA-2.1.gc'), 'code', 'name'))\
- .update(CodeList(path_for_codelist('TarifaImpuestoReteIVA-2.1.gc'), 'code', 'name'))\
- .update(CodeList(path_for_codelist('TarifaImpuestoReteRenta-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')
FormasPago = CodeList(path_for_codelist('FormasPago-2.1.gc'), 'code', 'name')
RegimenFiscal = CodeList(path_for_codelist('RegimenFiscal-2.1.custom.gc'), 'code', 'name')
TipoOperacionNC = CodeList(path_for_codelist('TipoOperacionNC-2.1.gc'), 'code', 'name')
-TipoOperacionNCDS = CodeList(path_for_codelist('TipoOperacionNCDS-2.1.gc'), 'code', 'name')
TipoOperacionND = CodeList(path_for_codelist('TipoOperacionND-2.1 - copia.gc'), 'code', 'name')
TipoOperacionF = CodeList(path_for_codelist('TipoOperacionF-2.1.gc'), 'code', 'name')\
.update(CodeList(path_for_codelist('TipoOperacionF-2.1.custom.gc'), 'code', 'name'))
diff --git a/facho/fe/fe.py b/facho/fe/fe.py
index 40005fb..39de2b8 100644
--- a/facho/fe/fe.py
+++ b/facho/fe/fe.py
@@ -1,5 +1,6 @@
# This file is part of facho. The COPYRIGHT file at the top level of
# this repository contains the full copyright notices and license terms.
+
from ..facho import FachoXML, FachoXMLExtension, LXMLBuilder
import uuid
import xmlsig
@@ -7,16 +8,13 @@ import xades
from datetime import datetime
import OpenSSL
import zipfile
-# import warnings
+import warnings
import hashlib
from contextlib import contextmanager
from .data.dian import codelist
from . import form
from collections import defaultdict
-# from pathlib import Path
-from dateutil import tz
-
-from cryptography.hazmat.primitives.serialization import pkcs12
+from pathlib import Path
AMBIENTE_PRUEBAS = codelist.TipoAmbiente.by_name('Pruebas')['code']
AMBIENTE_PRODUCCION = codelist.TipoAmbiente.by_name('Producción')['code']
@@ -32,51 +30,32 @@ SCHEME_AGENCY_ATTRS = {
POLICY_ID = 'https://facturaelectronica.dian.gov.co/politicadefirma/v2/politicadefirmav2.pdf'
POLICY_NAME = u'Política de firma para facturas electrónicas de la República de Colombia.'
-Bogota = tz.gettz('America/Bogota')
-# NAMESPACES = {
-# 'atd': 'urn:oasis:names:specification:ubl:schema:xsd:AttachedDocument-2',
-# 'nomina': 'dian:gov:co:facturaelectronica:NominaIndividual',
-# 'nominaajuste': 'dian:gov:co:facturaelectronica:NominaIndividualDeAjuste',
-# 'fe': 'http://www.dian.gov.co/contratos/facturaelectronica/v1',
-# 'xs': 'http://www.w3.org/2001/XMLSchema-instance',
-# 'cac': 'urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2',
-# 'cbc': 'urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2',
-# 'cdt': 'urn:DocumentInformation:names:specification:ubl:colombia:schema:xsd:DocumentInformationAggregateComponents-1',
-# 'clm54217': 'urn:un:unece:uncefact:codelist:specification:54217:2001',
-# 'clmIANAMIMEMediaType': 'urn:un:unece:uncefact:codelist:specification:IANAMIMEMediaType:2003',
-# 'ext': 'urn:oasis:names:specification:ubl:schema:xsd:CommonExtensionComponents-2',
-# 'qdt': 'urn:oasis:names:specification:ubl:schema:xsd:QualifiedDatatypes-2',
-# 'sts': 'dian:gov:co:facturaelectronica:Structures-2-1',
-# 'udt': 'urn:un:unece:uncefact:data:specification:UnqualifiedDataTypesSchemaModule:2',
-# 'xsi': 'http://www.w3.org/2001/XMLSchema-instance',
-# 'xades': 'http://uri.etsi.org/01903/v1.3.2#',
-# 'xades141': 'http://uri.etsi.org/01903/v1.4.1#',
-# 'ds': 'http://www.w3.org/2000/09/xmldsig#',
-# 'sig': 'http://www.w3.org/2000/09/xmldsig#',
-# }
-
NAMESPACES = {
- 'apr': 'urn:oasis:names:specification:ubl:schema:xsd:ApplicationResponse-2',
'atd': 'urn:oasis:names:specification:ubl:schema:xsd:AttachedDocument-2',
+ 'no': 'dian:gov:co:facturaelectronica:NominaIndividual',
'fe': 'http://www.dian.gov.co/contratos/facturaelectronica/v1',
+ 'xs': 'http://www.w3.org/2001/XMLSchema-instance',
'cac': 'urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2',
'cbc': 'urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2',
+ 'cdt': 'urn:DocumentInformation:names:specification:ubl:colombia:schema:xsd:DocumentInformationAggregateComponents-1',
+ 'clm54217': 'urn:un:unece:uncefact:codelist:specification:54217:2001',
+ 'clmIANAMIMEMediaType': 'urn:un:unece:uncefact:codelist:specification:IANAMIMEMediaType:2003',
'ext': 'urn:oasis:names:specification:ubl:schema:xsd:CommonExtensionComponents-2',
'qdt': 'urn:oasis:names:specification:ubl:schema:xsd:QualifiedDatatypes-2',
'sts': 'dian:gov:co:facturaelectronica:Structures-2-1',
- 'udt': 'urn:un:unece:uncefact:data:specification:UnqualifiedDataTypesSchemaModule:2',
+ 'udt': 'urn:un:unece:uncefact:data:specification:UnqualifiedDataTypesSchemaModule:2',
'xsi': 'http://www.w3.org/2001/XMLSchema-instance',
- 'ds': 'http://www.w3.org/2000/09/xmldsig#',
'xades': 'http://uri.etsi.org/01903/v1.3.2#',
+ 'xades141': 'http://uri.etsi.org/01903/v1.4.1#',
+ 'ds': 'http://www.w3.org/2000/09/xmldsig#',
+ 'sig': 'http://www.w3.org/2000/09/xmldsig#',
}
-
def fe_from_string(document: str) -> FachoXML:
return FeXML.from_string(document)
-
-# from contextlib import contextmanager
+from contextlib import contextmanager
@contextmanager
def mock_xades_policy():
from mock import patch
@@ -94,39 +73,29 @@ def mock_xades_policy():
mock.return_value = UrllibPolicyMock()
yield
-
+
class FeXML(FachoXML):
def __init__(self, root, namespace):
- # raise Exception(namespace)
+
super().__init__("{%s}%s" % (namespace, root),
nsmap=NAMESPACES)
@classmethod
def from_string(cls, document: str) -> 'FeXML':
return super().from_string(document, namespaces=NAMESPACES)
-
+
def tostring(self, **kw):
# MACHETE(bit4bit) la DIAN espera que la etiqueta raiz no este en un namespace
- urn_oasis = {
- 'AttachedDocument': 'urn:oasis:names:specification:ubl:schema:xsd:AttachedDocument-2',
- 'Invoice': 'urn:oasis:names:specification:ubl:schema:xsd:Invoice-2',
- 'CreditNote': 'urn:oasis:names:specification:ubl:schema:xsd:CreditNote-2',
- 'ApplicationResponse': 'urn:oasis:names:specification:ubl:schema:xsd:ApplicationResponse-2'
- }
-
- root_namespace = self.root_namespace()
- root_localname = self.root_localname()
- xmlns_name = {v: k for k, v in NAMESPACES.items()}[root_namespace]
-
return super().tostring(**kw)\
- .replace(xmlns_name + ':', '')\
- .replace('xmlns:'+xmlns_name, 'xmlns')\
- .replace(root_namespace, urn_oasis[root_localname])
+ .replace("fe:", "")\
+ .replace("xmlns:no", "xmlns")\
+ .replace("change", "xsi:schemaLocation")
class DianXMLExtensionCUDFE(FachoXMLExtension):
- def __init__(self, invoice, tipo_ambiente=AMBIENTE_PRUEBAS):
+
+ def __init__(self, invoice, tipo_ambiente = AMBIENTE_PRUEBAS):
self.tipo_ambiente = tipo_ambiente
self.invoice = invoice
@@ -151,25 +120,12 @@ class DianXMLExtensionCUDFE(FachoXMLExtension):
fachoxml.set_element('./cbc:UUID', cufe,
schemeID=self.tipo_ambiente,
schemeName=self.schemeName())
-
- if self.schemeName() == "CUDS-SHA384":
- if fachoxml.tag_document() == 'Invoice':
- fachoxml.set_element('./cbc:ProfileID',
- 'DIAN 2.1: documento soporte en adquisiciones efectuadas a no obligados a facturar.')
- else:
- fachoxml.set_element('./cbc:ProfileID',
- 'DIAN 2.1: Nota de ajuste al documento soporte en adquisiciones efectuadas a sujetos no obligados a expedir factura o documento equivalente')
- else:
- fachoxml.set_element('./cbc:ProfileID', 'DIAN 2.1: Factura Electrónica de Venta')
-
- # #DIAN 1.8.-2021: FAD03
- # fachoxml.set_element('./cbc:ProfileID', 'DIAN 2.1: Factura Electrónica de Venta')
- fachoxml.set_element(
- './cbc:ProfileExecutionID', self._tipo_ambiente_int())
+ #DIAN 1.8.-2021: FAD03
+ fachoxml.set_element('./cbc:ProfileID', 'DIAN 2.1: Factura Electrónica de Venta')
+ fachoxml.set_element('./cbc:ProfileExecutionID', self._tipo_ambiente_int())
#DIAN 1.7.-2020: FAB36
- fachoxml.set_element(
- './ext:UBLExtensions/ext:UBLExtension/ext:ExtensionContent/sts:DianExtensions/sts:QRCode',
- self._get_qrcode(cufe))
+ fachoxml.set_element('./ext:UBLExtensions/ext:UBLExtension/ext:ExtensionContent/sts:DianExtensions/sts:QRCode',
+ self._get_qrcode(cufe))
def issue_time(self, datetime_):
return datetime_.strftime('%H:%M:%S-05:00')
@@ -185,8 +141,7 @@ class DianXMLExtensionCUDFE(FachoXMLExtension):
build_vars['HoraFac'] = self.issue_time(invoice.invoice_issue)
# PAG 601
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))
build_vars['CodImpuesto1'] = '01'
build_vars['CodImpuesto2'] = '04'
@@ -216,8 +171,7 @@ class DianXMLExtensionCUDFE(FachoXMLExtension):
class DianXMLExtensionCUFE(DianXMLExtensionCUDFE):
- def __init__(
- self, invoice, clave_tecnica='', tipo_ambiente=AMBIENTE_PRUEBAS):
+ def __init__(self, invoice, clave_tecnica = '', tipo_ambiente = AMBIENTE_PRUEBAS):
self.tipo_ambiente = tipo_ambiente
self.clave_tecnica = clave_tecnica
self.invoice = invoice
@@ -253,7 +207,6 @@ class DianXMLExtensionCUFE(DianXMLExtensionCUDFE):
'%d' % build_vars['TipoAmb'],
]
-
class DianXMLExtensionCUDE(DianXMLExtensionCUDFE):
def __init__(self, invoice, software_pin, tipo_ambiente = AMBIENTE_PRUEBAS):
self.tipo_ambiente = tipo_ambiente
@@ -291,41 +244,6 @@ class DianXMLExtensionCUDE(DianXMLExtensionCUDFE):
'%d' % build_vars['TipoAmb'],
]
-
-class DianXMLExtensionCUDS(DianXMLExtensionCUDFE):
- def __init__(self, invoice, software_pin, tipo_ambiente = AMBIENTE_PRUEBAS):
- self.tipo_ambiente = tipo_ambiente
- self.software_pin = software_pin
- self.invoice = invoice
-
- def schemeName(self):
- return 'CUDS-SHA384'
-
- def buildVars(self):
- build_vars = super().buildVars()
- build_vars['Software-PIN'] = str(self.software_pin)
- return build_vars
-
- def formatVars(self):
- build_vars = self.buildVars()
- CodImpuesto1 = build_vars['CodImpuesto1']
- CodImpuesto2 = build_vars['CodImpuesto2']
- CodImpuesto3 = build_vars['CodImpuesto3']
- return [
- '%s' % build_vars['NumFac'],
- '%s' % build_vars['FecFac'],
- '%s' % build_vars['HoraFac'],
- form.Amount(build_vars['ValorBruto']).truncate_as_string(2),
- CodImpuesto1,
- form.Amount(build_vars['ValorImpuestoPara'].get(CodImpuesto1, 0.0)).truncate_as_string(2),
- form.Amount(build_vars['ValorTotalPagar']).truncate_as_string(2),
- '%s' % build_vars['NitOFE'],
- '%s' % build_vars['NumAdq'],
- '%s' % build_vars['Software-PIN'],
- '%d' % build_vars['TipoAmb'],
- ]
-
-
class DianXMLExtensionSoftwareProvider(FachoXMLExtension):
# RESOLUCION 0004: pagina 108
@@ -335,8 +253,7 @@ class DianXMLExtensionSoftwareProvider(FachoXMLExtension):
self.id_software = id_software
def build(self, fexml):
- software_provider = fexml.fragment(
- './ext:UBLExtensions/ext:UBLExtension/ext:ExtensionContent/sts:DianExtensions/sts:SoftwareProvider')
+ software_provider = fexml.fragment('./ext:UBLExtensions/ext:UBLExtension/ext:ExtensionContent/sts:DianExtensions/sts:SoftwareProvider')
provider_id_attrs = SCHEME_AGENCY_ATTRS.copy()
provider_id_attrs.update({'schemeID': self.dv})
#DIAN 1.7.-2020: FAB23
@@ -366,6 +283,7 @@ class DianXMLExtensionSoftwareSecurityCode(FachoXMLExtension):
class DianXMLExtensionSigner:
+
def __init__(self, pkcs12_path, passphrase=None, localpolicy=True):
self._pkcs12_data = open(pkcs12_path, 'rb').read()
self._passphrase = None
@@ -376,18 +294,17 @@ class DianXMLExtensionSigner:
@classmethod
def from_bytes(cls, data, passphrase=None, localpolicy=True):
self = cls.__new__(cls)
+
self._pkcs12_data = data
self._passphrase = None
self._localpolicy = localpolicy
if passphrase:
self._passphrase = passphrase.encode('utf-8')
-
+
return self
def _element_extension_content(self, fachoxml):
- return fachoxml.builder.xpath(
- fachoxml.root,
- './ext:UBLExtensions/ext:UBLExtension[2]/ext:ExtensionContent')
+ return fachoxml.builder.xpath(fachoxml.root, './ext:UBLExtensions/ext:UBLExtension[2]/ext:ExtensionContent')
def sign_xml_string(self, document):
xml = LXMLBuilder.from_string(document)
@@ -409,6 +326,7 @@ class DianXMLExtensionSigner:
)
xml.append(signature)
+
ref = xmlsig.template.add_reference(
signature, xmlsig.constants.TransformSha256, uri="", name="xmldsig-%s-ref0" % (id_uuid)
)
@@ -416,16 +334,14 @@ class DianXMLExtensionSigner:
id_keyinfo = "xmldsig-%s-KeyInfo" % (id_uuid)
xmlsig.template.add_reference(
- signature, xmlsig.constants.TransformSha256, uri="#%s" % (
- id_keyinfo), name="xmldsig-%s-ref1" % (id_uuid),
+ signature, xmlsig.constants.TransformSha256, uri="#%s" % (id_keyinfo), name="xmldsig-%s-ref1" % (id_uuid),
)
ki = xmlsig.template.ensure_key_info(signature, name=id_keyinfo)
data = xmlsig.template.add_x509_data(ki)
xmlsig.template.x509_data_add_certificate(data)
xmlsig.template.add_key_value(ki)
- qualifying = xades.template.create_qualifying_properties(
- signature, 'XadesObjects', 'xades')
+ qualifying = xades.template.create_qualifying_properties(signature, 'XadesObjects', 'xades')
xades.utils.ensure_id(qualifying)
id_props = "xmldsig-%s-signedprops" % (id_uuid)
@@ -433,12 +349,10 @@ class DianXMLExtensionSigner:
signature, xmlsig.constants.TransformSha256, uri="#%s" % (id_props),
uri_type="http://uri.etsi.org/01903#SignedProperties"
)
- xmlsig.template.add_transform(
- props_ref, xmlsig.constants.TransformInclC14N)
+ xmlsig.template.add_transform(props_ref, xmlsig.constants.TransformInclC14N)
# TODO assert with http://www.sic.gov.co/hora-legal-colombiana
- props = xades.template.create_signed_properties(
- qualifying, name=id_props, datetime=datetime.now(tz=Bogota))
+ props = xades.template.create_signed_properties(qualifying, name=id_props, datetime=datetime.now())
xades.template.add_claimed_role(props, "supplier")
policy = xades.policy.GenericPolicyId(
@@ -446,13 +360,9 @@ class DianXMLExtensionSigner:
POLICY_NAME,
xmlsig.constants.TransformSha256)
ctx = xades.XAdESContext(policy)
- ctx.load_pkcs12(pkcs12.load_key_and_certificates(
- self._pkcs12_data,
- self._passphrase))
+ ctx.load_pkcs12(OpenSSL.crypto.load_pkcs12(self._pkcs12_data,
+ self._passphrase))
- # ctx.load_pkcs12(OpenSSL.crypto.load_pkcs12(
- # self._pkcs12_data,
- # self._passphrase))
if self._localpolicy:
with mock_xades_policy():
ctx.sign(signature)
@@ -460,7 +370,7 @@ class DianXMLExtensionSigner:
else:
ctx.sign(signature)
ctx.verify(signature)
- # xmlsig take parent root
+ #xmlsig take parent root
xml.remove(signature)
return signature
@@ -469,28 +379,29 @@ class DianXMLExtensionSigner:
extcontent = self._element_extension_content(fachoxml)
fachoxml.append_element(extcontent, signature)
-
+
class DianXMLExtensionAuthorizationProvider(FachoXMLExtension):
# RESOLUCION 0004: pagina 176
def build(self, fexml):
attrs = {'schemeID': '4', 'schemeName': '31'}
attrs.update(SCHEME_AGENCY_ATTRS)
+
authorization_provider = fexml.fragment('./ext:UBLExtensions/ext:UBLExtension/ext:ExtensionContent/sts:DianExtensions/sts:AuthorizationProvider')
authorization_provider.set_element('./sts:AuthorizationProviderID',
'800197268',
**attrs)
+
class DianXMLExtensionInvoiceSource(FachoXMLExtension):
# CAB13
def build(self, fexml):
dian_path = '/fe:CreditNote/ext:UBLExtensions/ext:UBLExtension/ext:ExtensionContent/sts:DianExtensions/sts:InvoiceSource/cbc:IdentificationCode'
- fexml.set_element(
- dian_path, 'CO',
- listAgencyID="6",
- listAgencyName="United Nations Economic Commission for Europe",
- listSchemeURI="urn:oasis:names:specification:ubl:codelist:gc:CountryIdentificationCode-2.1")
+ fexml.set_element(dian_path, 'CO',
+ listAgencyID="6",
+ listAgencyName="United Nations Economic Commission for Europe",
+ listSchemeURI="urn:oasis:names:specification:ubl:codelist:gc:CountryIdentificationCode-2.1")
class DianXMLExtensionInvoiceAuthorization(FachoXMLExtension):
@@ -520,15 +431,16 @@ class DianXMLExtensionInvoiceAuthorization(FachoXMLExtension):
invoice_control.set_element('/sts:InvoiceControl/sts:AuthorizedInvoices/sts:To',
self.to)
- fexml.set_element(
- './ext:UBLExtensions/ext:UBLExtension/ext:ExtensionContent/sts:DianExtensions/sts:InvoiceSource/cbc:IdentificationCode',
- 'CO',
- # DIAN 1.7.-2020: FAB15
- listAgencyID="6",
- # DIAN 1.7.-2020: FAB16
- listAgencyName="United Nations Economic Commission for Europe",
- # DIAN 1.7.-2020: FAB17
- listSchemeURI="urn:oasis:names:specification:ubl:codelist:gc:CountryIdentificationCode-2.1")
+ fexml.set_element('./ext:UBLExtensions/ext:UBLExtension/ext:ExtensionContent/sts:DianExtensions/sts:InvoiceSource/cbc:IdentificationCode',
+ 'CO',
+ #DIAN 1.7.-2020: FAB15
+ listAgencyID="6",
+ #DIAN 1.7.-2020: FAB16
+ listAgencyName="United Nations Economic Commission for Europe",
+ #DIAN 1.7.-2020: FAB17
+ listSchemeURI="urn:oasis:names:specification:ubl:codelist:gc:CountryIdentificationCode-2.1"
+ )
+
class DianZIP:
@@ -537,8 +449,7 @@ class DianZIP:
MAX_FILES = 50
def __init__(self, file_like):
- self.zipfile = zipfile.ZipFile(
- file_like, mode='w', compression=zipfile.ZIP_DEFLATED)
+ self.zipfile = zipfile.ZipFile(file_like, mode='w', compression=zipfile.ZIP_DEFLATED)
self.num_files = 0
def add_xml(self, name, xml_data):
@@ -559,6 +470,7 @@ class DianZIP:
def __enter__(self):
"""
Facilita el uso de esta manera:
+
f = open('xxx', 'rb')
with DianZIP(f) as zip:
zip.add_invoice_xml('name', 'data xml')
@@ -581,7 +493,7 @@ class DianXMLExtensionSignerVerifier:
def verify_string(self, document):
# Obtener FachoXML
xml = LXMLBuilder.from_string(document)
- fachoxml = FachoXML(xml, nsmap=NAMESPACES)
+ fachoxml = FachoXML(xml,nsmap=NAMESPACES)
# Obtener Signature
signature = fachoxml.builder.xpath(fachoxml.root, '//ds:Signature')
diff --git a/facho/fe/form/__init__.py b/facho/fe/form/__init__.py
index 026eef8..0aa6318 100644
--- a/facho/fe/form/__init__.py
+++ b/facho/fe/form/__init__.py
@@ -1,26 +1,24 @@
# This file is part of facho. The COPYRIGHT file at the top level of
# this repository contains the full copyright notices and license terms.
-# import hashlib
-# from functools import reduce
-# import copy
-
+import hashlib
+from functools import reduce
+import copy
import dataclasses
-from dataclasses import dataclass, field
+from dataclasses import dataclass
from datetime import datetime, date
-# from collections import defaultdict
+from collections import defaultdict
import decimal
from decimal import Decimal
import typing
+
from ..data.dian import codelist
DECIMAL_PRECISION = 6
-
class AmountCurrencyError(TypeError):
pass
-
@dataclass
class Currency:
code: str
@@ -31,7 +29,6 @@ class Currency:
def __str__(self):
return self.code
-
class Collection:
def __init__(self, array):
@@ -48,7 +45,6 @@ class Collection:
def sum(self):
return sum(self.array)
-
class AmountCollection(Collection):
def sum(self):
@@ -57,13 +53,10 @@ class AmountCollection(Collection):
total += v
return total
-
class Amount:
- def __init__(
- self, amount: typing.Union[int, float, str, "Amount"],
- currency: Currency = Currency('COP')):
+ def __init__(self, amount: int or float or str or Amount, currency: Currency = Currency('COP')):
- # DIAN 1.7.-2020: 1.2.3.1
+ #DIAN 1.7.-2020: 1.2.3.1
if isinstance(amount, Amount):
if amount < Amount(0.0):
raise ValueError('amount must be positive >= 0')
@@ -74,16 +67,14 @@ class Amount:
if float(amount) < 0:
raise ValueError('amount must be positive >= 0')
- self.amount = Decimal(
- amount, decimal.Context(
- prec=DECIMAL_PRECISION,
- # DIAN 1.7.-2020: 1.2.1.1
- rounding=decimal.ROUND_HALF_EVEN))
+ self.amount = Decimal(amount, decimal.Context(prec=DECIMAL_PRECISION,
+ #DIAN 1.7.-2020: 1.2.1.1
+ rounding=decimal.ROUND_HALF_EVEN ))
self.currency = currency
def fromNumber(self, val):
return Amount(val, currency=self.currency)
-
+
def round(self, prec):
return Amount(round(self.amount, prec), currency=self.currency)
@@ -101,8 +92,7 @@ class Amount:
def __eq__(self, other):
if not self.is_same_currency(other):
raise AmountCurrencyError()
- return round(self.amount, DECIMAL_PRECISION) == round(
- other.amount, DECIMAL_PRECISION)
+ return round(self.amount, DECIMAL_PRECISION) == round(other.amount, DECIMAL_PRECISION)
def _cast(self, val):
if type(val) in [int, float]:
@@ -110,7 +100,7 @@ class Amount:
if isinstance(val, Amount):
return val
raise TypeError("cant cast to amount")
-
+
def __add__(self, rother):
other = self._cast(rother)
if not self.is_same_currency(other):
@@ -134,14 +124,14 @@ class Amount:
def truncate_as_string(self, prec):
parts = str(self.float()).split('.', 1)
- return '%s.%s' % (parts[0], parts[1][0:prec].ljust(prec, '0'))
+ return '%s.%s' % (parts[0], parts[1][0:prec].ljust(prec,'0'))
def float(self):
return float(round(self.amount, DECIMAL_PRECISION))
-
+
class Quantity:
-
+
def __init__(self, val, code):
if type(val) not in [float, int]:
raise ValueError('val expected int or float')
@@ -163,7 +153,6 @@ class Quantity:
def __repr__(self):
return str(self)
-
@dataclass
class Item:
scheme_name: str
@@ -174,10 +163,10 @@ class Item:
class StandardItem(Item):
- def __init__(self, id_: str, description: str = '', name: str = ''):
+ def __init__(self, id_: str, description: str = ''):
super().__init__(id=id_,
description=description,
- scheme_name=name,
+ scheme_name='',
scheme_id='999',
scheme_agency_id='')
@@ -188,9 +177,9 @@ class UNSPSCItem(Item):
description=description,
scheme_name='UNSPSC',
scheme_id='001',
- scheme_agency_id='10')
-
+ scheme_agency_id='10')
+
@dataclass
class Country:
code: str
@@ -201,7 +190,6 @@ class Country:
raise ValueError("code [%s] not found" % (self.code))
self.name = codelist.Paises[self.code]['name']
-
@dataclass
class CountrySubentity:
code: str
@@ -212,7 +200,6 @@ class CountrySubentity:
raise ValueError("code [%s] not found" % (self.code))
self.name = codelist.Departamento[self.code]['name']
-
@dataclass
class City:
code: str
@@ -223,22 +210,13 @@ class City:
raise ValueError("code [%s] not found" % (self.code))
self.name = codelist.Municipio[self.code]['name']
-
-@dataclass
-class PostalZone:
- code: str = ''
-
-
@dataclass
class Address:
name: str
street: str = ''
- city: City = field(default_factory=lambda: City('05001'))
- country: Country = field(default_factory=lambda: Country('CO'))
- countrysubentity: CountrySubentity = field(
- default_factory=lambda: CountrySubentity('05'))
- postalzone: PostalZone = field(default_factory=lambda: PostalZone(''))
-
+ city: City = City('05001')
+ country: Country = Country('CO')
+ countrysubentity: CountrySubentity = CountrySubentity('05')
@dataclass
class PartyIdentification:
@@ -259,7 +237,6 @@ class PartyIdentification:
if self.type_fiscal not in codelist.TipoIdFiscal:
raise ValueError("type_fiscal [%s] not found" % (self.type_fiscal))
-
@dataclass
class Responsability:
codes: list
@@ -284,12 +261,12 @@ class TaxScheme:
code: str
name: str = ''
+
def __post_init__(self):
if self.code not in codelist.TipoImpuesto:
raise ValueError("code not found")
self.name = codelist.TipoImpuesto[self.code]['name']
-
@dataclass
class Party:
name: str
@@ -297,10 +274,10 @@ class Party:
responsability_code: typing.List[Responsability]
responsability_regime_code: str
organization_code: str
- tax_scheme: TaxScheme = field(default_factory=lambda: TaxScheme('01'))
+ tax_scheme: TaxScheme = TaxScheme('01')
phone: str = ''
- address: Address = field(default_factory=lambda: Address(''))
+ address: Address = Address('')
email: str = ''
legal_name: str = ''
legal_company_ident: str = ''
@@ -328,7 +305,7 @@ class TaxScheme:
class TaxSubTotal:
percent: float
scheme: typing.Optional[TaxScheme] = None
- tax_amount: Amount = field(default_factory=lambda: Amount(0.0))
+ tax_amount: Amount = Amount(0.0)
def calculate(self, invline):
if self.percent is not None:
@@ -338,11 +315,12 @@ class TaxSubTotal:
@dataclass
class TaxTotal:
subtotals: list
- tax_amount: Amount = field(default_factory=lambda: Amount(0.0))
- taxable_amount: Amount = field(default_factory=lambda: Amount(0.0))
+ 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
@@ -355,40 +333,6 @@ class TaxTotalOmit(TaxTotal):
def calculate(self, invline):
pass
-
-@dataclass
-class WithholdingTaxSubTotal:
- percent: float
- scheme: typing.Optional[TaxScheme] = None
- tax_amount: Amount = field(default_factory=lambda: 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 = field(default_factory=lambda: Amount(0.0))
- taxable_amount: Amount = field(default_factory=lambda: 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
class Price:
amount: Amount
@@ -404,7 +348,6 @@ class Price:
self.amount *= self.quantity
-
@dataclass
class PaymentMean:
DEBIT = '01'
@@ -422,24 +365,8 @@ class PaymentMean:
@dataclass
class PrePaidPayment:
- # DIAN 1.7.-2020: FBD03
- paid_amount: Amount = field(default_factory=lambda: Amount(0.0))
-
-
-@dataclass
-class BillingResponse:
- id: str
- code: str
- description: str
-
-
-class SupportDocumentCreditNoteResponse(BillingResponse):
- """
- ReferenceID: Identifica la sección del Documento
- Soporte original a la cual se aplica la corrección.
- ResponseCode: Código de descripción de la corrección.
- Description: Descripción de la naturaleza de la corrección.
- """
+ #DIAN 1.7.-2020: FBD03
+ paid_amount: Amount = Amount(0.0)
@dataclass
@@ -448,7 +375,6 @@ class BillingReference:
uuid: str
date: date
-
class CreditNoteDocumentReference(BillingReference):
"""
ident: Prefijo + Numero de la factura relacionada
@@ -464,7 +390,6 @@ class DebitNoteDocumentReference(BillingReference):
date: fecha de emision de la factura relacionada
"""
-
class InvoiceDocumentReference(BillingReference):
"""
ident: Prefijo + Numero de la nota credito relacionada
@@ -472,7 +397,6 @@ class InvoiceDocumentReference(BillingReference):
date: fecha de emision de la nota credito relacionada
"""
-
@dataclass
class AllowanceChargeReason:
code: str
@@ -485,26 +409,22 @@ class AllowanceChargeReason:
@dataclass
class AllowanceCharge:
- # DIAN 1.7.-2020: FAQ03
+ #DIAN 1.7.-2020: FAQ03
charge_indicator: bool = True
- amount: Amount = field(default_factory=lambda: Amount(0.0))
+ amount: Amount = Amount(0.0)
reason: AllowanceChargeReason = None
- # Valor Base para calcular el descuento o el cargo
- base_amount: typing.Optional[Amount] = field(
- default_factory=lambda: Amount(0.0))
-
+ #Valor Base para calcular el descuento o el cargo
+ base_amount: typing.Optional[Amount] = Amount(0.0)
+
# Porcentaje: Porcentaje que aplicar.
- multiplier_factor_numeric: Amount = field(
- default_factory=lambda: Amount(1.0))
-
+ multiplier_factor_numeric: Amount = Amount(1.0)
+
def isCharge(self):
- charge_indicator = self.charge_indicator is True
- return charge_indicator
+ return self.charge_indicator == True
def isDiscount(self):
- charge_indicator = self.charge_indicator is False
- return charge_indicator
+ return self.charge_indicator == False
def asCharge(self):
self.charge_indicator = True
@@ -518,13 +438,11 @@ class AllowanceCharge:
def set_base_amount(self, amount):
self.base_amount = amount
-
class AllowanceChargeAsDiscount(AllowanceCharge):
def __init__(self, amount: Amount = Amount(0.0)):
self.charge_indicator = False
self.amount = amount
-
@dataclass
class InvoiceLine:
# RESOLUCION 0004: pagina 155
@@ -537,9 +455,8 @@ class InvoiceLine:
# la factura y el percent es unico por type_code
# de subtotal
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):
if not isinstance(charge, AllowanceCharge):
@@ -550,7 +467,7 @@ class InvoiceLine:
@property
def total_amount_without_charge(self):
return (self.quantity * self.price.amount)
-
+
@property
def total_amount(self):
charge = AmountCollection(self.allowance_charge)\
@@ -582,17 +499,8 @@ class InvoiceLine:
def taxable_amount(self):
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):
self.tax.calculate(self)
- self.withholding.calculate(self)
def __post_init__(self):
if not isinstance(self.quantity, Quantity):
@@ -601,22 +509,18 @@ class InvoiceLine:
if self.tax is None:
self.tax = TaxTotalOmit()
- if self.withholding is None:
- self.withholding = WithholdingTaxTotalOmit()
-
-
@dataclass
class LegalMonetaryTotal:
- line_extension_amount: Amount = field(default_factory=lambda: Amount(0.0))
- tax_exclusive_amount: Amount = field(default_factory=lambda: Amount(0.0))
- tax_inclusive_amount: Amount = field(default_factory=lambda: Amount(0.0))
- charge_total_amount: Amount = field(default_factory=lambda: Amount(0.0))
- allowance_total_amount: Amount = field(default_factory=lambda: Amount(0.0))
- payable_amount: Amount = field(default_factory=lambda: Amount(0.0))
- prepaid_amount: Amount = field(default_factory=lambda: Amount(0.0))
+ line_extension_amount: Amount = Amount(0.0)
+ tax_exclusive_amount: Amount = Amount(0.0)
+ tax_inclusive_amount: Amount = Amount(0.0)
+ charge_total_amount: Amount = Amount(0.0)
+ allowance_total_amount: Amount = Amount(0.0)
+ payable_amount: Amount = Amount(0.0)
+ prepaid_amount: Amount = Amount(0.0)
def calculate(self):
- # DIAN 1.7.-2020: FAU14
+ #DIAN 1.7.-2020: FAU14
self.payable_amount = \
self.tax_inclusive_amount \
+ self.allowance_total_amount \
@@ -624,29 +528,22 @@ class LegalMonetaryTotal:
- self.prepaid_amount
+
class NationalSalesInvoiceDocumentType(str):
def __str__(self):
# 6.1.3
return '01'
-
class CreditNoteDocumentType(str):
def __str__(self):
# 6.1.3
return '91'
-
class DebitNoteDocumentType(str):
def __str__(self):
# 6.1.3
return '92'
-
-class CreditNoteSupportDocumentType(str):
- def __str__(self):
- return '95'
-
-
class Invoice:
def __init__(self, type_code: str):
if str(type_code) not in codelist.TipoDocumento:
@@ -666,7 +563,6 @@ class Invoice:
self.invoice_allowance_charge = []
self.invoice_prepaid_payment = []
self.invoice_billing_reference = None
- self.invoice_discrepancy_response = None
self.invoice_type_code = str(type_code)
self.invoice_ident_prefix = None
@@ -692,8 +588,7 @@ class Invoice:
if len(prefix) <= 4:
self.invoice_ident_prefix = prefix
else:
- raise ValueError(
- 'ident prefix failed to get, expected 0 to 4 chars')
+ raise ValueError('ident prefix failed to get, expected 0 to 4 chars')
def set_ident(self, ident: str):
"""
@@ -724,7 +619,7 @@ class Invoice:
def _get_codelist_tipo_operacion(self):
return codelist.TipoOperacionF
-
+
def set_operation_type(self, operation):
if operation not in self._get_codelist_tipo_operacion():
raise ValueError("operation not found")
@@ -743,9 +638,6 @@ class Invoice:
def set_billing_reference(self, billing_reference: BillingReference):
self.invoice_billing_reference = billing_reference
- def set_discrepancy_response(self, billing_response: BillingResponse):
- self.invoice_discrepancy_response = billing_response
-
def accept(self, visitor):
visitor.visit_payment_mean(self.invoice_payment_mean)
visitor.visit_customer(self.invoice_customer)
@@ -757,34 +649,29 @@ class Invoice:
def _calculate_legal_monetary_total(self):
for invline in self.invoice_lines:
- self.invoice_legal_monetary_total.line_extension_amount +=\
- invline.total_amount
- self.invoice_legal_monetary_total.tax_exclusive_amount +=\
- invline.total_tax_exclusive_amount
- # DIAN 1.7.-2020: FAU6
- self.invoice_legal_monetary_total.tax_inclusive_amount +=\
- invline.total_tax_inclusive_amount
+ self.invoice_legal_monetary_total.line_extension_amount += invline.total_amount
+ self.invoice_legal_monetary_total.tax_exclusive_amount += invline.total_tax_exclusive_amount
+ #DIAN 1.7.-2020: FAU6
+ self.invoice_legal_monetary_total.tax_inclusive_amount += invline.total_tax_inclusive_amount
- # DIAN 1.7.-2020: FAU08
- self.invoice_legal_monetary_total.allowance_total_amount =\
- AmountCollection(self.invoice_allowance_charge)\
+ #DIAN 1.7.-2020: FAU08
+ self.invoice_legal_monetary_total.allowance_total_amount = AmountCollection(self.invoice_allowance_charge)\
.filter(lambda charge: charge.isDiscount())\
.map(lambda charge: charge.amount)\
.sum()
- # DIAN 1.7.-2020: FAU10
- self.invoice_legal_monetary_total.charge_total_amount =\
- AmountCollection(self.invoice_allowance_charge)\
+ #DIAN 1.7.-2020: FAU10
+ self.invoice_legal_monetary_total.charge_total_amount = AmountCollection(self.invoice_allowance_charge)\
.filter(lambda charge: charge.isCharge())\
.map(lambda charge: charge.amount)\
.sum()
- # DIAN 1.7.-2020: FAU12
- self.invoice_legal_monetary_total.prepaid_amount = AmountCollection(
- self.invoice_prepaid_payment).map(
- lambda paid: paid.paid_amount).sum()
+ #DIAN 1.7.-2020: FAU12
+ self.invoice_legal_monetary_total.prepaid_amount = AmountCollection(self.invoice_prepaid_payment)\
+ .map(lambda paid: paid.paid_amount)\
+ .sum()
- # DIAN 1.7.-2020: FAU14
+ #DIAN 1.7.-2020: FAU14
self.invoice_legal_monetary_total.calculate()
def _refresh_charges_base_amount(self):
@@ -792,21 +679,18 @@ class Invoice:
for invline in self.invoice_lines:
if invline.allowance_charge:
# TODO actualmente solo uno de los cargos es permitido
- raise ValueError(
- 'allowance charge in invoice exclude invoice line')
-
+ raise ValueError('allowance charge in invoice exclude invoice line')
+
# cargos a nivel de factura
for charge in self.invoice_allowance_charge:
- charge.set_base_amount(
- self.invoice_legal_monetary_total.line_extension_amount)
-
+ charge.set_base_amount(self.invoice_legal_monetary_total.line_extension_amount)
+
def calculate(self):
for invline in self.invoice_lines:
invline.calculate()
self._calculate_legal_monetary_total()
self._refresh_charges_base_amount()
-
class NationalSalesInvoice(Invoice):
def __init__(self):
super().__init__(NationalSalesInvoiceDocumentType())
@@ -822,7 +706,7 @@ class CreditNote(Invoice):
def _get_codelist_tipo_operacion(self):
return codelist.TipoOperacionNC
-
+
def _check_ident_prefix(self, prefix):
if len(prefix) != 6:
raise ValueError('prefix must be 6 length')
@@ -851,30 +735,3 @@ class DebitNote(Invoice):
if not self.invoice_ident_prefix:
self.invoice_ident_prefix = self.invoice_ident[0:6]
-
-class SupportDocument(Invoice):
- pass
-
-
-class SupportDocumentCreditNote(SupportDocument):
- def __init__(
- self, invoice_document_reference: BillingReference,
- invoice_discrepancy_response: BillingResponse):
- super().__init__(CreditNoteSupportDocumentType())
-
- if not isinstance(invoice_document_reference, BillingReference):
- raise TypeError('invoice_document_reference invalid type')
- self.invoice_billing_reference = invoice_document_reference
- self.invoice_discrepancy_response = invoice_discrepancy_response
-
- def _get_codelist_tipo_operacion(self):
- return codelist.TipoOperacionNCDS
-
- def _check_ident_prefix(self, prefix):
- if len(prefix) != 6:
- raise ValueError('prefix must be 6 length')
-
- def _set_ident_prefix_automatic(self):
- if not self.invoice_ident_prefix:
- self.invoice_ident_prefix = self.invoice_ident[0:6]
- pass
diff --git a/facho/fe/form_xml/__init__.py b/facho/fe/form_xml/__init__.py
index 4b302c0..b7530fa 100644
--- a/facho/fe/form_xml/__init__.py
+++ b/facho/fe/form_xml/__init__.py
@@ -2,7 +2,4 @@ from .invoice import *
from .credit_note import *
from .debit_note import *
from .utils import *
-from .support_document import *
-from .support_document_credit_note import *
from .attached_document import *
-from .application_response import *
diff --git a/facho/fe/form_xml/application_response.py b/facho/fe/form_xml/application_response.py
deleted file mode 100644
index 17fee92..0000000
--- a/facho/fe/form_xml/application_response.py
+++ /dev/null
@@ -1,177 +0,0 @@
-from .. import fe
-
-__all__ = ['ApplicationResponse']
-
-
-class ApplicationResponse:
-
- def __init__(self, invoice, tag_document='ApplicationResponse'):
- self.schema =\
- 'urn:oasis:names:specification:ubl:schema:xsd:ApplicationResponse-2'
- self.tag_document = tag_document
- self.invoice = invoice
- self.fexml = fe.FeXML(
- self.tag_document, self.schema)
- self.application_response = self.application_response()
-
- def application_response(self):
- # DIAN 1.9.-2023: AE02
- self.fexml.set_element(
- './cbc:UBLVersionID', 'UBL 2.1')
-
- # DIAN 1.9.-2023: AE03
- self.fexml.set_element(
- './cbc:CustomizationID', 'Documentos adjuntos')
-
- # DIAN 1.9.-2023: AE04
- self.fexml.set_element(
- './cbc:ProfileID', 'DIAN 2.1')
-
- # DIAN 1.9.-2023: AE04a
- self.fexml.set_element(
- './cbc:ProfileExecutionID', '1')
-
- self.fexml.set_element(
- './cbc:ID', '1')
-
- self.fexml.set_element(
- './cbc:UUID', '1', schemeName="CUDE-SHA384")
-
- self.fexml.set_element(
- './cbc:IssueDate',
- self.invoice.invoice_issue.strftime('%Y-%m-%d'))
-
- # DIAN 1.9.-2023: AE06
- self.fexml.set_element(
- './cbc:IssueTime', self.invoice.invoice_issue.strftime(
- '%H:%M:%S-05:00'))
-
- self.set_sender_party()
- self.set_receiver_party()
- self.set_document_response()
-
- def set_sender_party(self):
- # DIAN 1.9.-2023: AE09
- self.fexml.placeholder_for(
- './cac:SenderParty')
- # DIAN 1.9.-2023: AE10
- self.fexml.placeholder_for(
- './cac:SenderParty/cac:PartyTaxScheme')
- # DIAN 1.9.-2023: AE11
- self.fexml.set_element(
- './cac:SenderParty/cac:PartyTaxScheme/cbc:RegistrationName',
- self.invoice.invoice_supplier.name)
- # DIAN 1.9.-2023: AE12
- # DIAN 1.9.-2023: AE13
- # DIAN 1.9.-2023: AE14
- # DIAN 1.9.-2023: AE15
- self.fexml.set_element(
- './cac:SenderParty/cac:PartyTaxScheme/cbc:CompanyID',
- self.invoice.invoice_supplier.ident,
- schemeAgencyID='195',
- schemeID=self.invoice.invoice_supplier.ident.dv,
- schemeName=self.invoice.invoice_supplier.ident.type_fiscal)
-
- # DIAN 1.9.-2023: AE16
- self.fexml.set_element(
- './cac:SenderParty/cac:PartyTaxScheme/cbc:TaxLevelCode',
- self.invoice.invoice_supplier.responsability_code)
-
- # DIAN 1.9.-2023: AE18
- self.fexml.placeholder_for(
- './cac:SenderParty/cac:PartyTaxScheme/cac:TaxScheme')
-
- # DIAN 1.9.-2023: AE19
- self.fexml.set_element(
- './cac:SenderParty/cac:PartyTaxScheme/cac:TaxScheme/cbc:ID',
- self.invoice.invoice_supplier.tax_scheme.code)
-
- # DIAN 1.9.-2023: AE20
- self.fexml.set_element(
- './cac:SenderParty/cac:PartyTaxScheme/cac:TaxScheme/cbc:Name',
- self.invoice.invoice_supplier.tax_scheme.name)
-
- def set_receiver_party(self):
- # DIAN 1.9.-2023: AE21
- self.fexml.placeholder_for(
- './cac:ReceiverParty')
- # DIAN 1.9.-2023: AE22
- self.fexml.placeholder_for(
- './cac:ReceiverParty/cac:PartyTaxScheme')
- # DIAN 1.9.-2023: AE23
- self.fexml.set_element(
- './cac:ReceiverParty/cac:PartyTaxScheme/cbc:RegistrationName',
- self.invoice.invoice_customer.name)
- # DIAN 1.9.-2023: AE24
- # DIAN 1.9.-2023: AE25
- # DIAN 1.9.-2023: AE26
- # DIAN 1.9.-2023: AE27
- self.fexml.set_element(
- './cac:ReceiverParty/cac:PartyTaxScheme/cbc:CompanyID',
- self.invoice.invoice_customer.ident,
- schemeAgencyID='195',
- schemeID=self.invoice.invoice_customer.ident.dv,
- schemeName=self.invoice.invoice_customer.ident.type_fiscal)
- # DIAN 1.9.-2023: AE28
- self.fexml.set_element(
- './cac:ReceiverParty/cac:PartyTaxScheme/cbc:TaxLevelCode',
- self.invoice.invoice_customer.responsability_code)
- # DIAN 1.9.-2023: AE30
- self.fexml.placeholder_for(
- './cac:ReceiverParty/cac:PartyTaxScheme/cac:TaxScheme')
- # DIAN 1.9.-2023: AE31
- self.fexml.set_element(
- './cac:ReceiverParty/cac:PartyTaxScheme/cac:TaxScheme/cbc:ID',
- self.invoice.invoice_customer.tax_scheme.code)
- # DIAN 1.9.-2023: AE32
- self.fexml.set_element(
- './cac:ReceiverParty/cac:PartyTaxScheme/cac:TaxScheme/cbc:Name',
- self.invoice.invoice_customer.tax_scheme.name)
-
- def set_document_response(self):
- self.fexml.placeholder_for(
- './cac:DocumentResponse')
- self.fexml.placeholder_for(
- './cac:DocumentResponse/cac:Response')
- self.fexml.set_element(
- './cac:DocumentResponse/cac:Response/cbc:ResponseCode',
- '02')
- self.fexml.set_element(
- './cac:DocumentResponse/cac:Response/cbc:Description',
- 'Documento validado por la DIAN')
- self.set_documnent_reference()
-
- def set_documnent_reference(self):
- self.fexml.placeholder_for(
- './cac:DocumentResponse/cac:DocumentReference')
- self.fexml.set_element(
- './cac:DocumentResponse/cac:DocumentReference/cbc:ID',
- 'FESS19566058')
- self.fexml.set_element(
- './cac:DocumentResponse/cac:DocumentReference/cbc:UUID',
- 'f51ee529aabd19d10e39444f2f593b94d56d5885fbf433faf718d53a7e968f64bf54a6ee43c6a2df842771b54a6aae1a',
- schemeName="CUFE-SHA384")
- self.set_response_lines()
-
- def set_response_lines(self):
- lines = [{
- 'LineID': '1',
- 'ResponseCode': '0000',
- 'Description': '0',
- }]
-
- for line in lines:
- self.fexml.set_element(
- './cac:DocumentResponse/cac:LineResponse/cac:LineReference/cbc:LineID', line[
- 'LineID'])
- self.fexml.set_element(
- './cac:DocumentResponse/cac:LineResponse/cac:Response/cbc:ResponseCode', line[
- 'ResponseCode'])
- self.fexml.set_element(
- './cac:DocumentResponse/cac:LineResponse/cac:Response/cbc:Description', line[
- 'Description'])
-
-
-
- def toFachoXML(self):
- return self.fexml
diff --git a/facho/fe/form_xml/attached_document.py b/facho/fe/form_xml/attached_document.py
index a26ebe3..ae3df9b 100644
--- a/facho/fe/form_xml/attached_document.py
+++ b/facho/fe/form_xml/attached_document.py
@@ -1,227 +1,14 @@
from .. import fe
-from .application_response import ApplicationResponse
__all__ = ['AttachedDocument']
-
class AttachedDocument():
- def __init__(self, invoice, DIANInvoiceXML, id):
- self.schema =\
- 'urn:oasis:names:specification:ubl:schema:xsd:AttachedDocument-2'
- self.id = id
- self.invoice = invoice
- self.DIANInvoiceXML = DIANInvoiceXML
- self.attached_document_invoice = self.attached_document_invoice()
-
- def attached_document_invoice(self):
- self.fexml = fe.FeXML(
- 'AttachedDocument', self.schema)
-
- # DIAN 1.9.-2023: AE02
- self.fexml.set_element(
- './cbc:UBLVersionID', 'UBL 2.1')
-
- # DIAN 1.9.-2023: AE03
- self.fexml.set_element(
- './cbc:CustomizationID', 'Documentos adjuntos')
-
- # DIAN 1.9.-2023: AE04
- self.fexml.set_element(
- './cbc:ProfileID', 'Factura Electrónica de Venta')
-
- # DIAN 1.9.-2023: AE04a
- self.fexml.set_element(
- './cbc:ProfileExecutionID', '1')
-
- # DIAN 1.9.-2023: AE04b
- self.fexml.set_element(
- './cbc:ID', self.id)
-
- # DIAN 1.9.-2023: AE05
- self.fexml.set_element(
- './cbc:IssueDate',
- self.invoice.invoice_issue.strftime('%Y-%m-%d'))
-
- # DIAN 1.9.-2023: AE06
- self.fexml.set_element(
- './cbc:IssueTime', self.invoice.invoice_issue.strftime(
- '%H:%M:%S-05:00'))
-
- # DIAN 1.9.-2023: AE08
- self.fexml.set_element(
- './cbc:DocumentType', 'Contenedor de Factura Electrónica')
-
- # DIAN 1.9.-2023: AE08a
- self.fexml.set_element(
- './cbc:ParentDocumentID', self.invoice.invoice_ident)
-
- # DIAN 1.9.-2023: AE09
- self.set_sender_party()
-
- # DIAN 1.9.-2023: AE20
- self.set_receiver_party()
- # DIAN 1.9.-2023: AE33
- self.set_attachment()
- self.set_parent_document_line_reference()
-
- def set_sender_party(self):
- # DIAN 1.9.-2023: AE09
- self.fexml.placeholder_for(
- './cac:SenderParty')
- # DIAN 1.9.-2023: AE10
- self.fexml.placeholder_for(
- './cac:SenderParty/cac:PartyTaxScheme')
- # DIAN 1.9.-2023: AE11
- self.fexml.set_element(
- './cac:SenderParty/cac:PartyTaxScheme/cbc:RegistrationName',
- self.invoice.invoice_supplier.name)
- # DIAN 1.9.-2023: AE12
- # DIAN 1.9.-2023: AE13
- # DIAN 1.9.-2023: AE14
- # DIAN 1.9.-2023: AE15
- self.fexml.set_element(
- './cac:SenderParty/cac:PartyTaxScheme/cbc:CompanyID',
- self.invoice.invoice_supplier.ident,
- schemeAgencyID='195',
- schemeID=self.invoice.invoice_supplier.ident.dv,
- schemeName=self.invoice.invoice_supplier.ident.type_fiscal)
-
- # DIAN 1.9.-2023: AE16
- self.fexml.set_element(
- './cac:SenderParty/cac:PartyTaxScheme/cbc:TaxLevelCode',
- self.invoice.invoice_supplier.responsability_code)
-
- # DIAN 1.9.-2023: AE18
- self.fexml.placeholder_for(
- './cac:SenderParty/cac:PartyTaxScheme/cac:TaxScheme')
-
- # DIAN 1.9.-2023: AE19
- self.fexml.set_element(
- './cac:SenderParty/cac:PartyTaxScheme/cac:TaxScheme/cbc:ID',
- self.invoice.invoice_supplier.tax_scheme.code)
-
- # DIAN 1.9.-2023: AE20
- self.fexml.set_element(
- './cac:SenderParty/cac:PartyTaxScheme/cac:TaxScheme/cbc:Name',
- self.invoice.invoice_supplier.tax_scheme.name)
-
- def set_receiver_party(self):
- # DIAN 1.9.-2023: AE21
- self.fexml.placeholder_for(
- './cac:ReceiverParty')
- # DIAN 1.9.-2023: AE22
- self.fexml.placeholder_for(
- './cac:ReceiverParty/cac:PartyTaxScheme')
- # DIAN 1.9.-2023: AE23
- self.fexml.set_element(
- './cac:ReceiverParty/cac:PartyTaxScheme/cbc:RegistrationName',
- self.invoice.invoice_customer.name)
- # DIAN 1.9.-2023: AE24
- # DIAN 1.9.-2023: AE25
- # DIAN 1.9.-2023: AE26
- # DIAN 1.9.-2023: AE27
- self.fexml.set_element(
- './cac:ReceiverParty/cac:PartyTaxScheme/cbc:CompanyID',
- self.invoice.invoice_customer.ident,
- schemeAgencyID='195',
- schemeID=self.invoice.invoice_customer.ident.dv,
- schemeName=self.invoice.invoice_customer.ident.type_fiscal)
- # DIAN 1.9.-2023: AE28
- self.fexml.set_element(
- './cac:ReceiverParty/cac:PartyTaxScheme/cbc:TaxLevelCode',
- self.invoice.invoice_customer.responsability_code)
- # DIAN 1.9.-2023: AE30
- self.fexml.placeholder_for(
- './cac:ReceiverParty/cac:PartyTaxScheme/cac:TaxScheme')
- # DIAN 1.9.-2023: AE31
- self.fexml.set_element(
- './cac:ReceiverParty/cac:PartyTaxScheme/cac:TaxScheme/cbc:ID',
- self.invoice.invoice_customer.tax_scheme.code)
- # DIAN 1.9.-2023: AE32
- self.fexml.set_element(
- './cac:ReceiverParty/cac:PartyTaxScheme/cac:TaxScheme/cbc:Name',
- self.invoice.invoice_customer.tax_scheme.name)
-
- def set_attachment(self):
- # DIAN 1.9.-2023: AE33
- self.fexml.placeholder_for(
- './cac:Attachment')
- # DIAN 1.9.-2023: AE34
- self.fexml.placeholder_for(
- './cac:Attachment/cac:ExternalReference')
- # DIAN 1.9.-2023: AE35
- self.fexml.set_element(
- './cac:Attachment/cac:ExternalReference/cbc:MimeCode',
- 'text/xml')
- # DIAN 1.9.-2023: AE36
- self.fexml.set_element(
- './cac:Attachment/cac:ExternalReference/cbc:EncodingCode',
- 'UTF-8')
- # DIAN 1.9.-2023: AE37
- self.fexml.set_element(
- './cac:Attachment/cac:ExternalReference/cbc:Description',
- self._build_attachment(self.DIANInvoiceXML)
- )
-
- def set_parent_document_line_reference(self):
- self.fexml.placeholder_for(
- './cac:ParentDocumentLineReference')
- self.fexml.set_element(
- './cac:ParentDocumentLineReference/cbc:LineID', 1)
- self.fexml.placeholder_for(
- './cac:ParentDocumentLineReference/cac:DocumentReference')
- self.fexml.set_element(
- './cac:ParentDocumentLineReference/cac:DocumentReference/cbc:ID',
- '1234')
- self.fexml.set_element(
- './cac:ParentDocumentLineReference/cac:DocumentReference/cbc:UUID',
- '1234',
- schemeName="CUFE-SHA384")
- self.fexml.set_element(
- './cac:ParentDocumentLineReference/cac:DocumentReference/cbc:IssueDate',
- '2024-11-28')
- self.fexml.set_element(
- './cac:ParentDocumentLineReference/cac:DocumentReference/cbc:DocumentType',
- 'ApplicationResponse')
- self.fexml.placeholder_for(
- './cac:ParentDocumentLineReference/cac:DocumentReference/cac:Attachment')
- self.fexml.set_element(
- './cac:ParentDocumentLineReference/cac:DocumentReference/cac:Attachment/cac:ExternalReference/cbc:MimeCode',
- 'text/xml')
- self.fexml.set_element(
- './cac:ParentDocumentLineReference/cac:DocumentReference/cac:Attachment/cac:ExternalReference/cbc:EncodingCode',
- 'UTF-8')
-
- application_response = ApplicationResponse(
- self.invoice).toFachoXML()
-
- self.fexml.set_element(
- './cac:ParentDocumentLineReference/cac:DocumentReference/cac:Attachment/cac:ExternalReference/cbc:Description',
- self._build_attachment(application_response))
- self.fexml.placeholder_for(
- './cac:ParentDocumentLineReference/cac:DocumentReference/cac:ResultOfVerification')
- self.fexml.set_element(
- './cac:ParentDocumentLineReference/cac:DocumentReference/cac:ResultOfVerification/cbc:ValidatorID',
- 'Unidad Especial Dirección de Impuestos y Aduanas Nacionales')
- self.fexml.set_element(
- './cac:ParentDocumentLineReference/cac:DocumentReference/cac:ResultOfVerification/cbc:ValidationResultCode',
- '02')
- self.fexml.set_element(
- './cac:ParentDocumentLineReference/cac:DocumentReference/cac:ResultOfVerification/cbc:ValidationDate',
- '2024-11-28')
- self.fexml.set_element(
- './cac:ParentDocumentLineReference/cac:DocumentReference/cac:ResultOfVerification/cbc:ValidationTime',
- '10:35:11-05:00')
-
- def _build_attachment(self, DIANInvoiceXML):
- document = (
- ''
- ) + DIANInvoiceXML.tostring()
- attachment = "".format(
- document)
-
- return attachment
+ def __init__(self, id):
+ schema = 'urn:oasis:names:specification:ubl:schema:xsd:AttachedDocument-2'
+ self.fexml = fe.FeXML('AttachedDocument', schema)
+ self.fexml.set_element('./cbc:ID', id)
def toFachoXML(self):
return self.fexml
+
diff --git a/facho/fe/form_xml/credit_note.py b/facho/fe/form_xml/credit_note.py
index c6ad5bb..89820c0 100644
--- a/facho/fe/form_xml/credit_note.py
+++ b/facho/fe/form_xml/credit_note.py
@@ -1,10 +1,9 @@
-# from .. import fe
-# from ..form import *
+from .. import fe
+from ..form import *
from .invoice import DIANInvoiceXML
__all__ = ['DIANCreditNoteXML']
-
class DIANCreditNoteXML(DIANInvoiceXML):
"""
DianInvoiceXML mapea objeto form.Invoice a XML segun
diff --git a/facho/fe/form_xml/debit_note.py b/facho/fe/form_xml/debit_note.py
index c589124..1ba42b6 100644
--- a/facho/fe/form_xml/debit_note.py
+++ b/facho/fe/form_xml/debit_note.py
@@ -1,10 +1,9 @@
-# from .. import fe
-# from ..form import *
+from .. import fe
+from ..form import *
from .invoice import DIANInvoiceXML
__all__ = ['DIANDebitNoteXML']
-
class DIANDebitNoteXML(DIANInvoiceXML):
"""
DianInvoiceXML mapea objeto form.Invoice a XML segun
@@ -20,24 +19,19 @@ class DIANDebitNoteXML(DIANInvoiceXML):
def tag_document_concilied(fexml):
return 'Debited'
- # DIAN 1.7.-2020: DAU03
+ #DIAN 1.7.-2020: DAU03
def set_legal_monetary(fexml, invoice):
- fexml.set_element_amount(
- './cac:RequestedMonetaryTotal/cbc:LineExtensionAmount',
- invoice.invoice_legal_monetary_total.line_extension_amount)
+ fexml.set_element_amount('./cac:RequestedMonetaryTotal/cbc:LineExtensionAmount',
+ invoice.invoice_legal_monetary_total.line_extension_amount)
- fexml.set_element_amount(
- './cac:RequestedMonetaryTotal/cbc:TaxExclusiveAmount',
- invoice.invoice_legal_monetary_total.tax_exclusive_amount)
+ fexml.set_element_amount('./cac:RequestedMonetaryTotal/cbc:TaxExclusiveAmount',
+ invoice.invoice_legal_monetary_total.tax_exclusive_amount)
- fexml.set_element_amount(
- './cac:RequestedMonetaryTotal/cbc:TaxInclusiveAmount',
- invoice.invoice_legal_monetary_total.tax_inclusive_amount)
+ fexml.set_element_amount('./cac:RequestedMonetaryTotal/cbc:TaxInclusiveAmount',
+ invoice.invoice_legal_monetary_total.tax_inclusive_amount)
- fexml.set_element_amount(
- './cac:RequestedMonetaryTotal/cbc:ChargeTotalAmount',
- invoice.invoice_legal_monetary_total.charge_total_amount)
+ fexml.set_element_amount('./cac:RequestedMonetaryTotal/cbc:ChargeTotalAmount',
+ invoice.invoice_legal_monetary_total.charge_total_amount)
- fexml.set_element_amount(
- './cac:RequestedMonetaryTotal/cbc:PayableAmount',
- invoice.invoice_legal_monetary_total.payable_amount)
+ fexml.set_element_amount('./cac:RequestedMonetaryTotal/cbc:PayableAmount',
+ invoice.invoice_legal_monetary_total.payable_amount)
diff --git a/facho/fe/form_xml/invoice.py b/facho/fe/form_xml/invoice.py
index cbf541c..42b277a 100644
--- a/facho/fe/form_xml/invoice.py
+++ b/facho/fe/form_xml/invoice.py
@@ -1,7 +1,5 @@
from .. import fe
from ..form import *
-from collections import defaultdict
-from .attached_document import AttachedDocument
__all__ = ['DIANInvoiceXML']
@@ -11,478 +9,355 @@ class DIANInvoiceXML(fe.FeXML):
lo indicado para la facturacion electronica.
"""
- def __init__(self, invoice, tag_document='Invoice'):
+ def __init__(self, invoice, tag_document = 'Invoice'):
super().__init__(tag_document, 'http://www.dian.gov.co/contratos/facturaelectronica/v1')
self.placeholder_for('./ext:UBLExtensions/ext:UBLExtension/ext:ExtensionContent/sts:DianExtensions/sts:InvoiceControl')
self.placeholder_for('./ext:UBLExtensions/ext:UBLExtension/ext:ExtensionContent/sts:DianExtensions/sts:InvoiceSource')
self.placeholder_for('./ext:UBLExtensions/ext:UBLExtension/ext:ExtensionContent/sts:DianExtensions/sts:SoftwareProvider')
self.placeholder_for('./ext:UBLExtensions/ext:UBLExtension/ext:ExtensionContent/sts:DianExtensions/sts:SoftwareSecurityCode')
self.placeholder_for('./ext:UBLExtensions/ext:UBLExtension/ext:ExtensionContent/sts:DianExtensions/sts:AuthorizationProvider/sts:AuthorizationProviderID')
-
+
# ZE02 se requiere existencia para firmar
ublextension = self.fragment('./ext:UBLExtensions/ext:UBLExtension', append=True)
extcontent = ublextension.find_or_create_element('/ext:UBLExtension/ext:ExtensionContent')
- self.attach_invoice = self.attach_invoice(invoice)
-
- # self.attach_document = self.attached_document_invoice(attach_invoice)
-
- def attach_invoice(fexml, invoice):
- """adiciona etiquetas a FEXML y retorna FEXML
- en caso de fallar validacion retorna None"""
-
- fexml.placeholder_for('./ext:UBLExtensions')
- fexml.set_element('./cbc:UBLVersionID', 'UBL 2.1')
- fexml.set_element(
- './cbc:CustomizationID', invoice.invoice_operation_type)
- fexml.placeholder_for('./cbc:ProfileID')
- fexml.placeholder_for('./cbc:ProfileExecutionID')
- fexml.set_element('./cbc:ID', invoice.invoice_ident)
- fexml.placeholder_for('./cbc:UUID')
- fexml.set_element('./cbc:IssueDate', invoice.invoice_issue.strftime('%Y-%m-%d'))
- # DIAN 1.7.-2020: FAD10
- fexml.set_element('./cbc:IssueTime', invoice.invoice_issue.strftime('%H:%M:%S-05:00'))
- fexml.set_element(
- './cbc:%sTypeCode' % (fexml.tag_document()),
- invoice.invoice_type_code,
- listAgencyID='195',
- listAgencyName='No matching global declaration available for the validation root',
- listURI='http://www.dian.gov.co')
- fexml.set_element('./cbc:DocumentCurrencyCode', 'COP')
- fexml.set_element('./cbc:LineCountNumeric', len(invoice.invoice_lines))
- if fexml.tag_document() == 'Invoice':
- fexml.set_element('./cac:%sPeriod/cbc:StartDate' % (
- fexml.tag_document()),
- invoice.invoice_period_start.strftime(
- '%Y-%m-%d'))
- fexml.set_element('./cac:%sPeriod/cbc:EndDate' % (
- fexml.tag_document()),
- invoice.invoice_period_end.strftime('%Y-%m-%d'))
- fexml.set_billing_reference(invoice)
- fexml.customize(invoice)
- fexml.set_supplier(invoice)
- fexml.set_customer(invoice)
- fexml.set_payment_mean(invoice)
- fexml.set_invoice_totals(invoice)
- fexml.set_legal_monetary(invoice)
- fexml.set_invoice_lines(invoice)
- fexml.set_allowance_charge(invoice)
-
- return fexml
-
- def attached_document_invoice(fexml, invoice):
- attach_invoice = fexml.attach_invoice(invoice)
- attached_document = AttachedDocument(
- invoice, '123', attach_invoice)
-
- return attached_document
+ self.attach_invoice(invoice)
def set_supplier(fexml, invoice):
fexml.placeholder_for('./cac:AccountingSupplierParty')
- # DIAN 1.7.-2020: CAJ02
- # DIAN 1.7.-2020: FAJ02
- fexml.set_element(
- './cac:AccountingSupplierParty/cbc:AdditionalAccountID',
- invoice.invoice_supplier.organization_code)
+ #DIAN 1.7.-2020: CAJ02
+ #DIAN 1.7.-2020: FAJ02
+ fexml.set_element('./cac:AccountingSupplierParty/cbc:AdditionalAccountID',
+ invoice.invoice_supplier.organization_code)
- # DIAN 1.7.-2020: CAJ06
- # DIAN 1.7.-2020: FAJ06
- fexml.set_element(
- './cac:AccountingSupplierParty/cac:Party/cac:PartyName/cbc:Name',
- invoice.invoice_supplier.name)
+ #DIAN 1.7.-2020: CAJ06
+ #DIAN 1.7.-2020: FAJ06
+ fexml.set_element('./cac:AccountingSupplierParty/cac:Party/cac:PartyName/cbc:Name',
+ invoice.invoice_supplier.name)
- # DIAN 1.7.-2020: CAJ07, CAJ08
- # DIAN 1.7.-2020: FAJ07
- fexml.placeholder_for(
- './cac:AccountingSupplierParty/cac:Party/cac:PhysicalLocation/cac:Address')
+ #DIAN 1.7.-2020: CAJ07, CAJ08
+ #DIAN 1.7.-2020: FAJ07
+ fexml.placeholder_for('./cac:AccountingSupplierParty/cac:Party/cac:PhysicalLocation/cac:Address')
- # DIAN 1.7.-2020: FAJ08
- # DIAN 1.7.-2020: CAJ09
- fexml.set_element(
- './cac:AccountingSupplierParty/cac:Party/cac:PhysicalLocation/cac:Address/cbc:ID',
- invoice.invoice_supplier.address.city.code)
- # DIAN 1.7.-2020: FAJ09
- # DIAN 1.7.-2020: CAJ10
- fexml.set_element(
- './cac:AccountingSupplierParty/cac:Party/cac:PhysicalLocation/cac:Address/cbc:CityName',
- invoice.invoice_supplier.address.city.name)
- # DIAN 1.7.-2020: FAJ11
- # DIAN 1.7.-2020: CAJ11
- fexml.set_element(
- './cac:AccountingSupplierParty/cac:Party/cac:PhysicalLocation/cac:Address/cbc:CountrySubentity',
- invoice.invoice_supplier.address.countrysubentity.name)
+ #DIAN 1.7.-2020: FAJ08
+ #DIAN 1.7.-2020: CAJ09
+ fexml.set_element('./cac:AccountingSupplierParty/cac:Party/cac:PhysicalLocation/cac:Address/cbc:ID',
+ invoice.invoice_supplier.address.city.code)
+ #DIAN 1.7.-2020: FAJ09
+ #DIAN 1.7.-2020: CAJ10
+ fexml.set_element('./cac:AccountingSupplierParty/cac:Party/cac:PhysicalLocation/cac:Address/cbc:CityName',
+ invoice.invoice_supplier.address.city.name)
+ #DIAN 1.7.-2020: FAJ11
+ #DIAN 1.7.-2020: CAJ11
+ fexml.set_element('./cac:AccountingSupplierParty/cac:Party/cac:PhysicalLocation/cac:Address/cbc:CountrySubentity',
+ invoice.invoice_supplier.address.countrysubentity.name)
- # DIAN 1.7.-2020: FAJ12
- # DIAN 1.7.-2020: CAJ12
- fexml.set_element(
- './cac:AccountingSupplierParty/cac:Party/cac:PhysicalLocation/cac:Address/cbc:CountrySubentityCode',
- invoice.invoice_supplier.address.countrysubentity.code)
- # DIAN 1.7.-2020: FAJ14
- # DIAN 1.7.-2020: CAJ13, CAJ14
- fexml.set_element(
- './cac:AccountingSupplierParty/cac:Party/cac:PhysicalLocation/cac:Address/cac:AddressLine/cbc:Line',
- invoice.invoice_supplier.address.street)
+ #DIAN 1.7.-2020: FAJ12
+ #DIAN 1.7.-2020: CAJ12
+ fexml.set_element('./cac:AccountingSupplierParty/cac:Party/cac:PhysicalLocation/cac:Address/cbc:CountrySubentityCode',
+ invoice.invoice_supplier.address.countrysubentity.code)
+ #DIAN 1.7.-2020: FAJ14
+ #DIAN 1.7.-2020: CAJ13, CAJ14
+ fexml.set_element('./cac:AccountingSupplierParty/cac:Party/cac:PhysicalLocation/cac:Address/cac:AddressLine/cbc:Line',
+ invoice.invoice_supplier.address.street)
- # DIAN 1.7.-2020: FAJ16
- # DIAN 1.7.-2020: CAJ16, CAJ16
- fexml.set_element(
- './cac:AccountingSupplierParty/cac:Party/cac:PhysicalLocation/cac:Address/cac:Country/cbc:IdentificationCode',
- invoice.invoice_supplier.address.country.code)
+ #DIAN 1.7.-2020: FAJ16
+ #DIAN 1.7.-2020: CAJ16, CAJ16
+ fexml.set_element('./cac:AccountingSupplierParty/cac:Party/cac:PhysicalLocation/cac:Address/cac:Country/cbc:IdentificationCode',
+ invoice.invoice_supplier.address.country.code)
- # DIAN 1.7.-2020: FAJ17
- # DIAN 1.7.-2020: CAJ17
- fexml.set_element(
- './cac:AccountingSupplierParty/cac:Party/cac:PhysicalLocation/cac:Address/cac:Country/cbc:Name',
- invoice.invoice_supplier.address.country.name,
- # DIAN 1.7.-2020: FAJ18
- languageID='es')
+ #DIAN 1.7.-2020: FAJ17
+ #DIAN 1.7.-2020: CAJ17
+ fexml.set_element('./cac:AccountingSupplierParty/cac:Party/cac:PhysicalLocation/cac:Address/cac:Country/cbc:Name',
+ invoice.invoice_supplier.address.country.name,
+ #DIAN 1.7.-2020: FAJ18
+ languageID = 'es')
supplier_company_id_attrs = fe.SCHEME_AGENCY_ATTRS.copy()
- supplier_company_id_attrs.update({
- 'schemeID': invoice.invoice_supplier.ident.dv,
- 'schemeName': invoice.invoice_supplier.ident.type_fiscal
- })
+ supplier_company_id_attrs.update({'schemeID': invoice.invoice_supplier.ident.dv,
+ 'schemeName': invoice.invoice_supplier.ident.type_fiscal})
- # DIAN 1.7.-2020: FAJ19
- # DIAN 1.7.-2020: CAJ19
- fexml.placeholder_for(
- './cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme')
+ #DIAN 1.7.-2020: FAJ19
+ #DIAN 1.7.-2020: CAJ19
+ fexml.placeholder_for('./cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme')
- # DIAN 1.7.-2020: FAJ20
- # DIAN 1.7.-2020: CAJ20
- fexml.set_element(
- './cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme/cbc:RegistrationName',
- invoice.invoice_supplier.legal_name)
+ #DIAN 1.7.-2020: FAJ20
+ #DIAN 1.7.-2020: CAJ20
+ fexml.set_element('./cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme/cbc:RegistrationName',
+ invoice.invoice_supplier.legal_name)
- # DIAN 1.7.-2020: FAJ21
- # DIAN 1.7.-2020: CAJ21
- fexml.set_element(
- './cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme/cbc:CompanyID',
- invoice.invoice_supplier.ident,
- # DIAN 1.7.-2020: FAJ22,FAJ23,FAJ24,FAJ25
- **supplier_company_id_attrs)
- fexml.set_element(
- './cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme/cbc:TaxLevelCode',
- # DIAN 1.7.-2020: FAJ26
- # DIAN 1.7.-2020: CAJ26
- invoice.invoice_supplier.responsability_code,
- # DIAN 1.7.-2020: FAJ27
- # DIAN 1.7.-2020: CAJ27
- listName=invoice.invoice_supplier.responsability_regime_code)
- # DIAN 1.7.-2020: FAJ28
- # DIAN 1.7.-2020: CAJ28
- fexml.placeholder_for(
- './cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme/cac:RegistrationAddress')
+ #DIAN 1.7.-2020: FAJ21
+ #DIAN 1.7.-2020: CAJ21
+ fexml.set_element('./cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme/cbc:CompanyID',
+ invoice.invoice_supplier.ident,
+ #DIAN 1.7.-2020: FAJ22,FAJ23,FAJ24,FAJ25
+ **supplier_company_id_attrs)
+ fexml.set_element('./cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme/cbc:TaxLevelCode',
+ #DIAN 1.7.-2020: FAJ26
+ #DIAN 1.7.-2020: CAJ26
+ invoice.invoice_supplier.responsability_code,
+ #DIAN 1.7.-2020: FAJ27
+ #DIAN 1.7.-2020: CAJ27
+ listName=invoice.invoice_supplier.responsability_regime_code)
+ #DIAN 1.7.-2020: FAJ28
+ #DIAN 1.7.-2020: CAJ28
+ fexml.placeholder_for('./cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme/cac:RegistrationAddress')
- # DIAN 1.7.-2020: FAJ29
- # DIAN 1.7.-2020: CAJ29
- fexml.set_element(
- './cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme/cac:RegistrationAddress/cbc:ID',
- invoice.invoice_supplier.address.city.code)
+ #DIAN 1.7.-2020: FAJ29
+ #DIAN 1.7.-2020: CAJ29
+ fexml.set_element('./cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme/cac:RegistrationAddress/cbc:ID',
+ invoice.invoice_supplier.address.city.code)
- # DIAN 1.7.-2020: FAJ30
- # DIAN 1.7.-2020: CAJ30
- fexml.set_element(
- './cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme/cac:RegistrationAddress/cbc:CityName', invoice.invoice_supplier.address.city.name)
+ #DIAN 1.7.-2020: FAJ30
+ #DIAN 1.7.-2020: CAJ30
+ fexml.set_element('./cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme/cac:RegistrationAddress/cbc:CityName', invoice.invoice_supplier.address.city.name)
- # DIAN 1.7.-2020: FAJ31
- # DIAN 1.7.-2020: CAJ31
- fexml.set_element(
- './cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme/cac:RegistrationAddress/cbc:CountrySubentity',
- invoice.invoice_supplier.address.countrysubentity.name)
+ #DIAN 1.7.-2020: FAJ31
+ #DIAN 1.7.-2020: CAJ31
+ fexml.set_element('./cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme/cac:RegistrationAddress/cbc:CountrySubentity',
+ invoice.invoice_supplier.address.countrysubentity.name)
- # DIAN 1.7.-2020: FAJ32
- # DIAN 1.7.-2020: CAJ32
- fexml.set_element(
- './cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme/cac:RegistrationAddress/cbc:CountrySubentityCode',
- invoice.invoice_supplier.address.countrysubentity.code)
+ #DIAN 1.7.-2020: FAJ32
+ #DIAN 1.7.-2020: CAJ32
+ fexml.set_element('./cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme/cac:RegistrationAddress/cbc:CountrySubentityCode',
+ invoice.invoice_supplier.address.countrysubentity.code)
- # DIAN 1.7.-2020: FAJ33,FAJ34
- # DIAN 1.7.-2020: CAJ33,CAJ34
- fexml.set_element(
- './cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme/cac:RegistrationAddress/cac:AddressLine/cbc:Line',
- invoice.invoice_supplier.address.street)
+ #DIAN 1.7.-2020: FAJ33,FAJ34
+ #DIAN 1.7.-2020: CAJ33,CAJ34
+ fexml.set_element('./cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme/cac:RegistrationAddress/cac:AddressLine/cbc:Line',
+ invoice.invoice_supplier.address.street)
- # DIAN 1.7.-2020: FAJ35,FAJ36
- # DIAN 1.7.-2020: CAJ35,CAJ36
+ #DIAN 1.7.-2020: FAJ35,FAJ36
+ #DIAN 1.7.-2020: CAJ35,CAJ36
fexml.set_element('./cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme/cac:RegistrationAddress/cac:Country/cbc:IdentificationCode',
invoice.invoice_supplier.address.country.code)
- # DIAN 1.7.-2020: FAJ37,FAJ38
- # DIAN 1.7.-2020: CAJ37,CAJ38
- fexml.set_element(
- './cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme/cac:RegistrationAddress/cac:Country/cbc:Name',
- invoice.invoice_supplier.address.country.name,
- languageID='es')
+ #DIAN 1.7.-2020: FAJ37,FAJ38
+ #DIAN 1.7.-2020: CAJ37,CAJ38
+ fexml.set_element('./cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme/cac:RegistrationAddress/cac:Country/cbc:Name',
+ invoice.invoice_supplier.address.country.name,
+ languageID='es')
- # DIAN 1.7.-2020: FAJ39
- # DIAN 1.7.-2020: CAJ39
- fexml.placeholder_for(
- './cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme/cac:TaxScheme')
+ #DIAN 1.7.-2020: FAJ39
+ #DIAN 1.7.-2020: CAJ39
+ fexml.placeholder_for('./cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme/cac:TaxScheme')
- # DIAN 1.7.-2020: CAJ40
- # DIAN 1.7.-2020: FAJ40
- fexml.set_element(
- './cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme/cac:TaxScheme/cbc:ID',
- invoice.invoice_customer.tax_scheme.code)
+ #DIAN 1.7.-2020: CAJ40
+ #DIAN 1.7.-2020: FAJ40
+ fexml.set_element('./cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme/cac:TaxScheme/cbc:ID',
+ invoice.invoice_customer.tax_scheme.code)
- # DIAN 1.7.-2020: CAJ41
- fexml.set_element(
- './cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme/cac:TaxScheme/cbc:Name',
- invoice.invoice_customer.tax_scheme.name)
- # DIAN 1.7.-2020: FAJ42
- # DIAN 1.7.-2020: CAJ42
- fexml.placeholder_for(
- './cac:AccountingSupplierParty/cac:Party/cac:PartyLegalEntity')
+ #DIAN 1.7.-2020: CAJ41
+ fexml.set_element('./cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme/cac:TaxScheme/cbc:Name',
+ invoice.invoice_customer.tax_scheme.name)
- # DIAN 1.7.-2020: FAJ43
- # DIAN 1.7.-2020: CAJ43
- fexml.set_element(
- './cac:AccountingSupplierParty/cac:Party/cac:PartyLegalEntity/cbc:RegistrationName',
- invoice.invoice_supplier.legal_name)
+ #DIAN 1.7.-2020: FAJ42
+ #DIAN 1.7.-2020: CAJ42
+ fexml.placeholder_for('./cac:AccountingSupplierParty/cac:Party/cac:PartyLegalEntity')
- # DIAN 1.7.-2020: FAJ44,FAJ45,FAJ46,FAJ47,FAJ48
- # DIAN 1.7.-2020: CAJ44,CAJ45,CAJ46,CAJ47,CAJ48
- fexml.set_element(
- './cac:AccountingSupplierParty/cac:Party/cac:PartyLegalEntity/cbc:CompanyID',
- invoice.invoice_supplier.ident,
- **supplier_company_id_attrs)
+ #DIAN 1.7.-2020: FAJ43
+ #DIAN 1.7.-2020: CAJ43
+ fexml.set_element('./cac:AccountingSupplierParty/cac:Party/cac:PartyLegalEntity/cbc:RegistrationName',
+ invoice.invoice_supplier.legal_name)
- # DIAN 1.7.-2020: FAJ49
- # DIAN 1.7.-2020: CAJ49
- fexml.placeholder_for(
- './cac:AccountingSupplierParty/cac:Party/cac:PartyLegalEntity/cac:CorporateRegistrationScheme')
+ #DIAN 1.7.-2020: FAJ44,FAJ45,FAJ46,FAJ47,FAJ48
+ #DIAN 1.7.-2020: CAJ44,CAJ45,CAJ46,CAJ47,CAJ48
+ fexml.set_element('./cac:AccountingSupplierParty/cac:Party/cac:PartyLegalEntity/cbc:CompanyID',
+ invoice.invoice_supplier.ident,
+ **supplier_company_id_attrs)
- # DIAN 1.7.-2020: FAJ50
- # DIAN 1.7.-2020: CAJ50
- fexml.set_element(
- './cac:AccountingSupplierParty/cac:Party/cac:PartyLegalEntity/cac:CorporateRegistrationScheme/cbc:ID',
- invoice.invoice_ident_prefix)
+ #DIAN 1.7.-2020: FAJ49
+ #DIAN 1.7.-2020: CAJ49
+ fexml.placeholder_for('./cac:AccountingSupplierParty/cac:Party/cac:PartyLegalEntity/cac:CorporateRegistrationScheme')
- # DIAN 1.7.-2020: CAJ67
- fexml.placeholder_for(
- './cac:AccountingSupplierParty/cac:Party/cac:Contact')
+ #DIAN 1.7.-2020: FAJ50
+ #DIAN 1.7.-2020: CAJ50
+ fexml.set_element('./cac:AccountingSupplierParty/cac:Party/cac:PartyLegalEntity/cac:CorporateRegistrationScheme/cbc:ID',
+ invoice.invoice_ident_prefix)
+
+ #DIAN 1.7.-2020: CAJ67
+ fexml.placeholder_for('./cac:AccountingSupplierParty/cac:Party/cac:Contact')
+
+ #DIAN 1.7.-2020: FAJ71
+ #DIAN 1.7.-2020: CAJ71
+ fexml.set_element('./cac:AccountingSupplierParty/cac:Party/cac:Contact/cbc:ElectronicMail',
+ invoice.invoice_supplier.email)
- # DIAN 1.7.-2020: FAJ71
- # DIAN 1.7.-2020: CAJ71
- fexml.set_element(
- './cac:AccountingSupplierParty/cac:Party/cac:Contact/cbc:ElectronicMail',
- invoice.invoice_supplier.email)
def set_customer(fexml, invoice):
fexml.placeholder_for('./cac:AccountingCustomerParty')
- fexml.set_element(
- './cac:AccountingCustomerParty/cbc:AdditionalAccountID',
- invoice.invoice_customer.organization_code)
- fexml.set_element(
- './cac:AccountingCustomerParty/cac:Party/cac:PartyIdentification/cbc:ID',
- invoice.invoice_customer.ident)
- fexml.set_element(
- './cac:AccountingCustomerParty/cac:Party/cac:PartyName/cbc:Name',
- invoice.invoice_customer.name)
+ fexml.set_element('./cac:AccountingCustomerParty/cbc:AdditionalAccountID',
+ invoice.invoice_customer.organization_code)
+ fexml.set_element('./cac:AccountingCustomerParty/cac:Party/cac:PartyIdentification/cbc:ID',
+ invoice.invoice_customer.ident)
+ fexml.set_element('./cac:AccountingCustomerParty/cac:Party/cac:PartyName/cbc:Name',
+ invoice.invoice_customer.name)
- fexml.placeholder_for(
- './cac:AccountingCustomerParty/cac:Party/cac:PhysicalLocation')
+ fexml.placeholder_for('./cac:AccountingCustomerParty/cac:Party/cac:PhysicalLocation')
customer_company_id_attrs = fe.SCHEME_AGENCY_ATTRS.copy()
- # DIAN 1.7.-2020: FAK25
- # DIAN 1.7.-2020: CAK25
- customer_company_id_attrs.update({
- 'schemeID': invoice.invoice_customer.ident.dv,
- 'schemeName': invoice.invoice_customer.ident.type_fiscal})
+ #DIAN 1.7.-2020: FAK25
+ #DIAN 1.7.-2020: CAK25
+ customer_company_id_attrs.update({'schemeID': invoice.invoice_customer.ident.dv,
+ 'schemeName': invoice.invoice_customer.ident.type_fiscal})
- # DIAN 1.7.-2020: FAK07
- # DIAN 1.7.-2020: CAK07
- fexml.placeholder_for(
- './cac:AccountingCustomerParty/cac:Party/cac:PhysicalLocation/cac:Address')
+ #DIAN 1.7.-2020: FAK07
+ #DIAN 1.7.-2020: CAK07
+ fexml.placeholder_for('./cac:AccountingCustomerParty/cac:Party/cac:PhysicalLocation/cac:Address')
- # DIAN 1.7.-2020: FAK08
- # DIAN 1.7.-2020: CAK08
- fexml.set_element(
- './cac:AccountingCustomerParty/cac:Party/cac:PhysicalLocation/cac:Address/cbc:ID',
- invoice.invoice_customer.address.city.code)
- # DIAN 1.7.-2020: FAK09
- # DIAN 1.7.-2020: CAK09
- fexml.set_element(
- './cac:AccountingCustomerParty/cac:Party/cac:PhysicalLocation/cac:Address/cbc:CityName', invoice.invoice_customer.address.city.name)
+ #DIAN 1.7.-2020: FAK08
+ #DIAN 1.7.-2020: CAK08
+ fexml.set_element('./cac:AccountingCustomerParty/cac:Party/cac:PhysicalLocation/cac:Address/cbc:ID',
+ invoice.invoice_customer.address.city.code)
+ #DIAN 1.7.-2020: FAK09
+ #DIAN 1.7.-2020: CAK09
+ fexml.set_element('./cac:AccountingCustomerParty/cac:Party/cac:PhysicalLocation/cac:Address/cbc:CityName', invoice.invoice_customer.address.city.name)
- # DIAN 1.7.-2020: FAK11
- # DIAN 1.7.-2020: CAK11
- fexml.set_element(
- './cac:AccountingCustomerParty/cac:Party/cac:PhysicalLocation/cac:Address/cbc:CountrySubentity',
- invoice.invoice_customer.address.countrysubentity.name)
+ #DIAN 1.7.-2020: FAK11
+ #DIAN 1.7.-2020: CAK11
+ fexml.set_element('./cac:AccountingCustomerParty/cac:Party/cac:PhysicalLocation/cac:Address/cbc:CountrySubentity',
+ invoice.invoice_customer.address.countrysubentity.name)
- # DIAN 1.7.-2020: FAK12
- # DIAN 1.7.-2020: CAK12
- fexml.set_element(
- './cac:AccountingCustomerParty/cac:Party/cac:PhysicalLocation/cac:Address/cbc:CountrySubentityCode',
- invoice.invoice_customer.address.countrysubentity.code)
+ #DIAN 1.7.-2020: FAK12
+ #DIAN 1.7.-2020: CAK12
+ fexml.set_element('./cac:AccountingCustomerParty/cac:Party/cac:PhysicalLocation/cac:Address/cbc:CountrySubentityCode',
+ invoice.invoice_customer.address.countrysubentity.code)
- # DIAN 1.7.-2020: CAK13, CAK14
- fexml.set_element(
- './cac:AccountingCustomerParty/cac:Party/cac:PhysicalLocation/cac:Address/cac:AddressLine/cbc:Line',
- invoice.invoice_customer.address.street)
+ #DIAN 1.7.-2020: CAK13, CAK14
+ fexml.set_element('./cac:AccountingCustomerParty/cac:Party/cac:PhysicalLocation/cac:Address/cac:AddressLine/cbc:Line',
+ invoice.invoice_customer.address.street)
- # DIAN 1.7.-2020: CAK16
- # DIAN 1.7.-2020: FAK16
- fexml.set_element(
- './cac:AccountingCustomerParty/cac:Party/cac:PhysicalLocation/cac:Address/cac:Country/cbc:IdentificationCode',
- invoice.invoice_customer.address.country.code)
+ #DIAN 1.7.-2020: CAK16
+ #DIAN 1.7.-2020: FAK16
+ fexml.set_element('./cac:AccountingCustomerParty/cac:Party/cac:PhysicalLocation/cac:Address/cac:Country/cbc:IdentificationCode',
+ invoice.invoice_customer.address.country.code)
- # DIAN 1.7.-2020: FAK17
- # DIAN 1.7.-2020: CAK17
- fexml.set_element(
- './cac:AccountingCustomerParty/cac:Party/cac:PhysicalLocation/cac:Address/cac:Country/cbc:Name',
- invoice.invoice_customer.address.country.name,
- # DIAN 1.7.-2020: FAK18
- # DIAN 1.7.-2020: CAK18
- languageID='es')
+ #DIAN 1.7.-2020: FAK17
+ #DIAN 1.7.-2020: CAK17
+ fexml.set_element('./cac:AccountingCustomerParty/cac:Party/cac:PhysicalLocation/cac:Address/cac:Country/cbc:Name',
+ invoice.invoice_customer.address.country.name,
+ #DIAN 1.7.-2020: FAK18
+ #DIAN 1.7.-2020: CAK18
+ languageID='es')
- # DIAN 1.7.-2020: FAK17,FAK19
- # DIAN 1.7.-2020: CAK19
- fexml.placeholder_for(
- './cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme')
+ #DIAN 1.7.-2020: FAK17,FAK19
+ #DIAN 1.7.-2020: CAK19
+ fexml.placeholder_for('./cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme')
- # DIAN 1.7.-2020: FAK17,FAK20
- # DIAN 1.7.-2020: CAK20
- fexml.set_element(
- './cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cbc:RegistrationName',
- invoice.invoice_customer.legal_name)
+ #DIAN 1.7.-2020: FAK17,FAK20
+ #DIAN 1.7.-2020: CAK20
+ fexml.set_element('./cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cbc:RegistrationName',
+ invoice.invoice_customer.legal_name)
- # DIAN 1.7.-2020: CAK21
- # DIAN 1.7.-2020: FAK21
- fexml.set_element(
- './cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cbc:CompanyID',
- invoice.invoice_customer.ident,
- # DIAN 1.7.-2020: CAK22, CAK23, CAK24, CAK25
- **customer_company_id_attrs)
+ #DIAN 1.7.-2020: CAK21
+ #DIAN 1.7.-2020: FAK21
+ fexml.set_element('./cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cbc:CompanyID',
+ invoice.invoice_customer.ident,
+ #DIAN 1.7.-2020: CAK22, CAK23, CAK24, CAK25
+ **customer_company_id_attrs)
- # DIAN 1.7.-2020: CAK26
- fexml.set_element(
- './cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cbc:TaxLevelCode',
- # DIAN 1.7.-2020: FAK26
- invoice.invoice_customer.responsability_code,
- # DIAN 1.7.-2020: FAK27
- # DIAN 1.7.-2020: CAK27
- listName=invoice.invoice_customer.responsability_regime_code)
+ #DIAN 1.7.-2020: CAK26
+ fexml.set_element('./cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cbc:TaxLevelCode',
+ #DIAN 1.7.-2020: FAK26
+ invoice.invoice_customer.responsability_code,
+ #DIAN 1.7.-2020: FAK27
+ #DIAN 1.7.-2020: CAK27
+ listName=invoice.invoice_customer.responsability_regime_code)
- # DIAN 1.7.-2020: FAK28
- # DIAN 1.7.-2020: CAK28
- fexml.placeholder_for(
- './cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cac:RegistrationAddress')
+ #DIAN 1.7.-2020: FAK28
+ #DIAN 1.7.-2020: CAK28
+ fexml.placeholder_for('./cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cac:RegistrationAddress')
- # DIAN 1.7.-2020: FAK29
- # DIAN 1.7.-2020: CAK29
- fexml.set_element(
- './cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cac:RegistrationAddress/cbc:ID',
- invoice.invoice_customer.address.city.code)
+ #DIAN 1.7.-2020: FAK29
+ #DIAN 1.7.-2020: CAK29
+ fexml.set_element('./cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cac:RegistrationAddress/cbc:ID',
+ invoice.invoice_customer.address.city.code)
- # DIAN 1.7.-2020: FAK30
- # DIAN 1.7.-2020: CAK30
- fexml.set_element(
- './cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cac:RegistrationAddress/cbc:CityName',
- invoice.invoice_customer.address.city.name)
+ #DIAN 1.7.-2020: FAK30
+ #DIAN 1.7.-2020: CAK30
+ fexml.set_element('./cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cac:RegistrationAddress/cbc:CityName',
+ invoice.invoice_customer.address.city.name)
- # DIAN 1.7.-2020: FAK31
- # DIAN 1.7.-2020: CAK31
- fexml.set_element(
- './cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cac:RegistrationAddress/cbc:CountrySubentity',
- invoice.invoice_customer.address.countrysubentity.name)
+ #DIAN 1.7.-2020: FAK31
+ #DIAN 1.7.-2020: CAK31
+ fexml.set_element('./cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cac:RegistrationAddress/cbc:CountrySubentity',
+ invoice.invoice_customer.address.countrysubentity.name)
- # DIAN 1.7.-2020: FAK32
- # DIAN 1.7.-2020: CAK32
- fexml.set_element(
- './cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cac:RegistrationAddress/cbc:CountrySubentityCode',
- invoice.invoice_customer.address.countrysubentity.code)
+ #DIAN 1.7.-2020: FAK32
+ #DIAN 1.7.-2020: CAK32
+ fexml.set_element('./cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cac:RegistrationAddress/cbc:CountrySubentityCode',
+ invoice.invoice_customer.address.countrysubentity.code)
- # DIAN 1.7.-2020: FAK33
- # DIAN 1.7.-2020: CAK33
- fexml.placeholder_for(
- './cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cac:RegistrationAddress/cac:AddressLine')
+ #DIAN 1.7.-2020: FAK33
+ #DIAN 1.7.-2020: CAK33
+ fexml.placeholder_for('./cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cac:RegistrationAddress/cac:AddressLine')
- # DIAN 1.7.-2020: FAK34
- # DIAN 1.7.-2020: CAK34
- fexml.set_element(
- './cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cac:RegistrationAddress/cac:AddressLine/cbc:Line',
- invoice.invoice_customer.address.street)
+ #DIAN 1.7.-2020: FAK34
+ #DIAN 1.7.-2020: CAK34
+ fexml.set_element('./cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cac:RegistrationAddress/cac:AddressLine/cbc:Line',
+ invoice.invoice_customer.address.street)
- # DIAN 1.7.-2020: CAK35
- # DIAN 1.7.-2020: FAK35
- fexml.placeholder_for(
- './cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cac:RegistrationAddress/cac:Country')
+ #DIAN 1.7.-2020: CAK35
+ #DIAN 1.7.-2020: FAK35
+ fexml.placeholder_for('./cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cac:RegistrationAddress/cac:Country')
- # DIAN 1.7.-2020: CAK36
- # DIAN 1.7.-2020: FAK36
- fexml.set_element(
- './cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cac:RegistrationAddress/cac:Country/cbc:IdentificationCode',
- invoice.invoice_customer.address.country.code)
+ #DIAN 1.7.-2020: CAK36
+ #DIAN 1.7.-2020: FAK36
+ fexml.set_element('./cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cac:RegistrationAddress/cac:Country/cbc:IdentificationCode',
+ invoice.invoice_customer.address.country.code)
- # DIAN 1.7.-2020: CAK37
- # DIAN 1.7.-2020: FAK37
- fexml.set_element(
- './cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cac:RegistrationAddress/cac:Country/cbc:Name', invoice.invoice_customer.address.country.name)
+ #DIAN 1.7.-2020: CAK37
+ #DIAN 1.7.-2020: FAK37
+ fexml.set_element('./cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cac:RegistrationAddress/cac:Country/cbc:Name', invoice.invoice_customer.address.country.name)
- # DIAN 1.7.-2020: FAK38
- # DIAN 1.7.-2020: CAK38
- fexml.set_element(
- './cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cac:RegistrationAddress/cac:Country/cbc:IdentificationCode',
- invoice.invoice_customer.address.country.code,
- languageID='es')
+ #DIAN 1.7.-2020: FAK38
+ #DIAN 1.7.-2020: CAK38
+ fexml.set_element('./cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cac:RegistrationAddress/cac:Country/cbc:IdentificationCode',
+ invoice.invoice_customer.address.country.code,
+ languageID='es')
- # DIAN 1.7.-2020: CAK39
- # DIAN 1.7.-2020: FAK39
- fexml.placeholder_for(
- './cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cac:TaxScheme')
+ #DIAN 1.7.-2020: CAK39
+ #DIAN 1.7.-2020: FAK39
+ fexml.placeholder_for('./cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cac:TaxScheme')
- # DIAN 1.7.-2020: CAK40 Machete Construir Validación
- # DIAN 1.7.-2020: FAK40
- fexml.set_element(
- './cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cac:TaxScheme/cbc:ID',
- invoice.invoice_customer.tax_scheme.code)
+ #DIAN 1.7.-2020: CAK40 Machete Construir Validación
+ #DIAN 1.7.-2020: FAK40
+ fexml.set_element('./cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cac:TaxScheme/cbc:ID',
+ invoice.invoice_customer.tax_scheme.code)
- # DIAN 1.7.-2020: FAK41
- # DIAN 1.7.-2020: CAK41 Machete Construir Validación
- fexml.set_element(
- './cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cac:TaxScheme/cbc:Name',
- invoice.invoice_customer.tax_scheme.name)
+ #DIAN 1.7.-2020: FAK41
+ #DIAN 1.7.-2020: CAK41 Machete Construir Validación
+ fexml.set_element('./cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cac:TaxScheme/cbc:Name',
+ invoice.invoice_customer.tax_scheme.name)
+ #DIAN 1.7.-2020: FAK42
+ #DIAN 1.7.-2020: CAK42
+ fexml.placeholder_for('./cac:AccountingCustomerParty/cac:Party/cac:PartyLegalEntity')
- # DIAN 1.7.-2020: FAK42
- # DIAN 1.7.-2020: CAK42
- fexml.placeholder_for(
- './cac:AccountingCustomerParty/cac:Party/cac:PartyLegalEntity')
+ #DIAN 1.7.-2020: FAK43
+ #DIAN 1.7.-2020: CAK43
+ fexml.set_element('./cac:AccountingCustomerParty/cac:Party/cac:PartyLegalEntity/cbc:RegistrationName',
+ invoice.invoice_customer.legal_name)
- # DIAN 1.7.-2020: FAK43
- # DIAN 1.7.-2020: CAK43
- fexml.set_element(
- './cac:AccountingCustomerParty/cac:Party/cac:PartyLegalEntity/cbc:RegistrationName',
- invoice.invoice_customer.legal_name)
+ #DIAN 1.7.-2020: CAK44
+ #DIAN 1.7.-2020: FAK44,FAK45,FAK46,FAK47,FAK48
+ fexml.set_element('./cac:AccountingCustomerParty/cac:Party/cac:PartyLegalEntity/cbc:CompanyID',
+ invoice.invoice_customer.ident,
+ **customer_company_id_attrs)
- # DIAN 1.7.-2020: CAK44
- # DIAN 1.7.-2020: FAK44,FAK45,FAK46,FAK47,FAK48
- fexml.set_element(
- './cac:AccountingCustomerParty/cac:Party/cac:PartyLegalEntity/cbc:CompanyID',
- invoice.invoice_customer.ident,
- **customer_company_id_attrs)
+ fexml.placeholder_for('./cac:AccountingCustomerParty/cac:Party/cac:PartyLegalEntity')
- fexml.placeholder_for(
- './cac:AccountingCustomerParty/cac:Party/cac:PartyLegalEntity')
+ #DIAN 1.7.-2020: FAK55
+ #DIAN 1.7.-2020: CAK51, CAK55
+ fexml.set_element('./cac:AccountingCustomerParty/cac:Party/cac:Contact/cbc:ElectronicMail',
+ invoice.invoice_customer.email)
- # DIAN 1.7.-2020: FAK55
- # DIAN 1.7.-2020: CAK51, CAK55
- fexml.set_element(
- './cac:AccountingCustomerParty/cac:Party/cac:Contact/cbc:ElectronicMail',
- invoice.invoice_customer.email)
def set_payment_mean(fexml, invoice):
payment_mean = invoice.invoice_payment_mean
- fexml.set_element(
- './cac:PaymentMeans/cbc:ID', payment_mean.id)
- fexml.set_element(
- './cac:PaymentMeans/cbc:PaymentMeansCode', payment_mean.code)
- fexml.set_element(
- './cac:PaymentMeans/cbc:PaymentDueDate', payment_mean.due_at.strftime('%Y-%m-%d'))
- fexml.set_element(
- './cac:PaymentMeans/cbc:PaymentID', payment_mean.payment_id)
+ fexml.set_element('./cac:PaymentMeans/cbc:ID', payment_mean.id)
+ fexml.set_element('./cac:PaymentMeans/cbc:PaymentMeansCode', payment_mean.code)
+ fexml.set_element('./cac:PaymentMeans/cbc:PaymentDueDate', payment_mean.due_at.strftime('%Y-%m-%d'))
+ fexml.set_element('./cac:PaymentMeans/cbc:PaymentID', payment_mean.payment_id)
def set_element_amount_for(fexml, xml, xpath, amount):
if not isinstance(amount, Amount):
@@ -546,7 +421,6 @@ class DIANInvoiceXML(fe.FeXML):
def set_invoice_totals(fexml, invoice):
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)
#requeridos para CUFE
@@ -559,35 +433,23 @@ class DIANInvoiceXML(fe.FeXML):
#tax_amount_for['03']['taxable_amount'] += 0.0
total_tax_amount = Amount(0.0)
- total_withholding_amount = Amount(0.0)
for invoice_line in invoice.invoice_lines:
for subtotal in invoice_line.tax.subtotals:
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
- tax_amount_for[subtotal.scheme.code]['name'] = subtotal.scheme.name
# MACHETE ojo InvoiceLine.tax pasar a Invoice
percent_for[subtotal.scheme.code] = subtotal.percent
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):
fexml.placeholder_for('./cac:TaxTotal')
fexml.set_element_amount('./cac:TaxTotal/cbc:TaxAmount',
total_tax_amount)
+
for index, item in enumerate(tax_amount_for.items()):
cod_impuesto, amount_of = item
@@ -624,52 +486,8 @@ class DIANInvoiceXML(fe.FeXML):
line.set_element('/cac:TaxTotal/cac:TaxSubtotal/cac:TaxCategory/cac:TaxScheme/cbc:ID',
cod_impuesto)
line.set_element('/cac:TaxTotal/cac:TaxSubtotal/cac:TaxCategory/cac:TaxScheme/cbc:Name',
- amount_of['name'])
-
- 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')
-
+ 'IVA')
+
# abstract method
def tag_document(fexml):
return 'Invoice'
@@ -679,141 +497,115 @@ class DIANInvoiceXML(fe.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)
+ 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')
+ 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))
+ 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_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)
-
+ #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)
+ 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)
+ 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):
+ if not isinstance(invoice_line.tax, TaxTotalOmit):
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',
+ invoice_line.item.id,
+ schemeID=invoice_line.item.scheme_id,
+ schemeName=invoice_line.item.scheme_name,
+ schemeAgencyID=invoice_line.item.scheme_agency_id)
- line.set_element(
- './cac:Item/cac:StandardItemIdentification/cbc:ID',
- invoice_line.item.id,
- schemeID=invoice_line.item.scheme_id,
- schemeName=invoice_line.item.scheme_name,
- schemeAgencyID=invoice_line.item.scheme_agency_id)
-
- line.set_element(
- './cac:Price/cbc:PriceAmount',
- invoice_line.price.amount,
- currencyID=invoice_line.price.amount.currency.code)
-
- # DIAN 1.7.-2020: FBB04
- line.set_element(
- './cac:Price/cbc:BaseQuantity',
- invoice_line.price.quantity,
- unitCode=invoice_line.quantity.code)
+ line.set_element('./cac:Price/cbc:PriceAmount', invoice_line.price.amount, currencyID=invoice_line.price.amount.currency.code)
+ #DIAN 1.7.-2020: FBB04
+ line.set_element('./cac:Price/cbc:BaseQuantity',
+ invoice_line.price.quantity,
+ unitCode=invoice_line.quantity.code)
for idx, charge in enumerate(invoice_line.allowance_charge):
next_append_charge = idx > 0
- fexml.append_allowance_charge(
- line, index + 1, charge, append=next_append_charge)
-
+ fexml.append_allowance_charge(line, index + 1, charge, append=next_append_charge)
+
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)
+ fexml.append_allowance_charge(fexml, idx + 1, charge, append=next_append)
def append_allowance_charge(fexml, parent, idx, charge, append=False):
- line = parent.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)
- line.set_element(
- './cbc:MultiplierFactorNumeric', str(
- round(charge.multiplier_factor_numeric, 2)))
- fexml.set_element_amount_for(
- line, './cbc:Amount', charge.amount)
- fexml.set_element_amount_for(
- line, './cbc:BaseAmount', charge.base_amount)
+ line = parent.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)
+ line.set_element('./cbc:MultiplierFactorNumeric', str(round(charge.multiplier_factor_numeric, 2)))
+ fexml.set_element_amount_for(line, './cbc:Amount', charge.amount)
+ fexml.set_element_amount_for(line, './cbc:BaseAmount', charge.base_amount)
+
+ def attach_invoice(fexml, invoice):
+ """adiciona etiquetas a FEXML y retorna FEXML
+ en caso de fallar validacion retorna None"""
+
+ fexml.placeholder_for('./ext:UBLExtensions')
+ fexml.set_element('./cbc:UBLVersionID', 'UBL 2.1')
+ fexml.set_element('./cbc:CustomizationID', invoice.invoice_operation_type)
+ fexml.placeholder_for('./cbc:ProfileID')
+ fexml.placeholder_for('./cbc:ProfileExecutionID')
+ fexml.set_element('./cbc:ID', invoice.invoice_ident)
+ fexml.placeholder_for('./cbc:UUID')
+ fexml.set_element('./cbc:DocumentCurrencyCode', 'COP')
+ fexml.set_element('./cbc:IssueDate', invoice.invoice_issue.strftime('%Y-%m-%d'))
+ #DIAN 1.7.-2020: FAD10
+ fexml.set_element('./cbc:IssueTime', invoice.invoice_issue.strftime('%H:%M:%S-05:00'))
+ fexml.set_element('./cbc:%sTypeCode' % (fexml.tag_document()),
+ invoice.invoice_type_code,
+ listAgencyID='195',
+ listAgencyName='No matching global declaration available for the validation root',
+ listURI='http://www.dian.gov.co')
+ fexml.set_element('./cbc:LineCountNumeric', len(invoice.invoice_lines))
+ fexml.set_element('./cac:%sPeriod/cbc:StartDate' % (fexml.tag_document()),
+ invoice.invoice_period_start.strftime('%Y-%m-%d'))
+
+ fexml.set_element('./cac:%sPeriod/cbc:EndDate' % (fexml.tag_document()),
+ invoice.invoice_period_end.strftime('%Y-%m-%d'))
+
+ fexml.customize(invoice)
+
+ fexml.set_supplier(invoice)
+ fexml.set_customer(invoice)
+ fexml.set_legal_monetary(invoice)
+ fexml.set_invoice_totals(invoice)
+ fexml.set_invoice_lines(invoice)
+ fexml.set_payment_mean(invoice)
+ fexml.set_allowance_charge(invoice)
+ fexml.set_billing_reference(invoice)
+
+ return fexml
def customize(fexml, invoice):
"""adiciona etiquetas a FEXML y retorna FEXML
diff --git a/facho/fe/form_xml/support_document.py b/facho/fe/form_xml/support_document.py
deleted file mode 100644
index c425e22..0000000
--- a/facho/fe/form_xml/support_document.py
+++ /dev/null
@@ -1,647 +0,0 @@
-from .. import fe
-from ..form import (
- Amount, DebitNoteDocumentReference, CreditNoteDocumentReference,
- InvoiceDocumentReference, TaxTotalOmit, WithholdingTaxTotalOmit
-)
-
-from collections import defaultdict
-from datetime import datetime
-# from .attached_document import *
-
-__all__ = ['DIANSupportDocumentXML']
-
-
-class DIANSupportDocumentXML(fe.FeXML):
- """
- DianSupportDocumentXML mapea objeto form.Invoice a XML segun
- lo indicado para él Documento soporte en adquisiciones efectuadas con sujetos no obligados a expedir factura de venta o documento equivalente.
- """
-
- def __init__(self, invoice, tag_document='Invoice'):
- super().__init__(tag_document, 'http://www.dian.gov.co/contratos/facturaelectronica/v1')
-
- # DIAN 1.1.-2021: DSAB03
- # DIAN 1.1.-2021: NSAB03
- self.placeholder_for(
- './ext:UBLExtensions/ext:UBLExtension/ext:ExtensionContent/sts:DianExtensions/sts:InvoiceControl')
-
- # DIAN 1.1.-2021: DSAB13
- # DIAN 1.1.-2021: NSAB13
- self.placeholder_for(
- './ext:UBLExtensions/ext:UBLExtension/ext:ExtensionContent/sts:DianExtensions/sts:InvoiceSource')
-
- # DIAN 1.1.-2021: DSAB18
- # DIAN 1.1.-2021: NSAB18
- self.placeholder_for(
- './ext:UBLExtensions/ext:UBLExtension/ext:ExtensionContent/sts:DianExtensions/sts:SoftwareProvider')
-
- # DIAN 1.1.-2021: DSAB27
- # DIAN 1.1.-2021: NSAB27
- self.placeholder_for(
- './ext:UBLExtensions/ext:UBLExtension/ext:ExtensionContent/sts:DianExtensions/sts:SoftwareSecurityCode')
-
- # DIAN 1.1.-2021: DSAB30 DSAB31
- # DIAN 1.1.-2021: NSAB30 NSAB31
- self.placeholder_for(
- './ext:UBLExtensions/ext:UBLExtension/ext:ExtensionContent/sts:DianExtensions/sts:AuthorizationProvider/sts:AuthorizationProviderID')
-
- # ZE02 se requiere existencia para firmar
- # DIAN 1.1.-2021: DSAA02 DSAB01
- # DIAN 1.1.-2021: NSAA02 NSAB01
- ublextension = self.fragment(
- './ext:UBLExtensions/ext:UBLExtension', append=True)
- # DIAN 1.1.-2021: DSAB02
- # DIAN 1.1.-2021: NSAB02
- extcontent = ublextension.find_or_create_element(
- '/ext:UBLExtension/ext:ExtensionContent')
- self.attach_invoice(invoice)
-
- def set_supplier(fexml, invoice):
- # DIAN 1.1.-2021: DSAJ01
- # DIAN 1.1.-2021: NSAB01
- fexml.placeholder_for('./cac:AccountingSupplierParty')
-
- # DIAN 1.1.-2021: DSAJ02
- # DIAN 1.1.-2021: NSAJ02
- fexml.set_element(
- './cac:AccountingSupplierParty/cbc:AdditionalAccountID',
- invoice.invoice_supplier.organization_code)
-
- # DIAN 1.1.-2021: DSAJ07 DSAJ08
- # DIAN 1.1.-2021: NSAJ07 NSAJ08
- fexml.placeholder_for(
- './cac:AccountingSupplierParty/cac:Party/cac:PhysicalLocation/cac:Address')
-
- # DIAN 1.1.-2021: DSAJ09
- # DIAN 1.1.-2021: NSAJ09
- fexml.set_element(
- './cac:AccountingSupplierParty/cac:Party/cac:PhysicalLocation/cac:Address/cbc:ID',
- invoice.invoice_supplier.address.city.code)
-
- # DIAN 1.1.-2021: DSAJ10
- # DIAN 1.1.-2021: NSAJ10
- fexml.set_element(
- './cac:AccountingSupplierParty/cac:Party/cac:PhysicalLocation/cac:Address/cbc:CityName',
- invoice.invoice_supplier.address.city.name)
-
- # DIAN 1.1.-2021: DSAJ73
- # DIAN 1.1.-2021: NSAJ73
- fexml.set_element(
- './cac:AccountingSupplierParty/cac:Party/cac:PhysicalLocation/cac:Address/cbc:PostalZone',
- invoice.invoice_supplier.address.postalzone.code)
-
- # DIAN 1.1.-2021: DSAJ11
- # DIAN 1.1.-2021: NSAJ11
- fexml.set_element(
- './cac:AccountingSupplierParty/cac:Party/cac:PhysicalLocation/cac:Address/cbc:CountrySubentity',
- invoice.invoice_supplier.address.countrysubentity.name)
-
- # DIAN 1.1.-2021: DSAJ12
- # DIAN 1.1.-2021: NSAJ12
- fexml.set_element(
- './cac:AccountingSupplierParty/cac:Party/cac:PhysicalLocation/cac:Address/cbc:CountrySubentityCode',
- invoice.invoice_supplier.address.countrysubentity.code)
- # DIAN 1.1.-2021: NSAJ13 NSAJ14
- fexml.set_element(
- './cac:AccountingSupplierParty/cac:Party/cac:PhysicalLocation/cac:Address/cac:AddressLine/cbc:Line',
- invoice.invoice_supplier.address.street)
-
- # DIAN 1.1.-2021: DSAJ15 DSAJ16
- # DIAN 1.1.-2021: NSAJ15 NSAJ16
- fexml.set_element(
- './cac:AccountingSupplierParty/cac:Party/cac:PhysicalLocation/cac:Address/cac:Country/cbc:IdentificationCode',
- invoice.invoice_supplier.address.country.code)
-
- # DIAN 1.1.-2021: DSAJ17
- # DIAN 1.1.-2021: NSAJ17
- fexml.set_element('./cac:AccountingSupplierParty/cac:Party/cac:PhysicalLocation/cac:Address/cac:Country/cbc:Name',
- invoice.invoice_supplier.address.country.name,
- # DIAN 1.1.-2021: DSAJ18
- # # DIAN 1.1.-2021: NSAJ18
- languageID='es')
-
- supplier_company_id_attrs = fe.SCHEME_AGENCY_ATTRS.copy()
- supplier_company_id_attrs.update(
- {
- 'schemeID': invoice.invoice_supplier.ident.dv,
- 'schemeName': invoice.invoice_supplier.ident.type_fiscal})
-
- # DIAN 1.1.-2021: DSAJ19
- # DIAN 1.1.-2021: NSAJ19
- fexml.placeholder_for(
- './cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme')
-
- # DIAN 1.1.-2021: DSAJ20
- # DIAN 1.1.-2021: NSAJ20
- fexml.set_element(
- './cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme/cbc:RegistrationName',
- invoice.invoice_supplier.legal_name)
-
- # DIAN 1.1.-2021: DSAJ21
- # DIAN 1.1.-2021: NSAJ21
- fexml.set_element(
- './cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme/cbc:CompanyID',
- invoice.invoice_supplier.ident,
- # DIAN 1.1.-2021: DSAJ22 DSAJ23 DSAJ24 DSAJ25
- # DIAN 1.1.-2021: NSAJ22 NSAJ23 NSAJ24 NSAJ25
- **supplier_company_id_attrs)
-
- # DIAN 1.1.-2021: DSAJ26
- # DIAN 1.1.-2021: NSAJ26
- fexml.set_element(
- './cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme/cbc:TaxLevelCode',
- invoice.invoice_supplier.responsability_code,
- listName=invoice.invoice_supplier.responsability_regime_code)
-
- # DIAN 1.1.-2021: DSAJ39
- # DIAN 1.1.-2021: NSAJ39
- fexml.placeholder_for(
- './cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme/cac:TaxScheme')
-
- # DIAN 1.1.-2021: DSAJ40
- # DIAN 1.1.-2021: NSAJ40
- fexml.set_element(
- './cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme/cac:TaxScheme/cbc:ID',
- invoice.invoice_customer.tax_scheme.code)
-
- # DIAN 1.1.-2021: DSAJ41
- # DIAN 1.1.-2021: NSAJ41
- fexml.set_element(
- './cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme/cac:TaxScheme/cbc:Name',
- invoice.invoice_customer.tax_scheme.name)
-
- def set_customer(fexml, invoice):
- # DIAN 1.1.-2021: DSAK01
- # DIAN 1.1.-2021: NSAK01
- fexml.placeholder_for('./cac:AccountingCustomerParty')
-
- # DIAN 1.1.-2021: DSAK02
- # DIAN 1.1.-2021: NSAK02
- fexml.set_element(
- './cac:AccountingCustomerParty/cbc:AdditionalAccountID',
- invoice.invoice_customer.organization_code)
-
- # DIAN 1.1.-2021: DSAK03
- # DIAN 1.1.-2021: NSAK03
- fexml.placeholder_for('./cac:AccountingCustomerParty/cac:Party')
-
- # DIAN 1.1.-2021: DSAK19
- # DIAN 1.1.-2021: NSAK19
- fexml.placeholder_for(
- './cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme')
-
- # DIAN 1.1.-2021: DSAK20
- # DIAN 1.1.-2021: NSAK20
- fexml.set_element(
- './cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cbc:RegistrationName',
- invoice.invoice_customer.legal_name)
-
- customer_company_id_attrs = fe.SCHEME_AGENCY_ATTRS.copy()
- customer_company_id_attrs.update(
- {
- 'schemeID': invoice.invoice_customer.ident.dv,
- 'schemeName': invoice.invoice_customer.ident.type_fiscal})
-
- # DIAN 1.1.-2021: DSAK21
- # DIAN 1.1.-2021: NSAK21
- fexml.set_element(
- './cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cbc:CompanyID',
- invoice.invoice_customer.ident,
- # DIAN 1.1.-2021: DSAK22 DSAK23 DSAK24 DSAK25
- # DIAN 1.1.-2021: NSAK22 NSAK23 NSAK24 NSAK25
- **customer_company_id_attrs)
-
- # DIAN 1.1.-2021: DSAK26
- # DIAN 1.1.-2021: NSAK26
- fexml.set_element(
- './cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cbc:TaxLevelCode',
- invoice.invoice_customer.responsability_code)
-
- # DIAN 1.1.-2021: DSAK39
- # DIAN 1.1.-2021: NSAK39
- fexml.placeholder_for(
- './cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cac:TaxScheme')
-
- # DIAN 1.1.-2021: DSAK40
- # DIAN 1.1.-2021: NSAK40
- fexml.set_element(
- './cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cac:TaxScheme/cbc:ID',
- invoice.invoice_customer.tax_scheme.code)
-
- # DIAN 1.1.-2021: DSAK41
- # DIAN 1.1.-2021: NSAK41
- fexml.set_element(
- './cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cac:TaxScheme/cbc:Name',
- invoice.invoice_customer.tax_scheme.name)
-
- def set_payment_mean(fexml, invoice):
- payment_mean = invoice.invoice_payment_mean
-
- # DIAN 1.1.-2021: DSAN01 DSAN02
- # DIAN 1.1.-2021: NSAN02 NSAN02
- fexml.set_element('./cac:PaymentMeans/cbc:ID', payment_mean.id)
-
- # DIAN 1.1.-2021: DSAN03
- # DIAN 1.1.-2021: NSAN03
- fexml.set_element(
- './cac:PaymentMeans/cbc:PaymentMeansCode',
- payment_mean.code)
-
- # DIAN 1.1.-2021: DSAN04
- # DIAN 1.1.-2021: NSAN04
- fexml.set_element(
- './cac:PaymentMeans/cbc:PaymentDueDate',
- payment_mean.due_at.strftime('%Y-%m-%d'))
-
- # DIAN 1.1.-2021: DSAN05
- # DIAN 1.1.-2021: NSAN05
- fexml.set_element(
- './cac:PaymentMeans/cbc:PaymentID',
- payment_mean.payment_id)
-
- def set_element_amount_for(fexml, xml, xpath, amount):
- if not isinstance(amount, Amount):
- raise TypeError("amount not is Amount")
-
- xml.set_element(xpath, amount, currencyID=amount.currency.code)
-
- def set_element_amount(fexml, xpath, amount):
- if not isinstance(amount, Amount):
- raise TypeError("amount not is Amount")
-
- fexml.set_element(xpath, amount, currencyID=amount.currency.code)
-
- def set_legal_monetary(fexml, invoice):
- # DIAN 1.1.-2021: DSAU01 DSAU02 DSAU03
- # DIAN 1.1.-2021: NSAU01 NSAU02 NSAU03
- fexml.set_element_amount(
- './cac:LegalMonetaryTotal/cbc:LineExtensionAmount',
- invoice.invoice_legal_monetary_total.line_extension_amount)
-
- # DIAN 1.1.-2021: DSAU04 DSAU05
- # DIAN 1.1.-2021: NSAU04 NSAU05
- fexml.set_element_amount(
- './cac:LegalMonetaryTotal/cbc:TaxExclusiveAmount',
- invoice.invoice_legal_monetary_total.tax_exclusive_amount)
-
- # DIAN 1.1.-2021: DSAU06 DSAU07
- # DIAN 1.1.-2021: NSAU06 DSAU07
- fexml.set_element_amount(
- './cac:LegalMonetaryTotal/cbc:TaxInclusiveAmount',
- invoice.invoice_legal_monetary_total.tax_inclusive_amount)
-
- # DIAN 1.1.-2021: DSAU10 DSAU11
- # DIAN 1.1.-2021: NSAU10 DSAU11
- fexml.set_element_amount(
- './cac:LegalMonetaryTotal/cbc:ChargeTotalAmount',
- invoice.invoice_legal_monetary_total.charge_total_amount)
-
- # DIAN 1.1.-2021: DSAU14 DSAU15
- # DIAN 1.1.-2021: NSAU14 DSAU15
- fexml.set_element_amount(
- './cac:LegalMonetaryTotal/cbc:PayableAmount',
- invoice.invoice_legal_monetary_total.payable_amount)
-
- def _set_invoice_document_reference(fexml, reference):
- fexml._do_set_billing_reference(
- reference, 'cac:InvoiceDocumentReference')
-
- def _set_credit_note_document_reference(fexml, reference):
- fexml._do_set_billing_reference(
- reference, 'cac:CreditNoteDocumentReference')
-
- def _set_debit_note_document_reference(fexml, reference):
- fexml._do_set_billing_reference(
- reference, 'cac:DebitNoteDocumentReference')
-
- def _do_set_billing_reference(fexml, reference, tag_document):
-
- if tag_document == 'Invoice':
- schemeName = 'CUFE-SHA384'
- else:
- schemeName = 'CUDS-SHA384'
-
- fexml.set_element('./cac:BillingReference/%s/cbc:ID' % (tag_document),
- reference.ident)
- fexml.set_element(
- './cac:BillingReference/cac:InvoiceDocumentReference/cbc:UUID',
- reference.uuid,
- schemeName=schemeName)
- fexml.set_element(
- './cac:BillingReference/cac:InvoiceDocumentReference/cbc:IssueDate',
- reference.date.strftime("%Y-%m-%d"))
-
- def set_billing_reference(fexml, invoice):
- reference = invoice.invoice_billing_reference
- if reference is None:
- return
-
- if isinstance(reference, DebitNoteDocumentReference):
- return fexml._set_debit_note_document_reference(reference)
- if isinstance(reference, CreditNoteDocumentReference):
- return fexml._set_credit_note_document_reference(reference)
-
- if isinstance(reference, InvoiceDocumentReference):
- return fexml._set_invoice_document_reference(reference)
-
- def set_discrepancy_response(fexml, invoice):
- reference = invoice.invoice_discrepancy_response
- if reference is None:
- return
- if isinstance(reference, DebitNoteDocumentReference):
- return fexml._set_debit_note_document_reference(reference)
- if isinstance(reference, CreditNoteDocumentReference):
- return fexml._set_credit_note_document_reference(reference)
-
- if isinstance(reference, InvoiceDocumentReference):
- return fexml._set_invoice_document_reference(reference)
-
- fexml.set_element('./cac:DiscrepancyResponse/cbc:ReferenceID',
- reference.id)
- fexml.set_element('./cac:DiscrepancyResponse/cbc:ResponseCode',
- reference.code)
- fexml.set_element('./cac:DiscrepancyResponse/cbc:Description',
- reference.description)
-
- def set_invoice_totals(fexml, invoice):
- tax_amount_for = defaultdict(lambda: defaultdict(lambda: Amount(0.0)))
- percent_for = defaultdict(lambda: None)
-
- total_tax_amount = Amount(0.0)
-
- for invoice_line in invoice.invoice_lines:
- for subtotal in invoice_line.tax.subtotals:
- 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
-
- if total_tax_amount != Amount(0.0):
- fexml.placeholder_for('./cac:TaxTotal')
- fexml.set_element_amount('./cac:TaxTotal/cbc:TaxAmount',
- total_tax_amount)
-
- for index, item in enumerate(tax_amount_for.items()):
- cod_impuesto, amount_of = item
- next_append = index > 0
-
- # DIAN 1.7.-2020: FAS01
- line = fexml.fragment('./cac:TaxTotal', append=next_append)
- # DIAN 1.7.-2020: FAU06
- tax_amount = amount_of['tax_amount']
- fexml.set_element_amount_for(line,
- '/cac:TaxTotal/cbc:TaxAmount',
- tax_amount)
-
- # DIAN 1.7.-2020: FAS05
- fexml.set_element_amount_for(
- line,
- '/cac:TaxTotal/cac:TaxSubtotal/cbc:TaxableAmount',
- amount_of['taxable_amount'])
-
- # DIAN 1.7.-2020: FAU06
- fexml.set_element_amount_for(
- line,
- '/cac:TaxTotal/cac:TaxSubtotal/cbc:TaxAmount',
- amount_of['tax_amount'])
-
- # DIAN 1.7.-2020: FAS07
- if percent_for[cod_impuesto]:
- line.set_element('/cac:TaxTotal/cac:TaxSubtotal/cbc:Percent',
- percent_for[cod_impuesto])
-
- if percent_for[cod_impuesto]:
- line.set_element(
- '/cac:TaxTotal/cac:TaxSubtotal/cac:TaxCategory/cbc:Percent',
- percent_for[cod_impuesto])
-
- line.set_element(
- '/cac:TaxTotal/cac:TaxSubtotal/cac:TaxCategory/cac:TaxScheme/cbc:ID',
- cod_impuesto)
- line.set_element(
- '/cac:TaxTotal/cac:TaxSubtotal/cac:TaxCategory/cac:TaxScheme/cbc:Name', 'IVA')
-
- # abstract method
-
- def tag_document(fexml):
- return 'Invoice'
-
- # abstract method
- def tag_document_concilied(fexml):
- return 'Invoiced'
-
- 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_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):
- 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)
-
- period = line.fragment('./cac:InvoicePeriod')
- period.set_element('./cbc:StartDate',
- datetime.now().strftime('%Y-%m-%d'))
- period.set_element(
- './cbc:DescriptionCode', '1')
- period.set_element('./cbc:Description',
- 'Por operación')
-
- if not isinstance(invoice_line.tax, TaxTotalOmit):
- 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/cac:StandardItemIdentification/cbc:ID',
- invoice_line.item.id,
- schemeID=invoice_line.item.scheme_id,
- schemeName=invoice_line.item.scheme_name,
- schemeAgencyID=invoice_line.item.scheme_agency_id)
-
- line.set_element(
- './cac:Price/cbc:PriceAmount',
- invoice_line.price.amount,
- currencyID=invoice_line.price.amount.currency.code)
- # DIAN 1.7.-2020: FBB04
- line.set_element('./cac:Price/cbc:BaseQuantity',
- invoice_line.quantity,
- unitCode=invoice_line.quantity.code)
-
- for idx, charge in enumerate(invoice_line.allowance_charge):
- next_append_charge = idx > 0
- fexml.append_allowance_charge(
- line, index + 1, charge, append=next_append_charge)
-
- 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 = parent.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)
- line.set_element('./cbc:MultiplierFactorNumeric',
- str(round(charge.multiplier_factor_numeric, 2)))
- fexml.set_element_amount_for(line, './cbc:Amount', charge.amount)
- fexml.set_element_amount_for(
- line, './cbc:BaseAmount', charge.base_amount)
-
- def attach_invoice(fexml, invoice):
- """adiciona etiquetas a FEXML y retorna FEXML
- en caso de fallar validacion retorna None"""
-
- fexml.placeholder_for('./ext:UBLExtensions')
- fexml.set_element('./cbc:UBLVersionID', 'UBL 2.1')
- fexml.set_element(
- './cbc:CustomizationID',
- invoice.invoice_operation_type)
- fexml.placeholder_for('./cbc:ProfileID')
- fexml.placeholder_for('./cbc:ProfileExecutionID')
- fexml.set_element('./cbc:ID', invoice.invoice_ident)
- fexml.placeholder_for('./cbc:UUID')
- fexml.set_element('./cbc:DocumentCurrencyCode', 'COP')
- fexml.set_element(
- './cbc:IssueDate',
- invoice.invoice_issue.strftime('%Y-%m-%d'))
- # DIAN 1.7.-2020: FAD10
- fexml.set_element(
- './cbc:IssueTime',
- invoice.invoice_issue.strftime('%H:%M:%S-05:00'))
- fexml.set_element(
- './cbc:%sTypeCode' %
- (fexml.tag_document()),
- invoice.invoice_type_code,
- listAgencyID='195',
- listAgencyName='No matching global declaration available for the validation root',
- listURI='http://www.dian.gov.co')
- fexml.set_element('./cbc:LineCountNumeric', len(invoice.invoice_lines))
- fexml.set_element(
- './cac:%sPeriod/cbc:StartDate' %
- (fexml.tag_document()),
- invoice.invoice_period_start.strftime('%Y-%m-%d'))
-
- fexml.set_element(
- './cac:%sPeriod/cbc:EndDate' %
- (fexml.tag_document()),
- invoice.invoice_period_end.strftime('%Y-%m-%d'))
-
- fexml.customize(invoice)
-
- fexml.set_supplier(invoice)
- fexml.set_customer(invoice)
- fexml.set_legal_monetary(invoice)
- fexml.set_invoice_totals(invoice)
- fexml.set_invoice_lines(invoice)
- fexml.set_payment_mean(invoice)
- fexml.set_allowance_charge(invoice)
- fexml.set_discrepancy_response(invoice)
- fexml.set_billing_reference(invoice)
-
- return fexml
-
- def customize(fexml, invoice):
- """adiciona etiquetas a FEXML y retorna FEXML
- en caso de fallar validacion retorna None"""
diff --git a/facho/fe/form_xml/support_document_credit_note.py b/facho/fe/form_xml/support_document_credit_note.py
deleted file mode 100644
index 0145e71..0000000
--- a/facho/fe/form_xml/support_document_credit_note.py
+++ /dev/null
@@ -1,25 +0,0 @@
-# from .. import fe
-# from ..form import *
-from .support_document import DIANSupportDocumentXML
-
-__all__ = ['DIANSupportDocumentCreditNoteXML']
-
-
-class DIANSupportDocumentCreditNoteXML(DIANSupportDocumentXML):
- """
- DianInvoiceXML mapea objeto form.Invoice a XML segun
- lo indicado para la facturacion electronica.
- """
-
- def __init__(self, invoice):
- super(
- DIANSupportDocumentCreditNoteXML,
- self).__init__(
- invoice,
- 'CreditNote')
-
- def tag_document(fexml):
- return 'CreditNote'
-
- def tag_document_concilied(fexml):
- return 'Credited'
diff --git a/facho/fe/form_xml/utils.py b/facho/fe/form_xml/utils.py
index 2d084c7..c05c3d4 100644
--- a/facho/fe/form_xml/utils.py
+++ b/facho/fe/form_xml/utils.py
@@ -2,30 +2,18 @@ from .. import fe
__all__ = ['DIANWrite', 'DIANWriteSigned']
-
def DIANWrite(xml, filename):
document = xml.tostring(xml_declaration=True, encoding='UTF-8')
with open(filename, 'w') as f:
f.write(document)
-
-def DIANWriteSigned(
- xml,
- filename,
- private_key,
- passphrase,
- use_cache_policy=False,
- dian_signer=None):
- document = xml.tostring(
- xml_declaration=True,
- encoding='UTF-8').encode('utf-8')
+
+def DIANWriteSigned(xml, filename, private_key, passphrase, use_cache_policy=False, dian_signer=None):
+ document = xml.tostring(xml_declaration=True, encoding='UTF-8').encode('utf-8')
if dian_signer is None:
dian_signer = fe.DianXMLExtensionSigner
- signer = dian_signer(
- private_key,
- passphrase=passphrase,
- localpolicy=use_cache_policy)
+ signer = dian_signer(private_key, passphrase=passphrase, localpolicy=use_cache_policy)
with open(filename, 'w') as f:
f.write(signer.sign_xml_string(document))
diff --git a/facho/fe/nomina/__init__.py b/facho/fe/nomina/__init__.py
index a1f523a..5baaf57 100644
--- a/facho/fe/nomina/__init__.py
+++ b/facho/fe/nomina/__init__.py
@@ -53,20 +53,14 @@ class FechaPago(Fecha):
@dataclass
class Novedad:
- # cune de nomina a relacionar
- # NIE204
- cune: str
- # NIE199
- activa: bool = False
+ value: False
def apply(self, fragment):
- if self.cune != "":
- fragment.set_attributes('./Novedad',
- CUNENov=self.cune,
- )
-
+ fragment.set_attributes('./Novedad',
+ CUNENov=self.value,
+ )
def post_apply(self, fexml, scopexml, fragment):
- scopexml.set_element('./Novedad', self.activa)
+ scopexml.set_element('./Novedad', "false")
@dataclass
@@ -144,12 +138,13 @@ class Proveedor:
ambiente = fexml.get_element_attribute(scopexml.xpath_from_root('/InformacionGeneral'), 'Ambiente')
codigo_qr = f"https://catalogo-vpfe.dian.gov.co/document/searchqr?documentkey={cune}"
- if InformacionGeneral.AMBIENTE_PRUEBAS == ambiente:
+ if InformacionGeneral.AMBIENTE_PRUEBAS.same(ambiente):
codigo_qr = f"https://catalogo-vpfe-hab.dian.gov.co/document/searchqr?documentkey={cune}"
elif ambiente is None:
raise RuntimeError('fail to get InformacionGeneral/@Ambiente')
scopexml.set_element('./CodigoQR', codigo_qr)
+ scopexml.set_element('./Novedad', "false")
# NIE020
software_code = self._software_security_code(fexml, scopexml)
@@ -182,16 +177,14 @@ class Metadata:
proveedor: Proveedor
def apply(self, novedad, numero_secuencia_xml, lugar_generacion_xml, proveedor_xml):
- if novedad:
- self.novedad.apply(novedad)
+ self.novedad.apply(novedad)
self.secuencia.apply(numero_secuencia_xml)
self.lugar_generacion.apply(lugar_generacion_xml, './LugarGeneracionXML')
self.proveedor.apply(proveedor_xml)
def post_apply(self, fexml, scopexml, novedad, numero_secuencia_xml, lugar_generacion_xml, proveedor_xml):
self.proveedor.post_apply(fexml, scopexml, proveedor_xml)
- if novedad:
- self.novedad.post_apply(fexml, scopexml, proveedor_xml)
+ self.novedad.post_apply(fexml, scopexml, proveedor_xml)
@dataclass
class PeriodoNomina:
@@ -219,8 +212,9 @@ class InformacionGeneral:
class TIPO_AMBIENTE:
valor: str
- def __eq__(self, other):
- return self.valor == str(other)
+ @classmethod
+ def same(cls, value):
+ return cls.valor == str(value)
# TABLA 5.1.1
@dataclass
@@ -234,28 +228,6 @@ class InformacionGeneral:
class AMBIENTE_PRUEBAS(TIPO_AMBIENTE):
valor: str = '2'
- def __str__(self):
- self.valor
-
- # TABLA 5.5.7
- @dataclass
- class TIPO_XML:
- valor: str
-
- def __eq__(self, other):
- return self.valor == str(other)
-
- @dataclass
- class TIPO_XML_NORMAL(TIPO_XML):
- valor: str = '102'
-
- def __str__(self):
- self.valor
-
- @dataclass
- class TIPO_XML_AJUSTES(TIPO_XML):
- valor: str = '103'
-
def __str__(self):
self.valor
@@ -264,7 +236,6 @@ class InformacionGeneral:
periodo_nomina: PeriodoNomina
tipo_moneda: TipoMoneda
tipo_ambiente: TIPO_AMBIENTE
- tipo_xml: TIPO_XML
software_pin: str
def __post_init__(self):
@@ -279,7 +250,7 @@ class InformacionGeneral:
# NIE202
# TABLA 5.5.2
# TODO(bit4bit) solo NominaIndividual
- TipoXML = self.tipo_xml.valor,
+ TipoXML = '102',
# NIE024
CUNE = None,
# NIE025
@@ -338,18 +309,15 @@ class DianXMLExtensionSigner(fe.DianXMLExtensionSigner):
class DIANNominaXML:
- def __init__(self, tag_document, xpath_ajuste=None, schemaLocation=None, namespace_ajuste=None):
+ def __init__(self, tag_document, xpath_ajuste=None,schemaLocation=None):
self.informacion_general_version = None
self.tag_document = tag_document
+ self.fexml = fe.FeXML(tag_document, 'http://www.dian.gov.co/contratos/facturaelectronica/v1')
- if namespace_ajuste:
- self.fexml = fe.FeXML(tag_document, namespace_ajuste)
- else:
- self.fexml = fe.FeXML(tag_document, 'dian:gov:co:facturaelectronica:NominaIndividual')
-
- self.fexml.root.set("SchemaLocation", "")
- self.fexml.root.set("schemaLocation", schemaLocation)
+ if schemaLocation is not None:
+ self.fexml.root.set("SchemaLocation", "")
+ self.fexml.root.set("change", schemaLocation)
# layout, la dian requiere que los elementos
# esten ordenados segun el anexo tecnico
@@ -361,8 +329,7 @@ class DIANNominaXML:
self.root_fragment = self.fexml.fragment(xpath_ajuste)
self.root_fragment.placeholder_for('./ReemplazandoPredecesor', optional=True)
self.root_fragment.placeholder_for('./EliminandoPredecesor', optional=True)
- if not namespace_ajuste:
- self.root_fragment.placeholder_for('./Novedad', optional=False)
+ self.root_fragment.placeholder_for('./Novedad', optional=False)
self.root_fragment.placeholder_for('./Periodo')
self.root_fragment.placeholder_for('./NumeroSecuenciaXML')
self.root_fragment.placeholder_for('./LugarGeneracionXML')
@@ -375,10 +342,8 @@ class DIANNominaXML:
self.root_fragment.placeholder_for('./FechasPagos')
self.root_fragment.placeholder_for('./Devengados/Basico')
self.root_fragment.placeholder_for('./Devengados/Transporte', optional=True)
- if not namespace_ajuste:
- self.novedad = self.root_fragment.fragment('./Novedad')
- else:
- self.novedad = None
+
+ self.novedad = self.root_fragment.fragment('./Novedad')
self.informacion_general_xml = self.root_fragment.fragment('./InformacionGeneral')
self.periodo_xml = self.root_fragment.fragment('./Periodo')
self.fecha_pagos_xml = self.root_fragment.fragment('./FechasPagos')
@@ -398,7 +363,6 @@ class DIANNominaXML:
if not isinstance(metadata, Metadata):
raise ValueError('se espera tipo Metadata')
self.metadata = metadata
-
self.metadata.apply(self.novedad, self.numero_secuencia_xml, self.lugar_generacion_xml, self.proveedor_xml)
def asignar_informacion_general(self, general):
@@ -555,8 +519,6 @@ class DIANNominaXML:
devengados_total = Amount(0.0)
for devengado in devengados:
devengados_total += devengado
- # TODO(bit4bit) nque valor va redondeado?
- # NIE186
self.root_fragment.set_element('./Redondeo', str(round(0,2)))
self.root_fragment.set_element('./DevengadosTotal', str(round(devengados_total,2)))
@@ -592,8 +554,6 @@ class DIANNominaIndividualDeAjuste(DIANNominaXML):
fecha_generacion: str
def apply(self, fragment):
- # NIAE214
- fragment.set_element('./TipoNota', '1')
fragment.set_element('./Reemplazar/ReemplazandoPredecesor', None,
# NIAE090
NumeroPred = self.numero,
@@ -604,11 +564,9 @@ class DIANNominaIndividualDeAjuste(DIANNominaXML):
)
def __init__(self):
- schema = "dian:gov:co:facturaelectronica:NominaIndividualDeAjuste NominaIndividualDeAjusteElectronicaXSD.xsd"
-
- super().__init__('NominaIndividualDeAjuste', './Reemplazar', schemaLocation=schema, namespace_ajuste='dian:gov:co:facturaelectronica:NominaIndividualDeAjuste')
-
- self.informacion_general_version = 'V1.0: Nota de Ajuste de Documento Soporte de Pago de Nómina Electrónica'
+ super().__init__('NominaIndividualDeAjuste', './Reemplazar')
+ # NIAE214
+ self.root_fragment.set_element('./TipoNota', '1')
def asignar_predecesor(self, predecesor):
if not isinstance(predecesor, self.Predecesor):
@@ -625,7 +583,6 @@ class DIANNominaIndividualDeAjuste(DIANNominaXML):
fecha_generacion: str
def apply(self, fragment):
- fragment.set_element('./TipoNota', '2')
fragment.set_element('./Eliminar/EliminandoPredecesor', None,
# NIAE090
NumeroPred = self.numero,
@@ -636,9 +593,9 @@ class DIANNominaIndividualDeAjuste(DIANNominaXML):
)
def __init__(self):
- schema = "dian:gov:co:facturaelectronica:NominaIndividualDeAjuste NominaIndividualDeAjusteElectronicaXSD.xsd"
- super().__init__('NominaIndividualDeAjuste', './Eliminar', schemaLocation=schema, namespace_ajuste='dian:gov:co:facturaelectronica:NominaIndividualDeAjuste')
+ super().__init__('NominaIndividualDeAjuste', './Eliminar')
+ self.root_fragment.set_element('./TipoNota', '2')
self.informacion_general_version = "V1.0: Nota de Ajuste de Documento Soporte de Pago de Nómina Electrónica"
def asignar_predecesor(self, predecesor):
diff --git a/facho/fe/nomina/trabajador/__init__.py b/facho/fe/nomina/trabajador/__init__.py
index a262866..9867b3e 100644
--- a/facho/fe/nomina/trabajador/__init__.py
+++ b/facho/fe/nomina/trabajador/__init__.py
@@ -1,4 +1,4 @@
-from dataclasses import dataclass, field
+from dataclasses import dataclass
from ..amount import Amount
@@ -29,7 +29,7 @@ class Trabajador:
codigo_trabajador: str = None
otros_nombres: str = None
- sub_tipo: SubTipoTrabajador = field(default_factory=lambda: SubTipoTrabajador(code='00'))
+ sub_tipo: SubTipoTrabajador = SubTipoTrabajador(code='00')
def apply(self, fragment):
fragment.set_attributes('./Trabajador',
diff --git a/requirements_dev.txt b/requirements_dev.txt
deleted file mode 100644
index e96da27..0000000
--- a/requirements_dev.txt
+++ /dev/null
@@ -1,17 +0,0 @@
-attrs==22.1.0
-distlib==0.3.6
-filelock==3.8.0
-iniconfig==1.1.1
-packaging==21.3
-platformdirs==2.5.2
-pluggy==1.0.0
-py==1.11.0
-pyparsing==3.0.9
-pytest==7.1.3
-semantic-version==2.10.0
-setuptools-rust==1.5.2
-six==1.16.0
-tomli==2.0.1
-tox==3.26.0
-typing_extensions==4.4.0
-virtualenv==20.16.5
diff --git a/setup.cfg b/setup.cfg
index 0f67e9d..f5945f3 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -22,4 +22,4 @@ exclude = docs
test = pytest
[tool:pytest]
-addopts = --ignore=setup.py
+collect_ignore = ['setup.py']
diff --git a/setup.py b/setup.py
index 226c7f8..3a2147b 100644
--- a/setup.py
+++ b/setup.py
@@ -13,36 +13,19 @@ with open('README.rst') as readme_file:
with open('HISTORY.rst') as history_file:
history = history_file.read()
-requirements = ['Click>=8.1.7',
- 'zeep==4.2.1',
- 'lxml==5.2.2',
- 'cryptography==3.3.2',
- 'pyOpenSSL==20.0.1',
- 'xmlsig==0.1.7',
- 'xades==1.0.0',
- 'xmlsec==1.3.14',
- 'python-dateutil==2.9.0.post0',
- # usamos esta dependencia en runtime
- # para forzar uso de policy_id de archivo local
- 'mock>=5.1.0',
- 'xmlschema>=3.0.0']
-
-"""
-Listado de Versiones Anteriores
-requirements = ['Click>=6.0',
- 'zeep==4.0.0',
- 'lxml==4.6.3',
- 'cryptography==3.3.2',
- 'pyOpenSSL==20.0.1',
- 'xmlsig==0.1.7',
- 'xades==0.2.2',
- 'xmlsec==1.3.12',
- # usamos esta dependencia en runtime
- # para forzar uso de policy_id de archivo local
- 'mock>=2.0.0',
- 'xmlschema>=1.8']
-
-"""
+requirements = ['Click>=8.1.7',
+ 'zeep==4.2.1',
+ 'lxml==5.2.2',
+ 'cryptography==3.3.2',
+ 'pyOpenSSL==20.0.1',
+ 'xmlsig==0.1.7',
+ 'xades==1.0.0',
+ 'xmlsec==1.3.14',
+ 'python-dateutil==2.9.0.post0',
+ # usamos esta dependencia en runtime
+ # para forzar uso de policy_id de archivo local
+ 'mock>=5.1.0',
+ 'xmlschema>=3.0.0' ]
setup_requirements = ['pytest-runner', ]
@@ -57,10 +40,10 @@ setup(
'License :: OSI Approved :: GNU General Public License v3 (GPLv3)',
'Natural Language :: English',
'Programming Language :: Python :: 3',
- 'Programming Language :: Python :: 3.9',
- 'Programming Language :: Python :: 3.10',
- 'Programming Language :: Python :: 3.11',
- 'Programming Language :: Python :: 3.12',
+ 'Programming Language :: Python :: 3.4',
+ 'Programming Language :: Python :: 3.5',
+ 'Programming Language :: Python :: 3.6',
+ 'Programming Language :: Python :: 3.7',
],
description="Facturacion Electronica Colombia",
entry_points={
diff --git a/tests/cude.txt b/tests/cude.txt
deleted file mode 100644
index d0bee00..0000000
--- a/tests/cude.txt
+++ /dev/null
@@ -1 +0,0 @@
-907e4444decc9e59c160a2fb3b6659b33dc5b632a5008922b9a62f83f757b1c448e47f5867f2b50dbdb96f48c7681168
diff --git a/tests/cufe.txt b/tests/cufe.txt
deleted file mode 100644
index e69de29..0000000
diff --git a/tests/fixtures.py b/tests/fixtures.py
index 574e3ac..34501d7 100644
--- a/tests/fixtures.py
+++ b/tests/fixtures.py
@@ -4,32 +4,30 @@ from datetime import datetime
@pytest.fixture
def simple_debit_note_without_lines():
- inv = form.DebitNote(form.InvoiceDocumentReference(
- '1234', 'xx', datetime.now()))
+ inv = form.DebitNote(form.InvoiceDocumentReference('1234', 'xx', datetime.now()))
inv.set_period(datetime.now(), datetime.now())
inv.set_issue(datetime.now())
inv.set_ident('ABC123')
inv.set_operation_type('30')
- inv.set_payment_mean(form.PaymentMean(
- form.PaymentMean.DEBIT, '41', datetime.now(), '1234'))
+ inv.set_payment_mean(form.PaymentMean(form.PaymentMean.DEBIT, '41', datetime.now(), '1234'))
inv.set_supplier(form.Party(
- name='facho-supplier',
- ident=form.PartyIdentification('123', '', '31'),
- responsability_code = form.Responsability(['ZZ']),
- responsability_regime_code='48',
- organization_code='1',
- address=form.Address(
+ name = 'facho-supplier',
+ ident = form.PartyIdentification('123','', '31'),
+ responsability_code = form.Responsability(['O-07']),
+ responsability_regime_code = '48',
+ organization_code = '1',
+ address = form.Address(
'', '', form.City('05001', 'Medellín'),
form.Country('CO', 'Colombia'),
form.CountrySubentity('05', 'Antioquia'))
))
inv.set_customer(form.Party(
- name='facho-customer',
- ident=form.PartyIdentification('321', '', '31'),
- responsability_code=form.Responsability(['ZZ']),
- responsability_regime_code='48',
- organization_code='1',
- address=form.Address(
+ name = 'facho-customer',
+ ident = form.PartyIdentification('321', '', '31'),
+ responsability_code = form.Responsability(['O-07']),
+ responsability_regime_code = '48',
+ organization_code = '1',
+ address = form.Address(
'', '', form.City('05001', 'Medellín'),
form.Country('CO', 'Colombia'),
form.CountrySubentity('05', 'Antioquia'))
@@ -47,7 +45,7 @@ def simple_credit_note_without_lines():
inv.set_supplier(form.Party(
name = 'facho-supplier',
ident = form.PartyIdentification('123','', '31'),
- responsability_code = form.Responsability(['ZZ']),
+ responsability_code = form.Responsability(['O-07']),
responsability_regime_code = '48',
organization_code = '1',
address = form.Address(
@@ -58,7 +56,7 @@ def simple_credit_note_without_lines():
inv.set_customer(form.Party(
name = 'facho-customer',
ident = form.PartyIdentification('321', '', '31'),
- responsability_code = form.Responsability(['ZZ']),
+ responsability_code = form.Responsability(['O-07']),
responsability_regime_code = '48',
organization_code = '1',
address = form.Address(
@@ -79,7 +77,7 @@ def simple_invoice_without_lines():
inv.set_supplier(form.Party(
name = 'facho-supplier',
ident = form.PartyIdentification('123','', '31'),
- responsability_code = form.Responsability(['ZZ']),
+ responsability_code = form.Responsability(['O-07']),
responsability_regime_code = '48',
organization_code = '1',
address = form.Address(
@@ -90,7 +88,7 @@ def simple_invoice_without_lines():
inv.set_customer(form.Party(
name = 'facho-customer',
ident = form.PartyIdentification('321', '', '31'),
- responsability_code = form.Responsability(['ZZ']),
+ responsability_code = form.Responsability(['O-07']),
responsability_regime_code = '48',
organization_code = '1',
address = form.Address(
@@ -100,7 +98,6 @@ def simple_invoice_without_lines():
))
return inv
-
@pytest.fixture
def simple_invoice():
inv = form.NationalSalesInvoice()
@@ -112,7 +109,7 @@ def simple_invoice():
inv.set_supplier(form.Party(
name = 'facho-supplier',
ident = form.PartyIdentification('123','', '31'),
- responsability_code = form.Responsability(['ZZ']),
+ responsability_code = form.Responsability(['O-07']),
responsability_regime_code = '48',
organization_code = '1',
address = form.Address(
@@ -123,7 +120,7 @@ def simple_invoice():
inv.set_customer(form.Party(
name = 'facho-customer',
ident = form.PartyIdentification('321','', '31'),
- responsability_code = form.Responsability(['ZZ']),
+ responsability_code = form.Responsability(['O-07']),
responsability_regime_code = '48',
organization_code = '1',
address = form.Address(
@@ -131,20 +128,19 @@ def simple_invoice():
form.Country('CO', 'Colombia'),
form.CountrySubentity('05', 'Antioquia'))
))
-
inv.add_invoice_line(form.InvoiceLine(
- quantity=form.Quantity(1, '94'),
- description='productofacho',
- item=form.StandardItem(9999),
- price=form.Price(form.Amount(100.0),'01',''),
- tax=form.TaxTotal(
- tax_amount=form.Amount(0.0),
- taxable_amount=form.Amount(0.0),
- subtotals=[
+ quantity = form.Quantity(1, '94'),
+ description = 'producto facho',
+ item = form.StandardItem( 9999),
+ price = form.Price(form.Amount(100.0), '01', ''),
+ tax = form.TaxTotal(
+ tax_amount = form.Amount(0.0),
+ taxable_amount = form.Amount(0.0),
+ subtotals = [
form.TaxSubTotal(
- percent=19.0,
- )]),
- withholding=form.WithholdingTaxTotal(
- subtotals=[])
+ percent = 19.0,
+ )
+ ]
+ )
))
return inv
diff --git a/tests/test_application_response.py b/tests/test_application_response.py
deleted file mode 100644
index 01f03be..0000000
--- a/tests/test_application_response.py
+++ /dev/null
@@ -1,21 +0,0 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-# This file is part of facho. The COPYRIGHT file at the top level of
-# this repository contains the full copyright notices and license terms.
-import pytest
-from facho.fe import form_xml
-from fixtures import simple_invoice
-
-simple_invoice = simple_invoice
-
-
-def test_application_response(simple_invoice):
-
- doc = form_xml.ApplicationResponse(simple_invoice)
- xml = doc.toFachoXML()
-
- with open("application_response.xml", "w") as fh:
- fh.write(xml.tostring())
- # raise Exception(xml.tostring())
- # assert xml.get_element_text(
- # './apr:ApplicationResponse')
diff --git a/tests/test_attached_document.py b/tests/test_attached_document.py
index 868cfd2..0e58be9 100644
--- a/tests/test_attached_document.py
+++ b/tests/test_attached_document.py
@@ -2,124 +2,16 @@
# -*- coding: utf-8 -*-
# This file is part of facho. The COPYRIGHT file at the top level of
# this repository contains the full copyright notices and license terms.
-# from datetime import datetime
+from datetime import datetime
import pytest
from facho.fe import form_xml
-from datetime import datetime
+
import helpers
-from fixtures import simple_invoice
-simple_invoice = simple_invoice
-
-def test_xml_with_required_elements(simple_invoice):
-
- xml_header = ''
- DIANInvoiceXML = form_xml.DIANInvoiceXML(
- simple_invoice)
-
- doc = form_xml.AttachedDocument(
- simple_invoice,
- DIANInvoiceXML,
- id='123')
+def test_xml_with_required_elements():
+ doc = form_xml.AttachedDocument(id='123')
xml = doc.toFachoXML()
-
- DIANInvoiceXML = form_xml.DIANInvoiceXML(
- simple_invoice, 'Invoice').attach_invoice
-
- ApplicationResponse = xml_header + form_xml.ApplicationResponse(simple_invoice).toFachoXML().tostring()
-
- attached_document = xml_header + DIANInvoiceXML.tostring()
-
- assert xml.get_element_text(
- '/atd:AttachedDocument/cbc:UBLVersionID') == 'UBL 2.1'
- assert xml.get_element_text(
- '/atd:AttachedDocument/cbc:CustomizationID') == 'Documentos adjuntos'
- assert xml.get_element_text(
- '/atd:AttachedDocument/cbc:ProfileID') == 'Factura Electrónica de Venta'
- assert xml.get_element_text(
- '/atd:AttachedDocument/cbc:ProfileExecutionID') == '1'
- assert xml.get_element_text(
- '/atd:AttachedDocument/cbc:ID') == '123'
- assert xml.get_element_text(
- '/atd:AttachedDocument/cbc:IssueDate') == str(datetime.today().date())
- assert xml.get_element_text(
- '/atd:AttachedDocument/cbc:IssueTime') == datetime.today(
- ).time().strftime(
- '%H:%M:%S-05:00')
- assert xml.get_element_text(
- '/atd:AttachedDocument/cbc:DocumentType'
- ) == 'Contenedor de Factura Electrónica'
- assert xml.get_element_text(
- '/atd:AttachedDocument/cbc:ParentDocumentID'
- ) == 'ABC123'
-
- assert xml.get_element_text(
- '/atd:AttachedDocument/cac:SenderParty/cac:PartyTaxScheme/cbc:RegistrationName'
- ) == 'facho-supplier'
- assert xml.get_element_text(
- '/atd:AttachedDocument/cac:SenderParty/cac:PartyTaxScheme/cbc:CompanyID'
- ) == '123'
- assert xml.get_element_text(
- '/atd:AttachedDocument/cac:SenderParty/cac:PartyTaxScheme/cbc:TaxLevelCode'
- ) == 'ZZ'
- assert xml.get_element_text(
- '/atd:AttachedDocument/cac:SenderParty/cac:PartyTaxScheme/cac:TaxScheme/cbc:ID'
- ) == '01'
- assert xml.get_element_text(
- '/atd:AttachedDocument/cac:SenderParty/cac:PartyTaxScheme/cac:TaxScheme/cbc:Name'
- ) == 'IVA'
-
- assert xml.get_element_text(
- '/atd:AttachedDocument/cac:ReceiverParty/cac:PartyTaxScheme/cbc:RegistrationName'
- ) == 'facho-customer'
- assert xml.get_element_text(
- '/atd:AttachedDocument/cac:ReceiverParty/cac:PartyTaxScheme/cbc:CompanyID'
- ) == '321'
- assert xml.get_element_text(
- '/atd:AttachedDocument/cac:ReceiverParty/cac:PartyTaxScheme/cbc:TaxLevelCode'
- ) == 'ZZ'
- assert xml.get_element_text(
- '/atd:AttachedDocument/cac:ReceiverParty/cac:PartyTaxScheme/cac:TaxScheme/cbc:ID'
- ) == '01'
- assert xml.get_element_text(
- '/atd:AttachedDocument/cac:ReceiverParty/cac:PartyTaxScheme/cac:TaxScheme/cbc:Name'
- ) == 'IVA'
-
- assert xml.get_element_text(
- '/atd:AttachedDocument/cac:Attachment/cac:ExternalReference/cbc:MimeCode'
- ) == "text/xml"
- assert xml.get_element_text(
- '/atd:AttachedDocument/cac:Attachment/cac:ExternalReference/cbc:EncodingCode'
- ) == "UTF-8"
- assert xml.get_element_text(
- '/atd:AttachedDocument/cac:Attachment/cac:ExternalReference/cbc:Description'
- ) == "".format(attached_document)
-
- assert xml.get_element_text(
- '/atd:AttachedDocument/cac:ParentDocumentLineReference/cbc:LineID'
- ) == '1'
- assert xml.get_element_text(
- '/atd:AttachedDocument/cac:ParentDocumentLineReference/cac:DocumentReference/cbc:ID'
- ) == '1234'
- assert xml.get_element_text(
- '/atd:AttachedDocument/cac:ParentDocumentLineReference/cac:DocumentReference/cbc:UUID'
- ) == '1234'
- assert xml.get_element_text(
- '/atd:AttachedDocument/cac:ParentDocumentLineReference/cac:DocumentReference/cbc:IssueDate'
- ) == '2024-11-28'
- assert xml.get_element_text(
- '/atd:AttachedDocument/cac:ParentDocumentLineReference/cac:DocumentReference/cbc:DocumentType'
- ) == 'ApplicationResponse'
- assert xml.get_element_text(
- '/atd:AttachedDocument/cac:ParentDocumentLineReference/cac:DocumentReference/cac:Attachment/cac:ExternalReference/cbc:MimeCode'
- ) == 'text/xml'
-
- assert xml.get_element_text(
- '/atd:AttachedDocument/cac:ParentDocumentLineReference/cac:DocumentReference/cac:Attachment/cac:ExternalReference/cbc:EncodingCode'
- ) == "UTF-8"
-
- assert xml.get_element_text(
- '/atd:AttachedDocument/cac:ParentDocumentLineReference/cac:DocumentReference/cac:Attachment/cac:ExternalReference/cbc:Description'
- ) == "".format(ApplicationResponse)
+ assert xml.get_element_text('/atd:AttachedDocument/cbc:ID') == '123'
+
diff --git a/tests/test_data.py b/tests/test_data.py
index a463319..88381b0 100644
--- a/tests/test_data.py
+++ b/tests/test_data.py
@@ -4,26 +4,21 @@
# this repository contains the full copyright notices and license terms.
"""Tests for `facho` package."""
-from facho.fe.data.dian import codelist
+import pytest
+from facho.fe.data.dian import codelist
def test_tiporesponsabilidad():
assert codelist.TipoResponsabilidad.short_name == 'TipoResponsabilidad'
- assert codelist.TipoResponsabilidad.by_name(
- 'Autorretenedor')['name'] == 'Autorretenedor'
-
+ assert codelist.TipoResponsabilidad.by_name('Autorretenedor')['name'] == 'Autorretenedor'
def test_tipoorganizacion():
assert codelist.TipoOrganizacion.short_name == 'TipoOrganizacion'
- assert codelist.TipoOrganizacion.by_name(
- 'Persona Natural')['name'] == 'Persona Natural'
-
+ assert codelist.TipoOrganizacion.by_name('Persona Natural')['name'] == 'Persona Natural'
def test_tipodocumento():
assert codelist.TipoDocumento.short_name == 'TipoDocumento'
- assert codelist.TipoDocumento.by_name(
- 'Factura electrónica de Venta')['code'] == '01'
-
+ assert codelist.TipoDocumento.by_name('Factura de Venta Nacional')['code'] == '01'
def test_departamento():
assert codelist.Departamento['05']['name'] == 'Antioquia'
diff --git a/tests/test_fe.py b/tests/test_fe.py
index d1fb4bb..58f4583 100644
--- a/tests/test_fe.py
+++ b/tests/test_fe.py
@@ -5,15 +5,15 @@
from datetime import datetime
import pytest
-
from facho import fe
+
import helpers
def test_xmlsigned_build(monkeypatch):
- # openssl req -x509 -sha256 -nodes -subj "/CN=test" -days 1 -newkey rsa:2048 -keyout example.key -out example.pem
- # openssl pkcs12 -export -out example.p12 -inkey example.key -in example.pem
+ #openssl req -x509 -sha256 -nodes -subj "/CN=test" -days 1 -newkey rsa:2048 -keyout example.key -out example.pem
+ #openssl pkcs12 -export -out example.p12 -inkey example.key -in example.pem
signer = fe.DianXMLExtensionSigner('./tests/example.p12')
xml = fe.FeXML('Invoice',
@@ -116,17 +116,3 @@ def test_xml_sign_dian_using_bytes(monkeypatch):
xmlsigned = signer.sign_xml_string(xmlstring)
assert "Signature" in xmlsigned
-
-def test_xml_signature_timestamp(monkeypatch):
- xml = fe.FeXML(
- 'Invoice',
- 'http://www.dian.gov.co/contratos/facturaelectronica/v1')
- xml.find_or_create_element(
- '/fe:Invoice/ext:UBLExtensions/ext:UBLExtension/ext:ExtensionContent')
- ublextension = xml.fragment(
- '/fe:Invoice/ext:UBLExtensions/ext:UBLExtension', append=True)
- ublextension.find_or_create_element(
- '/ext:UBLExtension/ext:ExtensionContent')
- xmlstring = xml.tostring()
- signer = fe.DianXMLExtensionSigner('./tests/example.p12')
- xmlsigned = signer.sign_xml_string(xmlstring)
diff --git a/tests/test_fe_form.py b/tests/test_fe_form.py
index cfbc060..ca7f4b9 100644
--- a/tests/test_fe_form.py
+++ b/tests/test_fe_form.py
@@ -5,47 +5,24 @@
"""Tests for `facho` package."""
+import pytest
from datetime import datetime
import io
import zipfile
import facho.fe.form as form
from facho import fe
-from facho.fe.form_xml import (
- DIANInvoiceXML, DIANCreditNoteXML, DIANDebitNoteXML)
-
-from fixtures import (
- simple_invoice,
- simple_invoice_without_lines,
- simple_credit_note_without_lines,
- simple_debit_note_without_lines)
-
-try:
- CUDE_ = open("./tests/cude.txt", 'r').read().strip()
-except FileNotFoundError:
- raise Exception("Archivo Cude No encontrado")
-
-CUFE_ = (
- '8bb918b19ba22a694f1da'
- '11c643b5e9de39adf60311c'
- 'f179179e9b33381030bcd4c3c'
- '3f156c506ed5908f9276f5bd9b4')
-
-simple_invoice = simple_invoice
-simple_invoice_without_lines = simple_invoice_without_lines
-simple_credit_note_without_lines = simple_credit_note_without_lines
-simple_debit_note_without_lines = simple_debit_note_without_lines
+from facho.fe.form_xml import DIANInvoiceXML, DIANCreditNoteXML, DIANDebitNoteXML
+from fixtures import *
def test_invoicesimple_build(simple_invoice):
xml = DIANInvoiceXML(simple_invoice)
- supplier_name = xml.get_element_text(
- '/fe:Invoice/cac:AccountingSupplierParty/cac:Party/cac:PartyName/cbc:Name')
+ supplier_name = xml.get_element_text('/fe:Invoice/cac:AccountingSupplierParty/cac:Party/cac:PartyName/cbc:Name')
assert supplier_name == simple_invoice.invoice_supplier.name
- customer_name = xml.get_element_text(
- '/fe:Invoice/cac:AccountingCustomerParty/cac:Party/cac:PartyName/cbc:Name')
+ customer_name = xml.get_element_text('/fe:Invoice/cac:AccountingCustomerParty/cac:Party/cac:PartyName/cbc:Name')
assert customer_name == simple_invoice.invoice_customer.name
@@ -65,11 +42,9 @@ def test_invoicesimple_xml_signed(monkeypatch, simple_invoice):
print(xml.tostring())
xml.add_extension(signer)
- elem = xml.get_element(
- '/fe:Invoice/ext:UBLExtensions/ext:UBLExtension[2]/ext:ExtensionContent/ds:Signature')
+ elem = xml.get_element('/fe:Invoice/ext:UBLExtensions/ext:UBLExtension[2]/ext:ExtensionContent/ds:Signature')
assert elem.text is not None
-
def test_invoicesimple_zip(simple_invoice):
xml_invoice = DIANInvoiceXML(simple_invoice)
@@ -91,34 +66,26 @@ def test_invoicesimple_zip(simple_invoice):
def test_bug_cbcid_empty_on_invoice_line(simple_invoice):
xml_invoice = DIANInvoiceXML(simple_invoice)
- cbc_id = xml_invoice.get_element_text(
- '/fe:Invoice/cac:InvoiceLine[1]/cbc:ID', format_=int)
+ cbc_id = xml_invoice.get_element_text('/fe:Invoice/cac:InvoiceLine[1]/cbc:ID', format_=int)
assert cbc_id == 1
-
def test_invoice_line_count_numeric(simple_invoice):
xml_invoice = DIANInvoiceXML(simple_invoice)
- count = xml_invoice.get_element_text(
- '/fe:Invoice/cbc:LineCountNumeric', format_=int)
+ count = xml_invoice.get_element_text('/fe:Invoice/cbc:LineCountNumeric', format_=int)
assert count == len(simple_invoice.invoice_lines)
-
def test_invoice_profileexecutionid(simple_invoice):
xml_invoice = DIANInvoiceXML(simple_invoice)
cufe_extension = fe.DianXMLExtensionCUFE(simple_invoice)
xml_invoice.add_extension(cufe_extension)
- id_ = xml_invoice.get_element_text(
- '/fe:Invoice/cbc:ProfileExecutionID', format_=int)
+ id_ = xml_invoice.get_element_text('/fe:Invoice/cbc:ProfileExecutionID', format_=int)
assert id_ == 2
-
def test_invoice_invoice_type_code(simple_invoice):
xml_invoice = DIANInvoiceXML(simple_invoice)
- id_ = xml_invoice.get_element_text(
- '/fe:Invoice/cbc:InvoiceTypeCode', format_=int)
+ id_ = xml_invoice.get_element_text('/fe:Invoice/cbc:InvoiceTypeCode', format_=int)
assert id_ == 1
-
def test_invoice_totals(simple_invoice_without_lines):
simple_invoice = simple_invoice_without_lines
simple_invoice.invoice_ident = '323200000129'
@@ -126,50 +93,39 @@ def test_invoice_totals(simple_invoice_without_lines):
simple_invoice.invoice_supplier.ident = '700085371'
simple_invoice.invoice_customer.ident = '800199436'
simple_invoice.add_invoice_line(form.InvoiceLine(
- quantity=form.Quantity(1, '94'),
- description='producto',
- item=form.StandardItem(9999),
- price=form.Price(form.Amount(1_500_000), '01', ''),
- tax=form.TaxTotal(
- subtotals=[
+ quantity = form.Quantity(1, '94'),
+ description = 'producto',
+ item = form.StandardItem(9999),
+ price = form.Price(form.Amount(1_500_000), '01', ''),
+ tax = form.TaxTotal(
+ subtotals = [
form.TaxSubTotal(
- scheme=form.TaxScheme('01'),
- percent=19.0
- )]),
- withholding=form.WithholdingTaxTotal(
- subtotals=[])
+ scheme = form.TaxScheme('01'),
+ percent = 19.0
+ )])
))
simple_invoice.calculate()
assert 1 == len(simple_invoice.invoice_lines)
- assert form.Amount(1_500_000) == (
- simple_invoice.invoice_legal_monetary_total.line_extension_amount)
- assert form.Amount(1_785_000) == (
- simple_invoice.invoice_legal_monetary_total.payable_amount)
-
+ assert form.Amount(1_500_000) == simple_invoice.invoice_legal_monetary_total.line_extension_amount
+ assert form.Amount(1_785_000) == simple_invoice.invoice_legal_monetary_total.payable_amount
def test_invoice_cufe(simple_invoice_without_lines):
simple_invoice = simple_invoice_without_lines
simple_invoice.invoice_ident = '323200000129'
- simple_invoice.invoice_issue = datetime.strptime(
- '2019-01-16 10:53:10-05:00', '%Y-%m-%d %H:%M:%S%z')
- simple_invoice.invoice_supplier.ident = form.PartyIdentification(
- '700085371', '5', '31')
- simple_invoice.invoice_customer.ident = form.PartyIdentification(
- '800199436', '5', '31')
+ simple_invoice.invoice_issue = datetime.strptime('2019-01-16 10:53:10-05:00', '%Y-%m-%d %H:%M:%S%z')
+ simple_invoice.invoice_supplier.ident = form.PartyIdentification('700085371', '5', '31')
+ simple_invoice.invoice_customer.ident = form.PartyIdentification('800199436', '5', '31')
simple_invoice.add_invoice_line(form.InvoiceLine(
- quantity=form.Quantity(
- 1.00, '94'),
- description='producto',
- item=form.StandardItem(111),
- price=form.Price(form.Amount(1_500_000), '01', ''),
- tax=form.TaxTotal(
- subtotals=[
+ quantity = form.Quantity(1.00, '94'),
+ description = 'producto',
+ item = form.StandardItem(111),
+ price = form.Price(form.Amount(1_500_000), '01', ''),
+ tax = form.TaxTotal(
+ subtotals = [
form.TaxSubTotal(
- scheme=form.TaxScheme('01'),
- percent=19.0
- )]),
- withholding=form.WithholdingTaxTotal(
- subtotals=[])
+ scheme = form.TaxScheme('01'),
+ percent = 19.0
+ )])
))
simple_invoice.calculate()
@@ -177,86 +133,65 @@ def test_invoice_cufe(simple_invoice_without_lines):
cufe_extension = fe.DianXMLExtensionCUFE(
simple_invoice,
- tipo_ambiente=fe.AMBIENTE_PRODUCCION,
- clave_tecnica='693ff6f2a553c3646a063436fd4dd9ded0311471'
+ tipo_ambiente = fe.AMBIENTE_PRODUCCION,
+ clave_tecnica = '693ff6f2a553c3646a063436fd4dd9ded0311471'
)
formatVars = cufe_extension.formatVars()
-
- # NumFac
+ #NumFac
assert formatVars[0] == '323200000129', "NumFac"
-
- # FecFac
+ #FecFac
assert formatVars[1] == '2019-01-16', "FecFac"
-
- # HoraFac
+ #HoraFac
assert formatVars[2] == '10:53:10-05:00', "HoraFac"
-
- # ValorBruto
+ #ValorBruto
assert formatVars[3] == '1500000.00', "ValorBruto"
-
- # CodImpuesto1
+ #CodImpuesto1
assert formatVars[4] == '01', "CodImpuesto1"
-
- # ValorImpuesto1
+ #ValorImpuesto1
assert formatVars[5] == '285000.00', "ValorImpuesto1"
-
- # CodImpuesto2
+ #CodImpuesto2
assert formatVars[6] == '04', "CodImpuesto2"
-
- # ValorImpuesto2
+ #ValorImpuesto2
assert formatVars[7] == '0.00', "ValorImpuesto2"
-
- # CodImpuesto3
+ #CodImpuesto3
assert formatVars[8] == '03', "CodImpuesto3"
-
- # ValorImpuesto3
+ #ValorImpuesto3
assert formatVars[9] == '0.00', "ValorImpuesto3"
-
- # ValTotFac
+ #ValTotFac
assert formatVars[10] == '1785000.00', "ValTotFac"
-
- # NitOFE
+ #NitOFE
assert formatVars[11] == '700085371', "NitOFE"
-
- # NumAdq
+ #NumAdq
assert formatVars[12] == '800199436', "NumAdq"
-
- # ClTec
+ #ClTec
assert formatVars[13] == '693ff6f2a553c3646a063436fd4dd9ded0311471', "ClTec"
-
- # TipoAmbiente
+ #TipoAmbiente
assert formatVars[14] == '1', "TipoAmbiente"
xml_invoice.add_extension(cufe_extension)
cufe = xml_invoice.get_element_text('/fe:Invoice/cbc:UUID')
# RESOLUCION 004: pagina 689
- assert cufe == CUFE_
+ assert cufe == '8bb918b19ba22a694f1da11c643b5e9de39adf60311cf179179e9b33381030bcd4c3c3f156c506ed5908f9276f5bd9b4'
+
def test_credit_note_cude(simple_credit_note_without_lines):
simple_invoice = simple_credit_note_without_lines
simple_invoice.invoice_ident = '8110007871'
- simple_invoice.invoice_issue = datetime.strptime(
- '2019-01-12 07:00:00-05:00', '%Y-%m-%d %H:%M:%S%z')
- simple_invoice.invoice_supplier.ident = form.PartyIdentification(
- '900373076', '5', '31')
- simple_invoice.invoice_customer.ident = form.PartyIdentification(
- '8355990', '5', '31')
+ simple_invoice.invoice_issue = datetime.strptime('2019-01-12 07:00:00-05:00', '%Y-%m-%d %H:%M:%S%z')
+ simple_invoice.invoice_supplier.ident = form.PartyIdentification('900373076', '5', '31')
+ simple_invoice.invoice_customer.ident = form.PartyIdentification('8355990', '5', '31')
simple_invoice.add_invoice_line(form.InvoiceLine(
- quantity=form.Quantity(
- 1, '94'),
- description='producto',
- item=form.StandardItem(111),
- price=form.Price(
- form.Amount(5_000), '01', ''),
- tax=form.TaxTotal(
- subtotals=[
+ quantity = form.Quantity(1, '94'),
+ description = 'producto',
+ item = form.StandardItem(111),
+ price = form.Price(form.Amount(5_000), '01', ''),
+ tax = form.TaxTotal(
+ subtotals = [
form.TaxSubTotal(
- scheme=form.TaxScheme('01'),
- percent=19.0
- )]),
- withholding=form.WithholdingTaxTotal(
- subtotals=[])
+ scheme = form.TaxScheme('01'),
+ percent = 19.0
+ )])
))
simple_invoice.calculate()
@@ -265,39 +200,33 @@ def test_credit_note_cude(simple_credit_note_without_lines):
cude_extension = fe.DianXMLExtensionCUDE(
simple_invoice,
'12301',
- tipo_ambiente=fe.AMBIENTE_PRODUCCION,
+ tipo_ambiente = fe.AMBIENTE_PRODUCCION,
)
xml_invoice.add_extension(cude_extension)
cude = xml_invoice.get_element_text('/fe:CreditNote/cbc:UUID')
# pag 612
-
- assert cude == CUDE_
+ assert cude == '907e4444decc9e59c160a2fb3b6659b33dc5b632a5008922b9a62f83f757b1c448e47f5867f2b50dbdb96f48c7681168'
# pag 614
def test_debit_note_cude(simple_debit_note_without_lines):
simple_invoice = simple_debit_note_without_lines
simple_invoice.invoice_ident = 'ND1001'
- simple_invoice.invoice_issue = datetime.strptime(
- '2019-01-18 10:58:00-05:00', '%Y-%m-%d %H:%M:%S%z')
- simple_invoice.invoice_supplier.ident = form.PartyIdentification(
- '900197264', '5', '31')
- simple_invoice.invoice_customer.ident = form.PartyIdentification(
- '10254102', '5', '31')
+ simple_invoice.invoice_issue = datetime.strptime('2019-01-18 10:58:00-05:00', '%Y-%m-%d %H:%M:%S%z')
+ simple_invoice.invoice_supplier.ident = form.PartyIdentification('900197264', '5', '31')
+ simple_invoice.invoice_customer.ident = form.PartyIdentification('10254102', '5', '31')
simple_invoice.add_invoice_line(form.InvoiceLine(
- quantity=form.Quantity(1, '94'),
- description='producto',
- item=form.StandardItem(111),
- price=form.Price(form.Amount(30_000), '01', ''),
- tax=form.TaxTotal(
- subtotals=[
+ quantity = form.Quantity(1, '94'),
+ description = 'producto',
+ item = form.StandardItem(111),
+ price = form.Price(form.Amount(30_000), '01', ''),
+ tax = form.TaxTotal(
+ subtotals = [
form.TaxSubTotal(
- scheme=form.TaxScheme('04'),
- percent=8.0
- )]),
- withholding=form.WithholdingTaxTotal(
- subtotals=[])
+ scheme = form.TaxScheme('04'),
+ percent = 8.0
+ )])
))
simple_invoice.calculate()
@@ -306,7 +235,7 @@ def test_debit_note_cude(simple_debit_note_without_lines):
cude_extension = fe.DianXMLExtensionCUDE(
simple_invoice,
'10201',
- tipo_ambiente=fe.AMBIENTE_PRUEBAS,
+ tipo_ambiente = fe.AMBIENTE_PRUEBAS,
)
build_vars = cude_extension.buildVars()
assert build_vars['NumFac'] == 'ND1001'
@@ -322,7 +251,8 @@ def test_debit_note_cude(simple_debit_note_without_lines):
assert build_vars['Software-PIN'] == '10201'
assert build_vars['TipoAmb'] == 2
- cude_composicion = "".join(cude_extension.formatVars())
+
+ cude_composicion = "".join(cude_extension.formatVars())
assert cude_composicion == 'ND10012019-01-1810:58:00-05:0030000.00010.00042400.00030.0032400.0090019726410254102102012'
xml_invoice.add_extension(cude_extension)
diff --git a/tests/test_form.py b/tests/test_form.py
index 2e2559a..0476db8 100644
--- a/tests/test_form.py
+++ b/tests/test_form.py
@@ -6,117 +6,91 @@
"""Tests for `facho` package."""
import pytest
-# from datetime import datetime
-# import io
-# import zipfile
+from datetime import datetime
+import io
+import zipfile
import facho.fe.form as form
-# from facho import fe
-
-from fixtures import (
- simple_invoice,
- simple_invoice_without_lines,
- simple_credit_note_without_lines,
- simple_debit_note_without_lines)
-
-simple_invoice = simple_invoice
-simple_invoice_without_lines = simple_invoice_without_lines
-simple_credit_note_without_lines = simple_credit_note_without_lines
-simple_debit_note_without_lines = simple_debit_note_without_lines
-
+from facho import fe
def test_invoice_legalmonetary():
inv = form.NationalSalesInvoice()
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'
+ 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=[
+ tax = form.TaxTotal(
+ subtotals = [
form.TaxSubTotal(
- percent=19.0,
- )]),
- withholding=form.WithholdingTaxTotal(
- subtotals=[])
+ percent = 19.0,
+ )
+ ]
+ )
))
-
inv.calculate()
- assert inv.invoice_legal_monetary_total.line_extension_amount == (
- form.Amount(100.0))
- assert inv.invoice_legal_monetary_total.tax_exclusive_amount == (
- form.Amount(100.0))
- assert inv.invoice_legal_monetary_total.tax_inclusive_amount == (
- form.Amount(119.0))
- assert inv.invoice_legal_monetary_total.charge_total_amount == (
- form.Amount(0.0))
-
+ assert inv.invoice_legal_monetary_total.line_extension_amount == form.Amount(100.0)
+ assert inv.invoice_legal_monetary_total.tax_exclusive_amount == form.Amount(100.0)
+ assert inv.invoice_legal_monetary_total.tax_inclusive_amount == form.Amount(119.0)
+ assert inv.invoice_legal_monetary_total.charge_total_amount == form.Amount(0.0)
def test_allowancecharge_as_discount():
discount = form.AllowanceChargeAsDiscount(amount=form.Amount(1000.0))
-
- assert discount.isDiscount()
-
-
+ assert discount.isDiscount() == True
+
def test_FAU10():
inv = form.NationalSalesInvoice()
inv.add_invoice_line(form.InvoiceLine(
- quantity=form.Quantity(1, '94'),
- description='productofacho',
- item=form.StandardItem(9999),
- price=form.Price(
- amount=form.Amount(100.0),
- type_code='01',
- type='x'
+ 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=[
+ tax = form.TaxTotal(
+ subtotals = [
form.TaxSubTotal(
- percent=19.0)]),
- withholding=form.WithholdingTaxTotal(
- subtotals=[])
+ percent = 19.0,
+ )
+ ]
+ )
))
-
inv.add_allowance_charge(form.AllowanceCharge(amount=form.Amount(19.0)))
inv.calculate()
- assert inv.invoice_legal_monetary_total.line_extension_amount == (
- form.Amount(100.0))
- assert inv.invoice_legal_monetary_total.tax_exclusive_amount == (
- form.Amount(100.0))
- assert inv.invoice_legal_monetary_total.tax_inclusive_amount == (
- form.Amount(119.0))
- assert inv.invoice_legal_monetary_total.charge_total_amount == (
- form.Amount(19.0))
+ assert inv.invoice_legal_monetary_total.line_extension_amount == form.Amount(100.0)
+ assert inv.invoice_legal_monetary_total.tax_exclusive_amount == form.Amount(100.0)
+ assert inv.invoice_legal_monetary_total.tax_inclusive_amount == form.Amount(119.0)
+ assert inv.invoice_legal_monetary_total.charge_total_amount == form.Amount(19.0)
def test_FAU14():
inv = form.NationalSalesInvoice()
inv.add_invoice_line(form.InvoiceLine(
- quantity=form.Quantity(1, '94'),
- description='productofacho',
- item=form.StandardItem(9999),
- price=form.Price(
- amount=form.Amount(100.0),
- type_code='01',
- type='x'
+ 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=[
+ tax = form.TaxTotal(
+ subtotals = [
form.TaxSubTotal(
- percent=19.0,
- )]),
- withholding=form.WithholdingTaxTotal(
- subtotals=[])
+ percent = 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_allowance_charge(form.AllowanceCharge(amount=form.Amount(19.0)))
+ inv.add_prepaid_payment(form.PrePaidPayment(paid_amount = form.Amount(50.0)))
inv.calculate()
wants = form.Amount(119.0 + 19.0 - 50.0)
@@ -126,42 +100,38 @@ def test_FAU14():
def test_invalid_tipo_operacion_nota_debito():
reference = form.InvoiceDocumentReference(
- ident='11111',
- uuid='21312312',
- date='2020-05-05'
+ ident = '11111',
+ uuid = '21312312',
+ date = '2020-05-05'
)
inv = form.DebitNote(reference)
with pytest.raises(ValueError):
inv.set_operation_type(22)
-
def test_valid_tipo_operacion_nota_debito():
reference = form.InvoiceDocumentReference(
- ident='11111',
- uuid='21312312',
- date='2020-05-05'
+ ident = '11111',
+ uuid = '21312312',
+ date = '2020-05-05'
)
inv = form.DebitNote(reference)
inv.set_operation_type('30')
-
def test_invalid_tipo_operacion_nota_credito():
reference = form.InvoiceDocumentReference(
- ident='11111',
- uuid='21312312',
- date='2020-05-05'
+ ident = '11111',
+ uuid = '21312312',
+ date = '2020-05-05'
)
-
inv = form.DebitNote(reference)
with pytest.raises(ValueError):
inv.set_operation_type('990')
-
def test_valid_tipo_operacion_nota_credito():
reference = form.InvoiceDocumentReference(
- ident='11111',
- uuid='21312312',
- date='2020-05-05'
+ ident = '11111',
+ uuid = '21312312',
+ date = '2020-05-05'
)
inv = form.CreditNote(reference)
inv.set_operation_type('20')
@@ -171,52 +141,41 @@ def test_quantity():
quantity1 = form.Quantity(10, '94')
assert quantity1 * form.Amount(3) == form.Amount(30)
-
def test_invoice_line_quantity_without_taxes():
line = form.InvoiceLine(
- quantity=form.Quantity(10, '94'),
- description='',
- item=form.StandardItem('test', 9999),
- price=form.Price(
- amount=form.Amount(30.00),
- type_code='01',
- type='x'),
- tax=form.TaxTotal(subtotals=[]),
- withholding=form.WithholdingTaxTotal(
- subtotals=[])
- )
+ quantity = form.Quantity(10, '94'),
+ description = '',
+ item = form.StandardItem('test', 9999),
+ price = form.Price(
+ amount = form.Amount(30.00),
+ type_code = '01',
+ type = 'x'
+ ),
+ tax = form.TaxTotal(subtotals=[]))
line.calculate()
assert line.total_amount == form.Amount(300)
assert line.tax_amount == form.Amount(0)
-
def test_invoice_legalmonetary_with_taxes():
inv = form.NationalSalesInvoice()
inv.add_invoice_line(form.InvoiceLine(
- quantity=form.Quantity(1, '94'),
- description='productofacho',
- item=form.StandardItem(9999),
- price=form.Price(
- amount=form.Amount(100.0),
- type_code='01',
- type='x'
+ 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=[]),
- withholding=form.WithholdingTaxTotal(
- subtotals=[])
+ tax = form.TaxTotal(subtotals=[])
))
inv.calculate()
- assert inv.invoice_legal_monetary_total.line_extension_amount == (
- form.Amount(100.0))
- assert inv.invoice_legal_monetary_total.tax_exclusive_amount == (
- form.Amount(100.0))
- assert inv.invoice_legal_monetary_total.tax_inclusive_amount == (
- form.Amount(100.0))
- assert inv.invoice_legal_monetary_total.charge_total_amount == (
- form.Amount(0.0))
- assert inv.invoice_legal_monetary_total.payable_amount == (
- form.Amount(100.0))
+ assert inv.invoice_legal_monetary_total.line_extension_amount == form.Amount(100.0)
+ assert inv.invoice_legal_monetary_total.tax_exclusive_amount == form.Amount(100.0)
+ assert inv.invoice_legal_monetary_total.tax_inclusive_amount == form.Amount(100.0)
+ assert inv.invoice_legal_monetary_total.charge_total_amount == form.Amount(0.0)
+ assert inv.invoice_legal_monetary_total.payable_amount == form.Amount(100.0)
def test_invoice_ident_prefix_automatic_invalid():
@@ -224,7 +183,6 @@ def test_invoice_ident_prefix_automatic_invalid():
with pytest.raises(ValueError):
inv.set_ident('SETPQJQJ1234567')
-
def test_invoice_ident_prefix_automatic():
inv = form.NationalSalesInvoice()
inv.set_ident('SETP1234567')
@@ -242,15 +200,13 @@ def test_invoice_ident_prefix_automatic():
inv.set_ident('1234567')
assert inv.invoice_ident_prefix == ''
-
def test_invoice_ident_prefix_manual():
inv = form.NationalSalesInvoice()
inv.set_ident('SETP1234567')
inv.set_ident_prefix('SETA')
assert inv.invoice_ident_prefix == 'SETA'
-
def test_invoice_ident_prefix_automatic_debit():
- inv = form.DebitNote(form.BillingReference('', '', ''))
+ inv = form.DebitNote(form.BillingReference('','',''))
inv.set_ident('ABCDEF1234567')
assert inv.invoice_ident_prefix == 'ABCDEF'
diff --git a/tests/test_form_xml.py b/tests/test_form_xml.py
index 44a4ccc..74f8d9f 100644
--- a/tests/test_form_xml.py
+++ b/tests/test_form_xml.py
@@ -6,24 +6,13 @@
"""Tests for `facho` package."""
import pytest
-# from datetime import datetime
+from datetime import datetime
import copy
from facho.fe import form
from facho.fe import form_xml
-# from fixtures import *
-
-from fixtures import (
- simple_invoice,
- simple_invoice_without_lines,
- simple_credit_note_without_lines,
- simple_debit_note_without_lines)
-
-simple_invoice = simple_invoice
-simple_invoice_without_lines = simple_invoice_without_lines
-simple_credit_note_without_lines = simple_credit_note_without_lines
-simple_debit_note_without_lines = simple_debit_note_without_lines
+from fixtures import *
def test_import_DIANInvoiceXML():
try:
@@ -38,82 +27,70 @@ def test_import_DIANDebitNoteXML():
except AttributeError:
pytest.fail("unexpected not found")
-
def test_import_DIANCreditNoteXML():
try:
form_xml.DIANCreditNoteXML
except AttributeError:
pytest.fail("unexpected not found")
+def test_allowance_charge_in_invoice(simple_invoice_without_lines):
+ inv = copy.copy(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)))
+ inv.calculate()
+
+ 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'
+ assert xml.get_element_text('./cac:AllowanceCharge/cbc:BaseAmount') == '100.0'
-# def test_allowance_charge_in_invoice(simple_invoice_without_lines):
-# inv = copy.copy(simple_invoice_without_lines)
-# inv.add_invoice_line(form.InvoiceLine(
-# quantity=form.Quantity(1, '94'),
-# description='productofacho',
-# 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,
-# )]),
-# withholding=form.WithholdingTaxTotal(
-# subtotals=[])
-# ))
+def test_allowance_charge_in_invoice_line(simple_invoice_without_lines):
+ inv = copy.copy(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,
+ )
+ ]
+ ),
+ allowance_charge = [
+ form.AllowanceChargeAsDiscount(amount=form.Amount(10.0))
+ ]
+ ))
+ inv.calculate()
-# inv.add_allowance_charge(form.AllowanceCharge(amount=form.Amount(19.0)))
-# inv.calculate()
+ # se aplico descuento
+ assert inv.invoice_legal_monetary_total.line_extension_amount == form.Amount(90.0)
+
+ xml = form_xml.DIANInvoiceXML(inv)
-# 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'
-# assert xml.get_element_text(
-# './cac:AllowanceCharge/cbc:BaseAmount') == '100.0'
-
-
-# def test_allowance_charge_in_invoice_line(simple_invoice_without_lines):
-# inv = copy.copy(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,
-# )]),
-# withholding=form.WithholdingTaxTotal(
-# subtotals=[]),
-# allowance_charge=[
-# form.AllowanceChargeAsDiscount(amount=form.Amount(10.0))
-# ]
-# ))
-# inv.calculate()
-
-# # se aplico descuento
-# assert inv.invoice_legal_monetary_total.line_extension_amount == (
-# form.Amount(90.0))
-
-# xml = form_xml.DIANInvoiceXML(inv)
-
-# with pytest.raises(AttributeError):
-# assert xml.get_element_text(
-# '/fe:Invoice/cac:AllowanceCharge/cbc:ID') == '1'
-# xml.get_element_text(
-# '/fe:Invoice/cac:InvoiceLine/cac:AllowanceCharge/cbc:ID') == '1'
-# xml.get_element_text(
-# '/fe:Invoice/cac:InvoiceLine/cac:AllowanceCharge/cbc:BaseAmount'
-# ) == '100.0'
+ with pytest.raises(AttributeError):
+ assert xml.get_element_text('/fe:Invoice/cac:AllowanceCharge/cbc:ID') == '1'
+ xml.get_element_text('/fe:Invoice/cac:InvoiceLine/cac:AllowanceCharge/cbc:ID') == '1'
+ xml.get_element_text('/fe:Invoice/cac:InvoiceLine/cac:AllowanceCharge/cbc:BaseAmount') == '100.0'
diff --git a/tests/test_nomina.py b/tests/test_nomina.py
index 961bb77..a924ece 100644
--- a/tests/test_nomina.py
+++ b/tests/test_nomina.py
@@ -4,467 +4,597 @@
# this repository contains the full copyright notices and license terms.
"""Tests for `facho` package."""
-# import re
-# import pytest
+import pytest
-# from facho import fe
+from facho import fe
-# import helpers
+import helpers
-# def assert_error(errors, msg):
-# for error in errors:
-# if str(error) == msg:
-# return True
+def assert_error(errors, msg):
+ for error in errors:
+ if str(error) == msg:
+ return True
-# raise "wants error: %s" % (msg)
+ raise "wants error: %s" % (msg)
-# def test_adicionar_devengado_Basico():
-# nomina = fe.nomina.DIANNominaIndividual()
+def test_adicionar_devengado_Basico():
+ nomina = fe.nomina.DIANNominaIndividual()
-# nomina.adicionar_devengado(fe.nomina.DevengadoBasico(
-# dias_trabajados = 30,
-# sueldo_trabajado = fe.nomina.Amount(1_000_000)
-# ))
+ nomina.adicionar_devengado(fe.nomina.DevengadoBasico(
+ dias_trabajados = 30,
+ sueldo_trabajado = fe.nomina.Amount(1_000_000)
+ ))
-# xml = nomina.toFachoXML()
-# assert xml.get_element_attribute('/nomina:NominaIndividual/Devengados/Basico', 'DiasTrabajados') == '30'
-# assert xml.get_element_attribute('/nomina:NominaIndividual/Devengados/Basico', 'SueldoTrabajado') == '1000000.00'
+ xml = nomina.toFachoXML()
+ assert xml.get_element_attribute('/fe:NominaIndividual/Devengados/Basico', 'DiasTrabajados') == '30'
+ assert xml.get_element_attribute('/fe:NominaIndividual/Devengados/Basico', 'SueldoTrabajado') == '1000000.00'
-# def test_adicionar_devengado_transporte():
-# nomina = fe.nomina.DIANNominaIndividual()
+def test_adicionar_devengado_transporte():
+ nomina = fe.nomina.DIANNominaIndividual()
-# nomina.adicionar_devengado(fe.nomina.DevengadoTransporte(
-# auxilio_transporte = fe.nomina.Amount(2_000_000)
-# ))
+ nomina.adicionar_devengado(fe.nomina.DevengadoTransporte(
+ auxilio_transporte = fe.nomina.Amount(2_000_000)
+ ))
-# xml = nomina.toFachoXML()
+ xml = nomina.toFachoXML()
-# assert xml.get_element_attribute('/nomina:NominaIndividual/Devengados/Transporte', 'AuxilioTransporte') == '2000000.0'
+ assert xml.get_element_attribute('/fe:NominaIndividual/Devengados/Transporte', 'AuxilioTransporte') == '2000000.0'
-# def test_adicionar_devengado_comprobante_total():
-# nomina = fe.nomina.DIANNominaIndividual()
+def test_adicionar_devengado_comprobante_total():
+ nomina = fe.nomina.DIANNominaIndividual()
-# nomina.adicionar_devengado(fe.nomina.DevengadoBasico(
-# dias_trabajados = 60,
-# sueldo_trabajado = fe.nomina.Amount(2_000_000)
-# ))
+ nomina.adicionar_devengado(fe.nomina.DevengadoBasico(
+ dias_trabajados = 60,
+ sueldo_trabajado = fe.nomina.Amount(2_000_000)
+ ))
-# nomina.adicionar_deduccion(fe.nomina.DeduccionSalud(
-# porcentaje = fe.nomina.Amount(19),
-# deduccion = fe.nomina.Amount(1_000_000)
-# ))
+ nomina.adicionar_deduccion(fe.nomina.DeduccionSalud(
+ porcentaje = fe.nomina.Amount(19),
+ deduccion = fe.nomina.Amount(1_000_000)
+ ))
-
-# xml = nomina.toFachoXML()
-
-# assert xml.get_element_text('/nomina:NominaIndividual/ComprobanteTotal') == '1000000.00'
-
-# def test_adicionar_devengado_comprobante_total_cero():
-# nomina = fe.nomina.DIANNominaIndividual()
-
-# nomina.adicionar_devengado(fe.nomina.DevengadoBasico(
-# dias_trabajados = 60,
-# sueldo_trabajado = fe.nomina.Amount(1_000_000)
-# ))
-
-# nomina.adicionar_deduccion(fe.nomina.DeduccionSalud(
-# porcentaje = fe.nomina.Amount(19),
-# deduccion = fe.nomina.Amount(1_000_000)
-# ))
-
-# xml = nomina.toFachoXML()
-
-# assert xml.get_element_text('/nomina:NominaIndividual/ComprobanteTotal') == '0.00'
-
-# def test_adicionar_devengado_transporte_muchos():
-# nomina = fe.nomina.DIANNominaIndividual()
-
-# nomina.adicionar_devengado(fe.nomina.DevengadoTransporte(
-# auxilio_transporte = fe.nomina.Amount(2_000_000)
-# ))
-
-# nomina.adicionar_devengado(fe.nomina.DevengadoTransporte(
-# auxilio_transporte = fe.nomina.Amount(3_000_000)
-# ))
-
-# xml = nomina.toFachoXML()
-# print(xml)
-# assert xml.get_element_text('/nomina:NominaIndividual/DevengadosTotal') == '5000000.00'
-
-# def test_adicionar_deduccion_salud():
-# nomina = fe.nomina.DIANNominaIndividual()
-
-# nomina.adicionar_devengado(fe.nomina.DevengadoBasico(
-# dias_trabajados = 60,
-# sueldo_trabajado = fe.nomina.Amount(1000)
-# ))
-
-# nomina.adicionar_deduccion(fe.nomina.DeduccionSalud(
-# porcentaje = fe.nomina.Amount(19),
-# deduccion = fe.nomina.Amount(1000)
-# ))
-
-# xml = nomina.toFachoXML()
-# print(xml)
-# assert xml.get_element_text('/nomina:NominaIndividual/DeduccionesTotal') == '1000.00'
-
-# def test_nomina_obligatorios_segun_anexo_tecnico():
-# nomina = fe.nomina.DIANNominaIndividual()
-
-# errors = nomina.validate()
-
-# assert_error(errors, 'se requiere Periodo')
-# assert_error(errors, 'se requiere DevengadoBasico')
-# assert_error(errors, 'se requiere DeduccionSalud')
-# assert_error(errors, 'se requiere DeduccionFondoPension')
-
-# def test_nomina_xml():
-# nomina = fe.nomina.DIANNominaIndividual()
-
-# nomina.asignar_metadata(fe.nomina.Metadata(
-# novedad=fe.nomina.Novedad(
-# activa = True,
-# cune = "N0111"
-# ),
-# secuencia=fe.nomina.NumeroSecuencia(
-# prefijo = 'N',
-# consecutivo='00001'
-# ),
-# lugar_generacion=fe.nomina.Lugar(
-# pais = fe.nomina.Pais(
-# code = 'CO'
-# ),
-# departamento = fe.nomina.Departamento(
-# code = '05'
-# ),
-# municipio = fe.nomina.Municipio(
-# code = '05001'
-# ),
-# ),
-# proveedor=fe.nomina.Proveedor(
-# nit='999999',
-# dv=2,
-# software_id='xx',
-# software_pin='12',
-# razon_social='facho'
-# )
-# ))
-
-# nomina.asignar_informacion_general(fe.nomina.InformacionGeneral(
-# fecha_generacion = '2020-01-16',
-# hora_generacion = '1053:10-05:00',
-# tipo_ambiente = fe.nomina.InformacionGeneral.AMBIENTE_PRODUCCION,
-# software_pin = '693',
-# tipo_xml = fe.nomina.InformacionGeneral.TIPO_XML_NORMAL,
-# periodo_nomina = fe.nomina.PeriodoNomina(code='1'),
-# tipo_moneda = fe.nomina.TipoMoneda(code='COP')
-# ))
-
-# nomina.asignar_empleador(fe.nomina.Empleador(
-# razon_social='facho',
-# nit = '700085371',
-# dv = '1',
-# pais = fe.nomina.Pais(
-# code = 'CO'
-# ),
-# departamento = fe.nomina.Departamento(
-# code = '05'
-# ),
-# municipio = fe.nomina.Municipio(
-# code = '05001'
-# ),
-# direccion = 'calle etrivial'
-# ))
-
-# nomina.asignar_trabajador(fe.nomina.Trabajador(
-# tipo_contrato = fe.nomina.TipoContrato(
-# code = '1'
-# ),
-# alto_riesgo = False,
-# tipo_documento = fe.nomina.TipoDocumento(
-# code = '11'
-# ),
-# primer_apellido = 'gnu',
-# segundo_apellido = 'emacs',
-# primer_nombre = 'facho',
-# lugar_trabajo = fe.nomina.LugarTrabajo(
-# pais = fe.nomina.Pais(code='CO'),
-# departamento = fe.nomina.Departamento(code='05'),
-# municipio = fe.nomina.Municipio(code='05001'),
-# direccion = 'calle facho'
-# ),
-# numero_documento = '800199436',
-# tipo = fe.nomina.TipoTrabajador(
-# code = '01'
-# ),
-# salario_integral = True,
-# sueldo = fe.nomina.Amount(1_500_000)
-# ))
-
-# nomina.adicionar_devengado(fe.nomina.DevengadoBasico(
-# dias_trabajados = 60,
-# sueldo_trabajado = fe.nomina.Amount(3_500_000)
-# ))
-
-# nomina.adicionar_deduccion(fe.nomina.DeduccionSalud(
-# porcentaje = fe.nomina.Amount(19),
-# deduccion = fe.nomina.Amount(1_000_000)
-# ))
-
-# xml = nomina.toFachoXML()
-# expected_cune = 'b8f9b6c24de07ffd92ea5467433a3b69357cfaffa7c19722db94b2e0eca41d057085a54f484b5da15ff585e773b0b0ab'
-# assert xml.get_element_attribute('/nomina:NominaIndividual/InformacionGeneral', 'CUNE') == expected_cune
-# assert xml.get_element_attribute('/nomina:NominaIndividual/InformacionGeneral', 'TipoXML') == '102'
-# assert xml.get_element_text_or_attribute('/nomina:NominaIndividual/NumeroSecuenciaXML/@Numero') == 'N00001'
-# assert xml.get_element_text_or_attribute('/nomina:NominaIndividual/NumeroSecuenciaXML/@Consecutivo') == '00001'
-# assert xml.get_element_text_or_attribute('/nomina:NominaIndividual/LugarGeneracionXML/@Pais') == 'CO'
-# assert xml.get_element_text_or_attribute('/nomina:NominaIndividual/LugarGeneracionXML/@DepartamentoEstado') == '05'
-# assert xml.get_element_text_or_attribute('/nomina:NominaIndividual/LugarGeneracionXML/@MunicipioCiudad') == '05001'
-# assert xml.get_element_text_or_attribute('/nomina:NominaIndividual/ProveedorXML/@NIT') == '999999'
-# assert xml.get_element_text_or_attribute('/nomina:NominaIndividual/ProveedorXML/@DV') == '2'
-# assert xml.get_element_text_or_attribute('/nomina:NominaIndividual/ProveedorXML/@SoftwareID') == 'xx'
-# assert xml.get_element_text_or_attribute('/nomina:NominaIndividual/ProveedorXML/@SoftwareSC') is not None
-# assert xml.get_element_text_or_attribute('/nomina:NominaIndividual/CodigoQR') == f"https://catalogo-vpfe.dian.gov.co/document/searchqr?documentkey={expected_cune}"
-# assert xml.get_element_text_or_attribute('/nomina:NominaIndividual/Empleador/@NIT') == '700085371'
-# assert xml.get_element_text_or_attribute('/nomina:NominaIndividual/Trabajador/@NumeroDocumento') == '800199436'
-# assert xml.get_element_text_or_attribute('/nomina:NominaIndividual/Novedad') == 'True'
-# assert xml.get_element_text_or_attribute('/nomina:NominaIndividual/Novedad/@CUNENov') == 'N0111'
-
-# # confirmar el namespace
-# assert 'xmlns="dian:gov:co:facturaelectronica:NominaIndividual"' in xml.tostring()
-
-# def test_asignar_pago():
-# nomina = fe.nomina.DIANNominaIndividual()
-# nomina.asignar_pago(fe.nomina.Pago(
-# forma = fe.nomina.FormaPago(code='1'),
-# metodo = fe.nomina.MetodoPago(code='1')
-# ))
-
-# def test_nomina_xmlsign(monkeypatch):
-# nomina = fe.nomina.DIANNominaIndividual()
-# xml = nomina.toFachoXML()
-
-# signer = fe.nomina.DianXMLExtensionSigner('./tests/example.p12')
-# xml.add_extension(signer)
-
-# print(xml.tostring())
-# elem = xml.get_element('/nomina:NominaIndividual/ext:UBLExtensions/ext:UBLExtension/ext:ExtensionContent/ds:Signature')
-# assert elem is not None
-
-
-
-# def test_nomina_devengado_horas_extras_nocturnas():
-# nomina = fe.nomina.DIANNominaIndividual()
-
-# nomina.adicionar_devengado(fe.nomina.DevengadoHorasExtrasNocturnas(
-# horas_extras=[
-# fe.nomina.DevengadoHoraExtra(
-# hora_inicio='2021-11-30T19:09:55',
-# hora_fin='2021-11-30T20:09:55',
-# cantidad=1,
-# porcentaje=fe.nomina.Amount(1),
-# pago=fe.nomina.Amount(100)
-# ),
-# fe.nomina.DevengadoHoraExtra(
-# hora_inicio='2021-11-30T18:09:55',
-# hora_fin='2021-11-30T19:09:55',
-# cantidad=2,
-# porcentaje=fe.nomina.Amount(2),
-# pago=fe.nomina.Amount(200)
-# )
-# ]
-# ))
-
-# xml = nomina.toFachoXML()
-# extras = xml.get_element('/nomina:NominaIndividual/Devengados/HENs/HEN', multiple=True)
-# assert extras[0].get('HoraInicio') == '2021-11-30T19:09:55'
-# assert extras[0].get('HoraFin') == '2021-11-30T20:09:55'
-# assert extras[0].get('Cantidad') == '1'
-# assert extras[0].get('Porcentaje') == '1.00'
-# assert extras[0].get('Pago') == '100.00'
-# assert extras[1].get('HoraInicio') == '2021-11-30T18:09:55'
-# assert extras[1].get('HoraFin') == '2021-11-30T19:09:55'
-# assert extras[1].get('Cantidad') == '2'
-# assert extras[1].get('Porcentaje') == '2.00'
-# assert extras[1].get('Pago') == '200.00'
-
-# def test_nomina_devengado_horas_recargo_nocturno():
-# nomina = fe.nomina.DIANNominaIndividual()
-
-# nomina.adicionar_devengado(fe.nomina.DevengadoHorasRecargoNocturno(
-# horas_extras=[
-# fe.nomina.DevengadoHoraExtra(
-# hora_inicio='2021-11-30T19:09:55',
-# hora_fin='2021-11-30T20:09:55',
-# cantidad=1,
-# porcentaje=fe.nomina.Amount(1),
-# pago=fe.nomina.Amount(100)
-# ),
-# fe.nomina.DevengadoHoraExtra(
-# hora_inicio='2021-11-30T18:09:55',
-# hora_fin='2021-11-30T19:09:55',
-# cantidad=2,
-# porcentaje=fe.nomina.Amount(2),
-# pago=fe.nomina.Amount(200)
-# )
-# ]
-# ))
-
-# xml = nomina.toFachoXML()
-# extras = xml.get_element('/nomina:NominaIndividual/Devengados/HRNs/HRN', multiple=True)
-# assert extras[0].get('HoraInicio') == '2021-11-30T19:09:55'
-# assert extras[0].get('HoraFin') == '2021-11-30T20:09:55'
-# assert extras[0].get('Cantidad') == '1'
-# assert extras[0].get('Porcentaje') == '1.00'
-# assert extras[0].get('Pago') == '100.00'
-# assert extras[1].get('HoraInicio') == '2021-11-30T18:09:55'
-# assert extras[1].get('HoraFin') == '2021-11-30T19:09:55'
-# assert extras[1].get('Cantidad') == '2'
-# assert extras[1].get('Porcentaje') == '2.00'
-# assert extras[1].get('Pago') == '200.00'
-
-# def test_nomina_devengado_horas_extras_diarias_dominicales_y_festivos():
-# nomina = fe.nomina.DIANNominaIndividual()
-
-# nomina.adicionar_devengado(fe.nomina.DevengadoHorasExtrasDiariasDominicalesYFestivos(
-# horas_extras=[
-# fe.nomina.DevengadoHoraExtra(
-# hora_inicio='2021-11-30T19:09:55',
-# hora_fin='2021-11-30T20:09:55',
-# cantidad=1,
-# porcentaje=fe.nomina.Amount(1),
-# pago=fe.nomina.Amount(100)
-# ),
-# fe.nomina.DevengadoHoraExtra(
-# hora_inicio='2021-11-30T18:09:55',
-# hora_fin='2021-11-30T19:09:55',
-# cantidad=2,
-# porcentaje=fe.nomina.Amount(2),
-# pago=fe.nomina.Amount(200)
-# )
-# ]
-# ))
-
-# xml = nomina.toFachoXML()
-# extras = xml.get_element('/nomina:NominaIndividual/Devengados/HEDDFs/HEDDF', multiple=True)
-# assert extras[0].get('HoraInicio') == '2021-11-30T19:09:55'
-# assert extras[0].get('HoraFin') == '2021-11-30T20:09:55'
-# assert extras[0].get('Cantidad') == '1'
-# assert extras[0].get('Porcentaje') == '1.00'
-# assert extras[0].get('Pago') == '100.00'
-# assert extras[1].get('HoraInicio') == '2021-11-30T18:09:55'
-# assert extras[1].get('HoraFin') == '2021-11-30T19:09:55'
-# assert extras[1].get('Cantidad') == '2'
-# assert extras[1].get('Porcentaje') == '2.00'
-# assert extras[1].get('Pago') == '200.00'
-
-# def test_nomina_devengado_horas_recargo_diarias_dominicales_y_festivos():
-# nomina = fe.nomina.DIANNominaIndividual()
-
-# nomina.adicionar_devengado(fe.nomina.DevengadoHorasRecargoDiariasDominicalesYFestivos(
-# horas_extras=[
-# fe.nomina.DevengadoHoraExtra(
-# hora_inicio='2021-11-30T19:09:55',
-# hora_fin='2021-11-30T20:09:55',
-# cantidad=1,
-# porcentaje=fe.nomina.Amount(1),
-# pago=fe.nomina.Amount(100)
-# ),
-# fe.nomina.DevengadoHoraExtra(
-# hora_inicio='2021-11-30T18:09:55',
-# hora_fin='2021-11-30T19:09:55',
-# cantidad=2,
-# porcentaje=fe.nomina.Amount(2),
-# pago=fe.nomina.Amount(200)
-# )
-# ]
-# ))
-
-# xml = nomina.toFachoXML()
-# extras = xml.get_element('/nomina:NominaIndividual/Devengados/HRDDFs/HRDDF', multiple=True)
-# assert extras[0].get('HoraInicio') == '2021-11-30T19:09:55'
-# assert extras[0].get('HoraFin') == '2021-11-30T20:09:55'
-# assert extras[0].get('Cantidad') == '1'
-# assert extras[0].get('Porcentaje') == '1.00'
-# assert extras[0].get('Pago') == '100.00'
-# assert extras[1].get('HoraInicio') == '2021-11-30T18:09:55'
-# assert extras[1].get('HoraFin') == '2021-11-30T19:09:55'
-# assert extras[1].get('Cantidad') == '2'
-# assert extras[1].get('Porcentaje') == '2.00'
-# assert extras[1].get('Pago') == '200.00'
-
-
-# def test_nomina_devengado_horas_extras_nocturnas_dominicales_y_festivos():
-# nomina = fe.nomina.DIANNominaIndividual()
-
-# nomina.adicionar_devengado(fe.nomina.DevengadoHorasExtrasNocturnasDominicalesYFestivos(
-# horas_extras=[
-# fe.nomina.DevengadoHoraExtra(
-# hora_inicio='2021-11-30T19:09:55',
-# hora_fin='2021-11-30T20:09:55',
-# cantidad=1,
-# porcentaje=fe.nomina.Amount(1),
-# pago=fe.nomina.Amount(100)
-# ),
-# fe.nomina.DevengadoHoraExtra(
-# hora_inicio='2021-11-30T18:09:55',
-# hora_fin='2021-11-30T19:09:55',
-# cantidad=2,
-# porcentaje=fe.nomina.Amount(2),
-# pago=fe.nomina.Amount(200)
-# )
-# ]
-# ))
-
-# xml = nomina.toFachoXML()
-# extras = xml.get_element('/nomina:NominaIndividual/Devengados/HENDFs/HENDF', multiple=True)
-# assert extras[0].get('HoraInicio') == '2021-11-30T19:09:55'
-# assert extras[0].get('HoraFin') == '2021-11-30T20:09:55'
-# assert extras[0].get('Cantidad') == '1'
-# assert extras[0].get('Porcentaje') == '1.00'
-# assert extras[0].get('Pago') == '100.00'
-# assert extras[1].get('HoraInicio') == '2021-11-30T18:09:55'
-# assert extras[1].get('HoraFin') == '2021-11-30T19:09:55'
-# assert extras[1].get('Cantidad') == '2'
-# assert extras[1].get('Porcentaje') == '2.00'
-# assert extras[1].get('Pago') == '200.00'
-
-# def test_nomina_devengado_horas_recargo_nocturno_dominicales_y_festivos():
-# nomina = fe.nomina.DIANNominaIndividual()
-
-# nomina.adicionar_devengado(fe.nomina.DevengadoHorasRecargoNocturnoDominicalesYFestivos(
-# horas_extras=[
-# fe.nomina.DevengadoHoraExtra(
-# hora_inicio='2021-11-30T19:09:55',
-# hora_fin='2021-11-30T20:09:55',
-# cantidad=1,
-# porcentaje=fe.nomina.Amount(1),
-# pago=fe.nomina.Amount(100)
-# ),
-# fe.nomina.DevengadoHoraExtra(
-# hora_inicio='2021-11-30T18:09:55',
-# hora_fin='2021-11-30T19:09:55',
-# cantidad=2,
-# porcentaje=fe.nomina.Amount(2),
-# pago=fe.nomina.Amount(200)
-# )
-# ]
-# ))
-
-# xml = nomina.toFachoXML()
-# extras = xml.get_element('/nomina:NominaIndividual/Devengados/HRNDFs/HRNDF', multiple=True)
-# assert extras[0].get('HoraInicio') == '2021-11-30T19:09:55'
-# assert extras[0].get('HoraFin') == '2021-11-30T20:09:55'
-# assert extras[0].get('Cantidad') == '1'
-# assert extras[0].get('Porcentaje') == '1.00'
-# assert extras[0].get('Pago') == '100.00'
-# assert extras[1].get('HoraInicio') == '2021-11-30T18:09:55'
-# assert extras[1].get('HoraFin') == '2021-11-30T19:09:55'
-# assert extras[1].get('Cantidad') == '2'
-# assert extras[1].get('Porcentaje') == '2.00'
-# assert extras[1].get('Pago') == '200.00'
-
-# def test_fecha_validacion():
-# with pytest.raises(ValueError) as e:
-# fe.nomina.Fecha('535-35-3')
+
+ xml = nomina.toFachoXML()
+
+ assert xml.get_element_text('/fe:NominaIndividual/ComprobanteTotal') == '1000000.00'
+
+def test_adicionar_devengado_comprobante_total_cero():
+ nomina = fe.nomina.DIANNominaIndividual()
+
+ nomina.adicionar_devengado(fe.nomina.DevengadoBasico(
+ dias_trabajados = 60,
+ sueldo_trabajado = fe.nomina.Amount(1_000_000)
+ ))
+
+ nomina.adicionar_deduccion(fe.nomina.DeduccionSalud(
+ porcentaje = fe.nomina.Amount(19),
+ deduccion = fe.nomina.Amount(1_000_000)
+ ))
+
+ xml = nomina.toFachoXML()
+
+ assert xml.get_element_text('/fe:NominaIndividual/ComprobanteTotal') == '0.00'
+
+def test_adicionar_devengado_transporte_muchos():
+ nomina = fe.nomina.DIANNominaIndividual()
+
+ nomina.adicionar_devengado(fe.nomina.DevengadoTransporte(
+ auxilio_transporte = fe.nomina.Amount(2_000_000)
+ ))
+
+ nomina.adicionar_devengado(fe.nomina.DevengadoTransporte(
+ auxilio_transporte = fe.nomina.Amount(3_000_000)
+ ))
+
+ xml = nomina.toFachoXML()
+ print(xml)
+ assert xml.get_element_text('/fe:NominaIndividual/DevengadosTotal') == '5000000.00'
+
+def test_adicionar_deduccion_salud():
+ nomina = fe.nomina.DIANNominaIndividual()
+
+ nomina.adicionar_devengado(fe.nomina.DevengadoBasico(
+ dias_trabajados = 60,
+ sueldo_trabajado = fe.nomina.Amount(1000)
+ ))
+
+ nomina.adicionar_deduccion(fe.nomina.DeduccionSalud(
+ porcentaje = fe.nomina.Amount(19),
+ deduccion = fe.nomina.Amount(1000)
+ ))
+
+ xml = nomina.toFachoXML()
+ print(xml)
+ assert xml.get_element_text('/fe:NominaIndividual/DeduccionesTotal') == '1000.00'
+
+def test_nomina_obligatorios_segun_anexo_tecnico():
+ nomina = fe.nomina.DIANNominaIndividual()
+
+ errors = nomina.validate()
+
+ assert_error(errors, 'se requiere Periodo')
+ assert_error(errors, 'se requiere DevengadoBasico')
+ assert_error(errors, 'se requiere DeduccionSalud')
+ assert_error(errors, 'se requiere DeduccionFondoPension')
+
+def test_nomina_xml():
+ nomina = fe.nomina.DIANNominaIndividual()
+
+ nomina.asignar_metadata(fe.nomina.Metadata(
+ secuencia=fe.nomina.NumeroSecuencia(
+ prefijo = 'N',
+ consecutivo='00001'
+ ),
+ lugar_generacion=fe.nomina.Lugar(
+ pais = fe.nomina.Pais(
+ code = 'CO'
+ ),
+ departamento = fe.nomina.Departamento(
+ code = '05'
+ ),
+ municipio = fe.nomina.Municipio(
+ code = '05001'
+ ),
+ ),
+ proveedor=fe.nomina.Proveedor(
+ nit='999999',
+ dv=2,
+ software_id='xx',
+ software_pin='12',
+ razon_social='facho'
+ )
+ ))
+
+ nomina.asignar_informacion_general(fe.nomina.InformacionGeneral(
+ fecha_generacion = '2020-01-16',
+ hora_generacion = '1053:10-05:00',
+ tipo_ambiente = fe.nomina.InformacionGeneral.AMBIENTE_PRODUCCION,
+ software_pin = '693',
+ periodo_nomina = fe.nomina.PeriodoNomina(code='1'),
+ tipo_moneda = fe.nomina.TipoMoneda(code='COP')
+ ))
+
+ nomina.asignar_empleador(fe.nomina.Empleador(
+ razon_social='facho',
+ nit = '700085371',
+ dv = '1',
+ pais = fe.nomina.Pais(
+ code = 'CO'
+ ),
+ departamento = fe.nomina.Departamento(
+ code = '05'
+ ),
+ municipio = fe.nomina.Municipio(
+ code = '05001'
+ ),
+ direccion = 'calle etrivial'
+ ))
+
+ nomina.asignar_trabajador(fe.nomina.Trabajador(
+ tipo_contrato = fe.nomina.TipoContrato(
+ code = '1'
+ ),
+ alto_riesgo = False,
+ tipo_documento = fe.nomina.TipoDocumento(
+ code = '11'
+ ),
+ primer_apellido = 'gnu',
+ segundo_apellido = 'emacs',
+ primer_nombre = 'facho',
+ lugar_trabajo = fe.nomina.LugarTrabajo(
+ pais = fe.nomina.Pais(code='CO'),
+ departamento = fe.nomina.Departamento(code='05'),
+ municipio = fe.nomina.Municipio(code='05001'),
+ direccion = 'calle facho'
+ ),
+ numero_documento = '800199436',
+ tipo = fe.nomina.TipoTrabajador(
+ code = '01'
+ ),
+ salario_integral = True,
+ sueldo = fe.nomina.Amount(1_500_000)
+ ))
+
+ nomina.adicionar_devengado(fe.nomina.DevengadoBasico(
+ dias_trabajados = 60,
+ sueldo_trabajado = fe.nomina.Amount(3_500_000)
+ ))
+
+ nomina.adicionar_deduccion(fe.nomina.DeduccionSalud(
+ porcentaje = fe.nomina.Amount(19),
+ deduccion = fe.nomina.Amount(1_000_000)
+ ))
+
+ xml = nomina.toFachoXML()
+ # TODO(bit4bit) no logro generar cune igual al del anexo tecnico
+ #assert xml.get_element_attribute('/fe:NominaIndividual/InformacionGeneral', 'CUNE') == '16560dc8956122e84ffb743c817fe7d494e058a44d9ca3fa4c234c268b4f766003253fbee7ea4af9682dd57210f3bac2'
+
+ expected_cune = 'b8f9b6c24de07ffd92ea5467433a3b69357cfaffa7c19722db94b2e0eca41d057085a54f484b5da15ff585e773b0b0ab'
+ assert xml.get_element_attribute('/fe:NominaIndividual/InformacionGeneral', 'fachoCUNE') == "N000012020-01-161053:10-05:003500000.001000000.002500000.007000853718001994361026931"
+ assert xml.get_element_attribute('/fe:NominaIndividual/InformacionGeneral', 'CUNE') == expected_cune
+ assert xml.get_element_text_or_attribute('/fe:NominaIndividual/NumeroSecuenciaXML/@Numero') == 'N00001'
+ assert xml.get_element_text_or_attribute('/fe:NominaIndividual/NumeroSecuenciaXML/@Consecutivo') == '00001'
+ assert xml.get_element_text_or_attribute('/fe:NominaIndividual/LugarGeneracionXML/@Pais') == 'CO'
+ assert xml.get_element_text_or_attribute('/fe:NominaIndividual/LugarGeneracionXML/@DepartamentoEstado') == '05'
+ assert xml.get_element_text_or_attribute('/fe:NominaIndividual/LugarGeneracionXML/@MunicipioCiudad') == '05001'
+ assert xml.get_element_text_or_attribute('/fe:NominaIndividual/ProveedorXML/@NIT') == '999999'
+ assert xml.get_element_text_or_attribute('/fe:NominaIndividual/ProveedorXML/@DV') == '2'
+ assert xml.get_element_text_or_attribute('/fe:NominaIndividual/ProveedorXML/@SoftwareID') == 'xx'
+ assert xml.get_element_text_or_attribute('/fe:NominaIndividual/ProveedorXML/@fachoSoftwareSC') == 'xx12N00001'
+ assert xml.get_element_text_or_attribute('/fe:NominaIndividual/ProveedorXML/@SoftwareSC') is not None
+ assert xml.get_element_text_or_attribute('/fe:NominaIndividual/CodigoQR') == f"https://catalogo-vpfe.dian.gov.co/document/searchqr?documentkey={expected_cune}"
+ assert xml.get_element_text_or_attribute('/fe:NominaIndividual/Empleador/@NIT') == '700085371'
+ assert xml.get_element_text_or_attribute('/fe:NominaIndividual/Trabajador/@NumeroDocumento') == '800199436'
+
+def test_asignar_pago():
+ nomina = fe.nomina.DIANNominaIndividual()
+ nomina.asignar_pago(fe.nomina.Pago(
+ forma = fe.nomina.FormaPago(code='1'),
+ metodo = fe.nomina.MetodoPago(code='1')
+ ))
+
+def test_nomina_xmlsign(monkeypatch):
+ nomina = fe.nomina.DIANNominaIndividual()
+ xml = nomina.toFachoXML()
+
+ signer = fe.nomina.DianXMLExtensionSigner('./tests/example.p12')
+ xml.add_extension(signer)
+
+ print(xml.tostring())
+ elem = xml.get_element('/fe:NominaIndividual/ext:UBLExtensions/ext:UBLExtension/ext:ExtensionContent/ds:Signature')
+ assert elem is not None
+
+
+def atest_nomina_ajuste_reemplazar():
+ nomina = fe.nomina.DIANNominaIndividualDeAjuste.Reemplazar()
+
+ xml = nomina.toFachoXML()
+ print(xml)
+ assert False
+
+
+def test_adicionar_reemplazar_devengado_comprobante_total():
+ nomina = fe.nomina.DIANNominaIndividualDeAjuste.Reemplazar()
+
+ nomina.adicionar_devengado(fe.nomina.DevengadoBasico(
+ dias_trabajados = 60,
+ sueldo_trabajado = fe.nomina.Amount(2_000_000)
+ ))
+
+ nomina.adicionar_deduccion(fe.nomina.DeduccionSalud(
+ porcentaje = fe.nomina.Amount(19),
+ deduccion = fe.nomina.Amount(1_000_000)
+ ))
+
+ xml = nomina.toFachoXML()
+
+ assert xml.get_element_text('/fe:NominaIndividualDeAjuste/Reemplazar/ComprobanteTotal') == '1000000.00'
+
+
+def test_adicionar_reemplazar_asignar_predecesor():
+ nomina = fe.nomina.DIANNominaIndividualDeAjuste.Reemplazar()
+
+ nomina.asignar_predecesor(fe.nomina.DIANNominaIndividualDeAjuste.Reemplazar.Predecesor(
+ numero = '123456',
+ cune = 'ABC123456',
+ fecha_generacion = '2021-11-16'
+ ))
+
+ xml = nomina.toFachoXML()
+ print(xml.tostring())
+ assert xml.get_element_text_or_attribute('/fe:NominaIndividualDeAjuste/Reemplazar/ReemplazandoPredecesor/@NumeroPred') == '123456'
+ assert xml.get_element_text_or_attribute('/fe:NominaIndividualDeAjuste/Reemplazar/ReemplazandoPredecesor/@CUNEPred') == 'ABC123456'
+ assert xml.get_element_text_or_attribute('/fe:NominaIndividualDeAjuste/Reemplazar/ReemplazandoPredecesor/@FechaGenPred') == '2021-11-16'
+
+
+def test_adicionar_reemplazar_eliminar_predecesor_opcional():
+ nomina = fe.nomina.DIANNominaIndividualDeAjuste.Reemplazar()
+
+ nomina.asignar_predecesor(fe.nomina.DIANNominaIndividualDeAjuste.Reemplazar.Predecesor(
+ numero = '123456',
+ cune = 'ABC123456',
+ fecha_generacion = '2021-11-16'
+ ))
+
+ xml = nomina.toFachoXML()
+ print(xml.tostring())
+
+ assert xml.get_element('/fe:NominaIndividualDeAjuste/Reemplazar/ReemplazandoPredecesor') is not None
+ assert xml.get_element('/fe:NominaIndividualDeAjuste/Eliminar/EliminandoPredecesor') is None
+
+def test_adicionar_eliminar_reemplazar_predecesor_opcional():
+ nomina = fe.nomina.DIANNominaIndividualDeAjuste.Eliminar()
+
+ nomina.asignar_predecesor(fe.nomina.DIANNominaIndividualDeAjuste.Eliminar.Predecesor(
+ numero = '123456',
+ cune = 'ABC123456',
+ fecha_generacion = '2021-11-16'
+ ))
+
+ xml = nomina.toFachoXML()
+ print(xml.tostring())
+ assert xml.get_element('/fe:NominaIndividualDeAjuste/Eliminar/EliminandoPredecesor') is not None
+ assert xml.get_element('/fe:NominaIndividualDeAjuste/Reemplazar/ReemplazandoPredecesor') is None
+
+def test_adicionar_eliminar_devengado_comprobante_total():
+ nomina = fe.nomina.DIANNominaIndividualDeAjuste.Eliminar()
+
+ nomina.adicionar_devengado(fe.nomina.DevengadoBasico(
+ dias_trabajados = 60,
+ sueldo_trabajado = fe.nomina.Amount(2_000_000)
+ ))
+
+ nomina.adicionar_deduccion(fe.nomina.DeduccionSalud(
+ porcentaje = fe.nomina.Amount(19),
+ deduccion = fe.nomina.Amount(1_000_000)
+ ))
+
+ xml = nomina.toFachoXML()
+
+ assert xml.get_element_text('/fe:NominaIndividualDeAjuste/Eliminar/ComprobanteTotal') == '1000000.00'
+
+def test_adicionar_eliminar_asignar_predecesor():
+ nomina = fe.nomina.DIANNominaIndividualDeAjuste.Eliminar()
+
+ nomina.asignar_predecesor(fe.nomina.DIANNominaIndividualDeAjuste.Eliminar.Predecesor(
+ numero = '123456',
+ cune = 'ABC123456',
+ fecha_generacion = '2021-11-16'
+ ))
+
+ xml = nomina.toFachoXML()
+ print(xml.tostring())
+ assert xml.get_element_text_or_attribute('/fe:NominaIndividualDeAjuste/Eliminar/EliminandoPredecesor/@NumeroPred') == '123456'
+ assert xml.get_element_text_or_attribute('/fe:NominaIndividualDeAjuste/Eliminar/EliminandoPredecesor/@CUNEPred') == 'ABC123456'
+ assert xml.get_element_text_or_attribute('/fe:NominaIndividualDeAjuste/Eliminar/EliminandoPredecesor/@FechaGenPred') == '2021-11-16'
+
+def test_nomina_devengado_horas_extras_diarias():
+ nomina = fe.nomina.DIANNominaIndividual()
+
+ nomina.adicionar_devengado(fe.nomina.DevengadoHorasExtrasDiarias(
+ horas_extras=[
+ fe.nomina.DevengadoHoraExtra(
+ hora_inicio='2021-11-30T19:09:55',
+ hora_fin='2021-11-30T20:09:55',
+ cantidad=1,
+ porcentaje=fe.nomina.Amount(1),
+ pago=fe.nomina.Amount(100)
+ ),
+ fe.nomina.DevengadoHoraExtra(
+ hora_inicio='2021-11-30T18:09:55',
+ hora_fin='2021-11-30T19:09:55',
+ cantidad=2,
+ porcentaje=fe.nomina.Amount(2),
+ pago=fe.nomina.Amount(200)
+ )
+ ]
+ ))
+
+ xml = nomina.toFachoXML()
+ extras = xml.get_element('/fe:NominaIndividual/Devengados/HEDs/HED', multiple=True)
+ assert extras[0].get('HoraInicio') == '2021-11-30T19:09:55'
+ assert extras[0].get('HoraFin') == '2021-11-30T20:09:55'
+ assert extras[0].get('Cantidad') == '1'
+ assert extras[0].get('Porcentaje') == '1.00'
+ assert extras[0].get('Pago') == '100.00'
+ assert extras[1].get('HoraInicio') == '2021-11-30T18:09:55'
+ assert extras[1].get('HoraFin') == '2021-11-30T19:09:55'
+ assert extras[1].get('Cantidad') == '2'
+ assert extras[1].get('Porcentaje') == '2.00'
+ assert extras[1].get('Pago') == '200.00'
+
+def test_nomina_devengado_horas_extras_nocturnas():
+ nomina = fe.nomina.DIANNominaIndividual()
+
+ nomina.adicionar_devengado(fe.nomina.DevengadoHorasExtrasNocturnas(
+ horas_extras=[
+ fe.nomina.DevengadoHoraExtra(
+ hora_inicio='2021-11-30T19:09:55',
+ hora_fin='2021-11-30T20:09:55',
+ cantidad=1,
+ porcentaje=fe.nomina.Amount(1),
+ pago=fe.nomina.Amount(100)
+ ),
+ fe.nomina.DevengadoHoraExtra(
+ hora_inicio='2021-11-30T18:09:55',
+ hora_fin='2021-11-30T19:09:55',
+ cantidad=2,
+ porcentaje=fe.nomina.Amount(2),
+ pago=fe.nomina.Amount(200)
+ )
+ ]
+ ))
+
+ xml = nomina.toFachoXML()
+ extras = xml.get_element('/fe:NominaIndividual/Devengados/HENs/HEN', multiple=True)
+ assert extras[0].get('HoraInicio') == '2021-11-30T19:09:55'
+ assert extras[0].get('HoraFin') == '2021-11-30T20:09:55'
+ assert extras[0].get('Cantidad') == '1'
+ assert extras[0].get('Porcentaje') == '1.00'
+ assert extras[0].get('Pago') == '100.00'
+ assert extras[1].get('HoraInicio') == '2021-11-30T18:09:55'
+ assert extras[1].get('HoraFin') == '2021-11-30T19:09:55'
+ assert extras[1].get('Cantidad') == '2'
+ assert extras[1].get('Porcentaje') == '2.00'
+ assert extras[1].get('Pago') == '200.00'
+
+def test_nomina_devengado_horas_recargo_nocturno():
+ nomina = fe.nomina.DIANNominaIndividual()
+
+ nomina.adicionar_devengado(fe.nomina.DevengadoHorasRecargoNocturno(
+ horas_extras=[
+ fe.nomina.DevengadoHoraExtra(
+ hora_inicio='2021-11-30T19:09:55',
+ hora_fin='2021-11-30T20:09:55',
+ cantidad=1,
+ porcentaje=fe.nomina.Amount(1),
+ pago=fe.nomina.Amount(100)
+ ),
+ fe.nomina.DevengadoHoraExtra(
+ hora_inicio='2021-11-30T18:09:55',
+ hora_fin='2021-11-30T19:09:55',
+ cantidad=2,
+ porcentaje=fe.nomina.Amount(2),
+ pago=fe.nomina.Amount(200)
+ )
+ ]
+ ))
+
+ xml = nomina.toFachoXML()
+ extras = xml.get_element('/fe:NominaIndividual/Devengados/HRNs/HRN', multiple=True)
+ assert extras[0].get('HoraInicio') == '2021-11-30T19:09:55'
+ assert extras[0].get('HoraFin') == '2021-11-30T20:09:55'
+ assert extras[0].get('Cantidad') == '1'
+ assert extras[0].get('Porcentaje') == '1.00'
+ assert extras[0].get('Pago') == '100.00'
+ assert extras[1].get('HoraInicio') == '2021-11-30T18:09:55'
+ assert extras[1].get('HoraFin') == '2021-11-30T19:09:55'
+ assert extras[1].get('Cantidad') == '2'
+ assert extras[1].get('Porcentaje') == '2.00'
+ assert extras[1].get('Pago') == '200.00'
+
+def test_nomina_devengado_horas_extras_diarias_dominicales_y_festivos():
+ nomina = fe.nomina.DIANNominaIndividual()
+
+ nomina.adicionar_devengado(fe.nomina.DevengadoHorasExtrasDiariasDominicalesYFestivos(
+ horas_extras=[
+ fe.nomina.DevengadoHoraExtra(
+ hora_inicio='2021-11-30T19:09:55',
+ hora_fin='2021-11-30T20:09:55',
+ cantidad=1,
+ porcentaje=fe.nomina.Amount(1),
+ pago=fe.nomina.Amount(100)
+ ),
+ fe.nomina.DevengadoHoraExtra(
+ hora_inicio='2021-11-30T18:09:55',
+ hora_fin='2021-11-30T19:09:55',
+ cantidad=2,
+ porcentaje=fe.nomina.Amount(2),
+ pago=fe.nomina.Amount(200)
+ )
+ ]
+ ))
+
+ xml = nomina.toFachoXML()
+ extras = xml.get_element('/fe:NominaIndividual/Devengados/HEDDFs/HEDDF', multiple=True)
+ assert extras[0].get('HoraInicio') == '2021-11-30T19:09:55'
+ assert extras[0].get('HoraFin') == '2021-11-30T20:09:55'
+ assert extras[0].get('Cantidad') == '1'
+ assert extras[0].get('Porcentaje') == '1.00'
+ assert extras[0].get('Pago') == '100.00'
+ assert extras[1].get('HoraInicio') == '2021-11-30T18:09:55'
+ assert extras[1].get('HoraFin') == '2021-11-30T19:09:55'
+ assert extras[1].get('Cantidad') == '2'
+ assert extras[1].get('Porcentaje') == '2.00'
+ assert extras[1].get('Pago') == '200.00'
+
+def test_nomina_devengado_horas_recargo_diarias_dominicales_y_festivos():
+ nomina = fe.nomina.DIANNominaIndividual()
+
+ nomina.adicionar_devengado(fe.nomina.DevengadoHorasRecargoDiariasDominicalesYFestivos(
+ horas_extras=[
+ fe.nomina.DevengadoHoraExtra(
+ hora_inicio='2021-11-30T19:09:55',
+ hora_fin='2021-11-30T20:09:55',
+ cantidad=1,
+ porcentaje=fe.nomina.Amount(1),
+ pago=fe.nomina.Amount(100)
+ ),
+ fe.nomina.DevengadoHoraExtra(
+ hora_inicio='2021-11-30T18:09:55',
+ hora_fin='2021-11-30T19:09:55',
+ cantidad=2,
+ porcentaje=fe.nomina.Amount(2),
+ pago=fe.nomina.Amount(200)
+ )
+ ]
+ ))
+
+ xml = nomina.toFachoXML()
+ extras = xml.get_element('/fe:NominaIndividual/Devengados/HRDDFs/HRDDF', multiple=True)
+ assert extras[0].get('HoraInicio') == '2021-11-30T19:09:55'
+ assert extras[0].get('HoraFin') == '2021-11-30T20:09:55'
+ assert extras[0].get('Cantidad') == '1'
+ assert extras[0].get('Porcentaje') == '1.00'
+ assert extras[0].get('Pago') == '100.00'
+ assert extras[1].get('HoraInicio') == '2021-11-30T18:09:55'
+ assert extras[1].get('HoraFin') == '2021-11-30T19:09:55'
+ assert extras[1].get('Cantidad') == '2'
+ assert extras[1].get('Porcentaje') == '2.00'
+ assert extras[1].get('Pago') == '200.00'
+
+
+def test_nomina_devengado_horas_extras_nocturnas_dominicales_y_festivos():
+ nomina = fe.nomina.DIANNominaIndividual()
+
+ nomina.adicionar_devengado(fe.nomina.DevengadoHorasExtrasNocturnasDominicalesYFestivos(
+ horas_extras=[
+ fe.nomina.DevengadoHoraExtra(
+ hora_inicio='2021-11-30T19:09:55',
+ hora_fin='2021-11-30T20:09:55',
+ cantidad=1,
+ porcentaje=fe.nomina.Amount(1),
+ pago=fe.nomina.Amount(100)
+ ),
+ fe.nomina.DevengadoHoraExtra(
+ hora_inicio='2021-11-30T18:09:55',
+ hora_fin='2021-11-30T19:09:55',
+ cantidad=2,
+ porcentaje=fe.nomina.Amount(2),
+ pago=fe.nomina.Amount(200)
+ )
+ ]
+ ))
+
+ xml = nomina.toFachoXML()
+ extras = xml.get_element('/fe:NominaIndividual/Devengados/HENDFs/HENDF', multiple=True)
+ assert extras[0].get('HoraInicio') == '2021-11-30T19:09:55'
+ assert extras[0].get('HoraFin') == '2021-11-30T20:09:55'
+ assert extras[0].get('Cantidad') == '1'
+ assert extras[0].get('Porcentaje') == '1.00'
+ assert extras[0].get('Pago') == '100.00'
+ assert extras[1].get('HoraInicio') == '2021-11-30T18:09:55'
+ assert extras[1].get('HoraFin') == '2021-11-30T19:09:55'
+ assert extras[1].get('Cantidad') == '2'
+ assert extras[1].get('Porcentaje') == '2.00'
+ assert extras[1].get('Pago') == '200.00'
+
+def test_nomina_devengado_horas_recargo_nocturno_dominicales_y_festivos():
+ nomina = fe.nomina.DIANNominaIndividual()
+
+ nomina.adicionar_devengado(fe.nomina.DevengadoHorasRecargoNocturnoDominicalesYFestivos(
+ horas_extras=[
+ fe.nomina.DevengadoHoraExtra(
+ hora_inicio='2021-11-30T19:09:55',
+ hora_fin='2021-11-30T20:09:55',
+ cantidad=1,
+ porcentaje=fe.nomina.Amount(1),
+ pago=fe.nomina.Amount(100)
+ ),
+ fe.nomina.DevengadoHoraExtra(
+ hora_inicio='2021-11-30T18:09:55',
+ hora_fin='2021-11-30T19:09:55',
+ cantidad=2,
+ porcentaje=fe.nomina.Amount(2),
+ pago=fe.nomina.Amount(200)
+ )
+ ]
+ ))
+
+ xml = nomina.toFachoXML()
+ extras = xml.get_element('/fe:NominaIndividual/Devengados/HRNDFs/HRNDF', multiple=True)
+ assert extras[0].get('HoraInicio') == '2021-11-30T19:09:55'
+ assert extras[0].get('HoraFin') == '2021-11-30T20:09:55'
+ assert extras[0].get('Cantidad') == '1'
+ assert extras[0].get('Porcentaje') == '1.00'
+ assert extras[0].get('Pago') == '100.00'
+ assert extras[1].get('HoraInicio') == '2021-11-30T18:09:55'
+ assert extras[1].get('HoraFin') == '2021-11-30T19:09:55'
+ assert extras[1].get('Cantidad') == '2'
+ assert extras[1].get('Porcentaje') == '2.00'
+ assert extras[1].get('Pago') == '200.00'
+
+def test_fecha_validacion():
+ with pytest.raises(ValueError) as e:
+ fe.nomina.Fecha('535-35-3')
diff --git a/tests/test_nomina_ajuste.py b/tests/test_nomina_ajuste.py
deleted file mode 100644
index a20c159..0000000
--- a/tests/test_nomina_ajuste.py
+++ /dev/null
@@ -1,236 +0,0 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-# This file is part of facho. The COPYRIGHT file at the top level of
-# this repository contains the full copyright notices and license terms.
-
-"""Tests for `facho` package."""
-# import re
-
-# import pytest
-
-# from facho import fe
-
-# import helpers
-
-# def atest_nomina_ajuste_reemplazar():
-# nomina = fe.nomina.DIANNominaIndividualDeAjuste.Reemplazar()
-
-# xml = nomina.toFachoXML()
-# print(xml)
-# assert False
-
-# def test_nomina_ajuste_reemplazar_asignacion_tipo_xml():
-# nomina = fe.nomina.DIANNominaIndividualDeAjuste.Reemplazar()
-# nomina.asignar_metadata(fe.nomina.Metadata(
-# novedad=fe.nomina.Novedad(
-# activa = True,
-# cune = "N0111"
-# ),
-# secuencia=fe.nomina.NumeroSecuencia(
-# prefijo = 'N',
-# consecutivo='00001'
-# ),
-# lugar_generacion=fe.nomina.Lugar(
-# pais = fe.nomina.Pais(
-# code = 'CO'
-# ),
-# departamento = fe.nomina.Departamento(
-# code = '05'
-# ),
-# municipio = fe.nomina.Municipio(
-# code = '05001'
-# ),
-# ),
-# proveedor=fe.nomina.Proveedor(
-# nit='999999',
-# dv=2,
-# software_id='xx',
-# software_pin='12',
-# razon_social='facho'
-# )
-# ))
-# nomina.asignar_empleador(fe.nomina.Empleador(
-# razon_social='facho',
-# nit = '700085371',
-# dv = '1',
-# pais = fe.nomina.Pais(
-# code = 'CO'
-# ),
-# departamento = fe.nomina.Departamento(
-# code = '05'
-# ),
-# municipio = fe.nomina.Municipio(
-# code = '05001'
-# ),
-# direccion = 'calle etrivial'
-# ))
-
-# nomina.asignar_trabajador(fe.nomina.Trabajador(
-# tipo_contrato = fe.nomina.TipoContrato(
-# code = '1'
-# ),
-# alto_riesgo = False,
-# tipo_documento = fe.nomina.TipoDocumento(
-# code = '11'
-# ),
-# primer_apellido = 'gnu',
-# segundo_apellido = 'emacs',
-# primer_nombre = 'facho',
-# lugar_trabajo = fe.nomina.LugarTrabajo(
-# pais = fe.nomina.Pais(code='CO'),
-# departamento = fe.nomina.Departamento(code='05'),
-# municipio = fe.nomina.Municipio(code='05001'),
-# direccion = 'calle facho'
-# ),
-# numero_documento = '800199436',
-# tipo = fe.nomina.TipoTrabajador(
-# code = '01'
-# ),
-# salario_integral = True,
-# sueldo = fe.nomina.Amount(1_500_000)
-# ))
-# nomina.asignar_informacion_general(fe.nomina.InformacionGeneral(
-# fecha_generacion = '2020-01-16',
-# hora_generacion = '1053:10-05:00',
-# tipo_ambiente = fe.nomina.InformacionGeneral.AMBIENTE_PRODUCCION,
-# software_pin = '693',
-# tipo_xml = fe.nomina.InformacionGeneral.TIPO_XML_AJUSTES,
-# periodo_nomina = fe.nomina.PeriodoNomina(code='1'),
-# tipo_moneda = fe.nomina.TipoMoneda(code='COP')
-# ))
-
-# xml = nomina.toFachoXML()
-
-# assert xml.get_element_attribute('/nominaajuste:NominaIndividualDeAjuste/Reemplazar/InformacionGeneral', 'TipoXML') == '103'
-
-
-# def test_adicionar_reemplazar_devengado_comprobante_total():
-# nomina = fe.nomina.DIANNominaIndividualDeAjuste.Reemplazar()
-
-# nomina.adicionar_devengado(fe.nomina.DevengadoBasico(
-# dias_trabajados = 60,
-# sueldo_trabajado = fe.nomina.Amount(2_000_000)
-# ))
-
-# nomina.adicionar_deduccion(fe.nomina.DeduccionSalud(
-# porcentaje = fe.nomina.Amount(19),
-# deduccion = fe.nomina.Amount(1_000_000)
-# ))
-
-# xml = nomina.toFachoXML()
-
-# assert xml.get_element_text('/nominaajuste:NominaIndividualDeAjuste/Reemplazar/ComprobanteTotal') == '1000000.00'
-
-
-# def test_adicionar_reemplazar_asignar_predecesor():
-# nomina = fe.nomina.DIANNominaIndividualDeAjuste.Reemplazar()
-
-# nomina.asignar_predecesor(fe.nomina.DIANNominaIndividualDeAjuste.Reemplazar.Predecesor(
-# numero = '123456',
-# cune = 'ABC123456',
-# fecha_generacion = '2021-11-16'
-# ))
-
-# xml = nomina.toFachoXML()
-# print(xml.tostring())
-# assert xml.get_element_text_or_attribute('/nominaajuste:NominaIndividualDeAjuste/Reemplazar/ReemplazandoPredecesor/@NumeroPred') == '123456'
-# assert xml.get_element_text_or_attribute('/nominaajuste:NominaIndividualDeAjuste/Reemplazar/ReemplazandoPredecesor/@CUNEPred') == 'ABC123456'
-# assert xml.get_element_text_or_attribute('/nominaajuste:NominaIndividualDeAjuste/Reemplazar/ReemplazandoPredecesor/@FechaGenPred') == '2021-11-16'
-
-
-# def test_adicionar_reemplazar_eliminar_predecesor_opcional():
-# nomina = fe.nomina.DIANNominaIndividualDeAjuste.Reemplazar()
-
-# nomina.asignar_predecesor(fe.nomina.DIANNominaIndividualDeAjuste.Reemplazar.Predecesor(
-# numero = '123456',
-# cune = 'ABC123456',
-# fecha_generacion = '2021-11-16'
-# ))
-
-# xml = nomina.toFachoXML()
-# print(xml.tostring())
-
-# assert xml.get_element('/nominaajuste:NominaIndividualDeAjuste/Reemplazar/ReemplazandoPredecesor') is not None
-# assert xml.get_element('/nominaajuste:NominaIndividualDeAjuste/Eliminar/EliminandoPredecesor') is None
-
-# def test_adicionar_eliminar_reemplazar_predecesor_opcional():
-# nomina = fe.nomina.DIANNominaIndividualDeAjuste.Eliminar()
-
-# nomina.asignar_predecesor(fe.nomina.DIANNominaIndividualDeAjuste.Eliminar.Predecesor(
-# numero = '123456',
-# cune = 'ABC123456',
-# fecha_generacion = '2021-11-16'
-# ))
-
-# xml = nomina.toFachoXML()
-# print(xml.tostring())
-# assert xml.get_element('/nominaajuste:NominaIndividualDeAjuste/Eliminar/EliminandoPredecesor') is not None
-# assert xml.get_element('/nominaajuste:NominaIndividualDeAjuste/Reemplazar/ReemplazandoPredecesor') is None
-
-# def test_adicionar_eliminar_devengado_comprobante_total():
-# nomina = fe.nomina.DIANNominaIndividualDeAjuste.Eliminar()
-
-# nomina.adicionar_devengado(fe.nomina.DevengadoBasico(
-# dias_trabajados = 60,
-# sueldo_trabajado = fe.nomina.Amount(2_000_000)
-# ))
-
-# nomina.adicionar_deduccion(fe.nomina.DeduccionSalud(
-# porcentaje = fe.nomina.Amount(19),
-# deduccion = fe.nomina.Amount(1_000_000)
-# ))
-
-# xml = nomina.toFachoXML()
-
-# assert xml.get_element_text('/nominaajuste:NominaIndividualDeAjuste/Eliminar/ComprobanteTotal') == '1000000.00'
-
-# def test_adicionar_eliminar_asignar_predecesor():
-# nomina = fe.nomina.DIANNominaIndividualDeAjuste.Eliminar()
-
-# nomina.asignar_predecesor(fe.nomina.DIANNominaIndividualDeAjuste.Eliminar.Predecesor(
-# numero = '123456',
-# cune = 'ABC123456',
-# fecha_generacion = '2021-11-16'
-# ))
-
-# xml = nomina.toFachoXML()
-# print(xml.tostring())
-# assert xml.get_element_text_or_attribute('/nominaajuste:NominaIndividualDeAjuste/Eliminar/EliminandoPredecesor/@NumeroPred') == '123456'
-# assert xml.get_element_text_or_attribute('/nominaajuste:NominaIndividualDeAjuste/Eliminar/EliminandoPredecesor/@CUNEPred') == 'ABC123456'
-# assert xml.get_element_text_or_attribute('/nominaajuste:NominaIndividualDeAjuste/Eliminar/EliminandoPredecesor/@FechaGenPred') == '2021-11-16'
-
-# def test_nomina_devengado_horas_extras_diarias():
-# nomina = fe.nomina.DIANNominaIndividual()
-
-# nomina.adicionar_devengado(fe.nomina.DevengadoHorasExtrasDiarias(
-# horas_extras=[
-# fe.nomina.DevengadoHoraExtra(
-# hora_inicio='2021-11-30T19:09:55',
-# hora_fin='2021-11-30T20:09:55',
-# cantidad=1,
-# porcentaje=fe.nomina.Amount(1),
-# pago=fe.nomina.Amount(100)
-# ),
-# fe.nomina.DevengadoHoraExtra(
-# hora_inicio='2021-11-30T18:09:55',
-# hora_fin='2021-11-30T19:09:55',
-# cantidad=2,
-# porcentaje=fe.nomina.Amount(2),
-# pago=fe.nomina.Amount(200)
-# )
-# ]
-# ))
-
-# xml = nomina.toFachoXML()
-# extras = xml.get_element(
-# '/nomina:NominaIndividual/Devengados/HEDs/HED', multiple=True)
-# assert extras[0].get('HoraInicio') == '2021-11-30T19:09:55'
-# assert extras[0].get('HoraFin') == '2021-11-30T20:09:55'
-# assert extras[0].get('Cantidad') == '1'
-# assert extras[0].get('Porcentaje') == '1.00'
-# assert extras[0].get('Pago') == '100.00'
-# assert extras[1].get('HoraInicio') == '2021-11-30T18:09:55'
-# assert extras[1].get('HoraFin') == '2021-11-30T19:09:55'
-# assert extras[1].get('Cantidad') == '2'
-# assert extras[1].get('Porcentaje') == '2.00'
-# assert extras[1].get('Pago') == '200.00'
diff --git a/tests/test_query.py b/tests/test_query.py
index 2962977..0faf899 100644
--- a/tests/test_query.py
+++ b/tests/test_query.py
@@ -3,28 +3,22 @@
# This file is part of facho. The COPYRIGHT file at the top level of
# this repository contains the full copyright notices and license terms.
-# import pytest
+import pytest
import facho.fe.form as form
from facho import fe
+from facho.fe.form_xml import DIANInvoiceXML, DIANCreditNoteXML, DIANDebitNoteXML
-from facho.fe.form_xml import DIANInvoiceXML
-# from facho.fe.form_xml import (
-# DIANInvoiceXML, DIANCreditNoteXML, DIANDebitNoteXML)
-
-from fixtures import simple_invoice
+from fixtures import *
from facho.fe.form import query
-simple_invoice = simple_invoice
-
-
def test_query_billing_reference(simple_invoice):
xml = DIANInvoiceXML(simple_invoice)
cufe_extension = fe.DianXMLExtensionCUFE(simple_invoice)
xml.add_extension(cufe_extension)
out = xml.tostring()
-
+
reference = query.billing_reference(out, form.BillingReference)
assert isinstance(reference, form.BillingReference)
assert reference.ident != ''
diff --git a/tox.ini b/tox.ini
index f06bea6..f7186a0 100644
--- a/tox.ini
+++ b/tox.ini
@@ -1,12 +1,12 @@
[tox]
-envlist = py39, py310, py311, py312, flake8
+envlist = py27, py34, py35, py36, flake8
[travis]
python =
- 3.9: py39
- 3.10: py310
- 3.11: py311
- 3.12: py312
+ 3.6: py36
+ 3.5: py35
+ 3.4: py34
+ 2.7: py27
[testenv:flake8]
basepython = python