Compare commits
32 Commits
documento_
...
main
Author | SHA1 | Date | |
---|---|---|---|
c49e67b8a6 | |||
7672ae4b7f | |||
25c5fda3f0 | |||
a758b8678b | |||
3a385c63e3 | |||
9b33b4486c | |||
612aae1f86 | |||
4fe82daac6 | |||
e237d1b45f | |||
8cc6146be2 | |||
f98ab98c9c | |||
d04596ed3a | |||
9297be22e0 | |||
|
30773e042b | ||
fb44498e53 | |||
9f7349ccee | |||
9cc41e1c5d | |||
fc75126ca0 | |||
d061077b30 | |||
a3d2176068 | |||
98677bc162 | |||
398d27d049 | |||
8765a3d2c8 | |||
1935ed3048 | |||
097cf97fc3 | |||
de99633211 | |||
028cf8b687 | |||
e7a3976b14 | |||
f08954ee43 | |||
a0321020c7 | |||
dde24b9739 | |||
6e5d358c73 |
1
.gitignore
vendored
1
.gitignore
vendored
@ -215,3 +215,4 @@ tags
|
|||||||
pyvenv.cfg
|
pyvenv.cfg
|
||||||
.venv
|
.venv
|
||||||
pip-selfcheck.json
|
pip-selfcheck.json
|
||||||
|
invoice.xml
|
22
Dockerfile
22
Dockerfile
@ -7,18 +7,18 @@ RUN apt install software-properties-common -y \
|
|||||||
&& add-apt-repository ppa:deadsnakes/ppa
|
&& add-apt-repository ppa:deadsnakes/ppa
|
||||||
|
|
||||||
RUN apt-get install -y --no-install-recommends \
|
RUN apt-get install -y --no-install-recommends \
|
||||||
python3.7 python3.7-distutils python3.7-dev \
|
|
||||||
python3.8 python3.8-distutils python3.8-dev \
|
|
||||||
python3.9 python3.9-distutils python3.9-dev \
|
python3.9 python3.9-distutils python3.9-dev \
|
||||||
python3.10 python3.10-distutils python3.10-dev \
|
python3.10 python3.10-distutils python3.10-dev \
|
||||||
|
python3.11 python3.11-distutils python3.11-dev \
|
||||||
|
python3.12 python3-setuptools python3.12-dev \
|
||||||
wget \
|
wget \
|
||||||
ca-certificates
|
ca-certificates
|
||||||
|
|
||||||
RUN wget https://bootstrap.pypa.io/get-pip.py \
|
RUN wget https://bootstrap.pypa.io/get-pip.py \
|
||||||
&& python3.7 get-pip.py pip==22.2.2 \
|
&& python3.9 get-pip.py pip==23.2.1 --break-system-packages \
|
||||||
&& python3.8 get-pip.py pip==22.2.2 \
|
&& python3.10 get-pip.py pip==23.2.1 --break-system-packages \
|
||||||
&& python3.9 get-pip.py pip==22.2.2 \
|
&& python3.11 get-pip.py pip==23.2.1 --break-system-packages \
|
||||||
&& python3.10 get-pip.py pip==22.2.2 \
|
&& python3.12 get-pip.py pip==23.2.1 --break-system-packages \
|
||||||
&& rm get-pip.py
|
&& rm get-pip.py
|
||||||
|
|
||||||
RUN apt-get install -y --no-install-recommends \
|
RUN apt-get install -y --no-install-recommends \
|
||||||
@ -27,14 +27,14 @@ RUN apt-get install -y --no-install-recommends \
|
|||||||
build-essential \
|
build-essential \
|
||||||
zip
|
zip
|
||||||
|
|
||||||
RUN python3.7 --version
|
|
||||||
RUN python3.8 --version
|
|
||||||
RUN python3.9 --version
|
RUN python3.9 --version
|
||||||
RUN python3.10 --version
|
RUN python3.10 --version
|
||||||
|
RUN python3.11 --version
|
||||||
|
RUN python3.12 --version
|
||||||
|
|
||||||
RUN pip3.7 install setuptools setuptools-rust
|
|
||||||
RUN pip3.8 install setuptools setuptools-rust
|
|
||||||
RUN pip3.9 install setuptools setuptools-rust
|
RUN pip3.9 install setuptools setuptools-rust
|
||||||
RUN pip3.10 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 install tox pytest
|
RUN pip3 install tox pytest --break-system-packages
|
||||||
|
@ -15,7 +15,7 @@ dev-shell:
|
|||||||
docker run --rm -ti -v "$(PWD):/app" -w /app --name facho-cli facho bash
|
docker run --rm -ti -v "$(PWD):/app" -w /app --name facho-cli facho bash
|
||||||
|
|
||||||
test:
|
test:
|
||||||
docker run -t -v $(PWD):/app -w /app facho sh -c 'cd /app; python3.7 setup.py test'
|
docker run -t -v $(PWD):/app -w /app facho sh -c 'cd /app; python3.12 setup.py test'
|
||||||
|
|
||||||
tox:
|
tox:
|
||||||
docker run -it -v $(PWD)/:/app -w /app facho tox
|
docker run -it -v $(PWD)/:/app -w /app facho tox
|
||||||
|
@ -14,7 +14,7 @@ from facho.fe import fe
|
|||||||
from datetime import datetime, date
|
from datetime import datetime, date
|
||||||
|
|
||||||
# Datos del fomulario del SET de pruebas
|
# 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
|
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
|
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
|
CLAVE_TECNICA = 'fc9eac422eba16e21ffd8c5f94b3f30a6e38162d' ##Id suministrado por la Dian en el momento de la creación del SET de Pruebas
|
||||||
@ -36,6 +36,7 @@ def extensions(inv):
|
|||||||
'SETP', 990000000, 995000000)#del SET de pruebas
|
'SETP', 990000000, 995000000)#del SET de pruebas
|
||||||
return [security_code, authorization_provider, cufe, software_provider, inv_authorization]
|
return [security_code, authorization_provider, cufe, software_provider, inv_authorization]
|
||||||
|
|
||||||
|
|
||||||
def invoice():
|
def invoice():
|
||||||
# factura de venta nacional
|
# factura de venta nacional
|
||||||
inv = form.Invoice('01')
|
inv = form.Invoice('01')
|
||||||
@ -49,16 +50,17 @@ def invoice():
|
|||||||
inv.set_operation_type('10')
|
inv.set_operation_type('10')
|
||||||
inv.set_supplier(form.Party(
|
inv.set_supplier(form.Party(
|
||||||
legal_name = 'Nombre registrado de la empresa',
|
legal_name = 'Nombre registrado de la empresa',
|
||||||
name = 'Nombre comercial o él mismo nombre registrado',
|
name='Nombre comercial o él mismo nombre registrado',
|
||||||
ident = form.PartyIdentification('nit_empresa', 'digito_verificación', '31'),
|
ident=form.PartyIdentification(
|
||||||
|
'nit_empresa', 'digito_verificación', '31'),
|
||||||
# obligaciones del contribuyente ver DIAN:FAK26
|
# obligaciones del contribuyente ver DIAN:FAK26
|
||||||
responsability_code = form.Responsability(['O-07', 'O-14', 'O-48']),
|
responsability_code=form.Responsability(['ZZ', 'O-14', 'O-48']),
|
||||||
# ver DIAN:FAJ28
|
# ver DIAN:FAJ28
|
||||||
responsability_regime_code = '48',
|
responsability_regime_code='48',
|
||||||
# tipo de organizacion juridica ver DIAN:6.2.3
|
# tipo de organizacion juridica ver DIAN:6.2.3
|
||||||
organization_code = '1',
|
organization_code='1',
|
||||||
email = "correoempresa@correoempresa.correo",
|
email="correoempresa@correoempresa.correo",
|
||||||
address = form.Address(
|
address=form.Address(
|
||||||
'', '', form.City('05001', 'Medellín'),
|
'', '', form.City('05001', 'Medellín'),
|
||||||
form.Country('CO', 'Colombia'),
|
form.Country('CO', 'Colombia'),
|
||||||
form.CountrySubentity('05', 'Antioquia')),
|
form.CountrySubentity('05', 'Antioquia')),
|
||||||
@ -76,42 +78,43 @@ def invoice():
|
|||||||
'', '', form.City('05001', 'Medellín'),
|
'', '', form.City('05001', 'Medellín'),
|
||||||
form.Country('CO', 'Colombia'),
|
form.Country('CO', 'Colombia'),
|
||||||
form.CountrySubentity('05', 'Antioquia')),
|
form.CountrySubentity('05', 'Antioquia')),
|
||||||
#tax_scheme = form.TaxScheme('01', 'IVA')
|
# tax_scheme = form.TaxScheme('01', 'IVA')
|
||||||
))
|
))
|
||||||
# asignar metodo de pago
|
# asignar metodo de pago
|
||||||
inv.set_payment_mean(form.PaymentMean(
|
inv.set_payment_mean(form.PaymentMean(
|
||||||
# metodo de pago ver DIAN:3.4.1
|
# metodo de pago ver DIAN:3.4.1
|
||||||
id = '1',
|
id='1',
|
||||||
# codigo correspondiente al medio de pago ver DIAN:3.4.2
|
# codigocorrespondientealmediodepagoverDIAN:3.4.2
|
||||||
code = '20',
|
code='20',
|
||||||
# fecha de vencimiento de la factura
|
# fechadevencimientodelafactura
|
||||||
due_at = datetime.now(),
|
due_at=datetime.now(),
|
||||||
# identificador numerico
|
# identificadornumerico
|
||||||
payment_id = '2'
|
payment_id='2'
|
||||||
))
|
))
|
||||||
# adicionar una linea al documento
|
# adicionar una linea al documento
|
||||||
inv.add_invoice_line(form.InvoiceLine(
|
inv.add_invoice_line(
|
||||||
quantity = form.Quantity(int(20.5), '94'),
|
form.InvoiceLine(
|
||||||
|
quantity=form.Quantity(int(20.5), '94'),
|
||||||
# item general de codigo 999
|
# item general de codigo 999
|
||||||
description = 'productO3',
|
description='productO3',
|
||||||
item = form.StandardItem('test', 9999),
|
sitem=form.StandardItem('test', 9999),
|
||||||
price = form.Price(
|
price=form.Price(
|
||||||
# precio base del item (sin iva)
|
# precio base del item (sin iva)
|
||||||
amount = form.Amount(200.00),
|
amount=form.Amount(200.00),
|
||||||
# ver DIAN:6.3.5.1
|
# ver DIAN:6.3.5.1
|
||||||
type_code = '01',
|
type_code='01',
|
||||||
type = 'x'
|
type='x'
|
||||||
),
|
),
|
||||||
tax = form.TaxTotal(
|
tax=form.TaxTotal(
|
||||||
subtotals = [
|
subtotals=[
|
||||||
form.TaxSubTotal(
|
form.TaxSubTotal(
|
||||||
percent = 19.00,
|
percent=19.00,
|
||||||
scheme=form.TaxScheme('01')
|
scheme=form.TaxScheme('01')
|
||||||
)
|
)]
|
||||||
]
|
|
||||||
)
|
)
|
||||||
))
|
))
|
||||||
return inv
|
return inv
|
||||||
|
|
||||||
|
|
||||||
def document_xml():
|
def document_xml():
|
||||||
return form_xml.DIANInvoiceXML
|
return form_xml.DIANInvoiceXML
|
||||||
|
@ -1,109 +1,127 @@
|
|||||||
# importar libreria de modelos
|
# importar libreria de modelos
|
||||||
|
from facho import fe, form_xml
|
||||||
import facho.fe.form as form
|
import facho.fe.form as form
|
||||||
import facho.fe.form_xml
|
import datetime
|
||||||
|
|
||||||
|
PRIVATE_KEY_PATH = 'ruta a mi llave privada'
|
||||||
|
PRIVATE_PASSPHRASE = 'clave de la llave privada'
|
||||||
|
|
||||||
PRIVATE_KEY_PATH='ruta a mi llave privada'
|
|
||||||
PRIVATE_PASSPHRASE='clave de la llave privada'
|
|
||||||
|
|
||||||
# consultar las extensiones necesarias
|
# consultar las extensiones necesarias
|
||||||
def extensions(inv):
|
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()
|
authorization_provider = fe.DianXMLExtensionAuthorizationProvider()
|
||||||
cufe = fe.DianXMLExtensionCUFE(inv, fe.DianXMLExtensionCUFE.AMBIENTE_PRUEBAS,
|
cufe = fe.DianXMLExtensionCUFE(
|
||||||
|
inv, fe.DianXMLExtensionCUFE.AMBIENTE_PRUEBAS,
|
||||||
'clave tecnica')
|
'clave tecnica')
|
||||||
nit = form.PartyIdentification('nit', '5', '31')
|
nit = form.PartyIdentification('nit', '5', '31')
|
||||||
software_provider = fe.DianXMLExtensionSoftwareProvider(nit, nit.dv, 'id software')
|
software_provider = fe.DianXMLExtensionSoftwareProvider(
|
||||||
inv_authorization = fe.DianXMLExtensionInvoiceAuthorization('invoice autorization',
|
nit, nit.dv, 'id software')
|
||||||
|
inv_authorization = fe.DianXMLExtensionInvoiceAuthorization(
|
||||||
|
'invoice autorization',
|
||||||
datetime(2019, 1, 19),
|
datetime(2019, 1, 19),
|
||||||
datetime(2030, 1, 19),
|
datetime(2030, 1, 19),
|
||||||
'SETP', 990000001, 995000000)
|
'SETP', 990000001, 995000000)
|
||||||
return [security_code, authorization_provider, cufe, software_provider, inv_authorization]
|
return [
|
||||||
|
security_code,
|
||||||
|
authorization_provider,
|
||||||
|
cufe, software_provider,
|
||||||
|
inv_authorization
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
# generar documento desde modelo a ruta indicada
|
# generar documento desde modelo a ruta indicada
|
||||||
def generate_document(invoice, filepath):
|
def generate_document(invoice, filepath):
|
||||||
xml = form_xml.DIANInvoiceXML(invoice)
|
xml = form_xml.DIANInvoiceXML(invoice)
|
||||||
for extension in extensions(invoice):
|
for extension in extensions(invoice):
|
||||||
xml.add_extension(extension)
|
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
|
# Modelars las facturas
|
||||||
# ...
|
# ...
|
||||||
|
|
||||||
# factura de venta nacional
|
# factura de venta nacional
|
||||||
inv = form.NationalSalesInvoice()
|
inv = form.NationalSalesInvoice()
|
||||||
|
|
||||||
# asignar periodo de facturacion
|
# asignar periodo de facturacion
|
||||||
inv.set_period(datetime.now(), datetime.now())
|
inv.set_period(datetime.now(), datetime.now())
|
||||||
|
|
||||||
# asignar fecha de emision de la factura
|
# asignar fecha de emision de la factura
|
||||||
|
|
||||||
inv.set_issue(datetime.now())
|
inv.set_issue(datetime.now())
|
||||||
# asignar prefijo y numero del documento
|
# asignar prefijo y numero del documento
|
||||||
|
|
||||||
inv.set_ident('SETP990003033')
|
inv.set_ident('SETP990003033')
|
||||||
# asignar tipo de operacion ver DIAN:6.1.5
|
# asignar tipo de operacion ver DIAN:6.1.5
|
||||||
inv.set_operation_type('10')
|
inv.set_operation_type('10')
|
||||||
|
|
||||||
# asignar proveedor
|
# asignar proveedor
|
||||||
inv.set_supplier(form.Party(
|
inv.set_supplier(form.Party(
|
||||||
legal_name = 'FACHO SOS',
|
legal_name='FACHOSOS',
|
||||||
name = 'FACHO SOS',
|
name='FACHOSOS',
|
||||||
ident = form.PartyIdentification('900579212', '5', '31'),
|
ident=form.PartyIdentification('900579212', '5', '31'),
|
||||||
# obligaciones del contribuyente ver DIAN:FAK26
|
# obligaciones del contribuyente ver DIAN:FAK26
|
||||||
responsability_code = form.Responsability(['O-07', 'O-09', 'O-14', 'O-48']),
|
responsability_code=form.Responsability(['ZZ', 'O-09', 'O-14', 'O-48']),
|
||||||
# ver DIAN:FAJ28
|
# ver DIAN:FAJ28
|
||||||
responsability_regime_code = '48',
|
responsability_regime_code='48',
|
||||||
# tipo de organizacion juridica ver DIAN:6.2.3
|
# tipo de organizacion juridica ver DIAN:6.2.3
|
||||||
organization_code = '1',
|
organization_code='1',
|
||||||
email = "sdds@sd.com",
|
email="sdds@sd.com",
|
||||||
address = form.Address(
|
address=form.Address(
|
||||||
name = '',
|
name='',
|
||||||
street = '',
|
street='',
|
||||||
city = form.City('05001', 'Medellín'),
|
city=form.City('05001', 'Medellín'),
|
||||||
country = form.Country('CO', 'Colombia'),
|
country=form.Country('CO', 'Colombia'),
|
||||||
countrysubentity = form.CountrySubentity('05', 'Antioquia'))
|
countrysubentity=form.CountrySubentity('05', 'Antioquia'))
|
||||||
))
|
))
|
||||||
|
|
||||||
inv.set_customer(form.Party(
|
inv.set_customer(form.Party(
|
||||||
legal_name = 'facho-customer',
|
legal_name='facho-customer',
|
||||||
name = 'facho-customer',
|
name='facho-customer',
|
||||||
ident = form.PartyIdentification('999999999', '', '13'),
|
ident=form.PartyIdentification('999999999', '', '13'),
|
||||||
responsability_code = form.Responsability(['R-99-PN']),
|
responsability_code=form.Responsability(['R-99-PN']),
|
||||||
responsability_regime_code = '49',
|
responsability_regime_code='49',
|
||||||
organization_code = '2',
|
organization_code='2',
|
||||||
email = "sdds@sd.com",
|
email="sdds@sd.com",
|
||||||
address = form.Address(
|
address=form.Address(
|
||||||
name = '',
|
name='',
|
||||||
street = '',
|
street='',
|
||||||
city = form.City('05001', 'Medellín'),
|
city=form.City('05001', 'Medellín'),
|
||||||
country = form.Country('CO', 'Colombia'),
|
country=form.Country('CO', 'Colombia'),
|
||||||
countrysubentity = form.CountrySubentity('05', 'Antioquia'))
|
countrysubentity=form.CountrySubentity('05', 'Antioquia'))
|
||||||
))
|
))
|
||||||
# asignar metodo de pago
|
# asignar metodo de pago
|
||||||
inv.set_payment_mean(form.PaymentMean(
|
inv.set_payment_mean(form.PaymentMean(
|
||||||
# metodo de pago ver DIAN:3.4.1
|
# metodo de pago ver DIAN:3.4.1
|
||||||
id = '1',
|
id='1',
|
||||||
# codigo correspondiente al medio de pago ver DIAN:3.4.2
|
# codigo correspondiente al medio de pago ver DIAN:3.4.2
|
||||||
code = '10',
|
code='10',
|
||||||
# fecha de vencimiento de la factura
|
# fecha de vencimiento de la factura
|
||||||
due_at = datetime.now(),
|
due_at=datetime.now(),
|
||||||
|
|
||||||
# identificador numerico
|
# identificador numerico
|
||||||
payment_id = '1'
|
payment_id='1'
|
||||||
))
|
))
|
||||||
# adicionar una linea al documento
|
# adicionar una linea al documento
|
||||||
inv.add_invoice_line(form.InvoiceLine(
|
inv.add_invoice_line(form.InvoiceLine(
|
||||||
quantity = form.Quantity(1, '94'),
|
quantity=form.Quantity(1, '94'),
|
||||||
description = 'producto facho',
|
description='producto facho',
|
||||||
# item general de codigo 999
|
# item general de codigo 999
|
||||||
item = form.StandardItem('test', 9999),
|
item=form.StandardItem('test', 9999),
|
||||||
price = form.Price(
|
price=form.Price(
|
||||||
# precio base del tiem
|
# precio base del tiem
|
||||||
amount = form.Amount(100.00),
|
amount=form.Amount(100.00),
|
||||||
# ver DIAN:6.3.5.1
|
# ver DIAN:6.3.5.1
|
||||||
type_code = '01',
|
type_code='01',
|
||||||
type = 'x'
|
type='x'
|
||||||
),
|
),
|
||||||
tax = form.TaxTotal(
|
tax=form.TaxTotal(
|
||||||
subtotals = [
|
subtotals=[
|
||||||
form.TaxSubTotal(
|
form.TaxSubTotal(
|
||||||
percent = 19.00,
|
percent=19.00,
|
||||||
)
|
)]
|
||||||
]
|
|
||||||
)
|
)
|
||||||
))
|
))
|
||||||
|
|
||||||
|
@ -76,7 +76,7 @@
|
|||||||
<cac:PartyTaxScheme>
|
<cac:PartyTaxScheme>
|
||||||
<cbc:RegistrationName>NEUROTEC TECNOLOGIA S.A.S</cbc:RegistrationName>
|
<cbc:RegistrationName>NEUROTEC TECNOLOGIA S.A.S</cbc:RegistrationName>
|
||||||
<cbc:CompanyID schemeAgencyName="CO, DIAN (Dirección de Impuestos y Aduanas Nacionales)" schemeAgencyID="195" schemeID="5" schemeName="31">900579212</cbc:CompanyID>
|
<cbc:CompanyID schemeAgencyName="CO, DIAN (Dirección de Impuestos y Aduanas Nacionales)" schemeAgencyID="195" schemeID="5" schemeName="31">900579212</cbc:CompanyID>
|
||||||
<cbc:TaxLevelCode listName="48">O-07;O-09;O-14;O-48</cbc:TaxLevelCode>
|
<cbc:TaxLevelCode listName="48">ZZ;O-09;O-14;O-48</cbc:TaxLevelCode>
|
||||||
<cac:TaxScheme/>
|
<cac:TaxScheme/>
|
||||||
</cac:PartyTaxScheme>
|
</cac:PartyTaxScheme>
|
||||||
<cac:Contact>
|
<cac:Contact>
|
||||||
|
@ -96,27 +96,5 @@
|
|||||||
<SimpleValue>Exclusivo en referencias a documentos (elementos DocumentReference)</SimpleValue>
|
<SimpleValue>Exclusivo en referencias a documentos (elementos DocumentReference)</SimpleValue>
|
||||||
</Value>
|
</Value>
|
||||||
</Row>
|
</Row>
|
||||||
<Row>
|
|
||||||
<Value ColumnRef="code">
|
|
||||||
<SimpleValue>05</SimpleValue>
|
|
||||||
</Value>
|
|
||||||
<Value ColumnRef="name">
|
|
||||||
<SimpleValue>Documento soporte en adquisiciones efectuadas a sujetos no obligados a expedir factura o documento equivalente</SimpleValue>
|
|
||||||
</Value>
|
|
||||||
<Value ColumnRef="description">
|
|
||||||
<SimpleValue>Tipo de documento</SimpleValue>
|
|
||||||
</Value>
|
|
||||||
</Row>
|
|
||||||
<Row>
|
|
||||||
<Value ColumnRef="code">
|
|
||||||
<SimpleValue>95</SimpleValue>
|
|
||||||
</Value>
|
|
||||||
<Value ColumnRef="name">
|
|
||||||
<SimpleValue>Nota de ajuste al documento soporte en adquisiciones efectuadas a sujetos no obligados a expedir factura o documento equivalente</SimpleValue>
|
|
||||||
</Value>
|
|
||||||
<Value ColumnRef="description">
|
|
||||||
<SimpleValue>Exclusivo en referencias a documentos (elementos DocumentReference)</SimpleValue>
|
|
||||||
</Value>
|
|
||||||
</Row>
|
|
||||||
</SimpleCodeList>
|
</SimpleCodeList>
|
||||||
</gc:CodeList>
|
</gc:CodeList>
|
||||||
|
@ -64,26 +64,10 @@
|
|||||||
</Row>
|
</Row>
|
||||||
<Row>
|
<Row>
|
||||||
<Value ColumnRef="code">
|
<Value ColumnRef="code">
|
||||||
<SimpleValue>O-48</SimpleValue>
|
<SimpleValue>ZZ</SimpleValue>
|
||||||
</Value>
|
</Value>
|
||||||
<Value ColumnRef="name">
|
<Value ColumnRef="name">
|
||||||
<SimpleValue>Impuesto sobre las ventas – IVA</SimpleValue>
|
<SimpleValue>No aplica</SimpleValue>
|
||||||
</Value>
|
|
||||||
</Row>
|
|
||||||
<Row>
|
|
||||||
<Value ColumnRef="code">
|
|
||||||
<SimpleValue>O-49</SimpleValue>
|
|
||||||
</Value>
|
|
||||||
<Value ColumnRef="name">
|
|
||||||
<SimpleValue>No responsable de IVA</SimpleValue>
|
|
||||||
</Value>
|
|
||||||
</Row>
|
|
||||||
<Row>
|
|
||||||
<Value ColumnRef="code">
|
|
||||||
<SimpleValue>R-99-PN</SimpleValue>
|
|
||||||
</Value>
|
|
||||||
<Value ColumnRef="name">
|
|
||||||
<SimpleValue>No responsable</SimpleValue>
|
|
||||||
</Value>
|
</Value>
|
||||||
</Row>
|
</Row>
|
||||||
</SimpleCodeList>
|
</SimpleCodeList>
|
||||||
|
102
facho/fe/fe.py
102
facho/fe/fe.py
@ -1,6 +1,5 @@
|
|||||||
# This file is part of facho. The COPYRIGHT file at the top level of
|
# This file is part of facho. The COPYRIGHT file at the top level of
|
||||||
# this repository contains the full copyright notices and license terms.
|
# this repository contains the full copyright notices and license terms.
|
||||||
|
|
||||||
from ..facho import FachoXML, FachoXMLExtension, LXMLBuilder
|
from ..facho import FachoXML, FachoXMLExtension, LXMLBuilder
|
||||||
import uuid
|
import uuid
|
||||||
import xmlsig
|
import xmlsig
|
||||||
@ -8,13 +7,16 @@ import xades
|
|||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
import OpenSSL
|
import OpenSSL
|
||||||
import zipfile
|
import zipfile
|
||||||
import warnings
|
# import warnings
|
||||||
import hashlib
|
import hashlib
|
||||||
from contextlib import contextmanager
|
from contextlib import contextmanager
|
||||||
from .data.dian import codelist
|
from .data.dian import codelist
|
||||||
from . import form
|
from . import form
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
from pathlib import Path
|
# from pathlib import Path
|
||||||
|
from dateutil import tz
|
||||||
|
|
||||||
|
from cryptography.hazmat.primitives.serialization import pkcs12
|
||||||
|
|
||||||
AMBIENTE_PRUEBAS = codelist.TipoAmbiente.by_name('Pruebas')['code']
|
AMBIENTE_PRUEBAS = codelist.TipoAmbiente.by_name('Pruebas')['code']
|
||||||
AMBIENTE_PRODUCCION = codelist.TipoAmbiente.by_name('Producción')['code']
|
AMBIENTE_PRODUCCION = codelist.TipoAmbiente.by_name('Producción')['code']
|
||||||
@ -30,7 +32,7 @@ SCHEME_AGENCY_ATTRS = {
|
|||||||
POLICY_ID = 'https://facturaelectronica.dian.gov.co/politicadefirma/v2/politicadefirmav2.pdf'
|
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.'
|
POLICY_NAME = u'Política de firma para facturas electrónicas de la República de Colombia.'
|
||||||
|
|
||||||
|
Bogota = tz.gettz('America/Bogota')
|
||||||
# NAMESPACES = {
|
# NAMESPACES = {
|
||||||
# 'atd': 'urn:oasis:names:specification:ubl:schema:xsd:AttachedDocument-2',
|
# 'atd': 'urn:oasis:names:specification:ubl:schema:xsd:AttachedDocument-2',
|
||||||
# 'nomina': 'dian:gov:co:facturaelectronica:NominaIndividual',
|
# 'nomina': 'dian:gov:co:facturaelectronica:NominaIndividual',
|
||||||
@ -55,6 +57,8 @@ POLICY_NAME = u'Política de firma para facturas electrónicas de la República
|
|||||||
|
|
||||||
|
|
||||||
NAMESPACES = {
|
NAMESPACES = {
|
||||||
|
'apr': 'urn:oasis:names:specification:ubl:schema:xsd:ApplicationResponse-2',
|
||||||
|
'atd': 'urn:oasis:names:specification:ubl:schema:xsd:AttachedDocument-2',
|
||||||
'fe': 'http://www.dian.gov.co/contratos/facturaelectronica/v1',
|
'fe': 'http://www.dian.gov.co/contratos/facturaelectronica/v1',
|
||||||
'cac': 'urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2',
|
'cac': 'urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2',
|
||||||
'cbc': 'urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2',
|
'cbc': 'urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2',
|
||||||
@ -68,11 +72,11 @@ NAMESPACES = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def fe_from_string(document: str) -> FachoXML:
|
def fe_from_string(document: str) -> FachoXML:
|
||||||
return FeXML.from_string(document)
|
return FeXML.from_string(document)
|
||||||
|
|
||||||
from contextlib import contextmanager
|
|
||||||
|
# from contextlib import contextmanager
|
||||||
@contextmanager
|
@contextmanager
|
||||||
def mock_xades_policy():
|
def mock_xades_policy():
|
||||||
from mock import patch
|
from mock import patch
|
||||||
@ -104,22 +108,25 @@ class FeXML(FachoXML):
|
|||||||
|
|
||||||
def tostring(self, **kw):
|
def tostring(self, **kw):
|
||||||
# MACHETE(bit4bit) la DIAN espera que la etiqueta raiz no este en un namespace
|
# 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_namespace = self.root_namespace()
|
||||||
root_localname = self.root_localname()
|
root_localname = self.root_localname()
|
||||||
xmlns_name = {v: k for k, v in NAMESPACES.items()}[root_namespace]
|
xmlns_name = {v: k for k, v in NAMESPACES.items()}[root_namespace]
|
||||||
if root_localname == 'Invoice':
|
|
||||||
urn_oasis = 'urn:oasis:names:specification:ubl:schema:xsd:Invoice-2'
|
|
||||||
if root_localname == 'CreditNote':
|
|
||||||
urn_oasis = 'urn:oasis:names:specification:ubl:schema:xsd:CreditNote-2'
|
|
||||||
return super().tostring(**kw)\
|
return super().tostring(**kw)\
|
||||||
.replace(xmlns_name + ':', '')\
|
.replace(xmlns_name + ':', '')\
|
||||||
.replace('xmlns:'+xmlns_name, 'xmlns')\
|
.replace('xmlns:'+xmlns_name, 'xmlns')\
|
||||||
.replace(root_namespace, urn_oasis)
|
.replace(root_namespace, urn_oasis[root_localname])
|
||||||
|
|
||||||
|
|
||||||
class DianXMLExtensionCUDFE(FachoXMLExtension):
|
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.tipo_ambiente = tipo_ambiente
|
||||||
self.invoice = invoice
|
self.invoice = invoice
|
||||||
|
|
||||||
@ -157,9 +164,11 @@ class DianXMLExtensionCUDFE(FachoXMLExtension):
|
|||||||
|
|
||||||
# #DIAN 1.8.-2021: FAD03
|
# #DIAN 1.8.-2021: FAD03
|
||||||
# fachoxml.set_element('./cbc:ProfileID', 'DIAN 2.1: Factura Electrónica de Venta')
|
# fachoxml.set_element('./cbc:ProfileID', 'DIAN 2.1: Factura Electrónica de Venta')
|
||||||
fachoxml.set_element('./cbc:ProfileExecutionID', self._tipo_ambiente_int())
|
fachoxml.set_element(
|
||||||
|
'./cbc:ProfileExecutionID', self._tipo_ambiente_int())
|
||||||
#DIAN 1.7.-2020: FAB36
|
#DIAN 1.7.-2020: FAB36
|
||||||
fachoxml.set_element('./ext:UBLExtensions/ext:UBLExtension/ext:ExtensionContent/sts:DianExtensions/sts:QRCode',
|
fachoxml.set_element(
|
||||||
|
'./ext:UBLExtensions/ext:UBLExtension/ext:ExtensionContent/sts:DianExtensions/sts:QRCode',
|
||||||
self._get_qrcode(cufe))
|
self._get_qrcode(cufe))
|
||||||
|
|
||||||
def issue_time(self, datetime_):
|
def issue_time(self, datetime_):
|
||||||
@ -176,7 +185,8 @@ class DianXMLExtensionCUDFE(FachoXMLExtension):
|
|||||||
build_vars['HoraFac'] = self.issue_time(invoice.invoice_issue)
|
build_vars['HoraFac'] = self.issue_time(invoice.invoice_issue)
|
||||||
# PAG 601
|
# PAG 601
|
||||||
build_vars['ValorBruto'] = invoice.invoice_legal_monetary_total.line_extension_amount
|
build_vars['ValorBruto'] = invoice.invoice_legal_monetary_total.line_extension_amount
|
||||||
build_vars['ValorTotalPagar'] = invoice.invoice_legal_monetary_total.payable_amount
|
build_vars['ValorTotalPagar'
|
||||||
|
] = invoice.invoice_legal_monetary_total.payable_amount
|
||||||
ValorImpuestoPara = defaultdict(lambda: form.Amount(0.0))
|
ValorImpuestoPara = defaultdict(lambda: form.Amount(0.0))
|
||||||
build_vars['CodImpuesto1'] = '01'
|
build_vars['CodImpuesto1'] = '01'
|
||||||
build_vars['CodImpuesto2'] = '04'
|
build_vars['CodImpuesto2'] = '04'
|
||||||
@ -206,7 +216,8 @@ class DianXMLExtensionCUDFE(FachoXMLExtension):
|
|||||||
|
|
||||||
|
|
||||||
class DianXMLExtensionCUFE(DianXMLExtensionCUDFE):
|
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.tipo_ambiente = tipo_ambiente
|
||||||
self.clave_tecnica = clave_tecnica
|
self.clave_tecnica = clave_tecnica
|
||||||
self.invoice = invoice
|
self.invoice = invoice
|
||||||
@ -242,6 +253,7 @@ class DianXMLExtensionCUFE(DianXMLExtensionCUDFE):
|
|||||||
'%d' % build_vars['TipoAmb'],
|
'%d' % build_vars['TipoAmb'],
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class DianXMLExtensionCUDE(DianXMLExtensionCUDFE):
|
class DianXMLExtensionCUDE(DianXMLExtensionCUDFE):
|
||||||
def __init__(self, invoice, software_pin, tipo_ambiente = AMBIENTE_PRUEBAS):
|
def __init__(self, invoice, software_pin, tipo_ambiente = AMBIENTE_PRUEBAS):
|
||||||
self.tipo_ambiente = tipo_ambiente
|
self.tipo_ambiente = tipo_ambiente
|
||||||
@ -279,6 +291,7 @@ class DianXMLExtensionCUDE(DianXMLExtensionCUDFE):
|
|||||||
'%d' % build_vars['TipoAmb'],
|
'%d' % build_vars['TipoAmb'],
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class DianXMLExtensionCUDS(DianXMLExtensionCUDFE):
|
class DianXMLExtensionCUDS(DianXMLExtensionCUDFE):
|
||||||
def __init__(self, invoice, software_pin, tipo_ambiente = AMBIENTE_PRUEBAS):
|
def __init__(self, invoice, software_pin, tipo_ambiente = AMBIENTE_PRUEBAS):
|
||||||
self.tipo_ambiente = tipo_ambiente
|
self.tipo_ambiente = tipo_ambiente
|
||||||
@ -312,6 +325,7 @@ class DianXMLExtensionCUDS(DianXMLExtensionCUDFE):
|
|||||||
'%d' % build_vars['TipoAmb'],
|
'%d' % build_vars['TipoAmb'],
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class DianXMLExtensionSoftwareProvider(FachoXMLExtension):
|
class DianXMLExtensionSoftwareProvider(FachoXMLExtension):
|
||||||
# RESOLUCION 0004: pagina 108
|
# RESOLUCION 0004: pagina 108
|
||||||
|
|
||||||
@ -321,7 +335,8 @@ class DianXMLExtensionSoftwareProvider(FachoXMLExtension):
|
|||||||
self.id_software = id_software
|
self.id_software = id_software
|
||||||
|
|
||||||
def build(self, fexml):
|
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 = SCHEME_AGENCY_ATTRS.copy()
|
||||||
provider_id_attrs.update({'schemeID': self.dv})
|
provider_id_attrs.update({'schemeID': self.dv})
|
||||||
#DIAN 1.7.-2020: FAB23
|
#DIAN 1.7.-2020: FAB23
|
||||||
@ -351,7 +366,6 @@ class DianXMLExtensionSoftwareSecurityCode(FachoXMLExtension):
|
|||||||
|
|
||||||
|
|
||||||
class DianXMLExtensionSigner:
|
class DianXMLExtensionSigner:
|
||||||
|
|
||||||
def __init__(self, pkcs12_path, passphrase=None, localpolicy=True):
|
def __init__(self, pkcs12_path, passphrase=None, localpolicy=True):
|
||||||
self._pkcs12_data = open(pkcs12_path, 'rb').read()
|
self._pkcs12_data = open(pkcs12_path, 'rb').read()
|
||||||
self._passphrase = None
|
self._passphrase = None
|
||||||
@ -362,7 +376,6 @@ class DianXMLExtensionSigner:
|
|||||||
@classmethod
|
@classmethod
|
||||||
def from_bytes(cls, data, passphrase=None, localpolicy=True):
|
def from_bytes(cls, data, passphrase=None, localpolicy=True):
|
||||||
self = cls.__new__(cls)
|
self = cls.__new__(cls)
|
||||||
|
|
||||||
self._pkcs12_data = data
|
self._pkcs12_data = data
|
||||||
self._passphrase = None
|
self._passphrase = None
|
||||||
self._localpolicy = localpolicy
|
self._localpolicy = localpolicy
|
||||||
@ -372,7 +385,9 @@ class DianXMLExtensionSigner:
|
|||||||
return self
|
return self
|
||||||
|
|
||||||
def _element_extension_content(self, fachoxml):
|
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):
|
def sign_xml_string(self, document):
|
||||||
xml = LXMLBuilder.from_string(document)
|
xml = LXMLBuilder.from_string(document)
|
||||||
@ -394,7 +409,6 @@ class DianXMLExtensionSigner:
|
|||||||
)
|
)
|
||||||
xml.append(signature)
|
xml.append(signature)
|
||||||
|
|
||||||
|
|
||||||
ref = xmlsig.template.add_reference(
|
ref = xmlsig.template.add_reference(
|
||||||
signature, xmlsig.constants.TransformSha256, uri="", name="xmldsig-%s-ref0" % (id_uuid)
|
signature, xmlsig.constants.TransformSha256, uri="", name="xmldsig-%s-ref0" % (id_uuid)
|
||||||
)
|
)
|
||||||
@ -402,14 +416,16 @@ class DianXMLExtensionSigner:
|
|||||||
|
|
||||||
id_keyinfo = "xmldsig-%s-KeyInfo" % (id_uuid)
|
id_keyinfo = "xmldsig-%s-KeyInfo" % (id_uuid)
|
||||||
xmlsig.template.add_reference(
|
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)
|
ki = xmlsig.template.ensure_key_info(signature, name=id_keyinfo)
|
||||||
data = xmlsig.template.add_x509_data(ki)
|
data = xmlsig.template.add_x509_data(ki)
|
||||||
xmlsig.template.x509_data_add_certificate(data)
|
xmlsig.template.x509_data_add_certificate(data)
|
||||||
xmlsig.template.add_key_value(ki)
|
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)
|
xades.utils.ensure_id(qualifying)
|
||||||
|
|
||||||
id_props = "xmldsig-%s-signedprops" % (id_uuid)
|
id_props = "xmldsig-%s-signedprops" % (id_uuid)
|
||||||
@ -417,10 +433,12 @@ class DianXMLExtensionSigner:
|
|||||||
signature, xmlsig.constants.TransformSha256, uri="#%s" % (id_props),
|
signature, xmlsig.constants.TransformSha256, uri="#%s" % (id_props),
|
||||||
uri_type="http://uri.etsi.org/01903#SignedProperties"
|
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
|
# TODO assert with http://www.sic.gov.co/hora-legal-colombiana
|
||||||
props = xades.template.create_signed_properties(qualifying, name=id_props, datetime=datetime.now())
|
props = xades.template.create_signed_properties(
|
||||||
|
qualifying, name=id_props, datetime=datetime.now(tz=Bogota))
|
||||||
xades.template.add_claimed_role(props, "supplier")
|
xades.template.add_claimed_role(props, "supplier")
|
||||||
|
|
||||||
policy = xades.policy.GenericPolicyId(
|
policy = xades.policy.GenericPolicyId(
|
||||||
@ -428,9 +446,13 @@ class DianXMLExtensionSigner:
|
|||||||
POLICY_NAME,
|
POLICY_NAME,
|
||||||
xmlsig.constants.TransformSha256)
|
xmlsig.constants.TransformSha256)
|
||||||
ctx = xades.XAdESContext(policy)
|
ctx = xades.XAdESContext(policy)
|
||||||
ctx.load_pkcs12(OpenSSL.crypto.load_pkcs12(self._pkcs12_data,
|
ctx.load_pkcs12(pkcs12.load_key_and_certificates(
|
||||||
|
self._pkcs12_data,
|
||||||
self._passphrase))
|
self._passphrase))
|
||||||
|
|
||||||
|
# ctx.load_pkcs12(OpenSSL.crypto.load_pkcs12(
|
||||||
|
# self._pkcs12_data,
|
||||||
|
# self._passphrase))
|
||||||
if self._localpolicy:
|
if self._localpolicy:
|
||||||
with mock_xades_policy():
|
with mock_xades_policy():
|
||||||
ctx.sign(signature)
|
ctx.sign(signature)
|
||||||
@ -438,7 +460,7 @@ class DianXMLExtensionSigner:
|
|||||||
else:
|
else:
|
||||||
ctx.sign(signature)
|
ctx.sign(signature)
|
||||||
ctx.verify(signature)
|
ctx.verify(signature)
|
||||||
#xmlsig take parent root
|
# xmlsig take parent root
|
||||||
xml.remove(signature)
|
xml.remove(signature)
|
||||||
return signature
|
return signature
|
||||||
|
|
||||||
@ -454,19 +476,18 @@ class DianXMLExtensionAuthorizationProvider(FachoXMLExtension):
|
|||||||
def build(self, fexml):
|
def build(self, fexml):
|
||||||
attrs = {'schemeID': '4', 'schemeName': '31'}
|
attrs = {'schemeID': '4', 'schemeName': '31'}
|
||||||
attrs.update(SCHEME_AGENCY_ATTRS)
|
attrs.update(SCHEME_AGENCY_ATTRS)
|
||||||
|
|
||||||
authorization_provider = fexml.fragment('./ext:UBLExtensions/ext:UBLExtension/ext:ExtensionContent/sts:DianExtensions/sts:AuthorizationProvider')
|
authorization_provider = fexml.fragment('./ext:UBLExtensions/ext:UBLExtension/ext:ExtensionContent/sts:DianExtensions/sts:AuthorizationProvider')
|
||||||
authorization_provider.set_element('./sts:AuthorizationProviderID',
|
authorization_provider.set_element('./sts:AuthorizationProviderID',
|
||||||
'800197268',
|
'800197268',
|
||||||
**attrs)
|
**attrs)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class DianXMLExtensionInvoiceSource(FachoXMLExtension):
|
class DianXMLExtensionInvoiceSource(FachoXMLExtension):
|
||||||
# CAB13
|
# CAB13
|
||||||
def build(self, fexml):
|
def build(self, fexml):
|
||||||
dian_path = '/fe:CreditNote/ext:UBLExtensions/ext:UBLExtension/ext:ExtensionContent/sts:DianExtensions/sts:InvoiceSource/cbc:IdentificationCode'
|
dian_path = '/fe:CreditNote/ext:UBLExtensions/ext:UBLExtension/ext:ExtensionContent/sts:DianExtensions/sts:InvoiceSource/cbc:IdentificationCode'
|
||||||
fexml.set_element(dian_path, 'CO',
|
fexml.set_element(
|
||||||
|
dian_path, 'CO',
|
||||||
listAgencyID="6",
|
listAgencyID="6",
|
||||||
listAgencyName="United Nations Economic Commission for Europe",
|
listAgencyName="United Nations Economic Commission for Europe",
|
||||||
listSchemeURI="urn:oasis:names:specification:ubl:codelist:gc:CountryIdentificationCode-2.1")
|
listSchemeURI="urn:oasis:names:specification:ubl:codelist:gc:CountryIdentificationCode-2.1")
|
||||||
@ -499,16 +520,15 @@ class DianXMLExtensionInvoiceAuthorization(FachoXMLExtension):
|
|||||||
invoice_control.set_element('/sts:InvoiceControl/sts:AuthorizedInvoices/sts:To',
|
invoice_control.set_element('/sts:InvoiceControl/sts:AuthorizedInvoices/sts:To',
|
||||||
self.to)
|
self.to)
|
||||||
|
|
||||||
fexml.set_element('./ext:UBLExtensions/ext:UBLExtension/ext:ExtensionContent/sts:DianExtensions/sts:InvoiceSource/cbc:IdentificationCode',
|
fexml.set_element(
|
||||||
|
'./ext:UBLExtensions/ext:UBLExtension/ext:ExtensionContent/sts:DianExtensions/sts:InvoiceSource/cbc:IdentificationCode',
|
||||||
'CO',
|
'CO',
|
||||||
#DIAN 1.7.-2020: FAB15
|
# DIAN 1.7.-2020: FAB15
|
||||||
listAgencyID="6",
|
listAgencyID="6",
|
||||||
#DIAN 1.7.-2020: FAB16
|
# DIAN 1.7.-2020: FAB16
|
||||||
listAgencyName="United Nations Economic Commission for Europe",
|
listAgencyName="United Nations Economic Commission for Europe",
|
||||||
#DIAN 1.7.-2020: FAB17
|
# DIAN 1.7.-2020: FAB17
|
||||||
listSchemeURI="urn:oasis:names:specification:ubl:codelist:gc:CountryIdentificationCode-2.1"
|
listSchemeURI="urn:oasis:names:specification:ubl:codelist:gc:CountryIdentificationCode-2.1")
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class DianZIP:
|
class DianZIP:
|
||||||
@ -517,7 +537,8 @@ class DianZIP:
|
|||||||
MAX_FILES = 50
|
MAX_FILES = 50
|
||||||
|
|
||||||
def __init__(self, file_like):
|
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
|
self.num_files = 0
|
||||||
|
|
||||||
def add_xml(self, name, xml_data):
|
def add_xml(self, name, xml_data):
|
||||||
@ -538,7 +559,6 @@ class DianZIP:
|
|||||||
def __enter__(self):
|
def __enter__(self):
|
||||||
"""
|
"""
|
||||||
Facilita el uso de esta manera:
|
Facilita el uso de esta manera:
|
||||||
|
|
||||||
f = open('xxx', 'rb')
|
f = open('xxx', 'rb')
|
||||||
with DianZIP(f) as zip:
|
with DianZIP(f) as zip:
|
||||||
zip.add_invoice_xml('name', 'data xml')
|
zip.add_invoice_xml('name', 'data xml')
|
||||||
@ -561,7 +581,7 @@ class DianXMLExtensionSignerVerifier:
|
|||||||
def verify_string(self, document):
|
def verify_string(self, document):
|
||||||
# Obtener FachoXML
|
# Obtener FachoXML
|
||||||
xml = LXMLBuilder.from_string(document)
|
xml = LXMLBuilder.from_string(document)
|
||||||
fachoxml = FachoXML(xml,nsmap=NAMESPACES)
|
fachoxml = FachoXML(xml, nsmap=NAMESPACES)
|
||||||
|
|
||||||
# Obtener Signature
|
# Obtener Signature
|
||||||
signature = fachoxml.builder.xpath(fachoxml.root, '//ds:Signature')
|
signature = fachoxml.builder.xpath(fachoxml.root, '//ds:Signature')
|
||||||
|
@ -1,12 +1,14 @@
|
|||||||
# This file is part of facho. The COPYRIGHT file at the top level of
|
# This file is part of facho. The COPYRIGHT file at the top level of
|
||||||
# this repository contains the full copyright notices and license terms.
|
# this repository contains the full copyright notices and license terms.
|
||||||
import hashlib
|
|
||||||
from functools import reduce
|
# import hashlib
|
||||||
import copy
|
# from functools import reduce
|
||||||
|
# import copy
|
||||||
|
|
||||||
import dataclasses
|
import dataclasses
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass, field
|
||||||
from datetime import datetime, date
|
from datetime import datetime, date
|
||||||
from collections import defaultdict
|
# from collections import defaultdict
|
||||||
import decimal
|
import decimal
|
||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
import typing
|
import typing
|
||||||
@ -14,9 +16,11 @@ from ..data.dian import codelist
|
|||||||
|
|
||||||
DECIMAL_PRECISION = 6
|
DECIMAL_PRECISION = 6
|
||||||
|
|
||||||
|
|
||||||
class AmountCurrencyError(TypeError):
|
class AmountCurrencyError(TypeError):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class Currency:
|
class Currency:
|
||||||
code: str
|
code: str
|
||||||
@ -27,6 +31,7 @@ class Currency:
|
|||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.code
|
return self.code
|
||||||
|
|
||||||
|
|
||||||
class Collection:
|
class Collection:
|
||||||
|
|
||||||
def __init__(self, array):
|
def __init__(self, array):
|
||||||
@ -43,6 +48,7 @@ class Collection:
|
|||||||
def sum(self):
|
def sum(self):
|
||||||
return sum(self.array)
|
return sum(self.array)
|
||||||
|
|
||||||
|
|
||||||
class AmountCollection(Collection):
|
class AmountCollection(Collection):
|
||||||
|
|
||||||
def sum(self):
|
def sum(self):
|
||||||
@ -51,10 +57,13 @@ class AmountCollection(Collection):
|
|||||||
total += v
|
total += v
|
||||||
return total
|
return total
|
||||||
|
|
||||||
class Amount:
|
|
||||||
def __init__(self, amount: int or float or str or Amount, currency: Currency = Currency('COP')):
|
|
||||||
|
|
||||||
#DIAN 1.7.-2020: 1.2.3.1
|
class Amount:
|
||||||
|
def __init__(
|
||||||
|
self, amount: typing.Union[int, float, str, "Amount"],
|
||||||
|
currency: Currency = Currency('COP')):
|
||||||
|
|
||||||
|
# DIAN 1.7.-2020: 1.2.3.1
|
||||||
if isinstance(amount, Amount):
|
if isinstance(amount, Amount):
|
||||||
if amount < Amount(0.0):
|
if amount < Amount(0.0):
|
||||||
raise ValueError('amount must be positive >= 0')
|
raise ValueError('amount must be positive >= 0')
|
||||||
@ -65,9 +74,11 @@ class Amount:
|
|||||||
if float(amount) < 0:
|
if float(amount) < 0:
|
||||||
raise ValueError('amount must be positive >= 0')
|
raise ValueError('amount must be positive >= 0')
|
||||||
|
|
||||||
self.amount = Decimal(amount, decimal.Context(prec=DECIMAL_PRECISION,
|
self.amount = Decimal(
|
||||||
#DIAN 1.7.-2020: 1.2.1.1
|
amount, decimal.Context(
|
||||||
rounding=decimal.ROUND_HALF_EVEN ))
|
prec=DECIMAL_PRECISION,
|
||||||
|
# DIAN 1.7.-2020: 1.2.1.1
|
||||||
|
rounding=decimal.ROUND_HALF_EVEN))
|
||||||
self.currency = currency
|
self.currency = currency
|
||||||
|
|
||||||
def fromNumber(self, val):
|
def fromNumber(self, val):
|
||||||
@ -90,7 +101,8 @@ class Amount:
|
|||||||
def __eq__(self, other):
|
def __eq__(self, other):
|
||||||
if not self.is_same_currency(other):
|
if not self.is_same_currency(other):
|
||||||
raise AmountCurrencyError()
|
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):
|
def _cast(self, val):
|
||||||
if type(val) in [int, float]:
|
if type(val) in [int, float]:
|
||||||
@ -122,7 +134,7 @@ class Amount:
|
|||||||
|
|
||||||
def truncate_as_string(self, prec):
|
def truncate_as_string(self, prec):
|
||||||
parts = str(self.float()).split('.', 1)
|
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):
|
def float(self):
|
||||||
return float(round(self.amount, DECIMAL_PRECISION))
|
return float(round(self.amount, DECIMAL_PRECISION))
|
||||||
@ -151,6 +163,7 @@ class Quantity:
|
|||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return str(self)
|
return str(self)
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class Item:
|
class Item:
|
||||||
scheme_name: str
|
scheme_name: str
|
||||||
@ -188,6 +201,7 @@ class Country:
|
|||||||
raise ValueError("code [%s] not found" % (self.code))
|
raise ValueError("code [%s] not found" % (self.code))
|
||||||
self.name = codelist.Paises[self.code]['name']
|
self.name = codelist.Paises[self.code]['name']
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class CountrySubentity:
|
class CountrySubentity:
|
||||||
code: str
|
code: str
|
||||||
@ -198,6 +212,7 @@ class CountrySubentity:
|
|||||||
raise ValueError("code [%s] not found" % (self.code))
|
raise ValueError("code [%s] not found" % (self.code))
|
||||||
self.name = codelist.Departamento[self.code]['name']
|
self.name = codelist.Departamento[self.code]['name']
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class City:
|
class City:
|
||||||
code: str
|
code: str
|
||||||
@ -208,18 +223,22 @@ class City:
|
|||||||
raise ValueError("code [%s] not found" % (self.code))
|
raise ValueError("code [%s] not found" % (self.code))
|
||||||
self.name = codelist.Municipio[self.code]['name']
|
self.name = codelist.Municipio[self.code]['name']
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class PostalZone:
|
class PostalZone:
|
||||||
code: str = ''
|
code: str = ''
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class Address:
|
class Address:
|
||||||
name: str
|
name: str
|
||||||
street: str = ''
|
street: str = ''
|
||||||
city: City = City('05001')
|
city: City = field(default_factory=lambda: City('05001'))
|
||||||
country: Country = Country('CO')
|
country: Country = field(default_factory=lambda: Country('CO'))
|
||||||
countrysubentity: CountrySubentity = CountrySubentity('05')
|
countrysubentity: CountrySubentity = field(
|
||||||
postalzone: PostalZone = PostalZone('')
|
default_factory=lambda: CountrySubentity('05'))
|
||||||
|
postalzone: PostalZone = field(default_factory=lambda: PostalZone(''))
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class PartyIdentification:
|
class PartyIdentification:
|
||||||
@ -240,6 +259,7 @@ class PartyIdentification:
|
|||||||
if self.type_fiscal not in codelist.TipoIdFiscal:
|
if self.type_fiscal not in codelist.TipoIdFiscal:
|
||||||
raise ValueError("type_fiscal [%s] not found" % (self.type_fiscal))
|
raise ValueError("type_fiscal [%s] not found" % (self.type_fiscal))
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class Responsability:
|
class Responsability:
|
||||||
codes: list
|
codes: list
|
||||||
@ -269,6 +289,7 @@ class TaxScheme:
|
|||||||
raise ValueError("code not found")
|
raise ValueError("code not found")
|
||||||
self.name = codelist.TipoImpuesto[self.code]['name']
|
self.name = codelist.TipoImpuesto[self.code]['name']
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class Party:
|
class Party:
|
||||||
name: str
|
name: str
|
||||||
@ -276,10 +297,10 @@ class Party:
|
|||||||
responsability_code: typing.List[Responsability]
|
responsability_code: typing.List[Responsability]
|
||||||
responsability_regime_code: str
|
responsability_regime_code: str
|
||||||
organization_code: str
|
organization_code: str
|
||||||
tax_scheme: TaxScheme = TaxScheme('01')
|
tax_scheme: TaxScheme = field(default_factory=lambda: TaxScheme('01'))
|
||||||
|
|
||||||
phone: str = ''
|
phone: str = ''
|
||||||
address: Address = Address('')
|
address: Address = field(default_factory=lambda: Address(''))
|
||||||
email: str = ''
|
email: str = ''
|
||||||
legal_name: str = ''
|
legal_name: str = ''
|
||||||
legal_company_ident: str = ''
|
legal_company_ident: str = ''
|
||||||
@ -307,7 +328,7 @@ class TaxScheme:
|
|||||||
class TaxSubTotal:
|
class TaxSubTotal:
|
||||||
percent: float
|
percent: float
|
||||||
scheme: typing.Optional[TaxScheme] = None
|
scheme: typing.Optional[TaxScheme] = None
|
||||||
tax_amount: Amount = Amount(0.0)
|
tax_amount: Amount = field(default_factory=lambda: Amount(0.0))
|
||||||
|
|
||||||
def calculate(self, invline):
|
def calculate(self, invline):
|
||||||
if self.percent is not None:
|
if self.percent is not None:
|
||||||
@ -317,8 +338,8 @@ class TaxSubTotal:
|
|||||||
@dataclass
|
@dataclass
|
||||||
class TaxTotal:
|
class TaxTotal:
|
||||||
subtotals: list
|
subtotals: list
|
||||||
tax_amount: Amount = Amount(0.0)
|
tax_amount: Amount = field(default_factory=lambda: Amount(0.0))
|
||||||
taxable_amount: Amount = Amount(0.0)
|
taxable_amount: Amount = field(default_factory=lambda: Amount(0.0))
|
||||||
|
|
||||||
def calculate(self, invline):
|
def calculate(self, invline):
|
||||||
self.taxable_amount = invline.total_amount
|
self.taxable_amount = invline.total_amount
|
||||||
@ -334,21 +355,23 @@ class TaxTotalOmit(TaxTotal):
|
|||||||
def calculate(self, invline):
|
def calculate(self, invline):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class WithholdingTaxSubTotal:
|
class WithholdingTaxSubTotal:
|
||||||
percent: float
|
percent: float
|
||||||
scheme: typing.Optional[TaxScheme] = None
|
scheme: typing.Optional[TaxScheme] = None
|
||||||
tax_amount: Amount = Amount(0.0)
|
tax_amount: Amount = field(default_factory=lambda: Amount(0.0))
|
||||||
|
|
||||||
def calculate(self, invline):
|
def calculate(self, invline):
|
||||||
if self.percent is not None:
|
if self.percent is not None:
|
||||||
self.tax_amount = invline.total_amount * Amount(self.percent / 100)
|
self.tax_amount = invline.total_amount * Amount(self.percent / 100)
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class WithholdingTaxTotal:
|
class WithholdingTaxTotal:
|
||||||
subtotals: list
|
subtotals: list
|
||||||
tax_amount: Amount = Amount(0.0)
|
tax_amount: Amount = field(default_factory=lambda: Amount(0.0))
|
||||||
taxable_amount: Amount = Amount(0.0)
|
taxable_amount: Amount = field(default_factory=lambda: Amount(0.0))
|
||||||
|
|
||||||
def calculate(self, invline):
|
def calculate(self, invline):
|
||||||
self.taxable_amount = invline.total_amount
|
self.taxable_amount = invline.total_amount
|
||||||
@ -357,6 +380,7 @@ class WithholdingTaxTotal:
|
|||||||
subtax.calculate(invline)
|
subtax.calculate(invline)
|
||||||
self.tax_amount += subtax.tax_amount
|
self.tax_amount += subtax.tax_amount
|
||||||
|
|
||||||
|
|
||||||
class WithholdingTaxTotalOmit(WithholdingTaxTotal):
|
class WithholdingTaxTotalOmit(WithholdingTaxTotal):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super().__init__([])
|
super().__init__([])
|
||||||
@ -364,6 +388,7 @@ class WithholdingTaxTotalOmit(WithholdingTaxTotal):
|
|||||||
def calculate(self, invline):
|
def calculate(self, invline):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class Price:
|
class Price:
|
||||||
amount: Amount
|
amount: Amount
|
||||||
@ -379,6 +404,7 @@ class Price:
|
|||||||
|
|
||||||
self.amount *= self.quantity
|
self.amount *= self.quantity
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class PaymentMean:
|
class PaymentMean:
|
||||||
DEBIT = '01'
|
DEBIT = '01'
|
||||||
@ -396,8 +422,9 @@ class PaymentMean:
|
|||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class PrePaidPayment:
|
class PrePaidPayment:
|
||||||
#DIAN 1.7.-2020: FBD03
|
# DIAN 1.7.-2020: FBD03
|
||||||
paid_amount: Amount = Amount(0.0)
|
paid_amount: Amount = field(default_factory=lambda: Amount(0.0))
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class BillingResponse:
|
class BillingResponse:
|
||||||
@ -405,6 +432,7 @@ class BillingResponse:
|
|||||||
code: str
|
code: str
|
||||||
description: str
|
description: str
|
||||||
|
|
||||||
|
|
||||||
class SupportDocumentCreditNoteResponse(BillingResponse):
|
class SupportDocumentCreditNoteResponse(BillingResponse):
|
||||||
"""
|
"""
|
||||||
ReferenceID: Identifica la sección del Documento
|
ReferenceID: Identifica la sección del Documento
|
||||||
@ -414,13 +442,13 @@ class SupportDocumentCreditNoteResponse(BillingResponse):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class BillingReference:
|
class BillingReference:
|
||||||
ident: str
|
ident: str
|
||||||
uuid: str
|
uuid: str
|
||||||
date: date
|
date: date
|
||||||
|
|
||||||
|
|
||||||
class CreditNoteDocumentReference(BillingReference):
|
class CreditNoteDocumentReference(BillingReference):
|
||||||
"""
|
"""
|
||||||
ident: Prefijo + Numero de la factura relacionada
|
ident: Prefijo + Numero de la factura relacionada
|
||||||
@ -428,6 +456,7 @@ class CreditNoteDocumentReference(BillingReference):
|
|||||||
date: fecha de emision de la factura relacionada
|
date: fecha de emision de la factura relacionada
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
class DebitNoteDocumentReference(BillingReference):
|
class DebitNoteDocumentReference(BillingReference):
|
||||||
"""
|
"""
|
||||||
ident: Prefijo + Numero de la factura relacionada
|
ident: Prefijo + Numero de la factura relacionada
|
||||||
@ -435,6 +464,7 @@ class DebitNoteDocumentReference(BillingReference):
|
|||||||
date: fecha de emision de la factura relacionada
|
date: fecha de emision de la factura relacionada
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
class InvoiceDocumentReference(BillingReference):
|
class InvoiceDocumentReference(BillingReference):
|
||||||
"""
|
"""
|
||||||
ident: Prefijo + Numero de la nota credito relacionada
|
ident: Prefijo + Numero de la nota credito relacionada
|
||||||
@ -442,6 +472,7 @@ class InvoiceDocumentReference(BillingReference):
|
|||||||
date: fecha de emision de la nota credito relacionada
|
date: fecha de emision de la nota credito relacionada
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class AllowanceChargeReason:
|
class AllowanceChargeReason:
|
||||||
code: str
|
code: str
|
||||||
@ -454,22 +485,26 @@ class AllowanceChargeReason:
|
|||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class AllowanceCharge:
|
class AllowanceCharge:
|
||||||
#DIAN 1.7.-2020: FAQ03
|
# DIAN 1.7.-2020: FAQ03
|
||||||
charge_indicator: bool = True
|
charge_indicator: bool = True
|
||||||
amount: Amount = Amount(0.0)
|
amount: Amount = field(default_factory=lambda: Amount(0.0))
|
||||||
reason: AllowanceChargeReason = None
|
reason: AllowanceChargeReason = None
|
||||||
|
|
||||||
#Valor Base para calcular el descuento o el cargo
|
# Valor Base para calcular el descuento o el cargo
|
||||||
base_amount: typing.Optional[Amount] = Amount(0.0)
|
base_amount: typing.Optional[Amount] = field(
|
||||||
|
default_factory=lambda: Amount(0.0))
|
||||||
|
|
||||||
# Porcentaje: Porcentaje que aplicar.
|
# Porcentaje: Porcentaje que aplicar.
|
||||||
multiplier_factor_numeric: Amount = Amount(1.0)
|
multiplier_factor_numeric: Amount = field(
|
||||||
|
default_factory=lambda: Amount(1.0))
|
||||||
|
|
||||||
def isCharge(self):
|
def isCharge(self):
|
||||||
return self.charge_indicator == True
|
charge_indicator = self.charge_indicator is True
|
||||||
|
return charge_indicator
|
||||||
|
|
||||||
def isDiscount(self):
|
def isDiscount(self):
|
||||||
return self.charge_indicator == False
|
charge_indicator = self.charge_indicator is False
|
||||||
|
return charge_indicator
|
||||||
|
|
||||||
def asCharge(self):
|
def asCharge(self):
|
||||||
self.charge_indicator = True
|
self.charge_indicator = True
|
||||||
@ -483,11 +518,13 @@ class AllowanceCharge:
|
|||||||
def set_base_amount(self, amount):
|
def set_base_amount(self, amount):
|
||||||
self.base_amount = amount
|
self.base_amount = amount
|
||||||
|
|
||||||
|
|
||||||
class AllowanceChargeAsDiscount(AllowanceCharge):
|
class AllowanceChargeAsDiscount(AllowanceCharge):
|
||||||
def __init__(self, amount: Amount = Amount(0.0)):
|
def __init__(self, amount: Amount = Amount(0.0)):
|
||||||
self.charge_indicator = False
|
self.charge_indicator = False
|
||||||
self.amount = amount
|
self.amount = amount
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class InvoiceLine:
|
class InvoiceLine:
|
||||||
# RESOLUCION 0004: pagina 155
|
# RESOLUCION 0004: pagina 155
|
||||||
@ -501,7 +538,8 @@ class InvoiceLine:
|
|||||||
# de subtotal
|
# de subtotal
|
||||||
tax: typing.Optional[TaxTotal]
|
tax: typing.Optional[TaxTotal]
|
||||||
withholding: typing.Optional[WithholdingTaxTotal]
|
withholding: typing.Optional[WithholdingTaxTotal]
|
||||||
allowance_charge: typing.List[AllowanceCharge] = dataclasses.field(default_factory=list)
|
allowance_charge: typing.List[AllowanceCharge] = dataclasses.field(
|
||||||
|
default_factory=list)
|
||||||
|
|
||||||
def add_allowance_charge(self, charge):
|
def add_allowance_charge(self, charge):
|
||||||
if not isinstance(charge, AllowanceCharge):
|
if not isinstance(charge, AllowanceCharge):
|
||||||
@ -566,18 +604,19 @@ class InvoiceLine:
|
|||||||
if self.withholding is None:
|
if self.withholding is None:
|
||||||
self.withholding = WithholdingTaxTotalOmit()
|
self.withholding = WithholdingTaxTotalOmit()
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class LegalMonetaryTotal:
|
class LegalMonetaryTotal:
|
||||||
line_extension_amount: Amount = Amount(0.0)
|
line_extension_amount: Amount = field(default_factory=lambda: Amount(0.0))
|
||||||
tax_exclusive_amount: Amount = Amount(0.0)
|
tax_exclusive_amount: Amount = field(default_factory=lambda: Amount(0.0))
|
||||||
tax_inclusive_amount: Amount = Amount(0.0)
|
tax_inclusive_amount: Amount = field(default_factory=lambda: Amount(0.0))
|
||||||
charge_total_amount: Amount = Amount(0.0)
|
charge_total_amount: Amount = field(default_factory=lambda: Amount(0.0))
|
||||||
allowance_total_amount: Amount = Amount(0.0)
|
allowance_total_amount: Amount = field(default_factory=lambda: Amount(0.0))
|
||||||
payable_amount: Amount = Amount(0.0)
|
payable_amount: Amount = field(default_factory=lambda: Amount(0.0))
|
||||||
prepaid_amount: Amount = Amount(0.0)
|
prepaid_amount: Amount = field(default_factory=lambda: Amount(0.0))
|
||||||
|
|
||||||
def calculate(self):
|
def calculate(self):
|
||||||
#DIAN 1.7.-2020: FAU14
|
# DIAN 1.7.-2020: FAU14
|
||||||
self.payable_amount = \
|
self.payable_amount = \
|
||||||
self.tax_inclusive_amount \
|
self.tax_inclusive_amount \
|
||||||
+ self.allowance_total_amount \
|
+ self.allowance_total_amount \
|
||||||
@ -585,26 +624,29 @@ class LegalMonetaryTotal:
|
|||||||
- self.prepaid_amount
|
- self.prepaid_amount
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class NationalSalesInvoiceDocumentType(str):
|
class NationalSalesInvoiceDocumentType(str):
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
# 6.1.3
|
# 6.1.3
|
||||||
return '01'
|
return '01'
|
||||||
|
|
||||||
|
|
||||||
class CreditNoteDocumentType(str):
|
class CreditNoteDocumentType(str):
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
# 6.1.3
|
# 6.1.3
|
||||||
return '91'
|
return '91'
|
||||||
|
|
||||||
|
|
||||||
class DebitNoteDocumentType(str):
|
class DebitNoteDocumentType(str):
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
# 6.1.3
|
# 6.1.3
|
||||||
return '92'
|
return '92'
|
||||||
|
|
||||||
|
|
||||||
class CreditNoteSupportDocumentType(str):
|
class CreditNoteSupportDocumentType(str):
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return '95'
|
return '95'
|
||||||
|
|
||||||
|
|
||||||
class Invoice:
|
class Invoice:
|
||||||
def __init__(self, type_code: str):
|
def __init__(self, type_code: str):
|
||||||
if str(type_code) not in codelist.TipoDocumento:
|
if str(type_code) not in codelist.TipoDocumento:
|
||||||
@ -650,7 +692,8 @@ class Invoice:
|
|||||||
if len(prefix) <= 4:
|
if len(prefix) <= 4:
|
||||||
self.invoice_ident_prefix = prefix
|
self.invoice_ident_prefix = prefix
|
||||||
else:
|
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):
|
def set_ident(self, ident: str):
|
||||||
"""
|
"""
|
||||||
@ -703,7 +746,6 @@ class Invoice:
|
|||||||
def set_discrepancy_response(self, billing_response: BillingResponse):
|
def set_discrepancy_response(self, billing_response: BillingResponse):
|
||||||
self.invoice_discrepancy_response = billing_response
|
self.invoice_discrepancy_response = billing_response
|
||||||
|
|
||||||
|
|
||||||
def accept(self, visitor):
|
def accept(self, visitor):
|
||||||
visitor.visit_payment_mean(self.invoice_payment_mean)
|
visitor.visit_payment_mean(self.invoice_payment_mean)
|
||||||
visitor.visit_customer(self.invoice_customer)
|
visitor.visit_customer(self.invoice_customer)
|
||||||
@ -715,29 +757,34 @@ class Invoice:
|
|||||||
|
|
||||||
def _calculate_legal_monetary_total(self):
|
def _calculate_legal_monetary_total(self):
|
||||||
for invline in self.invoice_lines:
|
for invline in self.invoice_lines:
|
||||||
self.invoice_legal_monetary_total.line_extension_amount += invline.total_amount
|
self.invoice_legal_monetary_total.line_extension_amount +=\
|
||||||
self.invoice_legal_monetary_total.tax_exclusive_amount += invline.total_tax_exclusive_amount
|
invline.total_amount
|
||||||
#DIAN 1.7.-2020: FAU6
|
self.invoice_legal_monetary_total.tax_exclusive_amount +=\
|
||||||
self.invoice_legal_monetary_total.tax_inclusive_amount += invline.total_tax_inclusive_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
|
# DIAN 1.7.-2020: FAU08
|
||||||
self.invoice_legal_monetary_total.allowance_total_amount = AmountCollection(self.invoice_allowance_charge)\
|
self.invoice_legal_monetary_total.allowance_total_amount =\
|
||||||
|
AmountCollection(self.invoice_allowance_charge)\
|
||||||
.filter(lambda charge: charge.isDiscount())\
|
.filter(lambda charge: charge.isDiscount())\
|
||||||
.map(lambda charge: charge.amount)\
|
.map(lambda charge: charge.amount)\
|
||||||
.sum()
|
.sum()
|
||||||
|
|
||||||
#DIAN 1.7.-2020: FAU10
|
# DIAN 1.7.-2020: FAU10
|
||||||
self.invoice_legal_monetary_total.charge_total_amount = AmountCollection(self.invoice_allowance_charge)\
|
self.invoice_legal_monetary_total.charge_total_amount =\
|
||||||
|
AmountCollection(self.invoice_allowance_charge)\
|
||||||
.filter(lambda charge: charge.isCharge())\
|
.filter(lambda charge: charge.isCharge())\
|
||||||
.map(lambda charge: charge.amount)\
|
.map(lambda charge: charge.amount)\
|
||||||
.sum()
|
.sum()
|
||||||
|
|
||||||
#DIAN 1.7.-2020: FAU12
|
# DIAN 1.7.-2020: FAU12
|
||||||
self.invoice_legal_monetary_total.prepaid_amount = AmountCollection(self.invoice_prepaid_payment)\
|
self.invoice_legal_monetary_total.prepaid_amount = AmountCollection(
|
||||||
.map(lambda paid: paid.paid_amount)\
|
self.invoice_prepaid_payment).map(
|
||||||
.sum()
|
lambda paid: paid.paid_amount).sum()
|
||||||
|
|
||||||
#DIAN 1.7.-2020: FAU14
|
# DIAN 1.7.-2020: FAU14
|
||||||
self.invoice_legal_monetary_total.calculate()
|
self.invoice_legal_monetary_total.calculate()
|
||||||
|
|
||||||
def _refresh_charges_base_amount(self):
|
def _refresh_charges_base_amount(self):
|
||||||
@ -745,11 +792,13 @@ class Invoice:
|
|||||||
for invline in self.invoice_lines:
|
for invline in self.invoice_lines:
|
||||||
if invline.allowance_charge:
|
if invline.allowance_charge:
|
||||||
# TODO actualmente solo uno de los cargos es permitido
|
# 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
|
# cargos a nivel de factura
|
||||||
for charge in self.invoice_allowance_charge:
|
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):
|
def calculate(self):
|
||||||
for invline in self.invoice_lines:
|
for invline in self.invoice_lines:
|
||||||
@ -757,6 +806,7 @@ class Invoice:
|
|||||||
self._calculate_legal_monetary_total()
|
self._calculate_legal_monetary_total()
|
||||||
self._refresh_charges_base_amount()
|
self._refresh_charges_base_amount()
|
||||||
|
|
||||||
|
|
||||||
class NationalSalesInvoice(Invoice):
|
class NationalSalesInvoice(Invoice):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super().__init__(NationalSalesInvoiceDocumentType())
|
super().__init__(NationalSalesInvoiceDocumentType())
|
||||||
@ -801,11 +851,14 @@ class DebitNote(Invoice):
|
|||||||
if not self.invoice_ident_prefix:
|
if not self.invoice_ident_prefix:
|
||||||
self.invoice_ident_prefix = self.invoice_ident[0:6]
|
self.invoice_ident_prefix = self.invoice_ident[0:6]
|
||||||
|
|
||||||
|
|
||||||
class SupportDocument(Invoice):
|
class SupportDocument(Invoice):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class SupportDocumentCreditNote(SupportDocument):
|
class SupportDocumentCreditNote(SupportDocument):
|
||||||
def __init__(self, invoice_document_reference: BillingReference,
|
def __init__(
|
||||||
|
self, invoice_document_reference: BillingReference,
|
||||||
invoice_discrepancy_response: BillingResponse):
|
invoice_discrepancy_response: BillingResponse):
|
||||||
super().__init__(CreditNoteSupportDocumentType())
|
super().__init__(CreditNoteSupportDocumentType())
|
||||||
|
|
||||||
|
@ -2,6 +2,7 @@ from .invoice import *
|
|||||||
from .credit_note import *
|
from .credit_note import *
|
||||||
from .debit_note import *
|
from .debit_note import *
|
||||||
from .utils import *
|
from .utils import *
|
||||||
from .attached_document import *
|
|
||||||
from .support_document import *
|
from .support_document import *
|
||||||
from .support_document_credit_note import *
|
from .support_document_credit_note import *
|
||||||
|
from .attached_document import *
|
||||||
|
from .application_response import *
|
||||||
|
177
facho/fe/form_xml/application_response.py
Normal file
177
facho/fe/form_xml/application_response.py
Normal file
@ -0,0 +1,177 @@
|
|||||||
|
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
|
@ -1,14 +1,227 @@
|
|||||||
from .. import fe
|
from .. import fe
|
||||||
|
from .application_response import ApplicationResponse
|
||||||
|
|
||||||
__all__ = ['AttachedDocument']
|
__all__ = ['AttachedDocument']
|
||||||
|
|
||||||
|
|
||||||
class AttachedDocument():
|
class AttachedDocument():
|
||||||
|
|
||||||
def __init__(self, id):
|
def __init__(self, invoice, DIANInvoiceXML, id):
|
||||||
schema = 'urn:oasis:names:specification:ubl:schema:xsd:AttachedDocument-2'
|
self.schema =\
|
||||||
self.fexml = fe.FeXML('AttachedDocument', schema)
|
'urn:oasis:names:specification:ubl:schema:xsd:AttachedDocument-2'
|
||||||
self.fexml.set_element('./cbc:ID', id)
|
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 = (
|
||||||
|
'<?xml version="1.0" encoding="UTF-8" standalone="no"?>'
|
||||||
|
) + DIANInvoiceXML.tostring()
|
||||||
|
attachment = "<![CDATA[{}]]>".format(
|
||||||
|
document)
|
||||||
|
|
||||||
|
return attachment
|
||||||
|
|
||||||
def toFachoXML(self):
|
def toFachoXML(self):
|
||||||
return self.fexml
|
return self.fexml
|
||||||
|
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
from .. import fe
|
# from .. import fe
|
||||||
from ..form import *
|
# from ..form import *
|
||||||
from .invoice import DIANInvoiceXML
|
from .invoice import DIANInvoiceXML
|
||||||
|
|
||||||
__all__ = ['DIANCreditNoteXML']
|
__all__ = ['DIANCreditNoteXML']
|
||||||
|
|
||||||
|
|
||||||
class DIANCreditNoteXML(DIANInvoiceXML):
|
class DIANCreditNoteXML(DIANInvoiceXML):
|
||||||
"""
|
"""
|
||||||
DianInvoiceXML mapea objeto form.Invoice a XML segun
|
DianInvoiceXML mapea objeto form.Invoice a XML segun
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
from .. import fe
|
# from .. import fe
|
||||||
from ..form import *
|
# from ..form import *
|
||||||
from .invoice import DIANInvoiceXML
|
from .invoice import DIANInvoiceXML
|
||||||
|
|
||||||
__all__ = ['DIANDebitNoteXML']
|
__all__ = ['DIANDebitNoteXML']
|
||||||
|
|
||||||
|
|
||||||
class DIANDebitNoteXML(DIANInvoiceXML):
|
class DIANDebitNoteXML(DIANInvoiceXML):
|
||||||
"""
|
"""
|
||||||
DianInvoiceXML mapea objeto form.Invoice a XML segun
|
DianInvoiceXML mapea objeto form.Invoice a XML segun
|
||||||
@ -19,19 +20,24 @@ class DIANDebitNoteXML(DIANInvoiceXML):
|
|||||||
def tag_document_concilied(fexml):
|
def tag_document_concilied(fexml):
|
||||||
return 'Debited'
|
return 'Debited'
|
||||||
|
|
||||||
#DIAN 1.7.-2020: DAU03
|
# DIAN 1.7.-2020: DAU03
|
||||||
def set_legal_monetary(fexml, invoice):
|
def set_legal_monetary(fexml, invoice):
|
||||||
fexml.set_element_amount('./cac:RequestedMonetaryTotal/cbc:LineExtensionAmount',
|
fexml.set_element_amount(
|
||||||
|
'./cac:RequestedMonetaryTotal/cbc:LineExtensionAmount',
|
||||||
invoice.invoice_legal_monetary_total.line_extension_amount)
|
invoice.invoice_legal_monetary_total.line_extension_amount)
|
||||||
|
|
||||||
fexml.set_element_amount('./cac:RequestedMonetaryTotal/cbc:TaxExclusiveAmount',
|
fexml.set_element_amount(
|
||||||
|
'./cac:RequestedMonetaryTotal/cbc:TaxExclusiveAmount',
|
||||||
invoice.invoice_legal_monetary_total.tax_exclusive_amount)
|
invoice.invoice_legal_monetary_total.tax_exclusive_amount)
|
||||||
|
|
||||||
fexml.set_element_amount('./cac:RequestedMonetaryTotal/cbc:TaxInclusiveAmount',
|
fexml.set_element_amount(
|
||||||
|
'./cac:RequestedMonetaryTotal/cbc:TaxInclusiveAmount',
|
||||||
invoice.invoice_legal_monetary_total.tax_inclusive_amount)
|
invoice.invoice_legal_monetary_total.tax_inclusive_amount)
|
||||||
|
|
||||||
fexml.set_element_amount('./cac:RequestedMonetaryTotal/cbc:ChargeTotalAmount',
|
fexml.set_element_amount(
|
||||||
|
'./cac:RequestedMonetaryTotal/cbc:ChargeTotalAmount',
|
||||||
invoice.invoice_legal_monetary_total.charge_total_amount)
|
invoice.invoice_legal_monetary_total.charge_total_amount)
|
||||||
|
|
||||||
fexml.set_element_amount('./cac:RequestedMonetaryTotal/cbc:PayableAmount',
|
fexml.set_element_amount(
|
||||||
|
'./cac:RequestedMonetaryTotal/cbc:PayableAmount',
|
||||||
invoice.invoice_legal_monetary_total.payable_amount)
|
invoice.invoice_legal_monetary_total.payable_amount)
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
from .. import fe
|
from .. import fe
|
||||||
from ..form import *
|
from ..form import *
|
||||||
|
from collections import defaultdict
|
||||||
|
from .attached_document import AttachedDocument
|
||||||
|
|
||||||
__all__ = ['DIANInvoiceXML']
|
__all__ = ['DIANInvoiceXML']
|
||||||
|
|
||||||
@ -9,7 +11,7 @@ class DIANInvoiceXML(fe.FeXML):
|
|||||||
lo indicado para la facturacion electronica.
|
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')
|
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: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:InvoiceSource')
|
||||||
@ -20,343 +22,467 @@ class DIANInvoiceXML(fe.FeXML):
|
|||||||
# ZE02 se requiere existencia para firmar
|
# ZE02 se requiere existencia para firmar
|
||||||
ublextension = self.fragment('./ext:UBLExtensions/ext:UBLExtension', append=True)
|
ublextension = self.fragment('./ext:UBLExtensions/ext:UBLExtension', append=True)
|
||||||
extcontent = ublextension.find_or_create_element('/ext:UBLExtension/ext:ExtensionContent')
|
extcontent = ublextension.find_or_create_element('/ext:UBLExtension/ext:ExtensionContent')
|
||||||
self.attach_invoice(invoice)
|
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
|
||||||
|
|
||||||
def set_supplier(fexml, invoice):
|
def set_supplier(fexml, invoice):
|
||||||
fexml.placeholder_for('./cac:AccountingSupplierParty')
|
fexml.placeholder_for('./cac:AccountingSupplierParty')
|
||||||
|
|
||||||
#DIAN 1.7.-2020: CAJ02
|
# DIAN 1.7.-2020: CAJ02
|
||||||
#DIAN 1.7.-2020: FAJ02
|
# DIAN 1.7.-2020: FAJ02
|
||||||
fexml.set_element('./cac:AccountingSupplierParty/cbc:AdditionalAccountID',
|
fexml.set_element(
|
||||||
|
'./cac:AccountingSupplierParty/cbc:AdditionalAccountID',
|
||||||
invoice.invoice_supplier.organization_code)
|
invoice.invoice_supplier.organization_code)
|
||||||
|
|
||||||
#DIAN 1.7.-2020: CAJ06
|
# DIAN 1.7.-2020: CAJ06
|
||||||
#DIAN 1.7.-2020: FAJ06
|
# DIAN 1.7.-2020: FAJ06
|
||||||
fexml.set_element('./cac:AccountingSupplierParty/cac:Party/cac:PartyName/cbc:Name',
|
fexml.set_element(
|
||||||
|
'./cac:AccountingSupplierParty/cac:Party/cac:PartyName/cbc:Name',
|
||||||
invoice.invoice_supplier.name)
|
invoice.invoice_supplier.name)
|
||||||
|
|
||||||
#DIAN 1.7.-2020: CAJ07, CAJ08
|
# DIAN 1.7.-2020: CAJ07, CAJ08
|
||||||
#DIAN 1.7.-2020: FAJ07
|
# DIAN 1.7.-2020: FAJ07
|
||||||
fexml.placeholder_for('./cac:AccountingSupplierParty/cac:Party/cac:PhysicalLocation/cac:Address')
|
fexml.placeholder_for(
|
||||||
|
'./cac:AccountingSupplierParty/cac:Party/cac:PhysicalLocation/cac:Address')
|
||||||
|
|
||||||
#DIAN 1.7.-2020: FAJ08
|
# DIAN 1.7.-2020: FAJ08
|
||||||
#DIAN 1.7.-2020: CAJ09
|
# DIAN 1.7.-2020: CAJ09
|
||||||
fexml.set_element('./cac:AccountingSupplierParty/cac:Party/cac:PhysicalLocation/cac:Address/cbc:ID',
|
fexml.set_element(
|
||||||
|
'./cac:AccountingSupplierParty/cac:Party/cac:PhysicalLocation/cac:Address/cbc:ID',
|
||||||
invoice.invoice_supplier.address.city.code)
|
invoice.invoice_supplier.address.city.code)
|
||||||
#DIAN 1.7.-2020: FAJ09
|
# DIAN 1.7.-2020: FAJ09
|
||||||
#DIAN 1.7.-2020: CAJ10
|
# DIAN 1.7.-2020: CAJ10
|
||||||
fexml.set_element('./cac:AccountingSupplierParty/cac:Party/cac:PhysicalLocation/cac:Address/cbc:CityName',
|
fexml.set_element(
|
||||||
|
'./cac:AccountingSupplierParty/cac:Party/cac:PhysicalLocation/cac:Address/cbc:CityName',
|
||||||
invoice.invoice_supplier.address.city.name)
|
invoice.invoice_supplier.address.city.name)
|
||||||
#DIAN 1.7.-2020: FAJ11
|
# DIAN 1.7.-2020: FAJ11
|
||||||
#DIAN 1.7.-2020: CAJ11
|
# DIAN 1.7.-2020: CAJ11
|
||||||
fexml.set_element('./cac:AccountingSupplierParty/cac:Party/cac:PhysicalLocation/cac:Address/cbc:CountrySubentity',
|
fexml.set_element(
|
||||||
|
'./cac:AccountingSupplierParty/cac:Party/cac:PhysicalLocation/cac:Address/cbc:CountrySubentity',
|
||||||
invoice.invoice_supplier.address.countrysubentity.name)
|
invoice.invoice_supplier.address.countrysubentity.name)
|
||||||
|
|
||||||
#DIAN 1.7.-2020: FAJ12
|
# DIAN 1.7.-2020: FAJ12
|
||||||
#DIAN 1.7.-2020: CAJ12
|
# DIAN 1.7.-2020: CAJ12
|
||||||
fexml.set_element('./cac:AccountingSupplierParty/cac:Party/cac:PhysicalLocation/cac:Address/cbc:CountrySubentityCode',
|
fexml.set_element(
|
||||||
|
'./cac:AccountingSupplierParty/cac:Party/cac:PhysicalLocation/cac:Address/cbc:CountrySubentityCode',
|
||||||
invoice.invoice_supplier.address.countrysubentity.code)
|
invoice.invoice_supplier.address.countrysubentity.code)
|
||||||
#DIAN 1.7.-2020: FAJ14
|
# DIAN 1.7.-2020: FAJ14
|
||||||
#DIAN 1.7.-2020: CAJ13, CAJ14
|
# DIAN 1.7.-2020: CAJ13, CAJ14
|
||||||
fexml.set_element('./cac:AccountingSupplierParty/cac:Party/cac:PhysicalLocation/cac:Address/cac:AddressLine/cbc:Line',
|
fexml.set_element(
|
||||||
|
'./cac:AccountingSupplierParty/cac:Party/cac:PhysicalLocation/cac:Address/cac:AddressLine/cbc:Line',
|
||||||
invoice.invoice_supplier.address.street)
|
invoice.invoice_supplier.address.street)
|
||||||
|
|
||||||
#DIAN 1.7.-2020: FAJ16
|
# DIAN 1.7.-2020: FAJ16
|
||||||
#DIAN 1.7.-2020: CAJ16, CAJ16
|
# DIAN 1.7.-2020: CAJ16, CAJ16
|
||||||
fexml.set_element('./cac:AccountingSupplierParty/cac:Party/cac:PhysicalLocation/cac:Address/cac:Country/cbc:IdentificationCode',
|
fexml.set_element(
|
||||||
|
'./cac:AccountingSupplierParty/cac:Party/cac:PhysicalLocation/cac:Address/cac:Country/cbc:IdentificationCode',
|
||||||
invoice.invoice_supplier.address.country.code)
|
invoice.invoice_supplier.address.country.code)
|
||||||
|
|
||||||
#DIAN 1.7.-2020: FAJ17
|
# DIAN 1.7.-2020: FAJ17
|
||||||
#DIAN 1.7.-2020: CAJ17
|
# DIAN 1.7.-2020: CAJ17
|
||||||
fexml.set_element('./cac:AccountingSupplierParty/cac:Party/cac:PhysicalLocation/cac:Address/cac:Country/cbc:Name',
|
fexml.set_element(
|
||||||
|
'./cac:AccountingSupplierParty/cac:Party/cac:PhysicalLocation/cac:Address/cac:Country/cbc:Name',
|
||||||
invoice.invoice_supplier.address.country.name,
|
invoice.invoice_supplier.address.country.name,
|
||||||
#DIAN 1.7.-2020: FAJ18
|
# DIAN 1.7.-2020: FAJ18
|
||||||
languageID = 'es')
|
languageID='es')
|
||||||
|
|
||||||
supplier_company_id_attrs = fe.SCHEME_AGENCY_ATTRS.copy()
|
supplier_company_id_attrs = fe.SCHEME_AGENCY_ATTRS.copy()
|
||||||
supplier_company_id_attrs.update({'schemeID': invoice.invoice_supplier.ident.dv,
|
supplier_company_id_attrs.update({
|
||||||
'schemeName': invoice.invoice_supplier.ident.type_fiscal})
|
'schemeID': invoice.invoice_supplier.ident.dv,
|
||||||
|
'schemeName': invoice.invoice_supplier.ident.type_fiscal
|
||||||
|
})
|
||||||
|
|
||||||
#DIAN 1.7.-2020: FAJ19
|
# DIAN 1.7.-2020: FAJ19
|
||||||
#DIAN 1.7.-2020: CAJ19
|
# DIAN 1.7.-2020: CAJ19
|
||||||
fexml.placeholder_for('./cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme')
|
fexml.placeholder_for(
|
||||||
|
'./cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme')
|
||||||
|
|
||||||
#DIAN 1.7.-2020: FAJ20
|
# DIAN 1.7.-2020: FAJ20
|
||||||
#DIAN 1.7.-2020: CAJ20
|
# DIAN 1.7.-2020: CAJ20
|
||||||
fexml.set_element('./cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme/cbc:RegistrationName',
|
fexml.set_element(
|
||||||
|
'./cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme/cbc:RegistrationName',
|
||||||
invoice.invoice_supplier.legal_name)
|
invoice.invoice_supplier.legal_name)
|
||||||
|
|
||||||
#DIAN 1.7.-2020: FAJ21
|
# DIAN 1.7.-2020: FAJ21
|
||||||
#DIAN 1.7.-2020: CAJ21
|
# DIAN 1.7.-2020: CAJ21
|
||||||
fexml.set_element('./cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme/cbc:CompanyID',
|
fexml.set_element(
|
||||||
|
'./cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme/cbc:CompanyID',
|
||||||
invoice.invoice_supplier.ident,
|
invoice.invoice_supplier.ident,
|
||||||
#DIAN 1.7.-2020: FAJ22,FAJ23,FAJ24,FAJ25
|
# DIAN 1.7.-2020: FAJ22,FAJ23,FAJ24,FAJ25
|
||||||
**supplier_company_id_attrs)
|
**supplier_company_id_attrs)
|
||||||
fexml.set_element('./cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme/cbc:TaxLevelCode',
|
fexml.set_element(
|
||||||
#DIAN 1.7.-2020: FAJ26
|
'./cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme/cbc:TaxLevelCode',
|
||||||
#DIAN 1.7.-2020: CAJ26
|
# DIAN 1.7.-2020: FAJ26
|
||||||
|
# DIAN 1.7.-2020: CAJ26
|
||||||
invoice.invoice_supplier.responsability_code,
|
invoice.invoice_supplier.responsability_code,
|
||||||
#DIAN 1.7.-2020: FAJ27
|
# DIAN 1.7.-2020: FAJ27
|
||||||
#DIAN 1.7.-2020: CAJ27
|
# DIAN 1.7.-2020: CAJ27
|
||||||
listName=invoice.invoice_supplier.responsability_regime_code)
|
listName=invoice.invoice_supplier.responsability_regime_code)
|
||||||
#DIAN 1.7.-2020: FAJ28
|
# DIAN 1.7.-2020: FAJ28
|
||||||
#DIAN 1.7.-2020: CAJ28
|
# DIAN 1.7.-2020: CAJ28
|
||||||
fexml.placeholder_for('./cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme/cac:RegistrationAddress')
|
fexml.placeholder_for(
|
||||||
|
'./cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme/cac:RegistrationAddress')
|
||||||
|
|
||||||
#DIAN 1.7.-2020: FAJ29
|
# DIAN 1.7.-2020: FAJ29
|
||||||
#DIAN 1.7.-2020: CAJ29
|
# DIAN 1.7.-2020: CAJ29
|
||||||
fexml.set_element('./cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme/cac:RegistrationAddress/cbc:ID',
|
fexml.set_element(
|
||||||
|
'./cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme/cac:RegistrationAddress/cbc:ID',
|
||||||
invoice.invoice_supplier.address.city.code)
|
invoice.invoice_supplier.address.city.code)
|
||||||
|
|
||||||
#DIAN 1.7.-2020: FAJ30
|
# DIAN 1.7.-2020: FAJ30
|
||||||
#DIAN 1.7.-2020: CAJ30
|
# DIAN 1.7.-2020: CAJ30
|
||||||
fexml.set_element('./cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme/cac:RegistrationAddress/cbc:CityName', invoice.invoice_supplier.address.city.name)
|
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: FAJ31
|
||||||
#DIAN 1.7.-2020: CAJ31
|
# DIAN 1.7.-2020: CAJ31
|
||||||
fexml.set_element('./cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme/cac:RegistrationAddress/cbc:CountrySubentity',
|
fexml.set_element(
|
||||||
|
'./cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme/cac:RegistrationAddress/cbc:CountrySubentity',
|
||||||
invoice.invoice_supplier.address.countrysubentity.name)
|
invoice.invoice_supplier.address.countrysubentity.name)
|
||||||
|
|
||||||
#DIAN 1.7.-2020: FAJ32
|
# DIAN 1.7.-2020: FAJ32
|
||||||
#DIAN 1.7.-2020: CAJ32
|
# DIAN 1.7.-2020: CAJ32
|
||||||
fexml.set_element('./cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme/cac:RegistrationAddress/cbc:CountrySubentityCode',
|
fexml.set_element(
|
||||||
|
'./cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme/cac:RegistrationAddress/cbc:CountrySubentityCode',
|
||||||
invoice.invoice_supplier.address.countrysubentity.code)
|
invoice.invoice_supplier.address.countrysubentity.code)
|
||||||
|
|
||||||
#DIAN 1.7.-2020: FAJ33,FAJ34
|
# DIAN 1.7.-2020: FAJ33,FAJ34
|
||||||
#DIAN 1.7.-2020: CAJ33,CAJ34
|
# DIAN 1.7.-2020: CAJ33,CAJ34
|
||||||
fexml.set_element('./cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme/cac:RegistrationAddress/cac:AddressLine/cbc:Line',
|
fexml.set_element(
|
||||||
|
'./cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme/cac:RegistrationAddress/cac:AddressLine/cbc:Line',
|
||||||
invoice.invoice_supplier.address.street)
|
invoice.invoice_supplier.address.street)
|
||||||
|
|
||||||
#DIAN 1.7.-2020: FAJ35,FAJ36
|
# DIAN 1.7.-2020: FAJ35,FAJ36
|
||||||
#DIAN 1.7.-2020: CAJ35,CAJ36
|
# DIAN 1.7.-2020: CAJ35,CAJ36
|
||||||
fexml.set_element('./cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme/cac:RegistrationAddress/cac:Country/cbc:IdentificationCode',
|
fexml.set_element('./cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme/cac:RegistrationAddress/cac:Country/cbc:IdentificationCode',
|
||||||
invoice.invoice_supplier.address.country.code)
|
invoice.invoice_supplier.address.country.code)
|
||||||
|
|
||||||
#DIAN 1.7.-2020: FAJ37,FAJ38
|
# DIAN 1.7.-2020: FAJ37,FAJ38
|
||||||
#DIAN 1.7.-2020: CAJ37,CAJ38
|
# DIAN 1.7.-2020: CAJ37,CAJ38
|
||||||
fexml.set_element('./cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme/cac:RegistrationAddress/cac:Country/cbc:Name',
|
fexml.set_element(
|
||||||
|
'./cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme/cac:RegistrationAddress/cac:Country/cbc:Name',
|
||||||
invoice.invoice_supplier.address.country.name,
|
invoice.invoice_supplier.address.country.name,
|
||||||
languageID='es')
|
languageID='es')
|
||||||
|
|
||||||
#DIAN 1.7.-2020: FAJ39
|
# DIAN 1.7.-2020: FAJ39
|
||||||
#DIAN 1.7.-2020: CAJ39
|
# DIAN 1.7.-2020: CAJ39
|
||||||
fexml.placeholder_for('./cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme/cac:TaxScheme')
|
fexml.placeholder_for(
|
||||||
|
'./cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme/cac:TaxScheme')
|
||||||
|
|
||||||
#DIAN 1.7.-2020: CAJ40
|
# DIAN 1.7.-2020: CAJ40
|
||||||
#DIAN 1.7.-2020: FAJ40
|
# DIAN 1.7.-2020: FAJ40
|
||||||
fexml.set_element('./cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme/cac:TaxScheme/cbc:ID',
|
fexml.set_element(
|
||||||
|
'./cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme/cac:TaxScheme/cbc:ID',
|
||||||
invoice.invoice_customer.tax_scheme.code)
|
invoice.invoice_customer.tax_scheme.code)
|
||||||
|
|
||||||
#DIAN 1.7.-2020: CAJ41
|
# DIAN 1.7.-2020: CAJ41
|
||||||
fexml.set_element('./cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme/cac:TaxScheme/cbc:Name',
|
fexml.set_element(
|
||||||
|
'./cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme/cac:TaxScheme/cbc:Name',
|
||||||
invoice.invoice_customer.tax_scheme.name)
|
invoice.invoice_customer.tax_scheme.name)
|
||||||
|
|
||||||
#DIAN 1.7.-2020: FAJ42
|
# DIAN 1.7.-2020: FAJ42
|
||||||
#DIAN 1.7.-2020: CAJ42
|
# DIAN 1.7.-2020: CAJ42
|
||||||
fexml.placeholder_for('./cac:AccountingSupplierParty/cac:Party/cac:PartyLegalEntity')
|
fexml.placeholder_for(
|
||||||
|
'./cac:AccountingSupplierParty/cac:Party/cac:PartyLegalEntity')
|
||||||
|
|
||||||
#DIAN 1.7.-2020: FAJ43
|
# DIAN 1.7.-2020: FAJ43
|
||||||
#DIAN 1.7.-2020: CAJ43
|
# DIAN 1.7.-2020: CAJ43
|
||||||
fexml.set_element('./cac:AccountingSupplierParty/cac:Party/cac:PartyLegalEntity/cbc:RegistrationName',
|
fexml.set_element(
|
||||||
|
'./cac:AccountingSupplierParty/cac:Party/cac:PartyLegalEntity/cbc:RegistrationName',
|
||||||
invoice.invoice_supplier.legal_name)
|
invoice.invoice_supplier.legal_name)
|
||||||
|
|
||||||
#DIAN 1.7.-2020: FAJ44,FAJ45,FAJ46,FAJ47,FAJ48
|
# DIAN 1.7.-2020: FAJ44,FAJ45,FAJ46,FAJ47,FAJ48
|
||||||
#DIAN 1.7.-2020: CAJ44,CAJ45,CAJ46,CAJ47,CAJ48
|
# DIAN 1.7.-2020: CAJ44,CAJ45,CAJ46,CAJ47,CAJ48
|
||||||
fexml.set_element('./cac:AccountingSupplierParty/cac:Party/cac:PartyLegalEntity/cbc:CompanyID',
|
fexml.set_element(
|
||||||
|
'./cac:AccountingSupplierParty/cac:Party/cac:PartyLegalEntity/cbc:CompanyID',
|
||||||
invoice.invoice_supplier.ident,
|
invoice.invoice_supplier.ident,
|
||||||
**supplier_company_id_attrs)
|
**supplier_company_id_attrs)
|
||||||
|
|
||||||
#DIAN 1.7.-2020: FAJ49
|
# DIAN 1.7.-2020: FAJ49
|
||||||
#DIAN 1.7.-2020: CAJ49
|
# DIAN 1.7.-2020: CAJ49
|
||||||
fexml.placeholder_for('./cac:AccountingSupplierParty/cac:Party/cac:PartyLegalEntity/cac:CorporateRegistrationScheme')
|
fexml.placeholder_for(
|
||||||
|
'./cac:AccountingSupplierParty/cac:Party/cac:PartyLegalEntity/cac:CorporateRegistrationScheme')
|
||||||
|
|
||||||
#DIAN 1.7.-2020: FAJ50
|
# DIAN 1.7.-2020: FAJ50
|
||||||
#DIAN 1.7.-2020: CAJ50
|
# DIAN 1.7.-2020: CAJ50
|
||||||
fexml.set_element('./cac:AccountingSupplierParty/cac:Party/cac:PartyLegalEntity/cac:CorporateRegistrationScheme/cbc:ID',
|
fexml.set_element(
|
||||||
|
'./cac:AccountingSupplierParty/cac:Party/cac:PartyLegalEntity/cac:CorporateRegistrationScheme/cbc:ID',
|
||||||
invoice.invoice_ident_prefix)
|
invoice.invoice_ident_prefix)
|
||||||
|
|
||||||
#DIAN 1.7.-2020: CAJ67
|
# DIAN 1.7.-2020: CAJ67
|
||||||
fexml.placeholder_for('./cac:AccountingSupplierParty/cac:Party/cac:Contact')
|
fexml.placeholder_for(
|
||||||
|
'./cac:AccountingSupplierParty/cac:Party/cac:Contact')
|
||||||
|
|
||||||
#DIAN 1.7.-2020: FAJ71
|
# DIAN 1.7.-2020: FAJ71
|
||||||
#DIAN 1.7.-2020: CAJ71
|
# DIAN 1.7.-2020: CAJ71
|
||||||
fexml.set_element('./cac:AccountingSupplierParty/cac:Party/cac:Contact/cbc:ElectronicMail',
|
fexml.set_element(
|
||||||
|
'./cac:AccountingSupplierParty/cac:Party/cac:Contact/cbc:ElectronicMail',
|
||||||
invoice.invoice_supplier.email)
|
invoice.invoice_supplier.email)
|
||||||
|
|
||||||
|
|
||||||
def set_customer(fexml, invoice):
|
def set_customer(fexml, invoice):
|
||||||
fexml.placeholder_for('./cac:AccountingCustomerParty')
|
fexml.placeholder_for('./cac:AccountingCustomerParty')
|
||||||
fexml.set_element('./cac:AccountingCustomerParty/cbc:AdditionalAccountID',
|
fexml.set_element(
|
||||||
|
'./cac:AccountingCustomerParty/cbc:AdditionalAccountID',
|
||||||
invoice.invoice_customer.organization_code)
|
invoice.invoice_customer.organization_code)
|
||||||
fexml.set_element('./cac:AccountingCustomerParty/cac:Party/cac:PartyIdentification/cbc:ID',
|
fexml.set_element(
|
||||||
|
'./cac:AccountingCustomerParty/cac:Party/cac:PartyIdentification/cbc:ID',
|
||||||
invoice.invoice_customer.ident)
|
invoice.invoice_customer.ident)
|
||||||
fexml.set_element('./cac:AccountingCustomerParty/cac:Party/cac:PartyName/cbc:Name',
|
fexml.set_element(
|
||||||
|
'./cac:AccountingCustomerParty/cac:Party/cac:PartyName/cbc:Name',
|
||||||
invoice.invoice_customer.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()
|
customer_company_id_attrs = fe.SCHEME_AGENCY_ATTRS.copy()
|
||||||
#DIAN 1.7.-2020: FAK25
|
# DIAN 1.7.-2020: FAK25
|
||||||
#DIAN 1.7.-2020: CAK25
|
# DIAN 1.7.-2020: CAK25
|
||||||
customer_company_id_attrs.update({'schemeID': invoice.invoice_customer.ident.dv,
|
customer_company_id_attrs.update({
|
||||||
|
'schemeID': invoice.invoice_customer.ident.dv,
|
||||||
'schemeName': invoice.invoice_customer.ident.type_fiscal})
|
'schemeName': invoice.invoice_customer.ident.type_fiscal})
|
||||||
|
|
||||||
#DIAN 1.7.-2020: FAK07
|
# DIAN 1.7.-2020: FAK07
|
||||||
#DIAN 1.7.-2020: CAK07
|
# DIAN 1.7.-2020: CAK07
|
||||||
fexml.placeholder_for('./cac:AccountingCustomerParty/cac:Party/cac:PhysicalLocation/cac:Address')
|
fexml.placeholder_for(
|
||||||
|
'./cac:AccountingCustomerParty/cac:Party/cac:PhysicalLocation/cac:Address')
|
||||||
|
|
||||||
#DIAN 1.7.-2020: FAK08
|
# DIAN 1.7.-2020: FAK08
|
||||||
#DIAN 1.7.-2020: CAK08
|
# DIAN 1.7.-2020: CAK08
|
||||||
fexml.set_element('./cac:AccountingCustomerParty/cac:Party/cac:PhysicalLocation/cac:Address/cbc:ID',
|
fexml.set_element(
|
||||||
|
'./cac:AccountingCustomerParty/cac:Party/cac:PhysicalLocation/cac:Address/cbc:ID',
|
||||||
invoice.invoice_customer.address.city.code)
|
invoice.invoice_customer.address.city.code)
|
||||||
#DIAN 1.7.-2020: FAK09
|
# DIAN 1.7.-2020: FAK09
|
||||||
#DIAN 1.7.-2020: CAK09
|
# DIAN 1.7.-2020: CAK09
|
||||||
fexml.set_element('./cac:AccountingCustomerParty/cac:Party/cac:PhysicalLocation/cac:Address/cbc:CityName', invoice.invoice_customer.address.city.name)
|
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: FAK11
|
||||||
#DIAN 1.7.-2020: CAK11
|
# DIAN 1.7.-2020: CAK11
|
||||||
fexml.set_element('./cac:AccountingCustomerParty/cac:Party/cac:PhysicalLocation/cac:Address/cbc:CountrySubentity',
|
fexml.set_element(
|
||||||
|
'./cac:AccountingCustomerParty/cac:Party/cac:PhysicalLocation/cac:Address/cbc:CountrySubentity',
|
||||||
invoice.invoice_customer.address.countrysubentity.name)
|
invoice.invoice_customer.address.countrysubentity.name)
|
||||||
|
|
||||||
#DIAN 1.7.-2020: FAK12
|
# DIAN 1.7.-2020: FAK12
|
||||||
#DIAN 1.7.-2020: CAK12
|
# DIAN 1.7.-2020: CAK12
|
||||||
fexml.set_element('./cac:AccountingCustomerParty/cac:Party/cac:PhysicalLocation/cac:Address/cbc:CountrySubentityCode',
|
fexml.set_element(
|
||||||
|
'./cac:AccountingCustomerParty/cac:Party/cac:PhysicalLocation/cac:Address/cbc:CountrySubentityCode',
|
||||||
invoice.invoice_customer.address.countrysubentity.code)
|
invoice.invoice_customer.address.countrysubentity.code)
|
||||||
|
|
||||||
#DIAN 1.7.-2020: CAK13, CAK14
|
# DIAN 1.7.-2020: CAK13, CAK14
|
||||||
fexml.set_element('./cac:AccountingCustomerParty/cac:Party/cac:PhysicalLocation/cac:Address/cac:AddressLine/cbc:Line',
|
fexml.set_element(
|
||||||
|
'./cac:AccountingCustomerParty/cac:Party/cac:PhysicalLocation/cac:Address/cac:AddressLine/cbc:Line',
|
||||||
invoice.invoice_customer.address.street)
|
invoice.invoice_customer.address.street)
|
||||||
|
|
||||||
#DIAN 1.7.-2020: CAK16
|
# DIAN 1.7.-2020: CAK16
|
||||||
#DIAN 1.7.-2020: FAK16
|
# DIAN 1.7.-2020: FAK16
|
||||||
fexml.set_element('./cac:AccountingCustomerParty/cac:Party/cac:PhysicalLocation/cac:Address/cac:Country/cbc:IdentificationCode',
|
fexml.set_element(
|
||||||
|
'./cac:AccountingCustomerParty/cac:Party/cac:PhysicalLocation/cac:Address/cac:Country/cbc:IdentificationCode',
|
||||||
invoice.invoice_customer.address.country.code)
|
invoice.invoice_customer.address.country.code)
|
||||||
|
|
||||||
#DIAN 1.7.-2020: FAK17
|
# DIAN 1.7.-2020: FAK17
|
||||||
#DIAN 1.7.-2020: CAK17
|
# DIAN 1.7.-2020: CAK17
|
||||||
fexml.set_element('./cac:AccountingCustomerParty/cac:Party/cac:PhysicalLocation/cac:Address/cac:Country/cbc:Name',
|
fexml.set_element(
|
||||||
|
'./cac:AccountingCustomerParty/cac:Party/cac:PhysicalLocation/cac:Address/cac:Country/cbc:Name',
|
||||||
invoice.invoice_customer.address.country.name,
|
invoice.invoice_customer.address.country.name,
|
||||||
#DIAN 1.7.-2020: FAK18
|
# DIAN 1.7.-2020: FAK18
|
||||||
#DIAN 1.7.-2020: CAK18
|
# DIAN 1.7.-2020: CAK18
|
||||||
languageID='es')
|
languageID='es')
|
||||||
|
|
||||||
#DIAN 1.7.-2020: FAK17,FAK19
|
# DIAN 1.7.-2020: FAK17,FAK19
|
||||||
#DIAN 1.7.-2020: CAK19
|
# DIAN 1.7.-2020: CAK19
|
||||||
fexml.placeholder_for('./cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme')
|
fexml.placeholder_for(
|
||||||
|
'./cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme')
|
||||||
|
|
||||||
#DIAN 1.7.-2020: FAK17,FAK20
|
# DIAN 1.7.-2020: FAK17,FAK20
|
||||||
#DIAN 1.7.-2020: CAK20
|
# DIAN 1.7.-2020: CAK20
|
||||||
fexml.set_element('./cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cbc:RegistrationName',
|
fexml.set_element(
|
||||||
|
'./cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cbc:RegistrationName',
|
||||||
invoice.invoice_customer.legal_name)
|
invoice.invoice_customer.legal_name)
|
||||||
|
|
||||||
#DIAN 1.7.-2020: CAK21
|
# DIAN 1.7.-2020: CAK21
|
||||||
#DIAN 1.7.-2020: FAK21
|
# DIAN 1.7.-2020: FAK21
|
||||||
fexml.set_element('./cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cbc:CompanyID',
|
fexml.set_element(
|
||||||
|
'./cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cbc:CompanyID',
|
||||||
invoice.invoice_customer.ident,
|
invoice.invoice_customer.ident,
|
||||||
#DIAN 1.7.-2020: CAK22, CAK23, CAK24, CAK25
|
# DIAN 1.7.-2020: CAK22, CAK23, CAK24, CAK25
|
||||||
**customer_company_id_attrs)
|
**customer_company_id_attrs)
|
||||||
|
|
||||||
#DIAN 1.7.-2020: CAK26
|
# DIAN 1.7.-2020: CAK26
|
||||||
fexml.set_element('./cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cbc:TaxLevelCode',
|
fexml.set_element(
|
||||||
#DIAN 1.7.-2020: FAK26
|
'./cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cbc:TaxLevelCode',
|
||||||
|
# DIAN 1.7.-2020: FAK26
|
||||||
invoice.invoice_customer.responsability_code,
|
invoice.invoice_customer.responsability_code,
|
||||||
#DIAN 1.7.-2020: FAK27
|
# DIAN 1.7.-2020: FAK27
|
||||||
#DIAN 1.7.-2020: CAK27
|
# DIAN 1.7.-2020: CAK27
|
||||||
listName=invoice.invoice_customer.responsability_regime_code)
|
listName=invoice.invoice_customer.responsability_regime_code)
|
||||||
|
|
||||||
#DIAN 1.7.-2020: FAK28
|
# DIAN 1.7.-2020: FAK28
|
||||||
#DIAN 1.7.-2020: CAK28
|
# DIAN 1.7.-2020: CAK28
|
||||||
fexml.placeholder_for('./cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cac:RegistrationAddress')
|
fexml.placeholder_for(
|
||||||
|
'./cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cac:RegistrationAddress')
|
||||||
|
|
||||||
#DIAN 1.7.-2020: FAK29
|
# DIAN 1.7.-2020: FAK29
|
||||||
#DIAN 1.7.-2020: CAK29
|
# DIAN 1.7.-2020: CAK29
|
||||||
fexml.set_element('./cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cac:RegistrationAddress/cbc:ID',
|
fexml.set_element(
|
||||||
|
'./cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cac:RegistrationAddress/cbc:ID',
|
||||||
invoice.invoice_customer.address.city.code)
|
invoice.invoice_customer.address.city.code)
|
||||||
|
|
||||||
#DIAN 1.7.-2020: FAK30
|
# DIAN 1.7.-2020: FAK30
|
||||||
#DIAN 1.7.-2020: CAK30
|
# DIAN 1.7.-2020: CAK30
|
||||||
fexml.set_element('./cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cac:RegistrationAddress/cbc:CityName',
|
fexml.set_element(
|
||||||
|
'./cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cac:RegistrationAddress/cbc:CityName',
|
||||||
invoice.invoice_customer.address.city.name)
|
invoice.invoice_customer.address.city.name)
|
||||||
|
|
||||||
#DIAN 1.7.-2020: FAK31
|
# DIAN 1.7.-2020: FAK31
|
||||||
#DIAN 1.7.-2020: CAK31
|
# DIAN 1.7.-2020: CAK31
|
||||||
fexml.set_element('./cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cac:RegistrationAddress/cbc:CountrySubentity',
|
fexml.set_element(
|
||||||
|
'./cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cac:RegistrationAddress/cbc:CountrySubentity',
|
||||||
invoice.invoice_customer.address.countrysubentity.name)
|
invoice.invoice_customer.address.countrysubentity.name)
|
||||||
|
|
||||||
#DIAN 1.7.-2020: FAK32
|
# DIAN 1.7.-2020: FAK32
|
||||||
#DIAN 1.7.-2020: CAK32
|
# DIAN 1.7.-2020: CAK32
|
||||||
fexml.set_element('./cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cac:RegistrationAddress/cbc:CountrySubentityCode',
|
fexml.set_element(
|
||||||
|
'./cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cac:RegistrationAddress/cbc:CountrySubentityCode',
|
||||||
invoice.invoice_customer.address.countrysubentity.code)
|
invoice.invoice_customer.address.countrysubentity.code)
|
||||||
|
|
||||||
#DIAN 1.7.-2020: FAK33
|
# DIAN 1.7.-2020: FAK33
|
||||||
#DIAN 1.7.-2020: CAK33
|
# DIAN 1.7.-2020: CAK33
|
||||||
fexml.placeholder_for('./cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cac:RegistrationAddress/cac:AddressLine')
|
fexml.placeholder_for(
|
||||||
|
'./cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cac:RegistrationAddress/cac:AddressLine')
|
||||||
|
|
||||||
#DIAN 1.7.-2020: FAK34
|
# DIAN 1.7.-2020: FAK34
|
||||||
#DIAN 1.7.-2020: CAK34
|
# DIAN 1.7.-2020: CAK34
|
||||||
fexml.set_element('./cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cac:RegistrationAddress/cac:AddressLine/cbc:Line',
|
fexml.set_element(
|
||||||
|
'./cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cac:RegistrationAddress/cac:AddressLine/cbc:Line',
|
||||||
invoice.invoice_customer.address.street)
|
invoice.invoice_customer.address.street)
|
||||||
|
|
||||||
#DIAN 1.7.-2020: CAK35
|
# DIAN 1.7.-2020: CAK35
|
||||||
#DIAN 1.7.-2020: FAK35
|
# DIAN 1.7.-2020: FAK35
|
||||||
fexml.placeholder_for('./cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cac:RegistrationAddress/cac:Country')
|
fexml.placeholder_for(
|
||||||
|
'./cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cac:RegistrationAddress/cac:Country')
|
||||||
|
|
||||||
#DIAN 1.7.-2020: CAK36
|
# DIAN 1.7.-2020: CAK36
|
||||||
#DIAN 1.7.-2020: FAK36
|
# DIAN 1.7.-2020: FAK36
|
||||||
fexml.set_element('./cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cac:RegistrationAddress/cac:Country/cbc:IdentificationCode',
|
fexml.set_element(
|
||||||
|
'./cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cac:RegistrationAddress/cac:Country/cbc:IdentificationCode',
|
||||||
invoice.invoice_customer.address.country.code)
|
invoice.invoice_customer.address.country.code)
|
||||||
|
|
||||||
#DIAN 1.7.-2020: CAK37
|
# DIAN 1.7.-2020: CAK37
|
||||||
#DIAN 1.7.-2020: FAK37
|
# 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)
|
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: FAK38
|
||||||
#DIAN 1.7.-2020: CAK38
|
# DIAN 1.7.-2020: CAK38
|
||||||
fexml.set_element('./cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cac:RegistrationAddress/cac:Country/cbc:IdentificationCode',
|
fexml.set_element(
|
||||||
|
'./cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cac:RegistrationAddress/cac:Country/cbc:IdentificationCode',
|
||||||
invoice.invoice_customer.address.country.code,
|
invoice.invoice_customer.address.country.code,
|
||||||
languageID='es')
|
languageID='es')
|
||||||
|
|
||||||
#DIAN 1.7.-2020: CAK39
|
# DIAN 1.7.-2020: CAK39
|
||||||
#DIAN 1.7.-2020: FAK39
|
# DIAN 1.7.-2020: FAK39
|
||||||
fexml.placeholder_for('./cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cac:TaxScheme')
|
fexml.placeholder_for(
|
||||||
|
'./cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cac:TaxScheme')
|
||||||
|
|
||||||
#DIAN 1.7.-2020: CAK40 Machete Construir Validación
|
# DIAN 1.7.-2020: CAK40 Machete Construir Validación
|
||||||
#DIAN 1.7.-2020: FAK40
|
# DIAN 1.7.-2020: FAK40
|
||||||
fexml.set_element('./cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cac:TaxScheme/cbc:ID',
|
fexml.set_element(
|
||||||
|
'./cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cac:TaxScheme/cbc:ID',
|
||||||
invoice.invoice_customer.tax_scheme.code)
|
invoice.invoice_customer.tax_scheme.code)
|
||||||
|
|
||||||
#DIAN 1.7.-2020: FAK41
|
# DIAN 1.7.-2020: FAK41
|
||||||
#DIAN 1.7.-2020: CAK41 Machete Construir Validación
|
# DIAN 1.7.-2020: CAK41 Machete Construir Validación
|
||||||
fexml.set_element('./cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cac:TaxScheme/cbc:Name',
|
fexml.set_element(
|
||||||
|
'./cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cac:TaxScheme/cbc:Name',
|
||||||
invoice.invoice_customer.tax_scheme.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: FAK43
|
# DIAN 1.7.-2020: FAK42
|
||||||
#DIAN 1.7.-2020: CAK43
|
# DIAN 1.7.-2020: CAK42
|
||||||
fexml.set_element('./cac:AccountingCustomerParty/cac:Party/cac:PartyLegalEntity/cbc:RegistrationName',
|
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)
|
invoice.invoice_customer.legal_name)
|
||||||
|
|
||||||
#DIAN 1.7.-2020: CAK44
|
# DIAN 1.7.-2020: CAK44
|
||||||
#DIAN 1.7.-2020: FAK44,FAK45,FAK46,FAK47,FAK48
|
# DIAN 1.7.-2020: FAK44,FAK45,FAK46,FAK47,FAK48
|
||||||
fexml.set_element('./cac:AccountingCustomerParty/cac:Party/cac:PartyLegalEntity/cbc:CompanyID',
|
fexml.set_element(
|
||||||
|
'./cac:AccountingCustomerParty/cac:Party/cac:PartyLegalEntity/cbc:CompanyID',
|
||||||
invoice.invoice_customer.ident,
|
invoice.invoice_customer.ident,
|
||||||
**customer_company_id_attrs)
|
**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: FAK55
|
||||||
#DIAN 1.7.-2020: CAK51, CAK55
|
# DIAN 1.7.-2020: CAK51, CAK55
|
||||||
fexml.set_element('./cac:AccountingCustomerParty/cac:Party/cac:Contact/cbc:ElectronicMail',
|
fexml.set_element(
|
||||||
|
'./cac:AccountingCustomerParty/cac:Party/cac:Contact/cbc:ElectronicMail',
|
||||||
invoice.invoice_customer.email)
|
invoice.invoice_customer.email)
|
||||||
|
|
||||||
|
|
||||||
def set_payment_mean(fexml, invoice):
|
def set_payment_mean(fexml, invoice):
|
||||||
payment_mean = invoice.invoice_payment_mean
|
payment_mean = invoice.invoice_payment_mean
|
||||||
fexml.set_element('./cac:PaymentMeans/cbc:ID', payment_mean.id)
|
fexml.set_element(
|
||||||
fexml.set_element('./cac:PaymentMeans/cbc:PaymentMeansCode', payment_mean.code)
|
'./cac:PaymentMeans/cbc:ID', payment_mean.id)
|
||||||
fexml.set_element('./cac:PaymentMeans/cbc:PaymentDueDate', payment_mean.due_at.strftime('%Y-%m-%d'))
|
fexml.set_element(
|
||||||
fexml.set_element('./cac:PaymentMeans/cbc:PaymentID', payment_mean.payment_id)
|
'./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):
|
def set_element_amount_for(fexml, xml, xpath, amount):
|
||||||
if not isinstance(amount, Amount):
|
if not isinstance(amount, Amount):
|
||||||
@ -504,37 +630,44 @@ class DIANInvoiceXML(fe.FeXML):
|
|||||||
cod_impuesto, amount_of = item
|
cod_impuesto, amount_of = item
|
||||||
next_append = index > 0
|
next_append = index > 0
|
||||||
|
|
||||||
#DIAN 1.7.-2020: FAS01
|
# DIAN 1.7.-2020: FAS01
|
||||||
line = fexml.fragment('./cac:WithholdingTaxTotal', append=next_append)
|
line = fexml.fragment(
|
||||||
#DIAN 1.7.-2020: FAU06
|
'./cac:WithholdingTaxTotal', append=next_append)
|
||||||
|
# DIAN 1.7.-2020: FAU06
|
||||||
tax_amount = amount_of['tax_amount']
|
tax_amount = amount_of['tax_amount']
|
||||||
fexml.set_element_amount_for(line,
|
fexml.set_element_amount_for(
|
||||||
|
line,
|
||||||
'/cac:WithholdingTaxTotal/cbc:TaxAmount',
|
'/cac:WithholdingTaxTotal/cbc:TaxAmount',
|
||||||
tax_amount)
|
tax_amount)
|
||||||
|
|
||||||
#DIAN 1.7.-2020: FAS05
|
# DIAN 1.7.-2020: FAS05
|
||||||
fexml.set_element_amount_for(line,
|
fexml.set_element_amount_for(
|
||||||
|
line,
|
||||||
'/cac:WithholdingTaxTotal/cac:TaxSubtotal/cbc:TaxableAmount',
|
'/cac:WithholdingTaxTotal/cac:TaxSubtotal/cbc:TaxableAmount',
|
||||||
amount_of['taxable_amount'])
|
amount_of['taxable_amount'])
|
||||||
|
|
||||||
#DIAN 1.7.-2020: FAU06
|
# DIAN 1.7.-2020: FAU06
|
||||||
fexml.set_element_amount_for(line,
|
fexml.set_element_amount_for(
|
||||||
|
line,
|
||||||
'/cac:WithholdingTaxTotal/cac:TaxSubtotal/cbc:TaxAmount',
|
'/cac:WithholdingTaxTotal/cac:TaxSubtotal/cbc:TaxAmount',
|
||||||
amount_of['tax_amount'])
|
amount_of['tax_amount'])
|
||||||
|
|
||||||
#DIAN 1.7.-2020: FAS07
|
# DIAN 1.7.-2020: FAS07
|
||||||
if percent_for[cod_impuesto]:
|
if percent_for[cod_impuesto]:
|
||||||
line.set_element('/cac:WithholdingTaxTotal/cac:TaxSubtotal/cbc:Percent',
|
line.set_element(
|
||||||
|
'/cac:WithholdingTaxTotal/cac:TaxSubtotal/cbc:Percent',
|
||||||
percent_for[cod_impuesto])
|
percent_for[cod_impuesto])
|
||||||
|
|
||||||
|
|
||||||
if percent_for[cod_impuesto]:
|
if percent_for[cod_impuesto]:
|
||||||
line.set_element('/cac:WithholdingTaxTotal/cac:TaxSubtotal/cac:TaxCategory/cbc:Percent',
|
line.set_element(
|
||||||
|
'/cac:WithholdingTaxTotal/cac:TaxSubtotal/cac:TaxCategory/cbc:Percent',
|
||||||
percent_for[cod_impuesto])
|
percent_for[cod_impuesto])
|
||||||
|
|
||||||
line.set_element('/cac:WithholdingTaxTotal/cac:TaxSubtotal/cac:TaxCategory/cac:TaxScheme/cbc:ID',
|
line.set_element(
|
||||||
|
'/cac:WithholdingTaxTotal/cac:TaxSubtotal/cac:TaxCategory/cac:TaxScheme/cbc:ID',
|
||||||
cod_impuesto)
|
cod_impuesto)
|
||||||
line.set_element('/cac:WithholdingTaxTotal/cac:TaxSubtotal/cac:TaxCategory/cac:TaxScheme/cbc:Name',
|
line.set_element(
|
||||||
|
'/cac:WithholdingTaxTotal/cac:TaxSubtotal/cac:TaxCategory/cac:TaxScheme/cbc:Name',
|
||||||
'ReteRenta')
|
'ReteRenta')
|
||||||
|
|
||||||
# abstract method
|
# abstract method
|
||||||
@ -546,140 +679,141 @@ class DIANInvoiceXML(fe.FeXML):
|
|||||||
return 'Invoiced'
|
return 'Invoiced'
|
||||||
|
|
||||||
def set_invoice_line_tax(fexml, line, invoice_line):
|
def set_invoice_line_tax(fexml, line, invoice_line):
|
||||||
fexml.set_element_amount_for(line,
|
fexml.set_element_amount_for(
|
||||||
|
line,
|
||||||
'./cac:TaxTotal/cbc:TaxAmount',
|
'./cac:TaxTotal/cbc:TaxAmount',
|
||||||
invoice_line.tax_amount)
|
invoice_line.tax_amount)
|
||||||
|
|
||||||
#DIAN 1.7.-2020: FAX05
|
# DIAN 1.7.-2020: FAX05
|
||||||
fexml.set_element_amount_for(line,
|
fexml.set_element_amount_for(
|
||||||
|
line,
|
||||||
'./cac:TaxTotal/cac:TaxSubtotal/cbc:TaxableAmount',
|
'./cac:TaxTotal/cac:TaxSubtotal/cbc:TaxableAmount',
|
||||||
invoice_line.taxable_amount)
|
invoice_line.taxable_amount)
|
||||||
|
|
||||||
for subtotal in invoice_line.tax.subtotals:
|
for subtotal in invoice_line.tax.subtotals:
|
||||||
line.set_element('./cac:TaxTotal/cac:TaxSubtotal/cbc:TaxAmount', subtotal.tax_amount, currencyID='COP')
|
line.set_element(
|
||||||
|
'./cac:TaxTotal/cac:TaxSubtotal/cbc:TaxAmount',
|
||||||
|
subtotal.tax_amount,
|
||||||
|
currencyID='COP')
|
||||||
|
|
||||||
if subtotal.percent is not None:
|
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:
|
if subtotal.scheme is not None:
|
||||||
#DIAN 1.7.-2020: FAX15
|
# DIAN 1.7.-2020: FAX15
|
||||||
line.set_element('./cac:TaxTotal/cac:TaxSubtotal/cac:TaxCategory/cac:TaxScheme/cbc:ID', subtotal.scheme.code)
|
line.set_element(
|
||||||
line.set_element('./cac:TaxTotal/cac:TaxSubtotal/cac:TaxCategory/cac:TaxScheme/cbc:Name', subtotal.scheme.name)
|
'./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):
|
def set_invoice_line_withholding(fexml, line, invoice_line):
|
||||||
fexml.set_element_amount_for(line,
|
fexml.set_element_amount_for(
|
||||||
|
line,
|
||||||
'./cac:WithholdingTaxTotal/cbc:TaxAmount',
|
'./cac:WithholdingTaxTotal/cbc:TaxAmount',
|
||||||
invoice_line.withholding_amount)
|
invoice_line.withholding_amount)
|
||||||
#DIAN 1.7.-2020: FAX05
|
|
||||||
fexml.set_element_amount_for(line,
|
# DIAN 1.7.-2020: FAX05
|
||||||
|
fexml.set_element_amount_for(
|
||||||
|
line,
|
||||||
'./cac:WithholdingTaxTotal/cac:TaxSubtotal/cbc:TaxableAmount',
|
'./cac:WithholdingTaxTotal/cac:TaxSubtotal/cbc:TaxableAmount',
|
||||||
invoice_line.withholding_taxable_amount)
|
invoice_line.withholding_taxable_amount)
|
||||||
|
|
||||||
for subtotal in invoice_line.withholding.subtotals:
|
for subtotal in invoice_line.withholding.subtotals:
|
||||||
line.set_element('./cac:WithholdingTaxTotal/cac:TaxSubtotal/cbc:TaxAmount', subtotal.tax_amount, currencyID='COP')
|
line.set_element(
|
||||||
|
'./cac:WithholdingTaxTotal/cac:TaxSubtotal/cbc:TaxAmount',
|
||||||
|
subtotal.tax_amount,
|
||||||
|
currencyID='COP')
|
||||||
|
|
||||||
if subtotal.percent is not None:
|
if subtotal.percent is not None:
|
||||||
line.set_element('./cac:WithholdingTaxTotal/cac:TaxSubtotal/cac:TaxCategory/cbc:Percent', '%0.2f' % round(subtotal.percent, 2))
|
line.set_element(
|
||||||
|
'./cac:WithholdingTaxTotal/cac:TaxSubtotal/cac:TaxCategory/cbc:Percent', '%0.2f' % round(subtotal.percent, 2))
|
||||||
|
|
||||||
if subtotal.scheme is not None:
|
if subtotal.scheme is not None:
|
||||||
#DIAN 1.7.-2020: FAX15
|
# DIAN 1.7.-2020: FAX15
|
||||||
line.set_element('./cac:WithholdingTaxTotal/cac:TaxSubtotal/cac:TaxCategory/cac:TaxScheme/cbc:ID', subtotal.scheme.code)
|
line.set_element(
|
||||||
line.set_element('./cac:WithholdingTaxTotal/cac:TaxSubtotal/cac:TaxCategory/cac:TaxScheme/cbc:Name', subtotal.scheme.name)
|
'./cac:WithholdingTaxTotal/cac:TaxSubtotal/cac:TaxCategory/cac:TaxScheme/cbc:ID', subtotal.scheme.code)
|
||||||
|
line.set_element(
|
||||||
|
'./cac:WithholdingTaxTotal/cac:TaxSubtotal/cac:TaxCategory/cac:TaxScheme/cbc:Name', subtotal.scheme.name)
|
||||||
|
|
||||||
def set_invoice_lines(fexml, invoice):
|
def set_invoice_lines(fexml, invoice):
|
||||||
next_append = False
|
next_append = False
|
||||||
for index, invoice_line in enumerate(invoice.invoice_lines):
|
for index, invoice_line in enumerate(invoice.invoice_lines):
|
||||||
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
|
next_append = True
|
||||||
|
|
||||||
line.set_element('./cbc:ID', index + 1)
|
line.set_element('./cbc:ID', index + 1)
|
||||||
line.set_element('./cbc:%sQuantity' % (fexml.tag_document_concilied()), invoice_line.quantity, unitCode = 'NAR')
|
line.set_element(
|
||||||
fexml.set_element_amount_for(line,
|
'./cbc:%sQuantity' % (
|
||||||
|
fexml.tag_document_concilied()),
|
||||||
|
invoice_line.quantity,
|
||||||
|
unitCode='NAR')
|
||||||
|
fexml.set_element_amount_for(
|
||||||
|
line,
|
||||||
'./cbc:LineExtensionAmount',
|
'./cbc:LineExtensionAmount',
|
||||||
invoice_line.total_amount)
|
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)
|
fexml.set_invoice_line_tax(line, invoice_line)
|
||||||
|
|
||||||
if not isinstance(invoice_line.withholding, WithholdingTaxTotalOmit):
|
if not isinstance(
|
||||||
|
invoice_line.withholding, WithholdingTaxTotalOmit):
|
||||||
fexml.set_invoice_line_withholding(line, invoice_line)
|
fexml.set_invoice_line_withholding(line, invoice_line)
|
||||||
|
|
||||||
line.set_element('./cac:Item/cbc:Description', invoice_line.item.description)
|
line.set_element(
|
||||||
|
'./cac:Item/cbc:Description', invoice_line.item.description)
|
||||||
|
|
||||||
line.set_element('./cac:Item/cac:StandardItemIdentification/cbc:ID',
|
line.set_element(
|
||||||
|
'./cac:Item/cac:StandardItemIdentification/cbc:ID',
|
||||||
invoice_line.item.id,
|
invoice_line.item.id,
|
||||||
schemeID=invoice_line.item.scheme_id,
|
schemeID=invoice_line.item.scheme_id,
|
||||||
schemeName=invoice_line.item.scheme_name,
|
schemeName=invoice_line.item.scheme_name,
|
||||||
schemeAgencyID=invoice_line.item.scheme_agency_id)
|
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)
|
line.set_element(
|
||||||
#DIAN 1.7.-2020: FBB04
|
'./cac:Price/cbc:PriceAmount',
|
||||||
line.set_element('./cac:Price/cbc:BaseQuantity',
|
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,
|
invoice_line.price.quantity,
|
||||||
unitCode=invoice_line.quantity.code)
|
unitCode=invoice_line.quantity.code)
|
||||||
|
|
||||||
for idx, charge in enumerate(invoice_line.allowance_charge):
|
for idx, charge in enumerate(invoice_line.allowance_charge):
|
||||||
next_append_charge = idx > 0
|
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):
|
def set_allowance_charge(fexml, invoice):
|
||||||
for idx, charge in enumerate(invoice.invoice_allowance_charge):
|
for idx, charge in enumerate(invoice.invoice_allowance_charge):
|
||||||
next_append = idx > 0
|
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):
|
def append_allowance_charge(fexml, parent, idx, charge, append=False):
|
||||||
line = parent.fragment('./cac:AllowanceCharge', append=append)
|
line = parent.fragment('./cac:AllowanceCharge', append=append)
|
||||||
#DIAN 1.7.-2020: FAQ02
|
# DIAN 1.7.-2020: FAQ02
|
||||||
line.set_element('./cbc:ID', idx)
|
line.set_element('./cbc:ID', idx)
|
||||||
#DIAN 1.7.-2020: FAQ03
|
# DIAN 1.7.-2020: FAQ03
|
||||||
line.set_element('./cbc:ChargeIndicator', str(charge.charge_indicator).lower())
|
line.set_element('./cbc:ChargeIndicator', str(
|
||||||
|
charge.charge_indicator).lower())
|
||||||
if charge.reason:
|
if charge.reason:
|
||||||
line.set_element('./cbc:AllowanceChargeReasonCode', charge.reason.code)
|
line.set_element(
|
||||||
line.set_element('./cbc:allowanceChargeReason', charge.reason.reason)
|
'./cbc:AllowanceChargeReasonCode', charge.reason.code)
|
||||||
line.set_element('./cbc:MultiplierFactorNumeric', str(round(charge.multiplier_factor_numeric, 2)))
|
line.set_element(
|
||||||
fexml.set_element_amount_for(line, './cbc:Amount', charge.amount)
|
'./cbc:allowanceChargeReason', charge.reason.reason)
|
||||||
fexml.set_element_amount_for(line, './cbc:BaseAmount', charge.base_amount)
|
line.set_element(
|
||||||
|
'./cbc:MultiplierFactorNumeric', str(
|
||||||
def attach_invoice(fexml, invoice):
|
round(charge.multiplier_factor_numeric, 2)))
|
||||||
"""adiciona etiquetas a FEXML y retorna FEXML
|
fexml.set_element_amount_for(
|
||||||
en caso de fallar validacion retorna None"""
|
line, './cbc:Amount', charge.amount)
|
||||||
|
fexml.set_element_amount_for(
|
||||||
fexml.placeholder_for('./ext:UBLExtensions')
|
line, './cbc:BaseAmount', charge.base_amount)
|
||||||
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 customize(fexml, invoice):
|
def customize(fexml, invoice):
|
||||||
"""adiciona etiquetas a FEXML y retorna FEXML
|
"""adiciona etiquetas a FEXML y retorna FEXML
|
||||||
|
@ -1,219 +1,263 @@
|
|||||||
from .. import fe
|
from .. import fe
|
||||||
from ..form import *
|
from ..form import (
|
||||||
from datetime import datetime, date
|
Amount, DebitNoteDocumentReference, CreditNoteDocumentReference,
|
||||||
from .attached_document import *
|
InvoiceDocumentReference, TaxTotalOmit, WithholdingTaxTotalOmit
|
||||||
|
)
|
||||||
|
|
||||||
|
from collections import defaultdict
|
||||||
|
from datetime import datetime
|
||||||
|
# from .attached_document import *
|
||||||
|
|
||||||
__all__ = ['DIANSupportDocumentXML']
|
__all__ = ['DIANSupportDocumentXML']
|
||||||
|
|
||||||
|
|
||||||
class DIANSupportDocumentXML(fe.FeXML):
|
class DIANSupportDocumentXML(fe.FeXML):
|
||||||
"""
|
"""
|
||||||
DianSupportDocumentXML mapea objeto form.Invoice a XML segun
|
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.
|
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'):
|
def __init__(self, invoice, tag_document='Invoice'):
|
||||||
super().__init__(tag_document, 'http://www.dian.gov.co/contratos/facturaelectronica/v1')
|
super().__init__(tag_document, 'http://www.dian.gov.co/contratos/facturaelectronica/v1')
|
||||||
|
|
||||||
#DIAN 1.1.-2021: DSAB03
|
# DIAN 1.1.-2021: DSAB03
|
||||||
#DIAN 1.1.-2021: NSAB03
|
# DIAN 1.1.-2021: NSAB03
|
||||||
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:InvoiceControl')
|
||||||
|
|
||||||
#DIAN 1.1.-2021: DSAB13
|
# DIAN 1.1.-2021: DSAB13
|
||||||
#DIAN 1.1.-2021: NSAB13
|
# DIAN 1.1.-2021: NSAB13
|
||||||
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:InvoiceSource')
|
||||||
|
|
||||||
#DIAN 1.1.-2021: DSAB18
|
# DIAN 1.1.-2021: DSAB18
|
||||||
#DIAN 1.1.-2021: NSAB18
|
# DIAN 1.1.-2021: NSAB18
|
||||||
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:SoftwareProvider')
|
||||||
|
|
||||||
#DIAN 1.1.-2021: DSAB27
|
# DIAN 1.1.-2021: DSAB27
|
||||||
#DIAN 1.1.-2021: NSAB27
|
# DIAN 1.1.-2021: NSAB27
|
||||||
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:SoftwareSecurityCode')
|
||||||
|
|
||||||
#DIAN 1.1.-2021: DSAB30 DSAB31
|
# DIAN 1.1.-2021: DSAB30 DSAB31
|
||||||
#DIAN 1.1.-2021: NSAB30 NSAB31
|
# DIAN 1.1.-2021: NSAB30 NSAB31
|
||||||
self.placeholder_for('./ext:UBLExtensions/ext:UBLExtension/ext:ExtensionContent/sts:DianExtensions/sts:AuthorizationProvider/sts:AuthorizationProviderID')
|
self.placeholder_for(
|
||||||
|
'./ext:UBLExtensions/ext:UBLExtension/ext:ExtensionContent/sts:DianExtensions/sts:AuthorizationProvider/sts:AuthorizationProviderID')
|
||||||
|
|
||||||
#ZE02 se requiere existencia para firmar
|
# ZE02 se requiere existencia para firmar
|
||||||
#DIAN 1.1.-2021: DSAA02 DSAB01
|
# DIAN 1.1.-2021: DSAA02 DSAB01
|
||||||
#DIAN 1.1.-2021: NSAA02 NSAB01
|
# DIAN 1.1.-2021: NSAA02 NSAB01
|
||||||
ublextension = self.fragment('./ext:UBLExtensions/ext:UBLExtension', append=True)
|
ublextension = self.fragment(
|
||||||
#DIAN 1.1.-2021: DSAB02
|
'./ext:UBLExtensions/ext:UBLExtension', append=True)
|
||||||
#DIAN 1.1.-2021: NSAB02
|
# DIAN 1.1.-2021: DSAB02
|
||||||
extcontent = ublextension.find_or_create_element('/ext:UBLExtension/ext:ExtensionContent')
|
# DIAN 1.1.-2021: NSAB02
|
||||||
|
extcontent = ublextension.find_or_create_element(
|
||||||
|
'/ext:UBLExtension/ext:ExtensionContent')
|
||||||
self.attach_invoice(invoice)
|
self.attach_invoice(invoice)
|
||||||
|
|
||||||
def set_supplier(fexml, invoice):
|
def set_supplier(fexml, invoice):
|
||||||
#DIAN 1.1.-2021: DSAJ01
|
# DIAN 1.1.-2021: DSAJ01
|
||||||
#DIAN 1.1.-2021: NSAB01
|
# DIAN 1.1.-2021: NSAB01
|
||||||
fexml.placeholder_for('./cac:AccountingSupplierParty')
|
fexml.placeholder_for('./cac:AccountingSupplierParty')
|
||||||
|
|
||||||
#DIAN 1.1.-2021: DSAJ02
|
# DIAN 1.1.-2021: DSAJ02
|
||||||
#DIAN 1.1.-2021: NSAJ02
|
# DIAN 1.1.-2021: NSAJ02
|
||||||
fexml.set_element('./cac:AccountingSupplierParty/cbc:AdditionalAccountID',
|
fexml.set_element(
|
||||||
|
'./cac:AccountingSupplierParty/cbc:AdditionalAccountID',
|
||||||
invoice.invoice_supplier.organization_code)
|
invoice.invoice_supplier.organization_code)
|
||||||
|
|
||||||
#DIAN 1.1.-2021: DSAJ07 DSAJ08
|
# DIAN 1.1.-2021: DSAJ07 DSAJ08
|
||||||
#DIAN 1.1.-2021: NSAJ07 NSAJ08
|
# DIAN 1.1.-2021: NSAJ07 NSAJ08
|
||||||
fexml.placeholder_for('./cac:AccountingSupplierParty/cac:Party/cac:PhysicalLocation/cac:Address')
|
fexml.placeholder_for(
|
||||||
|
'./cac:AccountingSupplierParty/cac:Party/cac:PhysicalLocation/cac:Address')
|
||||||
|
|
||||||
#DIAN 1.1.-2021: DSAJ09
|
# DIAN 1.1.-2021: DSAJ09
|
||||||
#DIAN 1.1.-2021: NSAJ09
|
# DIAN 1.1.-2021: NSAJ09
|
||||||
fexml.set_element('./cac:AccountingSupplierParty/cac:Party/cac:PhysicalLocation/cac:Address/cbc:ID',
|
fexml.set_element(
|
||||||
|
'./cac:AccountingSupplierParty/cac:Party/cac:PhysicalLocation/cac:Address/cbc:ID',
|
||||||
invoice.invoice_supplier.address.city.code)
|
invoice.invoice_supplier.address.city.code)
|
||||||
|
|
||||||
#DIAN 1.1.-2021: DSAJ10
|
# DIAN 1.1.-2021: DSAJ10
|
||||||
#DIAN 1.1.-2021: NSAJ10
|
# DIAN 1.1.-2021: NSAJ10
|
||||||
fexml.set_element('./cac:AccountingSupplierParty/cac:Party/cac:PhysicalLocation/cac:Address/cbc:CityName',
|
fexml.set_element(
|
||||||
|
'./cac:AccountingSupplierParty/cac:Party/cac:PhysicalLocation/cac:Address/cbc:CityName',
|
||||||
invoice.invoice_supplier.address.city.name)
|
invoice.invoice_supplier.address.city.name)
|
||||||
|
|
||||||
#DIAN 1.1.-2021: DSAJ73
|
# DIAN 1.1.-2021: DSAJ73
|
||||||
#DIAN 1.1.-2021: NSAJ73
|
# DIAN 1.1.-2021: NSAJ73
|
||||||
fexml.set_element('./cac:AccountingSupplierParty/cac:Party/cac:PhysicalLocation/cac:Address/cbc:PostalZone',
|
fexml.set_element(
|
||||||
|
'./cac:AccountingSupplierParty/cac:Party/cac:PhysicalLocation/cac:Address/cbc:PostalZone',
|
||||||
invoice.invoice_supplier.address.postalzone.code)
|
invoice.invoice_supplier.address.postalzone.code)
|
||||||
|
|
||||||
#DIAN 1.1.-2021: DSAJ11
|
# DIAN 1.1.-2021: DSAJ11
|
||||||
#DIAN 1.1.-2021: NSAJ11
|
# DIAN 1.1.-2021: NSAJ11
|
||||||
fexml.set_element('./cac:AccountingSupplierParty/cac:Party/cac:PhysicalLocation/cac:Address/cbc:CountrySubentity',
|
fexml.set_element(
|
||||||
|
'./cac:AccountingSupplierParty/cac:Party/cac:PhysicalLocation/cac:Address/cbc:CountrySubentity',
|
||||||
invoice.invoice_supplier.address.countrysubentity.name)
|
invoice.invoice_supplier.address.countrysubentity.name)
|
||||||
|
|
||||||
#DIAN 1.1.-2021: DSAJ12
|
# DIAN 1.1.-2021: DSAJ12
|
||||||
#DIAN 1.1.-2021: NSAJ12
|
# DIAN 1.1.-2021: NSAJ12
|
||||||
fexml.set_element('./cac:AccountingSupplierParty/cac:Party/cac:PhysicalLocation/cac:Address/cbc:CountrySubentityCode',
|
fexml.set_element(
|
||||||
|
'./cac:AccountingSupplierParty/cac:Party/cac:PhysicalLocation/cac:Address/cbc:CountrySubentityCode',
|
||||||
invoice.invoice_supplier.address.countrysubentity.code)
|
invoice.invoice_supplier.address.countrysubentity.code)
|
||||||
#DIAN 1.1.-2021: NSAJ13 NSAJ14
|
# DIAN 1.1.-2021: NSAJ13 NSAJ14
|
||||||
fexml.set_element('./cac:AccountingSupplierParty/cac:Party/cac:PhysicalLocation/cac:Address/cac:AddressLine/cbc:Line',
|
fexml.set_element(
|
||||||
|
'./cac:AccountingSupplierParty/cac:Party/cac:PhysicalLocation/cac:Address/cac:AddressLine/cbc:Line',
|
||||||
invoice.invoice_supplier.address.street)
|
invoice.invoice_supplier.address.street)
|
||||||
|
|
||||||
#DIAN 1.1.-2021: DSAJ15 DSAJ16
|
# DIAN 1.1.-2021: DSAJ15 DSAJ16
|
||||||
#DIAN 1.1.-2021: NSAJ15 NSAJ16
|
# DIAN 1.1.-2021: NSAJ15 NSAJ16
|
||||||
fexml.set_element('./cac:AccountingSupplierParty/cac:Party/cac:PhysicalLocation/cac:Address/cac:Country/cbc:IdentificationCode',
|
fexml.set_element(
|
||||||
|
'./cac:AccountingSupplierParty/cac:Party/cac:PhysicalLocation/cac:Address/cac:Country/cbc:IdentificationCode',
|
||||||
invoice.invoice_supplier.address.country.code)
|
invoice.invoice_supplier.address.country.code)
|
||||||
|
|
||||||
#DIAN 1.1.-2021: DSAJ17
|
# DIAN 1.1.-2021: DSAJ17
|
||||||
#DIAN 1.1.-2021: NSAJ17
|
# DIAN 1.1.-2021: NSAJ17
|
||||||
fexml.set_element('./cac:AccountingSupplierParty/cac:Party/cac:PhysicalLocation/cac:Address/cac:Country/cbc:Name',
|
fexml.set_element('./cac:AccountingSupplierParty/cac:Party/cac:PhysicalLocation/cac:Address/cac:Country/cbc:Name',
|
||||||
invoice.invoice_supplier.address.country.name,
|
invoice.invoice_supplier.address.country.name,
|
||||||
#DIAN 1.1.-2021: DSAJ18
|
# DIAN 1.1.-2021: DSAJ18
|
||||||
#DIAN 1.1.-2021: NSAJ18
|
# # DIAN 1.1.-2021: NSAJ18
|
||||||
languageID = 'es')
|
languageID='es')
|
||||||
|
|
||||||
|
|
||||||
supplier_company_id_attrs = fe.SCHEME_AGENCY_ATTRS.copy()
|
supplier_company_id_attrs = fe.SCHEME_AGENCY_ATTRS.copy()
|
||||||
supplier_company_id_attrs.update({'schemeID': invoice.invoice_supplier.ident.dv,
|
supplier_company_id_attrs.update(
|
||||||
|
{
|
||||||
|
'schemeID': invoice.invoice_supplier.ident.dv,
|
||||||
'schemeName': invoice.invoice_supplier.ident.type_fiscal})
|
'schemeName': invoice.invoice_supplier.ident.type_fiscal})
|
||||||
|
|
||||||
#DIAN 1.1.-2021: DSAJ19
|
# DIAN 1.1.-2021: DSAJ19
|
||||||
#DIAN 1.1.-2021: NSAJ19
|
# DIAN 1.1.-2021: NSAJ19
|
||||||
fexml.placeholder_for('./cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme')
|
fexml.placeholder_for(
|
||||||
|
'./cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme')
|
||||||
|
|
||||||
#DIAN 1.1.-2021: DSAJ20
|
# DIAN 1.1.-2021: DSAJ20
|
||||||
#DIAN 1.1.-2021: NSAJ20
|
# DIAN 1.1.-2021: NSAJ20
|
||||||
fexml.set_element('./cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme/cbc:RegistrationName',
|
fexml.set_element(
|
||||||
|
'./cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme/cbc:RegistrationName',
|
||||||
invoice.invoice_supplier.legal_name)
|
invoice.invoice_supplier.legal_name)
|
||||||
|
|
||||||
#DIAN 1.1.-2021: DSAJ21
|
# DIAN 1.1.-2021: DSAJ21
|
||||||
#DIAN 1.1.-2021: NSAJ21
|
# DIAN 1.1.-2021: NSAJ21
|
||||||
fexml.set_element('./cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme/cbc:CompanyID',
|
fexml.set_element(
|
||||||
|
'./cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme/cbc:CompanyID',
|
||||||
invoice.invoice_supplier.ident,
|
invoice.invoice_supplier.ident,
|
||||||
#DIAN 1.1.-2021: DSAJ22 DSAJ23 DSAJ24 DSAJ25
|
# DIAN 1.1.-2021: DSAJ22 DSAJ23 DSAJ24 DSAJ25
|
||||||
#DIAN 1.1.-2021: NSAJ22 NSAJ23 NSAJ24 NSAJ25
|
# DIAN 1.1.-2021: NSAJ22 NSAJ23 NSAJ24 NSAJ25
|
||||||
**supplier_company_id_attrs)
|
**supplier_company_id_attrs)
|
||||||
|
|
||||||
#DIAN 1.1.-2021: DSAJ26
|
# DIAN 1.1.-2021: DSAJ26
|
||||||
#DIAN 1.1.-2021: NSAJ26
|
# DIAN 1.1.-2021: NSAJ26
|
||||||
fexml.set_element('./cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme/cbc:TaxLevelCode',
|
fexml.set_element(
|
||||||
|
'./cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme/cbc:TaxLevelCode',
|
||||||
invoice.invoice_supplier.responsability_code,
|
invoice.invoice_supplier.responsability_code,
|
||||||
listName=invoice.invoice_supplier.responsability_regime_code)
|
listName=invoice.invoice_supplier.responsability_regime_code)
|
||||||
|
|
||||||
#DIAN 1.1.-2021: DSAJ39
|
# DIAN 1.1.-2021: DSAJ39
|
||||||
#DIAN 1.1.-2021: NSAJ39
|
# DIAN 1.1.-2021: NSAJ39
|
||||||
fexml.placeholder_for('./cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme/cac:TaxScheme')
|
fexml.placeholder_for(
|
||||||
|
'./cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme/cac:TaxScheme')
|
||||||
|
|
||||||
#DIAN 1.1.-2021: DSAJ40
|
# DIAN 1.1.-2021: DSAJ40
|
||||||
#DIAN 1.1.-2021: NSAJ40
|
# DIAN 1.1.-2021: NSAJ40
|
||||||
fexml.set_element('./cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme/cac:TaxScheme/cbc:ID',
|
fexml.set_element(
|
||||||
|
'./cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme/cac:TaxScheme/cbc:ID',
|
||||||
invoice.invoice_customer.tax_scheme.code)
|
invoice.invoice_customer.tax_scheme.code)
|
||||||
|
|
||||||
#DIAN 1.1.-2021: DSAJ41
|
# DIAN 1.1.-2021: DSAJ41
|
||||||
#DIAN 1.1.-2021: NSAJ41
|
# DIAN 1.1.-2021: NSAJ41
|
||||||
fexml.set_element('./cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme/cac:TaxScheme/cbc:Name',
|
fexml.set_element(
|
||||||
|
'./cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme/cac:TaxScheme/cbc:Name',
|
||||||
invoice.invoice_customer.tax_scheme.name)
|
invoice.invoice_customer.tax_scheme.name)
|
||||||
|
|
||||||
|
|
||||||
def set_customer(fexml, invoice):
|
def set_customer(fexml, invoice):
|
||||||
#DIAN 1.1.-2021: DSAK01
|
# DIAN 1.1.-2021: DSAK01
|
||||||
#DIAN 1.1.-2021: NSAK01
|
# DIAN 1.1.-2021: NSAK01
|
||||||
fexml.placeholder_for('./cac:AccountingCustomerParty')
|
fexml.placeholder_for('./cac:AccountingCustomerParty')
|
||||||
|
|
||||||
#DIAN 1.1.-2021: DSAK02
|
# DIAN 1.1.-2021: DSAK02
|
||||||
#DIAN 1.1.-2021: NSAK02
|
# DIAN 1.1.-2021: NSAK02
|
||||||
fexml.set_element('./cac:AccountingCustomerParty/cbc:AdditionalAccountID',
|
fexml.set_element(
|
||||||
|
'./cac:AccountingCustomerParty/cbc:AdditionalAccountID',
|
||||||
invoice.invoice_customer.organization_code)
|
invoice.invoice_customer.organization_code)
|
||||||
|
|
||||||
#DIAN 1.1.-2021: DSAK03
|
# DIAN 1.1.-2021: DSAK03
|
||||||
#DIAN 1.1.-2021: NSAK03
|
# DIAN 1.1.-2021: NSAK03
|
||||||
fexml.placeholder_for('./cac:AccountingCustomerParty/cac:Party')
|
fexml.placeholder_for('./cac:AccountingCustomerParty/cac:Party')
|
||||||
|
|
||||||
#DIAN 1.1.-2021: DSAK19
|
# DIAN 1.1.-2021: DSAK19
|
||||||
#DIAN 1.1.-2021: NSAK19
|
# DIAN 1.1.-2021: NSAK19
|
||||||
fexml.placeholder_for('./cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme')
|
fexml.placeholder_for(
|
||||||
|
'./cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme')
|
||||||
|
|
||||||
#DIAN 1.1.-2021: DSAK20
|
# DIAN 1.1.-2021: DSAK20
|
||||||
#DIAN 1.1.-2021: NSAK20
|
# DIAN 1.1.-2021: NSAK20
|
||||||
fexml.set_element('./cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cbc:RegistrationName',
|
fexml.set_element(
|
||||||
|
'./cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cbc:RegistrationName',
|
||||||
invoice.invoice_customer.legal_name)
|
invoice.invoice_customer.legal_name)
|
||||||
|
|
||||||
customer_company_id_attrs = fe.SCHEME_AGENCY_ATTRS.copy()
|
customer_company_id_attrs = fe.SCHEME_AGENCY_ATTRS.copy()
|
||||||
customer_company_id_attrs.update({'schemeID': invoice.invoice_customer.ident.dv,
|
customer_company_id_attrs.update(
|
||||||
|
{
|
||||||
|
'schemeID': invoice.invoice_customer.ident.dv,
|
||||||
'schemeName': invoice.invoice_customer.ident.type_fiscal})
|
'schemeName': invoice.invoice_customer.ident.type_fiscal})
|
||||||
|
|
||||||
#DIAN 1.1.-2021: DSAK21
|
# DIAN 1.1.-2021: DSAK21
|
||||||
#DIAN 1.1.-2021: NSAK21
|
# DIAN 1.1.-2021: NSAK21
|
||||||
fexml.set_element('./cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cbc:CompanyID',
|
fexml.set_element(
|
||||||
|
'./cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cbc:CompanyID',
|
||||||
invoice.invoice_customer.ident,
|
invoice.invoice_customer.ident,
|
||||||
#DIAN 1.1.-2021: DSAK22 DSAK23 DSAK24 DSAK25
|
# DIAN 1.1.-2021: DSAK22 DSAK23 DSAK24 DSAK25
|
||||||
#DIAN 1.1.-2021: NSAK22 NSAK23 NSAK24 NSAK25
|
# DIAN 1.1.-2021: NSAK22 NSAK23 NSAK24 NSAK25
|
||||||
**customer_company_id_attrs)
|
**customer_company_id_attrs)
|
||||||
|
|
||||||
#DIAN 1.1.-2021: DSAK26
|
# DIAN 1.1.-2021: DSAK26
|
||||||
#DIAN 1.1.-2021: NSAK26
|
# DIAN 1.1.-2021: NSAK26
|
||||||
fexml.set_element('./cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cbc:TaxLevelCode',
|
fexml.set_element(
|
||||||
|
'./cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cbc:TaxLevelCode',
|
||||||
invoice.invoice_customer.responsability_code)
|
invoice.invoice_customer.responsability_code)
|
||||||
|
|
||||||
#DIAN 1.1.-2021: DSAK39
|
# DIAN 1.1.-2021: DSAK39
|
||||||
#DIAN 1.1.-2021: NSAK39
|
# DIAN 1.1.-2021: NSAK39
|
||||||
fexml.placeholder_for('./cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cac:TaxScheme')
|
fexml.placeholder_for(
|
||||||
|
'./cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cac:TaxScheme')
|
||||||
|
|
||||||
#DIAN 1.1.-2021: DSAK40
|
# DIAN 1.1.-2021: DSAK40
|
||||||
#DIAN 1.1.-2021: NSAK40
|
# DIAN 1.1.-2021: NSAK40
|
||||||
fexml.set_element('./cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cac:TaxScheme/cbc:ID',
|
fexml.set_element(
|
||||||
|
'./cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cac:TaxScheme/cbc:ID',
|
||||||
invoice.invoice_customer.tax_scheme.code)
|
invoice.invoice_customer.tax_scheme.code)
|
||||||
|
|
||||||
#DIAN 1.1.-2021: DSAK41
|
# DIAN 1.1.-2021: DSAK41
|
||||||
#DIAN 1.1.-2021: NSAK41
|
# DIAN 1.1.-2021: NSAK41
|
||||||
fexml.set_element('./cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cac:TaxScheme/cbc:Name',
|
fexml.set_element(
|
||||||
|
'./cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cac:TaxScheme/cbc:Name',
|
||||||
invoice.invoice_customer.tax_scheme.name)
|
invoice.invoice_customer.tax_scheme.name)
|
||||||
|
|
||||||
|
|
||||||
def set_payment_mean(fexml, invoice):
|
def set_payment_mean(fexml, invoice):
|
||||||
payment_mean = invoice.invoice_payment_mean
|
payment_mean = invoice.invoice_payment_mean
|
||||||
|
|
||||||
#DIAN 1.1.-2021: DSAN01 DSAN02
|
# DIAN 1.1.-2021: DSAN01 DSAN02
|
||||||
#DIAN 1.1.-2021: NSAN02 NSAN02
|
# DIAN 1.1.-2021: NSAN02 NSAN02
|
||||||
fexml.set_element('./cac:PaymentMeans/cbc:ID', payment_mean.id)
|
fexml.set_element('./cac:PaymentMeans/cbc:ID', payment_mean.id)
|
||||||
|
|
||||||
#DIAN 1.1.-2021: DSAN03
|
# DIAN 1.1.-2021: DSAN03
|
||||||
#DIAN 1.1.-2021: NSAN03
|
# DIAN 1.1.-2021: NSAN03
|
||||||
fexml.set_element('./cac:PaymentMeans/cbc:PaymentMeansCode', payment_mean.code)
|
fexml.set_element(
|
||||||
|
'./cac:PaymentMeans/cbc:PaymentMeansCode',
|
||||||
|
payment_mean.code)
|
||||||
|
|
||||||
#DIAN 1.1.-2021: DSAN04
|
# DIAN 1.1.-2021: DSAN04
|
||||||
#DIAN 1.1.-2021: NSAN04
|
# DIAN 1.1.-2021: NSAN04
|
||||||
fexml.set_element('./cac:PaymentMeans/cbc:PaymentDueDate', payment_mean.due_at.strftime('%Y-%m-%d'))
|
fexml.set_element(
|
||||||
|
'./cac:PaymentMeans/cbc:PaymentDueDate',
|
||||||
|
payment_mean.due_at.strftime('%Y-%m-%d'))
|
||||||
|
|
||||||
#DIAN 1.1.-2021: DSAN05
|
# DIAN 1.1.-2021: DSAN05
|
||||||
#DIAN 1.1.-2021: NSAN05
|
# DIAN 1.1.-2021: NSAN05
|
||||||
fexml.set_element('./cac:PaymentMeans/cbc:PaymentID', payment_mean.payment_id)
|
fexml.set_element(
|
||||||
|
'./cac:PaymentMeans/cbc:PaymentID',
|
||||||
|
payment_mean.payment_id)
|
||||||
|
|
||||||
def set_element_amount_for(fexml, xml, xpath, amount):
|
def set_element_amount_for(fexml, xml, xpath, amount):
|
||||||
if not isinstance(amount, Amount):
|
if not isinstance(amount, Amount):
|
||||||
@ -228,40 +272,47 @@ class DIANSupportDocumentXML(fe.FeXML):
|
|||||||
fexml.set_element(xpath, amount, currencyID=amount.currency.code)
|
fexml.set_element(xpath, amount, currencyID=amount.currency.code)
|
||||||
|
|
||||||
def set_legal_monetary(fexml, invoice):
|
def set_legal_monetary(fexml, invoice):
|
||||||
#DIAN 1.1.-2021: DSAU01 DSAU02 DSAU03
|
# DIAN 1.1.-2021: DSAU01 DSAU02 DSAU03
|
||||||
#DIAN 1.1.-2021: NSAU01 NSAU02 NSAU03
|
# DIAN 1.1.-2021: NSAU01 NSAU02 NSAU03
|
||||||
fexml.set_element_amount('./cac:LegalMonetaryTotal/cbc:LineExtensionAmount',
|
fexml.set_element_amount(
|
||||||
|
'./cac:LegalMonetaryTotal/cbc:LineExtensionAmount',
|
||||||
invoice.invoice_legal_monetary_total.line_extension_amount)
|
invoice.invoice_legal_monetary_total.line_extension_amount)
|
||||||
|
|
||||||
#DIAN 1.1.-2021: DSAU04 DSAU05
|
# DIAN 1.1.-2021: DSAU04 DSAU05
|
||||||
#DIAN 1.1.-2021: NSAU04 NSAU05
|
# DIAN 1.1.-2021: NSAU04 NSAU05
|
||||||
fexml.set_element_amount('./cac:LegalMonetaryTotal/cbc:TaxExclusiveAmount',
|
fexml.set_element_amount(
|
||||||
|
'./cac:LegalMonetaryTotal/cbc:TaxExclusiveAmount',
|
||||||
invoice.invoice_legal_monetary_total.tax_exclusive_amount)
|
invoice.invoice_legal_monetary_total.tax_exclusive_amount)
|
||||||
|
|
||||||
#DIAN 1.1.-2021: DSAU06 DSAU07
|
# DIAN 1.1.-2021: DSAU06 DSAU07
|
||||||
#DIAN 1.1.-2021: NSAU06 DSAU07
|
# DIAN 1.1.-2021: NSAU06 DSAU07
|
||||||
fexml.set_element_amount('./cac:LegalMonetaryTotal/cbc:TaxInclusiveAmount',
|
fexml.set_element_amount(
|
||||||
|
'./cac:LegalMonetaryTotal/cbc:TaxInclusiveAmount',
|
||||||
invoice.invoice_legal_monetary_total.tax_inclusive_amount)
|
invoice.invoice_legal_monetary_total.tax_inclusive_amount)
|
||||||
|
|
||||||
#DIAN 1.1.-2021: DSAU10 DSAU11
|
# DIAN 1.1.-2021: DSAU10 DSAU11
|
||||||
#DIAN 1.1.-2021: NSAU10 DSAU11
|
# DIAN 1.1.-2021: NSAU10 DSAU11
|
||||||
fexml.set_element_amount('./cac:LegalMonetaryTotal/cbc:ChargeTotalAmount',
|
fexml.set_element_amount(
|
||||||
|
'./cac:LegalMonetaryTotal/cbc:ChargeTotalAmount',
|
||||||
invoice.invoice_legal_monetary_total.charge_total_amount)
|
invoice.invoice_legal_monetary_total.charge_total_amount)
|
||||||
|
|
||||||
#DIAN 1.1.-2021: DSAU14 DSAU15
|
# DIAN 1.1.-2021: DSAU14 DSAU15
|
||||||
#DIAN 1.1.-2021: NSAU14 DSAU15
|
# DIAN 1.1.-2021: NSAU14 DSAU15
|
||||||
fexml.set_element_amount('./cac:LegalMonetaryTotal/cbc:PayableAmount',
|
fexml.set_element_amount(
|
||||||
|
'./cac:LegalMonetaryTotal/cbc:PayableAmount',
|
||||||
invoice.invoice_legal_monetary_total.payable_amount)
|
invoice.invoice_legal_monetary_total.payable_amount)
|
||||||
|
|
||||||
|
|
||||||
def _set_invoice_document_reference(fexml, reference):
|
def _set_invoice_document_reference(fexml, reference):
|
||||||
fexml._do_set_billing_reference(reference, 'cac:InvoiceDocumentReference')
|
fexml._do_set_billing_reference(
|
||||||
|
reference, 'cac:InvoiceDocumentReference')
|
||||||
|
|
||||||
def _set_credit_note_document_reference(fexml, reference):
|
def _set_credit_note_document_reference(fexml, reference):
|
||||||
fexml._do_set_billing_reference(reference, 'cac:CreditNoteDocumentReference')
|
fexml._do_set_billing_reference(
|
||||||
|
reference, 'cac:CreditNoteDocumentReference')
|
||||||
|
|
||||||
def _set_debit_note_document_reference(fexml, reference):
|
def _set_debit_note_document_reference(fexml, reference):
|
||||||
fexml._do_set_billing_reference(reference, 'cac:DebitNoteDocumentReference')
|
fexml._do_set_billing_reference(
|
||||||
|
reference, 'cac:DebitNoteDocumentReference')
|
||||||
|
|
||||||
def _do_set_billing_reference(fexml, reference, tag_document):
|
def _do_set_billing_reference(fexml, reference, tag_document):
|
||||||
|
|
||||||
@ -270,12 +321,14 @@ class DIANSupportDocumentXML(fe.FeXML):
|
|||||||
else:
|
else:
|
||||||
schemeName = 'CUDS-SHA384'
|
schemeName = 'CUDS-SHA384'
|
||||||
|
|
||||||
fexml.set_element('./cac:BillingReference/%s/cbc:ID' %(tag_document),
|
fexml.set_element('./cac:BillingReference/%s/cbc:ID' % (tag_document),
|
||||||
reference.ident)
|
reference.ident)
|
||||||
fexml.set_element('./cac:BillingReference/cac:InvoiceDocumentReference/cbc:UUID',
|
fexml.set_element(
|
||||||
|
'./cac:BillingReference/cac:InvoiceDocumentReference/cbc:UUID',
|
||||||
reference.uuid,
|
reference.uuid,
|
||||||
schemeName=schemeName)
|
schemeName=schemeName)
|
||||||
fexml.set_element('./cac:BillingReference/cac:InvoiceDocumentReference/cbc:IssueDate',
|
fexml.set_element(
|
||||||
|
'./cac:BillingReference/cac:InvoiceDocumentReference/cbc:IssueDate',
|
||||||
reference.date.strftime("%Y-%m-%d"))
|
reference.date.strftime("%Y-%m-%d"))
|
||||||
|
|
||||||
def set_billing_reference(fexml, invoice):
|
def set_billing_reference(fexml, invoice):
|
||||||
@ -319,60 +372,64 @@ class DIANSupportDocumentXML(fe.FeXML):
|
|||||||
for invoice_line in invoice.invoice_lines:
|
for invoice_line in invoice.invoice_lines:
|
||||||
for subtotal in invoice_line.tax.subtotals:
|
for subtotal in invoice_line.tax.subtotals:
|
||||||
if subtotal.scheme is not None:
|
if subtotal.scheme is not None:
|
||||||
tax_amount_for[subtotal.scheme.code]['tax_amount'] += subtotal.tax_amount
|
tax_amount_for[
|
||||||
tax_amount_for[subtotal.scheme.code]['taxable_amount'] += invoice_line.taxable_amount
|
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
|
# MACHETE ojo InvoiceLine.tax pasar a Invoice
|
||||||
percent_for[subtotal.scheme.code] = subtotal.percent
|
percent_for[subtotal.scheme.code] = subtotal.percent
|
||||||
|
|
||||||
total_tax_amount += subtotal.tax_amount
|
total_tax_amount += subtotal.tax_amount
|
||||||
|
|
||||||
|
|
||||||
if total_tax_amount != Amount(0.0):
|
if total_tax_amount != Amount(0.0):
|
||||||
fexml.placeholder_for('./cac:TaxTotal')
|
fexml.placeholder_for('./cac:TaxTotal')
|
||||||
fexml.set_element_amount('./cac:TaxTotal/cbc:TaxAmount',
|
fexml.set_element_amount('./cac:TaxTotal/cbc:TaxAmount',
|
||||||
total_tax_amount)
|
total_tax_amount)
|
||||||
|
|
||||||
|
|
||||||
for index, item in enumerate(tax_amount_for.items()):
|
for index, item in enumerate(tax_amount_for.items()):
|
||||||
cod_impuesto, amount_of = item
|
cod_impuesto, amount_of = item
|
||||||
next_append = index > 0
|
next_append = index > 0
|
||||||
|
|
||||||
#DIAN 1.7.-2020: FAS01
|
# DIAN 1.7.-2020: FAS01
|
||||||
line = fexml.fragment('./cac:TaxTotal', append=next_append)
|
line = fexml.fragment('./cac:TaxTotal', append=next_append)
|
||||||
#DIAN 1.7.-2020: FAU06
|
# DIAN 1.7.-2020: FAU06
|
||||||
tax_amount = amount_of['tax_amount']
|
tax_amount = amount_of['tax_amount']
|
||||||
fexml.set_element_amount_for(line,
|
fexml.set_element_amount_for(line,
|
||||||
'/cac:TaxTotal/cbc:TaxAmount',
|
'/cac:TaxTotal/cbc:TaxAmount',
|
||||||
tax_amount)
|
tax_amount)
|
||||||
|
|
||||||
#DIAN 1.7.-2020: FAS05
|
# DIAN 1.7.-2020: FAS05
|
||||||
fexml.set_element_amount_for(line,
|
fexml.set_element_amount_for(
|
||||||
|
line,
|
||||||
'/cac:TaxTotal/cac:TaxSubtotal/cbc:TaxableAmount',
|
'/cac:TaxTotal/cac:TaxSubtotal/cbc:TaxableAmount',
|
||||||
amount_of['taxable_amount'])
|
amount_of['taxable_amount'])
|
||||||
|
|
||||||
#DIAN 1.7.-2020: FAU06
|
# DIAN 1.7.-2020: FAU06
|
||||||
fexml.set_element_amount_for(line,
|
fexml.set_element_amount_for(
|
||||||
|
line,
|
||||||
'/cac:TaxTotal/cac:TaxSubtotal/cbc:TaxAmount',
|
'/cac:TaxTotal/cac:TaxSubtotal/cbc:TaxAmount',
|
||||||
amount_of['tax_amount'])
|
amount_of['tax_amount'])
|
||||||
|
|
||||||
#DIAN 1.7.-2020: FAS07
|
# DIAN 1.7.-2020: FAS07
|
||||||
if percent_for[cod_impuesto]:
|
if percent_for[cod_impuesto]:
|
||||||
line.set_element('/cac:TaxTotal/cac:TaxSubtotal/cbc:Percent',
|
line.set_element('/cac:TaxTotal/cac:TaxSubtotal/cbc:Percent',
|
||||||
percent_for[cod_impuesto])
|
percent_for[cod_impuesto])
|
||||||
|
|
||||||
|
|
||||||
if percent_for[cod_impuesto]:
|
if percent_for[cod_impuesto]:
|
||||||
line.set_element('/cac:TaxTotal/cac:TaxSubtotal/cac:TaxCategory/cbc:Percent',
|
line.set_element(
|
||||||
|
'/cac:TaxTotal/cac:TaxSubtotal/cac:TaxCategory/cbc:Percent',
|
||||||
percent_for[cod_impuesto])
|
percent_for[cod_impuesto])
|
||||||
|
|
||||||
line.set_element('/cac:TaxTotal/cac:TaxSubtotal/cac:TaxCategory/cac:TaxScheme/cbc:ID',
|
line.set_element(
|
||||||
|
'/cac:TaxTotal/cac:TaxSubtotal/cac:TaxCategory/cac:TaxScheme/cbc:ID',
|
||||||
cod_impuesto)
|
cod_impuesto)
|
||||||
line.set_element('/cac:TaxTotal/cac:TaxSubtotal/cac:TaxCategory/cac:TaxScheme/cbc:Name',
|
line.set_element(
|
||||||
'IVA')
|
'/cac:TaxTotal/cac:TaxSubtotal/cac:TaxCategory/cac:TaxScheme/cbc:Name', 'IVA')
|
||||||
|
|
||||||
|
|
||||||
# abstract method
|
# abstract method
|
||||||
|
|
||||||
def tag_document(fexml):
|
def tag_document(fexml):
|
||||||
return 'Invoice'
|
return 'Invoice'
|
||||||
|
|
||||||
@ -384,50 +441,83 @@ class DIANSupportDocumentXML(fe.FeXML):
|
|||||||
fexml.set_element_amount_for(line,
|
fexml.set_element_amount_for(line,
|
||||||
'./cac:WithholdingTaxTotal/cbc:TaxAmount',
|
'./cac:WithholdingTaxTotal/cbc:TaxAmount',
|
||||||
invoice_line.withholding_amount)
|
invoice_line.withholding_amount)
|
||||||
#DIAN 1.7.-2020: FAX05
|
# DIAN 1.7.-2020: FAX05
|
||||||
fexml.set_element_amount_for(line,
|
fexml.set_element_amount_for(
|
||||||
|
line,
|
||||||
'./cac:WithholdingTaxTotal/cac:TaxSubtotal/cbc:TaxableAmount',
|
'./cac:WithholdingTaxTotal/cac:TaxSubtotal/cbc:TaxableAmount',
|
||||||
invoice_line.withholding_taxable_amount)
|
invoice_line.withholding_taxable_amount)
|
||||||
|
|
||||||
for subtotal in invoice_line.withholding.subtotals:
|
for subtotal in invoice_line.withholding.subtotals:
|
||||||
line.set_element('./cac:WithholdingTaxTotal/cac:TaxSubtotal/cbc:TaxAmount', subtotal.tax_amount, currencyID='COP')
|
line.set_element(
|
||||||
|
'./cac:WithholdingTaxTotal/cac:TaxSubtotal/cbc:TaxAmount',
|
||||||
|
subtotal.tax_amount,
|
||||||
|
currencyID='COP')
|
||||||
|
|
||||||
if subtotal.percent is not None:
|
if subtotal.percent is not None:
|
||||||
line.set_element('./cac:WithholdingTaxTotal/cac:TaxSubtotal/cac:TaxCategory/cbc:Percent', '%0.2f' % round(subtotal.percent, 2))
|
line.set_element(
|
||||||
|
'./cac:WithholdingTaxTotal/cac:TaxSubtotal/cac:TaxCategory/cbc:Percent',
|
||||||
|
'%0.2f' %
|
||||||
|
round(
|
||||||
|
subtotal.percent,
|
||||||
|
2))
|
||||||
|
|
||||||
if subtotal.scheme is not None:
|
if subtotal.scheme is not None:
|
||||||
#DIAN 1.7.-2020: FAX15
|
# DIAN 1.7.-2020: FAX15
|
||||||
line.set_element('./cac:WithholdingTaxTotal/cac:TaxSubtotal/cac:TaxCategory/cac:TaxScheme/cbc:ID', subtotal.scheme.code)
|
line.set_element(
|
||||||
line.set_element('./cac:WithholdingTaxTotal/cac:TaxSubtotal/cac:TaxCategory/cac:TaxScheme/cbc:Name', subtotal.scheme.name)
|
'./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):
|
def set_invoice_line_tax(fexml, line, invoice_line):
|
||||||
fexml.set_element_amount_for(line,
|
fexml.set_element_amount_for(line,
|
||||||
'./cac:TaxTotal/cbc:TaxAmount',
|
'./cac:TaxTotal/cbc:TaxAmount',
|
||||||
invoice_line.tax_amount)
|
invoice_line.tax_amount)
|
||||||
|
|
||||||
#DIAN 1.7.-2020: FAX05
|
# DIAN 1.7.-2020: FAX05
|
||||||
fexml.set_element_amount_for(line,
|
fexml.set_element_amount_for(
|
||||||
|
line,
|
||||||
'./cac:TaxTotal/cac:TaxSubtotal/cbc:TaxableAmount',
|
'./cac:TaxTotal/cac:TaxSubtotal/cbc:TaxableAmount',
|
||||||
invoice_line.taxable_amount)
|
invoice_line.taxable_amount)
|
||||||
for subtotal in invoice_line.tax.subtotals:
|
for subtotal in invoice_line.tax.subtotals:
|
||||||
line.set_element('./cac:TaxTotal/cac:TaxSubtotal/cbc:TaxAmount', subtotal.tax_amount, currencyID='COP')
|
line.set_element(
|
||||||
|
'./cac:TaxTotal/cac:TaxSubtotal/cbc:TaxAmount',
|
||||||
|
subtotal.tax_amount,
|
||||||
|
currencyID='COP')
|
||||||
|
|
||||||
if subtotal.percent is not None:
|
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:
|
if subtotal.scheme is not None:
|
||||||
#DIAN 1.7.-2020: FAX15
|
# DIAN 1.7.-2020: FAX15
|
||||||
line.set_element('./cac:TaxTotal/cac:TaxSubtotal/cac:TaxCategory/cac:TaxScheme/cbc:ID', subtotal.scheme.code)
|
line.set_element(
|
||||||
line.set_element('./cac:TaxTotal/cac:TaxSubtotal/cac:TaxCategory/cac:TaxScheme/cbc:Name', subtotal.scheme.name)
|
'./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):
|
def set_invoice_lines(fexml, invoice):
|
||||||
next_append = False
|
next_append = False
|
||||||
for index, invoice_line in enumerate(invoice.invoice_lines):
|
for index, invoice_line in enumerate(invoice.invoice_lines):
|
||||||
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
|
next_append = True
|
||||||
|
|
||||||
line.set_element('./cbc:ID', index + 1)
|
line.set_element('./cbc:ID', index + 1)
|
||||||
line.set_element('./cbc:%sQuantity' % (fexml.tag_document_concilied()), invoice_line.quantity, unitCode = 'NAR')
|
line.set_element(
|
||||||
|
'./cbc:%sQuantity' %
|
||||||
|
(fexml.tag_document_concilied()),
|
||||||
|
invoice_line.quantity,
|
||||||
|
unitCode='NAR')
|
||||||
fexml.set_element_amount_for(line,
|
fexml.set_element_amount_for(line,
|
||||||
'./cbc:LineExtensionAmount',
|
'./cbc:LineExtensionAmount',
|
||||||
invoice_line.total_amount)
|
invoice_line.total_amount)
|
||||||
@ -435,53 +525,69 @@ class DIANSupportDocumentXML(fe.FeXML):
|
|||||||
period = line.fragment('./cac:InvoicePeriod')
|
period = line.fragment('./cac:InvoicePeriod')
|
||||||
period.set_element('./cbc:StartDate',
|
period.set_element('./cbc:StartDate',
|
||||||
datetime.now().strftime('%Y-%m-%d'))
|
datetime.now().strftime('%Y-%m-%d'))
|
||||||
period.set_element('./cbc:DescriptionCode',
|
period.set_element(
|
||||||
'1')
|
'./cbc:DescriptionCode', '1')
|
||||||
period.set_element('./cbc:Description',
|
period.set_element('./cbc:Description',
|
||||||
'Por operación')
|
'Por operación')
|
||||||
|
|
||||||
if not isinstance(invoice_line.tax, TaxTotalOmit):
|
if not isinstance(invoice_line.tax, TaxTotalOmit):
|
||||||
fexml.set_invoice_line_tax(line, invoice_line)
|
fexml.set_invoice_line_tax(line, invoice_line)
|
||||||
|
|
||||||
if not isinstance(invoice_line.withholding, WithholdingTaxTotalOmit):
|
if not isinstance(
|
||||||
|
invoice_line.withholding,
|
||||||
|
WithholdingTaxTotalOmit):
|
||||||
fexml.set_invoice_line_withholding(line, invoice_line)
|
fexml.set_invoice_line_withholding(line, invoice_line)
|
||||||
|
|
||||||
|
line.set_element(
|
||||||
|
'./cac:Item/cbc:Description',
|
||||||
|
invoice_line.item.description)
|
||||||
|
|
||||||
line.set_element('./cac:Item/cbc:Description', invoice_line.item.description)
|
line.set_element(
|
||||||
|
'./cac:Item/cac:StandardItemIdentification/cbc:ID',
|
||||||
line.set_element('./cac:Item/cac:StandardItemIdentification/cbc:ID',
|
|
||||||
invoice_line.item.id,
|
invoice_line.item.id,
|
||||||
schemeID=invoice_line.item.scheme_id,
|
schemeID=invoice_line.item.scheme_id,
|
||||||
schemeName=invoice_line.item.scheme_name,
|
schemeName=invoice_line.item.scheme_name,
|
||||||
schemeAgencyID=invoice_line.item.scheme_agency_id)
|
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)
|
line.set_element(
|
||||||
#DIAN 1.7.-2020: FBB04
|
'./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',
|
line.set_element('./cac:Price/cbc:BaseQuantity',
|
||||||
invoice_line.quantity,
|
invoice_line.quantity,
|
||||||
unitCode=invoice_line.quantity.code)
|
unitCode=invoice_line.quantity.code)
|
||||||
|
|
||||||
for idx, charge in enumerate(invoice_line.allowance_charge):
|
for idx, charge in enumerate(invoice_line.allowance_charge):
|
||||||
next_append_charge = idx > 0
|
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):
|
def set_allowance_charge(fexml, invoice):
|
||||||
for idx, charge in enumerate(invoice.invoice_allowance_charge):
|
for idx, charge in enumerate(invoice.invoice_allowance_charge):
|
||||||
next_append = idx > 0
|
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):
|
def append_allowance_charge(fexml, parent, idx, charge, append=False):
|
||||||
line = parent.fragment('./cac:AllowanceCharge', append=append)
|
line = parent.fragment('./cac:AllowanceCharge', append=append)
|
||||||
#DIAN 1.7.-2020: FAQ02
|
# DIAN 1.7.-2020: FAQ02
|
||||||
line.set_element('./cbc:ID', idx)
|
line.set_element('./cbc:ID', idx)
|
||||||
#DIAN 1.7.-2020: FAQ03
|
# DIAN 1.7.-2020: FAQ03
|
||||||
line.set_element('./cbc:ChargeIndicator', str(charge.charge_indicator).lower())
|
line.set_element('./cbc:ChargeIndicator',
|
||||||
|
str(charge.charge_indicator).lower())
|
||||||
if charge.reason:
|
if charge.reason:
|
||||||
line.set_element('./cbc:AllowanceChargeReasonCode', charge.reason.code)
|
line.set_element(
|
||||||
line.set_element('./cbc:allowanceChargeReason', charge.reason.reason)
|
'./cbc:AllowanceChargeReasonCode',
|
||||||
line.set_element('./cbc:MultiplierFactorNumeric', str(round(charge.multiplier_factor_numeric, 2)))
|
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:Amount', charge.amount)
|
||||||
fexml.set_element_amount_for(line, './cbc:BaseAmount', charge.base_amount)
|
fexml.set_element_amount_for(
|
||||||
|
line, './cbc:BaseAmount', charge.base_amount)
|
||||||
|
|
||||||
def attach_invoice(fexml, invoice):
|
def attach_invoice(fexml, invoice):
|
||||||
"""adiciona etiquetas a FEXML y retorna FEXML
|
"""adiciona etiquetas a FEXML y retorna FEXML
|
||||||
@ -489,25 +595,37 @@ class DIANSupportDocumentXML(fe.FeXML):
|
|||||||
|
|
||||||
fexml.placeholder_for('./ext:UBLExtensions')
|
fexml.placeholder_for('./ext:UBLExtensions')
|
||||||
fexml.set_element('./cbc:UBLVersionID', 'UBL 2.1')
|
fexml.set_element('./cbc:UBLVersionID', 'UBL 2.1')
|
||||||
fexml.set_element('./cbc:CustomizationID', invoice.invoice_operation_type)
|
fexml.set_element(
|
||||||
|
'./cbc:CustomizationID',
|
||||||
|
invoice.invoice_operation_type)
|
||||||
fexml.placeholder_for('./cbc:ProfileID')
|
fexml.placeholder_for('./cbc:ProfileID')
|
||||||
fexml.placeholder_for('./cbc:ProfileExecutionID')
|
fexml.placeholder_for('./cbc:ProfileExecutionID')
|
||||||
fexml.set_element('./cbc:ID', invoice.invoice_ident)
|
fexml.set_element('./cbc:ID', invoice.invoice_ident)
|
||||||
fexml.placeholder_for('./cbc:UUID')
|
fexml.placeholder_for('./cbc:UUID')
|
||||||
fexml.set_element('./cbc:DocumentCurrencyCode', 'COP')
|
fexml.set_element('./cbc:DocumentCurrencyCode', 'COP')
|
||||||
fexml.set_element('./cbc:IssueDate', invoice.invoice_issue.strftime('%Y-%m-%d'))
|
fexml.set_element(
|
||||||
#DIAN 1.7.-2020: FAD10
|
'./cbc:IssueDate',
|
||||||
fexml.set_element('./cbc:IssueTime', invoice.invoice_issue.strftime('%H:%M:%S-05:00'))
|
invoice.invoice_issue.strftime('%Y-%m-%d'))
|
||||||
fexml.set_element('./cbc:%sTypeCode' % (fexml.tag_document()),
|
# 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,
|
invoice.invoice_type_code,
|
||||||
listAgencyID='195',
|
listAgencyID='195',
|
||||||
listAgencyName='No matching global declaration available for the validation root',
|
listAgencyName='No matching global declaration available for the validation root',
|
||||||
listURI='http://www.dian.gov.co')
|
listURI='http://www.dian.gov.co')
|
||||||
fexml.set_element('./cbc:LineCountNumeric', len(invoice.invoice_lines))
|
fexml.set_element('./cbc:LineCountNumeric', len(invoice.invoice_lines))
|
||||||
fexml.set_element('./cac:%sPeriod/cbc:StartDate' % (fexml.tag_document()),
|
fexml.set_element(
|
||||||
|
'./cac:%sPeriod/cbc:StartDate' %
|
||||||
|
(fexml.tag_document()),
|
||||||
invoice.invoice_period_start.strftime('%Y-%m-%d'))
|
invoice.invoice_period_start.strftime('%Y-%m-%d'))
|
||||||
|
|
||||||
fexml.set_element('./cac:%sPeriod/cbc:EndDate' % (fexml.tag_document()),
|
fexml.set_element(
|
||||||
|
'./cac:%sPeriod/cbc:EndDate' %
|
||||||
|
(fexml.tag_document()),
|
||||||
invoice.invoice_period_end.strftime('%Y-%m-%d'))
|
invoice.invoice_period_end.strftime('%Y-%m-%d'))
|
||||||
|
|
||||||
fexml.customize(invoice)
|
fexml.customize(invoice)
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
from .. import fe
|
# from .. import fe
|
||||||
from ..form import *
|
# from ..form import *
|
||||||
from .support_document import DIANSupportDocumentXML
|
from .support_document import DIANSupportDocumentXML
|
||||||
|
|
||||||
__all__ = ['DIANSupportDocumentCreditNoteXML']
|
__all__ = ['DIANSupportDocumentCreditNoteXML']
|
||||||
|
|
||||||
|
|
||||||
class DIANSupportDocumentCreditNoteXML(DIANSupportDocumentXML):
|
class DIANSupportDocumentCreditNoteXML(DIANSupportDocumentXML):
|
||||||
"""
|
"""
|
||||||
DianInvoiceXML mapea objeto form.Invoice a XML segun
|
DianInvoiceXML mapea objeto form.Invoice a XML segun
|
||||||
@ -11,11 +12,14 @@ class DIANSupportDocumentCreditNoteXML(DIANSupportDocumentXML):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, invoice):
|
def __init__(self, invoice):
|
||||||
super(DIANSupportDocumentCreditNoteXML, self).__init__(invoice, 'CreditNote')
|
super(
|
||||||
|
DIANSupportDocumentCreditNoteXML,
|
||||||
|
self).__init__(
|
||||||
|
invoice,
|
||||||
|
'CreditNote')
|
||||||
|
|
||||||
def tag_document(fexml):
|
def tag_document(fexml):
|
||||||
return 'CreditNote'
|
return 'CreditNote'
|
||||||
|
|
||||||
def tag_document_concilied(fexml):
|
def tag_document_concilied(fexml):
|
||||||
return 'Credited'
|
return 'Credited'
|
||||||
|
|
||||||
|
@ -2,18 +2,30 @@ from .. import fe
|
|||||||
|
|
||||||
__all__ = ['DIANWrite', 'DIANWriteSigned']
|
__all__ = ['DIANWrite', 'DIANWriteSigned']
|
||||||
|
|
||||||
|
|
||||||
def DIANWrite(xml, filename):
|
def DIANWrite(xml, filename):
|
||||||
document = xml.tostring(xml_declaration=True, encoding='UTF-8')
|
document = xml.tostring(xml_declaration=True, encoding='UTF-8')
|
||||||
with open(filename, 'w') as f:
|
with open(filename, 'w') as f:
|
||||||
f.write(document)
|
f.write(document)
|
||||||
|
|
||||||
|
|
||||||
def DIANWriteSigned(xml, filename, private_key, passphrase, use_cache_policy=False, dian_signer=None):
|
def DIANWriteSigned(
|
||||||
document = xml.tostring(xml_declaration=True, encoding='UTF-8').encode('utf-8')
|
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:
|
if dian_signer is None:
|
||||||
dian_signer = fe.DianXMLExtensionSigner
|
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:
|
with open(filename, 'w') as f:
|
||||||
f.write(signer.sign_xml_string(document))
|
f.write(signer.sign_xml_string(document))
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
from dataclasses import dataclass
|
from dataclasses import dataclass, field
|
||||||
|
|
||||||
from ..amount import Amount
|
from ..amount import Amount
|
||||||
|
|
||||||
@ -29,7 +29,7 @@ class Trabajador:
|
|||||||
|
|
||||||
codigo_trabajador: str = None
|
codigo_trabajador: str = None
|
||||||
otros_nombres: str = None
|
otros_nombres: str = None
|
||||||
sub_tipo: SubTipoTrabajador = SubTipoTrabajador(code='00')
|
sub_tipo: SubTipoTrabajador = field(default_factory=lambda: SubTipoTrabajador(code='00'))
|
||||||
|
|
||||||
def apply(self, fragment):
|
def apply(self, fragment):
|
||||||
fragment.set_attributes('./Trabajador',
|
fragment.set_attributes('./Trabajador',
|
||||||
|
17
requirements_dev.txt
Normal file
17
requirements_dev.txt
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
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
|
@ -22,4 +22,4 @@ exclude = docs
|
|||||||
test = pytest
|
test = pytest
|
||||||
|
|
||||||
[tool:pytest]
|
[tool:pytest]
|
||||||
collect_ignore = ['setup.py']
|
addopts = --ignore=setup.py
|
||||||
|
26
setup.py
26
setup.py
@ -13,6 +13,22 @@ with open('README.rst') as readme_file:
|
|||||||
with open('HISTORY.rst') as history_file:
|
with open('HISTORY.rst') as history_file:
|
||||||
history = history_file.read()
|
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',
|
requirements = ['Click>=6.0',
|
||||||
'zeep==4.0.0',
|
'zeep==4.0.0',
|
||||||
'lxml==4.6.3',
|
'lxml==4.6.3',
|
||||||
@ -26,6 +42,8 @@ requirements = ['Click>=6.0',
|
|||||||
'mock>=2.0.0',
|
'mock>=2.0.0',
|
||||||
'xmlschema>=1.8']
|
'xmlschema>=1.8']
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
setup_requirements = ['pytest-runner', ]
|
setup_requirements = ['pytest-runner', ]
|
||||||
|
|
||||||
test_requirements = ['pytest', ]
|
test_requirements = ['pytest', ]
|
||||||
@ -39,10 +57,10 @@ setup(
|
|||||||
'License :: OSI Approved :: GNU General Public License v3 (GPLv3)',
|
'License :: OSI Approved :: GNU General Public License v3 (GPLv3)',
|
||||||
'Natural Language :: English',
|
'Natural Language :: English',
|
||||||
'Programming Language :: Python :: 3',
|
'Programming Language :: Python :: 3',
|
||||||
'Programming Language :: Python :: 3.4',
|
'Programming Language :: Python :: 3.9',
|
||||||
'Programming Language :: Python :: 3.5',
|
'Programming Language :: Python :: 3.10',
|
||||||
'Programming Language :: Python :: 3.6',
|
'Programming Language :: Python :: 3.11',
|
||||||
'Programming Language :: Python :: 3.7',
|
'Programming Language :: Python :: 3.12',
|
||||||
],
|
],
|
||||||
description="Facturacion Electronica Colombia",
|
description="Facturacion Electronica Colombia",
|
||||||
entry_points={
|
entry_points={
|
||||||
|
1
tests/cude.txt
Normal file
1
tests/cude.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
907e4444decc9e59c160a2fb3b6659b33dc5b632a5008922b9a62f83f757b1c448e47f5867f2b50dbdb96f48c7681168
|
0
tests/cufe.txt
Normal file
0
tests/cufe.txt
Normal file
@ -4,30 +4,32 @@ from datetime import datetime
|
|||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def simple_debit_note_without_lines():
|
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_period(datetime.now(), datetime.now())
|
||||||
inv.set_issue(datetime.now())
|
inv.set_issue(datetime.now())
|
||||||
inv.set_ident('ABC123')
|
inv.set_ident('ABC123')
|
||||||
inv.set_operation_type('30')
|
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(
|
inv.set_supplier(form.Party(
|
||||||
name = 'facho-supplier',
|
name='facho-supplier',
|
||||||
ident = form.PartyIdentification('123','', '31'),
|
ident=form.PartyIdentification('123', '', '31'),
|
||||||
responsability_code = form.Responsability(['O-07']),
|
responsability_code = form.Responsability(['ZZ']),
|
||||||
responsability_regime_code = '48',
|
responsability_regime_code='48',
|
||||||
organization_code = '1',
|
organization_code='1',
|
||||||
address = form.Address(
|
address=form.Address(
|
||||||
'', '', form.City('05001', 'Medellín'),
|
'', '', form.City('05001', 'Medellín'),
|
||||||
form.Country('CO', 'Colombia'),
|
form.Country('CO', 'Colombia'),
|
||||||
form.CountrySubentity('05', 'Antioquia'))
|
form.CountrySubentity('05', 'Antioquia'))
|
||||||
))
|
))
|
||||||
inv.set_customer(form.Party(
|
inv.set_customer(form.Party(
|
||||||
name = 'facho-customer',
|
name='facho-customer',
|
||||||
ident = form.PartyIdentification('321', '', '31'),
|
ident=form.PartyIdentification('321', '', '31'),
|
||||||
responsability_code = form.Responsability(['O-07']),
|
responsability_code=form.Responsability(['ZZ']),
|
||||||
responsability_regime_code = '48',
|
responsability_regime_code='48',
|
||||||
organization_code = '1',
|
organization_code='1',
|
||||||
address = form.Address(
|
address=form.Address(
|
||||||
'', '', form.City('05001', 'Medellín'),
|
'', '', form.City('05001', 'Medellín'),
|
||||||
form.Country('CO', 'Colombia'),
|
form.Country('CO', 'Colombia'),
|
||||||
form.CountrySubentity('05', 'Antioquia'))
|
form.CountrySubentity('05', 'Antioquia'))
|
||||||
@ -45,7 +47,7 @@ def simple_credit_note_without_lines():
|
|||||||
inv.set_supplier(form.Party(
|
inv.set_supplier(form.Party(
|
||||||
name = 'facho-supplier',
|
name = 'facho-supplier',
|
||||||
ident = form.PartyIdentification('123','', '31'),
|
ident = form.PartyIdentification('123','', '31'),
|
||||||
responsability_code = form.Responsability(['O-07']),
|
responsability_code = form.Responsability(['ZZ']),
|
||||||
responsability_regime_code = '48',
|
responsability_regime_code = '48',
|
||||||
organization_code = '1',
|
organization_code = '1',
|
||||||
address = form.Address(
|
address = form.Address(
|
||||||
@ -56,7 +58,7 @@ def simple_credit_note_without_lines():
|
|||||||
inv.set_customer(form.Party(
|
inv.set_customer(form.Party(
|
||||||
name = 'facho-customer',
|
name = 'facho-customer',
|
||||||
ident = form.PartyIdentification('321', '', '31'),
|
ident = form.PartyIdentification('321', '', '31'),
|
||||||
responsability_code = form.Responsability(['O-07']),
|
responsability_code = form.Responsability(['ZZ']),
|
||||||
responsability_regime_code = '48',
|
responsability_regime_code = '48',
|
||||||
organization_code = '1',
|
organization_code = '1',
|
||||||
address = form.Address(
|
address = form.Address(
|
||||||
@ -77,7 +79,7 @@ def simple_invoice_without_lines():
|
|||||||
inv.set_supplier(form.Party(
|
inv.set_supplier(form.Party(
|
||||||
name = 'facho-supplier',
|
name = 'facho-supplier',
|
||||||
ident = form.PartyIdentification('123','', '31'),
|
ident = form.PartyIdentification('123','', '31'),
|
||||||
responsability_code = form.Responsability(['O-07']),
|
responsability_code = form.Responsability(['ZZ']),
|
||||||
responsability_regime_code = '48',
|
responsability_regime_code = '48',
|
||||||
organization_code = '1',
|
organization_code = '1',
|
||||||
address = form.Address(
|
address = form.Address(
|
||||||
@ -88,7 +90,7 @@ def simple_invoice_without_lines():
|
|||||||
inv.set_customer(form.Party(
|
inv.set_customer(form.Party(
|
||||||
name = 'facho-customer',
|
name = 'facho-customer',
|
||||||
ident = form.PartyIdentification('321', '', '31'),
|
ident = form.PartyIdentification('321', '', '31'),
|
||||||
responsability_code = form.Responsability(['O-07']),
|
responsability_code = form.Responsability(['ZZ']),
|
||||||
responsability_regime_code = '48',
|
responsability_regime_code = '48',
|
||||||
organization_code = '1',
|
organization_code = '1',
|
||||||
address = form.Address(
|
address = form.Address(
|
||||||
@ -98,6 +100,7 @@ def simple_invoice_without_lines():
|
|||||||
))
|
))
|
||||||
return inv
|
return inv
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def simple_invoice():
|
def simple_invoice():
|
||||||
inv = form.NationalSalesInvoice()
|
inv = form.NationalSalesInvoice()
|
||||||
@ -109,7 +112,7 @@ def simple_invoice():
|
|||||||
inv.set_supplier(form.Party(
|
inv.set_supplier(form.Party(
|
||||||
name = 'facho-supplier',
|
name = 'facho-supplier',
|
||||||
ident = form.PartyIdentification('123','', '31'),
|
ident = form.PartyIdentification('123','', '31'),
|
||||||
responsability_code = form.Responsability(['O-07']),
|
responsability_code = form.Responsability(['ZZ']),
|
||||||
responsability_regime_code = '48',
|
responsability_regime_code = '48',
|
||||||
organization_code = '1',
|
organization_code = '1',
|
||||||
address = form.Address(
|
address = form.Address(
|
||||||
@ -120,7 +123,7 @@ def simple_invoice():
|
|||||||
inv.set_customer(form.Party(
|
inv.set_customer(form.Party(
|
||||||
name = 'facho-customer',
|
name = 'facho-customer',
|
||||||
ident = form.PartyIdentification('321','', '31'),
|
ident = form.PartyIdentification('321','', '31'),
|
||||||
responsability_code = form.Responsability(['O-07']),
|
responsability_code = form.Responsability(['ZZ']),
|
||||||
responsability_regime_code = '48',
|
responsability_regime_code = '48',
|
||||||
organization_code = '1',
|
organization_code = '1',
|
||||||
address = form.Address(
|
address = form.Address(
|
||||||
@ -128,19 +131,20 @@ def simple_invoice():
|
|||||||
form.Country('CO', 'Colombia'),
|
form.Country('CO', 'Colombia'),
|
||||||
form.CountrySubentity('05', 'Antioquia'))
|
form.CountrySubentity('05', 'Antioquia'))
|
||||||
))
|
))
|
||||||
|
|
||||||
inv.add_invoice_line(form.InvoiceLine(
|
inv.add_invoice_line(form.InvoiceLine(
|
||||||
quantity = form.Quantity(1, '94'),
|
quantity=form.Quantity(1, '94'),
|
||||||
description = 'producto facho',
|
description='productofacho',
|
||||||
item = form.StandardItem( 9999),
|
item=form.StandardItem(9999),
|
||||||
price = form.Price(form.Amount(100.0), '01', ''),
|
price=form.Price(form.Amount(100.0),'01',''),
|
||||||
tax = form.TaxTotal(
|
tax=form.TaxTotal(
|
||||||
tax_amount = form.Amount(0.0),
|
tax_amount=form.Amount(0.0),
|
||||||
taxable_amount = form.Amount(0.0),
|
taxable_amount=form.Amount(0.0),
|
||||||
subtotals = [
|
subtotals=[
|
||||||
form.TaxSubTotal(
|
form.TaxSubTotal(
|
||||||
percent = 19.0,
|
percent=19.0,
|
||||||
)
|
)]),
|
||||||
]
|
withholding=form.WithholdingTaxTotal(
|
||||||
)
|
subtotals=[])
|
||||||
))
|
))
|
||||||
return inv
|
return inv
|
||||||
|
21
tests/test_application_response.py
Normal file
21
tests/test_application_response.py
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
#!/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')
|
@ -2,16 +2,124 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
# This file is part of facho. The COPYRIGHT file at the top level of
|
# This file is part of facho. The COPYRIGHT file at the top level of
|
||||||
# this repository contains the full copyright notices and license terms.
|
# this repository contains the full copyright notices and license terms.
|
||||||
from datetime import datetime
|
# from datetime import datetime
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from facho.fe import form_xml
|
from facho.fe import form_xml
|
||||||
|
from datetime import datetime
|
||||||
import helpers
|
import helpers
|
||||||
|
from fixtures import simple_invoice
|
||||||
|
|
||||||
def test_xml_with_required_elements():
|
simple_invoice = simple_invoice
|
||||||
doc = form_xml.AttachedDocument(id='123')
|
|
||||||
|
def test_xml_with_required_elements(simple_invoice):
|
||||||
|
|
||||||
|
xml_header = '<?xml version="1.0" encoding="UTF-8" standalone="no"?>'
|
||||||
|
DIANInvoiceXML = form_xml.DIANInvoiceXML(
|
||||||
|
simple_invoice)
|
||||||
|
|
||||||
|
doc = form_xml.AttachedDocument(
|
||||||
|
simple_invoice,
|
||||||
|
DIANInvoiceXML,
|
||||||
|
id='123')
|
||||||
|
|
||||||
xml = doc.toFachoXML()
|
xml = doc.toFachoXML()
|
||||||
assert xml.get_element_text('/atd:AttachedDocument/cbc:ID') == '123'
|
|
||||||
|
|
||||||
|
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'
|
||||||
|
) == "<![CDATA[{}]]>".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'
|
||||||
|
) == "<![CDATA[{}]]>".format(ApplicationResponse)
|
||||||
|
@ -4,21 +4,26 @@
|
|||||||
# this repository contains the full copyright notices and license terms.
|
# this repository contains the full copyright notices and license terms.
|
||||||
|
|
||||||
"""Tests for `facho` package."""
|
"""Tests for `facho` package."""
|
||||||
|
|
||||||
import pytest
|
|
||||||
from facho.fe.data.dian import codelist
|
from facho.fe.data.dian import codelist
|
||||||
|
|
||||||
|
|
||||||
def test_tiporesponsabilidad():
|
def test_tiporesponsabilidad():
|
||||||
assert codelist.TipoResponsabilidad.short_name == '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():
|
def test_tipoorganizacion():
|
||||||
assert codelist.TipoOrganizacion.short_name == '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():
|
def test_tipodocumento():
|
||||||
assert codelist.TipoDocumento.short_name == 'TipoDocumento'
|
assert codelist.TipoDocumento.short_name == 'TipoDocumento'
|
||||||
assert codelist.TipoDocumento.by_name('Factura de Venta Nacional')['code'] == '01'
|
assert codelist.TipoDocumento.by_name(
|
||||||
|
'Factura electrónica de Venta')['code'] == '01'
|
||||||
|
|
||||||
|
|
||||||
def test_departamento():
|
def test_departamento():
|
||||||
assert codelist.Departamento['05']['name'] == 'Antioquia'
|
assert codelist.Departamento['05']['name'] == 'Antioquia'
|
||||||
|
@ -5,15 +5,15 @@
|
|||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from facho import fe
|
|
||||||
|
|
||||||
|
from facho import fe
|
||||||
|
|
||||||
import helpers
|
import helpers
|
||||||
|
|
||||||
|
|
||||||
def test_xmlsigned_build(monkeypatch):
|
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 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 pkcs12 -export -out example.p12 -inkey example.key -in example.pem
|
||||||
signer = fe.DianXMLExtensionSigner('./tests/example.p12')
|
signer = fe.DianXMLExtensionSigner('./tests/example.p12')
|
||||||
|
|
||||||
xml = fe.FeXML('Invoice',
|
xml = fe.FeXML('Invoice',
|
||||||
@ -116,3 +116,17 @@ def test_xml_sign_dian_using_bytes(monkeypatch):
|
|||||||
|
|
||||||
xmlsigned = signer.sign_xml_string(xmlstring)
|
xmlsigned = signer.sign_xml_string(xmlstring)
|
||||||
assert "Signature" in xmlsigned
|
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)
|
||||||
|
@ -5,24 +5,47 @@
|
|||||||
|
|
||||||
"""Tests for `facho` package."""
|
"""Tests for `facho` package."""
|
||||||
|
|
||||||
import pytest
|
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
import io
|
import io
|
||||||
import zipfile
|
import zipfile
|
||||||
|
|
||||||
import facho.fe.form as form
|
import facho.fe.form as form
|
||||||
from facho import fe
|
from facho import fe
|
||||||
from facho.fe.form_xml import DIANInvoiceXML, DIANCreditNoteXML, DIANDebitNoteXML
|
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 fixtures import *
|
|
||||||
|
|
||||||
def test_invoicesimple_build(simple_invoice):
|
def test_invoicesimple_build(simple_invoice):
|
||||||
xml = DIANInvoiceXML(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
|
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
|
assert customer_name == simple_invoice.invoice_customer.name
|
||||||
|
|
||||||
|
|
||||||
@ -42,9 +65,11 @@ def test_invoicesimple_xml_signed(monkeypatch, simple_invoice):
|
|||||||
print(xml.tostring())
|
print(xml.tostring())
|
||||||
xml.add_extension(signer)
|
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
|
assert elem.text is not None
|
||||||
|
|
||||||
|
|
||||||
def test_invoicesimple_zip(simple_invoice):
|
def test_invoicesimple_zip(simple_invoice):
|
||||||
xml_invoice = DIANInvoiceXML(simple_invoice)
|
xml_invoice = DIANInvoiceXML(simple_invoice)
|
||||||
|
|
||||||
@ -66,26 +91,34 @@ def test_invoicesimple_zip(simple_invoice):
|
|||||||
|
|
||||||
def test_bug_cbcid_empty_on_invoice_line(simple_invoice):
|
def test_bug_cbcid_empty_on_invoice_line(simple_invoice):
|
||||||
xml_invoice = DIANInvoiceXML(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
|
assert cbc_id == 1
|
||||||
|
|
||||||
|
|
||||||
def test_invoice_line_count_numeric(simple_invoice):
|
def test_invoice_line_count_numeric(simple_invoice):
|
||||||
xml_invoice = DIANInvoiceXML(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)
|
assert count == len(simple_invoice.invoice_lines)
|
||||||
|
|
||||||
|
|
||||||
def test_invoice_profileexecutionid(simple_invoice):
|
def test_invoice_profileexecutionid(simple_invoice):
|
||||||
xml_invoice = DIANInvoiceXML(simple_invoice)
|
xml_invoice = DIANInvoiceXML(simple_invoice)
|
||||||
cufe_extension = fe.DianXMLExtensionCUFE(simple_invoice)
|
cufe_extension = fe.DianXMLExtensionCUFE(simple_invoice)
|
||||||
xml_invoice.add_extension(cufe_extension)
|
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
|
assert id_ == 2
|
||||||
|
|
||||||
|
|
||||||
def test_invoice_invoice_type_code(simple_invoice):
|
def test_invoice_invoice_type_code(simple_invoice):
|
||||||
xml_invoice = DIANInvoiceXML(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
|
assert id_ == 1
|
||||||
|
|
||||||
|
|
||||||
def test_invoice_totals(simple_invoice_without_lines):
|
def test_invoice_totals(simple_invoice_without_lines):
|
||||||
simple_invoice = simple_invoice_without_lines
|
simple_invoice = simple_invoice_without_lines
|
||||||
simple_invoice.invoice_ident = '323200000129'
|
simple_invoice.invoice_ident = '323200000129'
|
||||||
@ -93,39 +126,50 @@ def test_invoice_totals(simple_invoice_without_lines):
|
|||||||
simple_invoice.invoice_supplier.ident = '700085371'
|
simple_invoice.invoice_supplier.ident = '700085371'
|
||||||
simple_invoice.invoice_customer.ident = '800199436'
|
simple_invoice.invoice_customer.ident = '800199436'
|
||||||
simple_invoice.add_invoice_line(form.InvoiceLine(
|
simple_invoice.add_invoice_line(form.InvoiceLine(
|
||||||
quantity = form.Quantity(1, '94'),
|
quantity=form.Quantity(1, '94'),
|
||||||
description = 'producto',
|
description='producto',
|
||||||
item = form.StandardItem(9999),
|
item=form.StandardItem(9999),
|
||||||
price = form.Price(form.Amount(1_500_000), '01', ''),
|
price=form.Price(form.Amount(1_500_000), '01', ''),
|
||||||
tax = form.TaxTotal(
|
tax=form.TaxTotal(
|
||||||
subtotals = [
|
subtotals=[
|
||||||
form.TaxSubTotal(
|
form.TaxSubTotal(
|
||||||
scheme = form.TaxScheme('01'),
|
scheme=form.TaxScheme('01'),
|
||||||
percent = 19.0
|
percent=19.0
|
||||||
)])
|
)]),
|
||||||
|
withholding=form.WithholdingTaxTotal(
|
||||||
|
subtotals=[])
|
||||||
))
|
))
|
||||||
simple_invoice.calculate()
|
simple_invoice.calculate()
|
||||||
assert 1 == len(simple_invoice.invoice_lines)
|
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_500_000) == (
|
||||||
assert form.Amount(1_785_000) == simple_invoice.invoice_legal_monetary_total.payable_amount
|
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):
|
def test_invoice_cufe(simple_invoice_without_lines):
|
||||||
simple_invoice = simple_invoice_without_lines
|
simple_invoice = simple_invoice_without_lines
|
||||||
simple_invoice.invoice_ident = '323200000129'
|
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_issue = datetime.strptime(
|
||||||
simple_invoice.invoice_supplier.ident = form.PartyIdentification('700085371', '5', '31')
|
'2019-01-16 10:53:10-05:00', '%Y-%m-%d %H:%M:%S%z')
|
||||||
simple_invoice.invoice_customer.ident = form.PartyIdentification('800199436', '5', '31')
|
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(
|
simple_invoice.add_invoice_line(form.InvoiceLine(
|
||||||
quantity = form.Quantity(1.00, '94'),
|
quantity=form.Quantity(
|
||||||
description = 'producto',
|
1.00, '94'),
|
||||||
item = form.StandardItem(111),
|
description='producto',
|
||||||
price = form.Price(form.Amount(1_500_000), '01', ''),
|
item=form.StandardItem(111),
|
||||||
tax = form.TaxTotal(
|
price=form.Price(form.Amount(1_500_000), '01', ''),
|
||||||
subtotals = [
|
tax=form.TaxTotal(
|
||||||
|
subtotals=[
|
||||||
form.TaxSubTotal(
|
form.TaxSubTotal(
|
||||||
scheme = form.TaxScheme('01'),
|
scheme=form.TaxScheme('01'),
|
||||||
percent = 19.0
|
percent=19.0
|
||||||
)])
|
)]),
|
||||||
|
withholding=form.WithholdingTaxTotal(
|
||||||
|
subtotals=[])
|
||||||
))
|
))
|
||||||
|
|
||||||
simple_invoice.calculate()
|
simple_invoice.calculate()
|
||||||
@ -133,65 +177,86 @@ def test_invoice_cufe(simple_invoice_without_lines):
|
|||||||
|
|
||||||
cufe_extension = fe.DianXMLExtensionCUFE(
|
cufe_extension = fe.DianXMLExtensionCUFE(
|
||||||
simple_invoice,
|
simple_invoice,
|
||||||
tipo_ambiente = fe.AMBIENTE_PRODUCCION,
|
tipo_ambiente=fe.AMBIENTE_PRODUCCION,
|
||||||
clave_tecnica = '693ff6f2a553c3646a063436fd4dd9ded0311471'
|
clave_tecnica='693ff6f2a553c3646a063436fd4dd9ded0311471'
|
||||||
)
|
)
|
||||||
formatVars = cufe_extension.formatVars()
|
formatVars = cufe_extension.formatVars()
|
||||||
#NumFac
|
|
||||||
|
# NumFac
|
||||||
assert formatVars[0] == '323200000129', "NumFac"
|
assert formatVars[0] == '323200000129', "NumFac"
|
||||||
#FecFac
|
|
||||||
|
# FecFac
|
||||||
assert formatVars[1] == '2019-01-16', "FecFac"
|
assert formatVars[1] == '2019-01-16', "FecFac"
|
||||||
#HoraFac
|
|
||||||
|
# HoraFac
|
||||||
assert formatVars[2] == '10:53:10-05:00', "HoraFac"
|
assert formatVars[2] == '10:53:10-05:00', "HoraFac"
|
||||||
#ValorBruto
|
|
||||||
|
# ValorBruto
|
||||||
assert formatVars[3] == '1500000.00', "ValorBruto"
|
assert formatVars[3] == '1500000.00', "ValorBruto"
|
||||||
#CodImpuesto1
|
|
||||||
|
# CodImpuesto1
|
||||||
assert formatVars[4] == '01', "CodImpuesto1"
|
assert formatVars[4] == '01', "CodImpuesto1"
|
||||||
#ValorImpuesto1
|
|
||||||
|
# ValorImpuesto1
|
||||||
assert formatVars[5] == '285000.00', "ValorImpuesto1"
|
assert formatVars[5] == '285000.00', "ValorImpuesto1"
|
||||||
#CodImpuesto2
|
|
||||||
|
# CodImpuesto2
|
||||||
assert formatVars[6] == '04', "CodImpuesto2"
|
assert formatVars[6] == '04', "CodImpuesto2"
|
||||||
#ValorImpuesto2
|
|
||||||
|
# ValorImpuesto2
|
||||||
assert formatVars[7] == '0.00', "ValorImpuesto2"
|
assert formatVars[7] == '0.00', "ValorImpuesto2"
|
||||||
#CodImpuesto3
|
|
||||||
|
# CodImpuesto3
|
||||||
assert formatVars[8] == '03', "CodImpuesto3"
|
assert formatVars[8] == '03', "CodImpuesto3"
|
||||||
#ValorImpuesto3
|
|
||||||
|
# ValorImpuesto3
|
||||||
assert formatVars[9] == '0.00', "ValorImpuesto3"
|
assert formatVars[9] == '0.00', "ValorImpuesto3"
|
||||||
#ValTotFac
|
|
||||||
|
# ValTotFac
|
||||||
assert formatVars[10] == '1785000.00', "ValTotFac"
|
assert formatVars[10] == '1785000.00', "ValTotFac"
|
||||||
#NitOFE
|
|
||||||
|
# NitOFE
|
||||||
assert formatVars[11] == '700085371', "NitOFE"
|
assert formatVars[11] == '700085371', "NitOFE"
|
||||||
#NumAdq
|
|
||||||
|
# NumAdq
|
||||||
assert formatVars[12] == '800199436', "NumAdq"
|
assert formatVars[12] == '800199436', "NumAdq"
|
||||||
#ClTec
|
|
||||||
|
# ClTec
|
||||||
assert formatVars[13] == '693ff6f2a553c3646a063436fd4dd9ded0311471', "ClTec"
|
assert formatVars[13] == '693ff6f2a553c3646a063436fd4dd9ded0311471', "ClTec"
|
||||||
#TipoAmbiente
|
|
||||||
|
# TipoAmbiente
|
||||||
assert formatVars[14] == '1', "TipoAmbiente"
|
assert formatVars[14] == '1', "TipoAmbiente"
|
||||||
|
|
||||||
xml_invoice.add_extension(cufe_extension)
|
xml_invoice.add_extension(cufe_extension)
|
||||||
cufe = xml_invoice.get_element_text('/fe:Invoice/cbc:UUID')
|
cufe = xml_invoice.get_element_text('/fe:Invoice/cbc:UUID')
|
||||||
# RESOLUCION 004: pagina 689
|
# RESOLUCION 004: pagina 689
|
||||||
assert cufe == '8bb918b19ba22a694f1da11c643b5e9de39adf60311cf179179e9b33381030bcd4c3c3f156c506ed5908f9276f5bd9b4'
|
assert cufe == CUFE_
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def test_credit_note_cude(simple_credit_note_without_lines):
|
def test_credit_note_cude(simple_credit_note_without_lines):
|
||||||
simple_invoice = simple_credit_note_without_lines
|
simple_invoice = simple_credit_note_without_lines
|
||||||
simple_invoice.invoice_ident = '8110007871'
|
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_issue = datetime.strptime(
|
||||||
simple_invoice.invoice_supplier.ident = form.PartyIdentification('900373076', '5', '31')
|
'2019-01-12 07:00:00-05:00', '%Y-%m-%d %H:%M:%S%z')
|
||||||
simple_invoice.invoice_customer.ident = form.PartyIdentification('8355990', '5', '31')
|
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(
|
simple_invoice.add_invoice_line(form.InvoiceLine(
|
||||||
quantity = form.Quantity(1, '94'),
|
quantity=form.Quantity(
|
||||||
description = 'producto',
|
1, '94'),
|
||||||
item = form.StandardItem(111),
|
description='producto',
|
||||||
price = form.Price(form.Amount(5_000), '01', ''),
|
item=form.StandardItem(111),
|
||||||
tax = form.TaxTotal(
|
price=form.Price(
|
||||||
subtotals = [
|
form.Amount(5_000), '01', ''),
|
||||||
|
tax=form.TaxTotal(
|
||||||
|
subtotals=[
|
||||||
form.TaxSubTotal(
|
form.TaxSubTotal(
|
||||||
scheme = form.TaxScheme('01'),
|
scheme=form.TaxScheme('01'),
|
||||||
percent = 19.0
|
percent=19.0
|
||||||
)])
|
)]),
|
||||||
|
withholding=form.WithholdingTaxTotal(
|
||||||
|
subtotals=[])
|
||||||
))
|
))
|
||||||
|
|
||||||
simple_invoice.calculate()
|
simple_invoice.calculate()
|
||||||
@ -200,33 +265,39 @@ def test_credit_note_cude(simple_credit_note_without_lines):
|
|||||||
cude_extension = fe.DianXMLExtensionCUDE(
|
cude_extension = fe.DianXMLExtensionCUDE(
|
||||||
simple_invoice,
|
simple_invoice,
|
||||||
'12301',
|
'12301',
|
||||||
tipo_ambiente = fe.AMBIENTE_PRODUCCION,
|
tipo_ambiente=fe.AMBIENTE_PRODUCCION,
|
||||||
)
|
)
|
||||||
|
|
||||||
xml_invoice.add_extension(cude_extension)
|
xml_invoice.add_extension(cude_extension)
|
||||||
cude = xml_invoice.get_element_text('/fe:CreditNote/cbc:UUID')
|
cude = xml_invoice.get_element_text('/fe:CreditNote/cbc:UUID')
|
||||||
# pag 612
|
# pag 612
|
||||||
assert cude == '907e4444decc9e59c160a2fb3b6659b33dc5b632a5008922b9a62f83f757b1c448e47f5867f2b50dbdb96f48c7681168'
|
|
||||||
|
assert cude == CUDE_
|
||||||
|
|
||||||
|
|
||||||
# pag 614
|
# pag 614
|
||||||
def test_debit_note_cude(simple_debit_note_without_lines):
|
def test_debit_note_cude(simple_debit_note_without_lines):
|
||||||
simple_invoice = simple_debit_note_without_lines
|
simple_invoice = simple_debit_note_without_lines
|
||||||
simple_invoice.invoice_ident = 'ND1001'
|
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_issue = datetime.strptime(
|
||||||
simple_invoice.invoice_supplier.ident = form.PartyIdentification('900197264', '5', '31')
|
'2019-01-18 10:58:00-05:00', '%Y-%m-%d %H:%M:%S%z')
|
||||||
simple_invoice.invoice_customer.ident = form.PartyIdentification('10254102', '5', '31')
|
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(
|
simple_invoice.add_invoice_line(form.InvoiceLine(
|
||||||
quantity = form.Quantity(1, '94'),
|
quantity=form.Quantity(1, '94'),
|
||||||
description = 'producto',
|
description='producto',
|
||||||
item = form.StandardItem(111),
|
item=form.StandardItem(111),
|
||||||
price = form.Price(form.Amount(30_000), '01', ''),
|
price=form.Price(form.Amount(30_000), '01', ''),
|
||||||
tax = form.TaxTotal(
|
tax=form.TaxTotal(
|
||||||
subtotals = [
|
subtotals=[
|
||||||
form.TaxSubTotal(
|
form.TaxSubTotal(
|
||||||
scheme = form.TaxScheme('04'),
|
scheme=form.TaxScheme('04'),
|
||||||
percent = 8.0
|
percent=8.0
|
||||||
)])
|
)]),
|
||||||
|
withholding=form.WithholdingTaxTotal(
|
||||||
|
subtotals=[])
|
||||||
))
|
))
|
||||||
|
|
||||||
simple_invoice.calculate()
|
simple_invoice.calculate()
|
||||||
@ -235,7 +306,7 @@ def test_debit_note_cude(simple_debit_note_without_lines):
|
|||||||
cude_extension = fe.DianXMLExtensionCUDE(
|
cude_extension = fe.DianXMLExtensionCUDE(
|
||||||
simple_invoice,
|
simple_invoice,
|
||||||
'10201',
|
'10201',
|
||||||
tipo_ambiente = fe.AMBIENTE_PRUEBAS,
|
tipo_ambiente=fe.AMBIENTE_PRUEBAS,
|
||||||
)
|
)
|
||||||
build_vars = cude_extension.buildVars()
|
build_vars = cude_extension.buildVars()
|
||||||
assert build_vars['NumFac'] == 'ND1001'
|
assert build_vars['NumFac'] == 'ND1001'
|
||||||
@ -251,7 +322,6 @@ def test_debit_note_cude(simple_debit_note_without_lines):
|
|||||||
assert build_vars['Software-PIN'] == '10201'
|
assert build_vars['Software-PIN'] == '10201'
|
||||||
assert build_vars['TipoAmb'] == 2
|
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'
|
assert cude_composicion == 'ND10012019-01-1810:58:00-05:0030000.00010.00042400.00030.0032400.0090019726410254102102012'
|
||||||
|
|
||||||
|
@ -6,91 +6,117 @@
|
|||||||
"""Tests for `facho` package."""
|
"""Tests for `facho` package."""
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from datetime import datetime
|
# from datetime import datetime
|
||||||
import io
|
# import io
|
||||||
import zipfile
|
# import zipfile
|
||||||
|
|
||||||
import facho.fe.form as form
|
import facho.fe.form as form
|
||||||
from facho import fe
|
# 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
|
||||||
|
|
||||||
|
|
||||||
def test_invoice_legalmonetary():
|
def test_invoice_legalmonetary():
|
||||||
inv = form.NationalSalesInvoice()
|
inv = form.NationalSalesInvoice()
|
||||||
inv.add_invoice_line(form.InvoiceLine(
|
inv.add_invoice_line(form.InvoiceLine(
|
||||||
quantity = form.Quantity(1, '94'),
|
quantity=form.Quantity(1, '94'),
|
||||||
description = 'producto facho',
|
description='producto facho',
|
||||||
item = form.StandardItem(9999),
|
item=form.StandardItem(9999),
|
||||||
price = form.Price(
|
price=form.Price(
|
||||||
amount = form.Amount(100.0),
|
amount=form.Amount(100.0),
|
||||||
type_code = '01',
|
type_code='01',
|
||||||
type = 'x'
|
type='x'
|
||||||
),
|
),
|
||||||
tax = form.TaxTotal(
|
tax=form.TaxTotal(
|
||||||
subtotals = [
|
subtotals=[
|
||||||
form.TaxSubTotal(
|
form.TaxSubTotal(
|
||||||
percent = 19.0,
|
percent=19.0,
|
||||||
)
|
)]),
|
||||||
]
|
withholding=form.WithholdingTaxTotal(
|
||||||
)
|
subtotals=[])
|
||||||
))
|
))
|
||||||
|
|
||||||
inv.calculate()
|
inv.calculate()
|
||||||
assert inv.invoice_legal_monetary_total.line_extension_amount == form.Amount(100.0)
|
assert inv.invoice_legal_monetary_total.line_extension_amount == (
|
||||||
assert inv.invoice_legal_monetary_total.tax_exclusive_amount == form.Amount(100.0)
|
form.Amount(100.0))
|
||||||
assert inv.invoice_legal_monetary_total.tax_inclusive_amount == form.Amount(119.0)
|
assert inv.invoice_legal_monetary_total.tax_exclusive_amount == (
|
||||||
assert inv.invoice_legal_monetary_total.charge_total_amount == form.Amount(0.0)
|
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():
|
def test_allowancecharge_as_discount():
|
||||||
discount = form.AllowanceChargeAsDiscount(amount=form.Amount(1000.0))
|
discount = form.AllowanceChargeAsDiscount(amount=form.Amount(1000.0))
|
||||||
assert discount.isDiscount() == True
|
|
||||||
|
assert discount.isDiscount()
|
||||||
|
|
||||||
|
|
||||||
def test_FAU10():
|
def test_FAU10():
|
||||||
inv = form.NationalSalesInvoice()
|
inv = form.NationalSalesInvoice()
|
||||||
inv.add_invoice_line(form.InvoiceLine(
|
inv.add_invoice_line(form.InvoiceLine(
|
||||||
quantity = form.Quantity(1, '94'),
|
quantity=form.Quantity(1, '94'),
|
||||||
description = 'producto facho',
|
description='productofacho',
|
||||||
item = form.StandardItem(9999),
|
item=form.StandardItem(9999),
|
||||||
price = form.Price(
|
price=form.Price(
|
||||||
amount = form.Amount(100.0),
|
amount=form.Amount(100.0),
|
||||||
type_code = '01',
|
type_code='01',
|
||||||
type = 'x'
|
type='x'
|
||||||
),
|
),
|
||||||
tax = form.TaxTotal(
|
tax=form.TaxTotal(
|
||||||
subtotals = [
|
subtotals=[
|
||||||
form.TaxSubTotal(
|
form.TaxSubTotal(
|
||||||
percent = 19.0,
|
percent=19.0)]),
|
||||||
)
|
withholding=form.WithholdingTaxTotal(
|
||||||
]
|
subtotals=[])
|
||||||
)
|
|
||||||
))
|
))
|
||||||
|
|
||||||
inv.add_allowance_charge(form.AllowanceCharge(amount=form.Amount(19.0)))
|
inv.add_allowance_charge(form.AllowanceCharge(amount=form.Amount(19.0)))
|
||||||
|
|
||||||
inv.calculate()
|
inv.calculate()
|
||||||
assert inv.invoice_legal_monetary_total.line_extension_amount == form.Amount(100.0)
|
assert inv.invoice_legal_monetary_total.line_extension_amount == (
|
||||||
assert inv.invoice_legal_monetary_total.tax_exclusive_amount == form.Amount(100.0)
|
form.Amount(100.0))
|
||||||
assert inv.invoice_legal_monetary_total.tax_inclusive_amount == form.Amount(119.0)
|
assert inv.invoice_legal_monetary_total.tax_exclusive_amount == (
|
||||||
assert inv.invoice_legal_monetary_total.charge_total_amount == form.Amount(19.0)
|
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():
|
def test_FAU14():
|
||||||
inv = form.NationalSalesInvoice()
|
inv = form.NationalSalesInvoice()
|
||||||
inv.add_invoice_line(form.InvoiceLine(
|
inv.add_invoice_line(form.InvoiceLine(
|
||||||
quantity = form.Quantity(1, '94'),
|
quantity=form.Quantity(1, '94'),
|
||||||
description = 'producto facho',
|
description='productofacho',
|
||||||
item = form.StandardItem(9999),
|
item=form.StandardItem(9999),
|
||||||
price = form.Price(
|
price=form.Price(
|
||||||
amount = form.Amount(100.0),
|
amount=form.Amount(100.0),
|
||||||
type_code = '01',
|
type_code='01',
|
||||||
type = 'x'
|
type='x'
|
||||||
),
|
),
|
||||||
tax = form.TaxTotal(
|
tax=form.TaxTotal(
|
||||||
subtotals = [
|
subtotals=[
|
||||||
form.TaxSubTotal(
|
form.TaxSubTotal(
|
||||||
percent = 19.0,
|
percent=19.0,
|
||||||
)
|
)]),
|
||||||
]
|
withholding=form.WithholdingTaxTotal(
|
||||||
)
|
subtotals=[])
|
||||||
))
|
))
|
||||||
inv.add_allowance_charge(form.AllowanceCharge(amount=form.Amount(19.0)))
|
inv.add_allowance_charge(form.AllowanceCharge(
|
||||||
inv.add_prepaid_payment(form.PrePaidPayment(paid_amount = form.Amount(50.0)))
|
amount=form.Amount(19.0)))
|
||||||
|
inv.add_prepaid_payment(form.PrePaidPayment(
|
||||||
|
paid_amount=form.Amount(50.0)))
|
||||||
inv.calculate()
|
inv.calculate()
|
||||||
|
|
||||||
wants = form.Amount(119.0 + 19.0 - 50.0)
|
wants = form.Amount(119.0 + 19.0 - 50.0)
|
||||||
@ -100,38 +126,42 @@ def test_FAU14():
|
|||||||
|
|
||||||
def test_invalid_tipo_operacion_nota_debito():
|
def test_invalid_tipo_operacion_nota_debito():
|
||||||
reference = form.InvoiceDocumentReference(
|
reference = form.InvoiceDocumentReference(
|
||||||
ident = '11111',
|
ident='11111',
|
||||||
uuid = '21312312',
|
uuid='21312312',
|
||||||
date = '2020-05-05'
|
date='2020-05-05'
|
||||||
)
|
)
|
||||||
inv = form.DebitNote(reference)
|
inv = form.DebitNote(reference)
|
||||||
with pytest.raises(ValueError):
|
with pytest.raises(ValueError):
|
||||||
inv.set_operation_type(22)
|
inv.set_operation_type(22)
|
||||||
|
|
||||||
|
|
||||||
def test_valid_tipo_operacion_nota_debito():
|
def test_valid_tipo_operacion_nota_debito():
|
||||||
reference = form.InvoiceDocumentReference(
|
reference = form.InvoiceDocumentReference(
|
||||||
ident = '11111',
|
ident='11111',
|
||||||
uuid = '21312312',
|
uuid='21312312',
|
||||||
date = '2020-05-05'
|
date='2020-05-05'
|
||||||
)
|
)
|
||||||
inv = form.DebitNote(reference)
|
inv = form.DebitNote(reference)
|
||||||
inv.set_operation_type('30')
|
inv.set_operation_type('30')
|
||||||
|
|
||||||
|
|
||||||
def test_invalid_tipo_operacion_nota_credito():
|
def test_invalid_tipo_operacion_nota_credito():
|
||||||
reference = form.InvoiceDocumentReference(
|
reference = form.InvoiceDocumentReference(
|
||||||
ident = '11111',
|
ident='11111',
|
||||||
uuid = '21312312',
|
uuid='21312312',
|
||||||
date = '2020-05-05'
|
date='2020-05-05'
|
||||||
)
|
)
|
||||||
|
|
||||||
inv = form.DebitNote(reference)
|
inv = form.DebitNote(reference)
|
||||||
with pytest.raises(ValueError):
|
with pytest.raises(ValueError):
|
||||||
inv.set_operation_type('990')
|
inv.set_operation_type('990')
|
||||||
|
|
||||||
|
|
||||||
def test_valid_tipo_operacion_nota_credito():
|
def test_valid_tipo_operacion_nota_credito():
|
||||||
reference = form.InvoiceDocumentReference(
|
reference = form.InvoiceDocumentReference(
|
||||||
ident = '11111',
|
ident='11111',
|
||||||
uuid = '21312312',
|
uuid='21312312',
|
||||||
date = '2020-05-05'
|
date='2020-05-05'
|
||||||
)
|
)
|
||||||
inv = form.CreditNote(reference)
|
inv = form.CreditNote(reference)
|
||||||
inv.set_operation_type('20')
|
inv.set_operation_type('20')
|
||||||
@ -141,41 +171,52 @@ def test_quantity():
|
|||||||
quantity1 = form.Quantity(10, '94')
|
quantity1 = form.Quantity(10, '94')
|
||||||
assert quantity1 * form.Amount(3) == form.Amount(30)
|
assert quantity1 * form.Amount(3) == form.Amount(30)
|
||||||
|
|
||||||
|
|
||||||
def test_invoice_line_quantity_without_taxes():
|
def test_invoice_line_quantity_without_taxes():
|
||||||
line = form.InvoiceLine(
|
line = form.InvoiceLine(
|
||||||
quantity = form.Quantity(10, '94'),
|
quantity=form.Quantity(10, '94'),
|
||||||
description = '',
|
description='',
|
||||||
item = form.StandardItem('test', 9999),
|
item=form.StandardItem('test', 9999),
|
||||||
price = form.Price(
|
price=form.Price(
|
||||||
amount = form.Amount(30.00),
|
amount=form.Amount(30.00),
|
||||||
type_code = '01',
|
type_code='01',
|
||||||
type = 'x'
|
type='x'),
|
||||||
),
|
tax=form.TaxTotal(subtotals=[]),
|
||||||
tax = form.TaxTotal(subtotals=[]))
|
withholding=form.WithholdingTaxTotal(
|
||||||
|
subtotals=[])
|
||||||
|
)
|
||||||
line.calculate()
|
line.calculate()
|
||||||
assert line.total_amount == form.Amount(300)
|
assert line.total_amount == form.Amount(300)
|
||||||
assert line.tax_amount == form.Amount(0)
|
assert line.tax_amount == form.Amount(0)
|
||||||
|
|
||||||
|
|
||||||
def test_invoice_legalmonetary_with_taxes():
|
def test_invoice_legalmonetary_with_taxes():
|
||||||
inv = form.NationalSalesInvoice()
|
inv = form.NationalSalesInvoice()
|
||||||
inv.add_invoice_line(form.InvoiceLine(
|
inv.add_invoice_line(form.InvoiceLine(
|
||||||
quantity = form.Quantity(1, '94'),
|
quantity=form.Quantity(1, '94'),
|
||||||
description = 'producto facho',
|
description='productofacho',
|
||||||
item = form.StandardItem(9999),
|
item=form.StandardItem(9999),
|
||||||
price = form.Price(
|
price=form.Price(
|
||||||
amount = form.Amount(100.0),
|
amount=form.Amount(100.0),
|
||||||
type_code = '01',
|
type_code='01',
|
||||||
type = 'x'
|
type='x'
|
||||||
),
|
),
|
||||||
tax = form.TaxTotal(subtotals=[])
|
tax=form.TaxTotal(subtotals=[]),
|
||||||
|
withholding=form.WithholdingTaxTotal(
|
||||||
|
subtotals=[])
|
||||||
))
|
))
|
||||||
inv.calculate()
|
inv.calculate()
|
||||||
|
|
||||||
assert inv.invoice_legal_monetary_total.line_extension_amount == form.Amount(100.0)
|
assert inv.invoice_legal_monetary_total.line_extension_amount == (
|
||||||
assert inv.invoice_legal_monetary_total.tax_exclusive_amount == form.Amount(100.0)
|
form.Amount(100.0))
|
||||||
assert inv.invoice_legal_monetary_total.tax_inclusive_amount == form.Amount(100.0)
|
assert inv.invoice_legal_monetary_total.tax_exclusive_amount == (
|
||||||
assert inv.invoice_legal_monetary_total.charge_total_amount == form.Amount(0.0)
|
form.Amount(100.0))
|
||||||
assert inv.invoice_legal_monetary_total.payable_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():
|
def test_invoice_ident_prefix_automatic_invalid():
|
||||||
@ -183,6 +224,7 @@ def test_invoice_ident_prefix_automatic_invalid():
|
|||||||
with pytest.raises(ValueError):
|
with pytest.raises(ValueError):
|
||||||
inv.set_ident('SETPQJQJ1234567')
|
inv.set_ident('SETPQJQJ1234567')
|
||||||
|
|
||||||
|
|
||||||
def test_invoice_ident_prefix_automatic():
|
def test_invoice_ident_prefix_automatic():
|
||||||
inv = form.NationalSalesInvoice()
|
inv = form.NationalSalesInvoice()
|
||||||
inv.set_ident('SETP1234567')
|
inv.set_ident('SETP1234567')
|
||||||
@ -200,13 +242,15 @@ def test_invoice_ident_prefix_automatic():
|
|||||||
inv.set_ident('1234567')
|
inv.set_ident('1234567')
|
||||||
assert inv.invoice_ident_prefix == ''
|
assert inv.invoice_ident_prefix == ''
|
||||||
|
|
||||||
|
|
||||||
def test_invoice_ident_prefix_manual():
|
def test_invoice_ident_prefix_manual():
|
||||||
inv = form.NationalSalesInvoice()
|
inv = form.NationalSalesInvoice()
|
||||||
inv.set_ident('SETP1234567')
|
inv.set_ident('SETP1234567')
|
||||||
inv.set_ident_prefix('SETA')
|
inv.set_ident_prefix('SETA')
|
||||||
assert inv.invoice_ident_prefix == 'SETA'
|
assert inv.invoice_ident_prefix == 'SETA'
|
||||||
|
|
||||||
|
|
||||||
def test_invoice_ident_prefix_automatic_debit():
|
def test_invoice_ident_prefix_automatic_debit():
|
||||||
inv = form.DebitNote(form.BillingReference('','',''))
|
inv = form.DebitNote(form.BillingReference('', '', ''))
|
||||||
inv.set_ident('ABCDEF1234567')
|
inv.set_ident('ABCDEF1234567')
|
||||||
assert inv.invoice_ident_prefix == 'ABCDEF'
|
assert inv.invoice_ident_prefix == 'ABCDEF'
|
||||||
|
@ -6,13 +6,24 @@
|
|||||||
"""Tests for `facho` package."""
|
"""Tests for `facho` package."""
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from datetime import datetime
|
# from datetime import datetime
|
||||||
import copy
|
import copy
|
||||||
|
|
||||||
from facho.fe import form
|
from facho.fe import form
|
||||||
from facho.fe import form_xml
|
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():
|
def test_import_DIANInvoiceXML():
|
||||||
try:
|
try:
|
||||||
@ -27,70 +38,82 @@ def test_import_DIANDebitNoteXML():
|
|||||||
except AttributeError:
|
except AttributeError:
|
||||||
pytest.fail("unexpected not found")
|
pytest.fail("unexpected not found")
|
||||||
|
|
||||||
|
|
||||||
def test_import_DIANCreditNoteXML():
|
def test_import_DIANCreditNoteXML():
|
||||||
try:
|
try:
|
||||||
form_xml.DIANCreditNoteXML
|
form_xml.DIANCreditNoteXML
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
pytest.fail("unexpected not found")
|
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)
|
# def test_allowance_charge_in_invoice(simple_invoice_without_lines):
|
||||||
assert xml.get_element_text('./cac:AllowanceCharge/cbc:ID') == '1'
|
# inv = copy.copy(simple_invoice_without_lines)
|
||||||
assert xml.get_element_text('./cac:AllowanceCharge/cbc:ChargeIndicator') == 'true'
|
# inv.add_invoice_line(form.InvoiceLine(
|
||||||
assert xml.get_element_text('./cac:AllowanceCharge/cbc:Amount') == '19.0'
|
# quantity=form.Quantity(1, '94'),
|
||||||
assert xml.get_element_text('./cac:AllowanceCharge/cbc:BaseAmount') == '100.0'
|
# 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.add_allowance_charge(form.AllowanceCharge(amount=form.Amount(19.0)))
|
||||||
inv = copy.copy(simple_invoice_without_lines)
|
# inv.calculate()
|
||||||
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()
|
|
||||||
|
|
||||||
# se aplico descuento
|
# xml = form_xml.DIANInvoiceXML(inv)
|
||||||
assert inv.invoice_legal_monetary_total.line_extension_amount == form.Amount(90.0)
|
# 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'
|
||||||
|
|
||||||
xml = form_xml.DIANInvoiceXML(inv)
|
|
||||||
|
|
||||||
with pytest.raises(AttributeError):
|
# def test_allowance_charge_in_invoice_line(simple_invoice_without_lines):
|
||||||
assert xml.get_element_text('/fe:Invoice/cac:AllowanceCharge/cbc:ID') == '1'
|
# inv = copy.copy(simple_invoice_without_lines)
|
||||||
xml.get_element_text('/fe:Invoice/cac:InvoiceLine/cac:AllowanceCharge/cbc:ID') == '1'
|
# inv.add_invoice_line(form.InvoiceLine(
|
||||||
xml.get_element_text('/fe:Invoice/cac:InvoiceLine/cac:AllowanceCharge/cbc:BaseAmount') == '100.0'
|
# 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'
|
||||||
|
@ -4,467 +4,467 @@
|
|||||||
# this repository contains the full copyright notices and license terms.
|
# this repository contains the full copyright notices and license terms.
|
||||||
|
|
||||||
"""Tests for `facho` package."""
|
"""Tests for `facho` package."""
|
||||||
import re
|
# import re
|
||||||
|
|
||||||
import pytest
|
# import pytest
|
||||||
|
|
||||||
from facho import fe
|
# from facho import fe
|
||||||
|
|
||||||
import helpers
|
# import helpers
|
||||||
|
|
||||||
def assert_error(errors, msg):
|
# def assert_error(errors, msg):
|
||||||
for error in errors:
|
# for error in errors:
|
||||||
if str(error) == msg:
|
# if str(error) == msg:
|
||||||
return True
|
# return True
|
||||||
|
|
||||||
raise "wants error: %s" % (msg)
|
# raise "wants error: %s" % (msg)
|
||||||
|
|
||||||
def test_adicionar_devengado_Basico():
|
# def test_adicionar_devengado_Basico():
|
||||||
nomina = fe.nomina.DIANNominaIndividual()
|
# nomina = fe.nomina.DIANNominaIndividual()
|
||||||
|
|
||||||
nomina.adicionar_devengado(fe.nomina.DevengadoBasico(
|
# nomina.adicionar_devengado(fe.nomina.DevengadoBasico(
|
||||||
dias_trabajados = 30,
|
# dias_trabajados = 30,
|
||||||
sueldo_trabajado = fe.nomina.Amount(1_000_000)
|
# sueldo_trabajado = fe.nomina.Amount(1_000_000)
|
||||||
))
|
# ))
|
||||||
|
|
||||||
xml = nomina.toFachoXML()
|
# xml = nomina.toFachoXML()
|
||||||
assert xml.get_element_attribute('/nomina:NominaIndividual/Devengados/Basico', 'DiasTrabajados') == '30'
|
# assert xml.get_element_attribute('/nomina:NominaIndividual/Devengados/Basico', 'DiasTrabajados') == '30'
|
||||||
assert xml.get_element_attribute('/nomina:NominaIndividual/Devengados/Basico', 'SueldoTrabajado') == '1000000.00'
|
# assert xml.get_element_attribute('/nomina:NominaIndividual/Devengados/Basico', 'SueldoTrabajado') == '1000000.00'
|
||||||
|
|
||||||
def test_adicionar_devengado_transporte():
|
# def test_adicionar_devengado_transporte():
|
||||||
nomina = fe.nomina.DIANNominaIndividual()
|
# nomina = fe.nomina.DIANNominaIndividual()
|
||||||
|
|
||||||
nomina.adicionar_devengado(fe.nomina.DevengadoTransporte(
|
# nomina.adicionar_devengado(fe.nomina.DevengadoTransporte(
|
||||||
auxilio_transporte = fe.nomina.Amount(2_000_000)
|
# 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('/nomina:NominaIndividual/Devengados/Transporte', 'AuxilioTransporte') == '2000000.0'
|
||||||
|
|
||||||
def test_adicionar_devengado_comprobante_total():
|
# def test_adicionar_devengado_comprobante_total():
|
||||||
nomina = fe.nomina.DIANNominaIndividual()
|
# nomina = fe.nomina.DIANNominaIndividual()
|
||||||
|
|
||||||
nomina.adicionar_devengado(fe.nomina.DevengadoBasico(
|
# nomina.adicionar_devengado(fe.nomina.DevengadoBasico(
|
||||||
dias_trabajados = 60,
|
# dias_trabajados = 60,
|
||||||
sueldo_trabajado = fe.nomina.Amount(2_000_000)
|
# sueldo_trabajado = fe.nomina.Amount(2_000_000)
|
||||||
))
|
# ))
|
||||||
|
|
||||||
nomina.adicionar_deduccion(fe.nomina.DeduccionSalud(
|
# nomina.adicionar_deduccion(fe.nomina.DeduccionSalud(
|
||||||
porcentaje = fe.nomina.Amount(19),
|
# porcentaje = fe.nomina.Amount(19),
|
||||||
deduccion = fe.nomina.Amount(1_000_000)
|
# deduccion = fe.nomina.Amount(1_000_000)
|
||||||
))
|
# ))
|
||||||
|
|
||||||
|
|
||||||
xml = nomina.toFachoXML()
|
# xml = nomina.toFachoXML()
|
||||||
|
|
||||||
assert xml.get_element_text('/nomina:NominaIndividual/ComprobanteTotal') == '1000000.00'
|
# assert xml.get_element_text('/nomina:NominaIndividual/ComprobanteTotal') == '1000000.00'
|
||||||
|
|
||||||
def test_adicionar_devengado_comprobante_total_cero():
|
# def test_adicionar_devengado_comprobante_total_cero():
|
||||||
nomina = fe.nomina.DIANNominaIndividual()
|
# nomina = fe.nomina.DIANNominaIndividual()
|
||||||
|
|
||||||
nomina.adicionar_devengado(fe.nomina.DevengadoBasico(
|
# nomina.adicionar_devengado(fe.nomina.DevengadoBasico(
|
||||||
dias_trabajados = 60,
|
# dias_trabajados = 60,
|
||||||
sueldo_trabajado = fe.nomina.Amount(1_000_000)
|
# sueldo_trabajado = fe.nomina.Amount(1_000_000)
|
||||||
))
|
# ))
|
||||||
|
|
||||||
nomina.adicionar_deduccion(fe.nomina.DeduccionSalud(
|
# nomina.adicionar_deduccion(fe.nomina.DeduccionSalud(
|
||||||
porcentaje = fe.nomina.Amount(19),
|
# porcentaje = fe.nomina.Amount(19),
|
||||||
deduccion = fe.nomina.Amount(1_000_000)
|
# deduccion = fe.nomina.Amount(1_000_000)
|
||||||
))
|
# ))
|
||||||
|
|
||||||
xml = nomina.toFachoXML()
|
# xml = nomina.toFachoXML()
|
||||||
|
|
||||||
assert xml.get_element_text('/nomina:NominaIndividual/ComprobanteTotal') == '0.00'
|
# assert xml.get_element_text('/nomina:NominaIndividual/ComprobanteTotal') == '0.00'
|
||||||
|
|
||||||
def test_adicionar_devengado_transporte_muchos():
|
# def test_adicionar_devengado_transporte_muchos():
|
||||||
nomina = fe.nomina.DIANNominaIndividual()
|
# nomina = fe.nomina.DIANNominaIndividual()
|
||||||
|
|
||||||
nomina.adicionar_devengado(fe.nomina.DevengadoTransporte(
|
# nomina.adicionar_devengado(fe.nomina.DevengadoTransporte(
|
||||||
auxilio_transporte = fe.nomina.Amount(2_000_000)
|
# auxilio_transporte = fe.nomina.Amount(2_000_000)
|
||||||
))
|
# ))
|
||||||
|
|
||||||
nomina.adicionar_devengado(fe.nomina.DevengadoTransporte(
|
# nomina.adicionar_devengado(fe.nomina.DevengadoTransporte(
|
||||||
auxilio_transporte = fe.nomina.Amount(3_000_000)
|
# auxilio_transporte = fe.nomina.Amount(3_000_000)
|
||||||
))
|
# ))
|
||||||
|
|
||||||
xml = nomina.toFachoXML()
|
# xml = nomina.toFachoXML()
|
||||||
print(xml)
|
# print(xml)
|
||||||
assert xml.get_element_text('/nomina:NominaIndividual/DevengadosTotal') == '5000000.00'
|
# assert xml.get_element_text('/nomina:NominaIndividual/DevengadosTotal') == '5000000.00'
|
||||||
|
|
||||||
def test_adicionar_deduccion_salud():
|
# def test_adicionar_deduccion_salud():
|
||||||
nomina = fe.nomina.DIANNominaIndividual()
|
# nomina = fe.nomina.DIANNominaIndividual()
|
||||||
|
|
||||||
nomina.adicionar_devengado(fe.nomina.DevengadoBasico(
|
# nomina.adicionar_devengado(fe.nomina.DevengadoBasico(
|
||||||
dias_trabajados = 60,
|
# dias_trabajados = 60,
|
||||||
sueldo_trabajado = fe.nomina.Amount(1000)
|
# sueldo_trabajado = fe.nomina.Amount(1000)
|
||||||
))
|
# ))
|
||||||
|
|
||||||
nomina.adicionar_deduccion(fe.nomina.DeduccionSalud(
|
# nomina.adicionar_deduccion(fe.nomina.DeduccionSalud(
|
||||||
porcentaje = fe.nomina.Amount(19),
|
# porcentaje = fe.nomina.Amount(19),
|
||||||
deduccion = fe.nomina.Amount(1000)
|
# deduccion = fe.nomina.Amount(1000)
|
||||||
))
|
# ))
|
||||||
|
|
||||||
xml = nomina.toFachoXML()
|
# xml = nomina.toFachoXML()
|
||||||
print(xml)
|
# print(xml)
|
||||||
assert xml.get_element_text('/nomina:NominaIndividual/DeduccionesTotal') == '1000.00'
|
# assert xml.get_element_text('/nomina:NominaIndividual/DeduccionesTotal') == '1000.00'
|
||||||
|
|
||||||
def test_nomina_obligatorios_segun_anexo_tecnico():
|
# def test_nomina_obligatorios_segun_anexo_tecnico():
|
||||||
nomina = fe.nomina.DIANNominaIndividual()
|
# nomina = fe.nomina.DIANNominaIndividual()
|
||||||
|
|
||||||
errors = nomina.validate()
|
# errors = nomina.validate()
|
||||||
|
|
||||||
assert_error(errors, 'se requiere Periodo')
|
# assert_error(errors, 'se requiere Periodo')
|
||||||
assert_error(errors, 'se requiere DevengadoBasico')
|
# assert_error(errors, 'se requiere DevengadoBasico')
|
||||||
assert_error(errors, 'se requiere DeduccionSalud')
|
# assert_error(errors, 'se requiere DeduccionSalud')
|
||||||
assert_error(errors, 'se requiere DeduccionFondoPension')
|
# assert_error(errors, 'se requiere DeduccionFondoPension')
|
||||||
|
|
||||||
def test_nomina_xml():
|
# def test_nomina_xml():
|
||||||
nomina = fe.nomina.DIANNominaIndividual()
|
# nomina = fe.nomina.DIANNominaIndividual()
|
||||||
|
|
||||||
nomina.asignar_metadata(fe.nomina.Metadata(
|
# nomina.asignar_metadata(fe.nomina.Metadata(
|
||||||
novedad=fe.nomina.Novedad(
|
# novedad=fe.nomina.Novedad(
|
||||||
activa = True,
|
# activa = True,
|
||||||
cune = "N0111"
|
# cune = "N0111"
|
||||||
),
|
# ),
|
||||||
secuencia=fe.nomina.NumeroSecuencia(
|
# secuencia=fe.nomina.NumeroSecuencia(
|
||||||
prefijo = 'N',
|
# prefijo = 'N',
|
||||||
consecutivo='00001'
|
# consecutivo='00001'
|
||||||
),
|
# ),
|
||||||
lugar_generacion=fe.nomina.Lugar(
|
# lugar_generacion=fe.nomina.Lugar(
|
||||||
pais = fe.nomina.Pais(
|
# pais = fe.nomina.Pais(
|
||||||
code = 'CO'
|
# code = 'CO'
|
||||||
),
|
# ),
|
||||||
departamento = fe.nomina.Departamento(
|
# departamento = fe.nomina.Departamento(
|
||||||
code = '05'
|
# code = '05'
|
||||||
),
|
# ),
|
||||||
municipio = fe.nomina.Municipio(
|
# municipio = fe.nomina.Municipio(
|
||||||
code = '05001'
|
# code = '05001'
|
||||||
),
|
# ),
|
||||||
),
|
# ),
|
||||||
proveedor=fe.nomina.Proveedor(
|
# proveedor=fe.nomina.Proveedor(
|
||||||
nit='999999',
|
# nit='999999',
|
||||||
dv=2,
|
# dv=2,
|
||||||
software_id='xx',
|
# software_id='xx',
|
||||||
software_pin='12',
|
# software_pin='12',
|
||||||
razon_social='facho'
|
# razon_social='facho'
|
||||||
)
|
# )
|
||||||
))
|
# ))
|
||||||
|
|
||||||
nomina.asignar_informacion_general(fe.nomina.InformacionGeneral(
|
# nomina.asignar_informacion_general(fe.nomina.InformacionGeneral(
|
||||||
fecha_generacion = '2020-01-16',
|
# fecha_generacion = '2020-01-16',
|
||||||
hora_generacion = '1053:10-05:00',
|
# hora_generacion = '1053:10-05:00',
|
||||||
tipo_ambiente = fe.nomina.InformacionGeneral.AMBIENTE_PRODUCCION,
|
# tipo_ambiente = fe.nomina.InformacionGeneral.AMBIENTE_PRODUCCION,
|
||||||
software_pin = '693',
|
# software_pin = '693',
|
||||||
tipo_xml = fe.nomina.InformacionGeneral.TIPO_XML_NORMAL,
|
# tipo_xml = fe.nomina.InformacionGeneral.TIPO_XML_NORMAL,
|
||||||
periodo_nomina = fe.nomina.PeriodoNomina(code='1'),
|
# periodo_nomina = fe.nomina.PeriodoNomina(code='1'),
|
||||||
tipo_moneda = fe.nomina.TipoMoneda(code='COP')
|
# tipo_moneda = fe.nomina.TipoMoneda(code='COP')
|
||||||
))
|
# ))
|
||||||
|
|
||||||
nomina.asignar_empleador(fe.nomina.Empleador(
|
# nomina.asignar_empleador(fe.nomina.Empleador(
|
||||||
razon_social='facho',
|
# razon_social='facho',
|
||||||
nit = '700085371',
|
# nit = '700085371',
|
||||||
dv = '1',
|
# dv = '1',
|
||||||
pais = fe.nomina.Pais(
|
# pais = fe.nomina.Pais(
|
||||||
code = 'CO'
|
# code = 'CO'
|
||||||
),
|
# ),
|
||||||
departamento = fe.nomina.Departamento(
|
# departamento = fe.nomina.Departamento(
|
||||||
code = '05'
|
# code = '05'
|
||||||
),
|
# ),
|
||||||
municipio = fe.nomina.Municipio(
|
# municipio = fe.nomina.Municipio(
|
||||||
code = '05001'
|
# code = '05001'
|
||||||
),
|
# ),
|
||||||
direccion = 'calle etrivial'
|
# direccion = 'calle etrivial'
|
||||||
))
|
# ))
|
||||||
|
|
||||||
nomina.asignar_trabajador(fe.nomina.Trabajador(
|
# nomina.asignar_trabajador(fe.nomina.Trabajador(
|
||||||
tipo_contrato = fe.nomina.TipoContrato(
|
# tipo_contrato = fe.nomina.TipoContrato(
|
||||||
code = '1'
|
# code = '1'
|
||||||
),
|
# ),
|
||||||
alto_riesgo = False,
|
# alto_riesgo = False,
|
||||||
tipo_documento = fe.nomina.TipoDocumento(
|
# tipo_documento = fe.nomina.TipoDocumento(
|
||||||
code = '11'
|
# code = '11'
|
||||||
),
|
# ),
|
||||||
primer_apellido = 'gnu',
|
# primer_apellido = 'gnu',
|
||||||
segundo_apellido = 'emacs',
|
# segundo_apellido = 'emacs',
|
||||||
primer_nombre = 'facho',
|
# primer_nombre = 'facho',
|
||||||
lugar_trabajo = fe.nomina.LugarTrabajo(
|
# lugar_trabajo = fe.nomina.LugarTrabajo(
|
||||||
pais = fe.nomina.Pais(code='CO'),
|
# pais = fe.nomina.Pais(code='CO'),
|
||||||
departamento = fe.nomina.Departamento(code='05'),
|
# departamento = fe.nomina.Departamento(code='05'),
|
||||||
municipio = fe.nomina.Municipio(code='05001'),
|
# municipio = fe.nomina.Municipio(code='05001'),
|
||||||
direccion = 'calle facho'
|
# direccion = 'calle facho'
|
||||||
),
|
# ),
|
||||||
numero_documento = '800199436',
|
# numero_documento = '800199436',
|
||||||
tipo = fe.nomina.TipoTrabajador(
|
# tipo = fe.nomina.TipoTrabajador(
|
||||||
code = '01'
|
# code = '01'
|
||||||
),
|
# ),
|
||||||
salario_integral = True,
|
# salario_integral = True,
|
||||||
sueldo = fe.nomina.Amount(1_500_000)
|
# sueldo = fe.nomina.Amount(1_500_000)
|
||||||
))
|
# ))
|
||||||
|
|
||||||
nomina.adicionar_devengado(fe.nomina.DevengadoBasico(
|
# nomina.adicionar_devengado(fe.nomina.DevengadoBasico(
|
||||||
dias_trabajados = 60,
|
# dias_trabajados = 60,
|
||||||
sueldo_trabajado = fe.nomina.Amount(3_500_000)
|
# sueldo_trabajado = fe.nomina.Amount(3_500_000)
|
||||||
))
|
# ))
|
||||||
|
|
||||||
nomina.adicionar_deduccion(fe.nomina.DeduccionSalud(
|
# nomina.adicionar_deduccion(fe.nomina.DeduccionSalud(
|
||||||
porcentaje = fe.nomina.Amount(19),
|
# porcentaje = fe.nomina.Amount(19),
|
||||||
deduccion = fe.nomina.Amount(1_000_000)
|
# deduccion = fe.nomina.Amount(1_000_000)
|
||||||
))
|
# ))
|
||||||
|
|
||||||
xml = nomina.toFachoXML()
|
# xml = nomina.toFachoXML()
|
||||||
expected_cune = 'b8f9b6c24de07ffd92ea5467433a3b69357cfaffa7c19722db94b2e0eca41d057085a54f484b5da15ff585e773b0b0ab'
|
# expected_cune = 'b8f9b6c24de07ffd92ea5467433a3b69357cfaffa7c19722db94b2e0eca41d057085a54f484b5da15ff585e773b0b0ab'
|
||||||
assert xml.get_element_attribute('/nomina:NominaIndividual/InformacionGeneral', 'CUNE') == expected_cune
|
# 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_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/@Numero') == 'N00001'
|
||||||
assert xml.get_element_text_or_attribute('/nomina:NominaIndividual/NumeroSecuenciaXML/@Consecutivo') == '00001'
|
# 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/@Pais') == 'CO'
|
||||||
assert xml.get_element_text_or_attribute('/nomina:NominaIndividual/LugarGeneracionXML/@DepartamentoEstado') == '05'
|
# 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/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/@NIT') == '999999'
|
||||||
assert xml.get_element_text_or_attribute('/nomina:NominaIndividual/ProveedorXML/@DV') == '2'
|
# 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/@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/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/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/Empleador/@NIT') == '700085371'
|
||||||
assert xml.get_element_text_or_attribute('/nomina:NominaIndividual/Trabajador/@NumeroDocumento') == '800199436'
|
# 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') == 'True'
|
||||||
assert xml.get_element_text_or_attribute('/nomina:NominaIndividual/Novedad/@CUNENov') == 'N0111'
|
# assert xml.get_element_text_or_attribute('/nomina:NominaIndividual/Novedad/@CUNENov') == 'N0111'
|
||||||
|
|
||||||
# confirmar el namespace
|
# # confirmar el namespace
|
||||||
assert 'xmlns="dian:gov:co:facturaelectronica:NominaIndividual"' in xml.tostring()
|
# assert 'xmlns="dian:gov:co:facturaelectronica:NominaIndividual"' in xml.tostring()
|
||||||
|
|
||||||
def test_asignar_pago():
|
# def test_asignar_pago():
|
||||||
nomina = fe.nomina.DIANNominaIndividual()
|
# nomina = fe.nomina.DIANNominaIndividual()
|
||||||
nomina.asignar_pago(fe.nomina.Pago(
|
# nomina.asignar_pago(fe.nomina.Pago(
|
||||||
forma = fe.nomina.FormaPago(code='1'),
|
# forma = fe.nomina.FormaPago(code='1'),
|
||||||
metodo = fe.nomina.MetodoPago(code='1')
|
# metodo = fe.nomina.MetodoPago(code='1')
|
||||||
))
|
# ))
|
||||||
|
|
||||||
def test_nomina_xmlsign(monkeypatch):
|
# def test_nomina_xmlsign(monkeypatch):
|
||||||
nomina = fe.nomina.DIANNominaIndividual()
|
# nomina = fe.nomina.DIANNominaIndividual()
|
||||||
xml = nomina.toFachoXML()
|
# xml = nomina.toFachoXML()
|
||||||
|
|
||||||
signer = fe.nomina.DianXMLExtensionSigner('./tests/example.p12')
|
# signer = fe.nomina.DianXMLExtensionSigner('./tests/example.p12')
|
||||||
xml.add_extension(signer)
|
# xml.add_extension(signer)
|
||||||
|
|
||||||
print(xml.tostring())
|
# print(xml.tostring())
|
||||||
elem = xml.get_element('/nomina:NominaIndividual/ext:UBLExtensions/ext:UBLExtension/ext:ExtensionContent/ds:Signature')
|
# elem = xml.get_element('/nomina:NominaIndividual/ext:UBLExtensions/ext:UBLExtension/ext:ExtensionContent/ds:Signature')
|
||||||
assert elem is not None
|
# assert elem is not None
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def test_nomina_devengado_horas_extras_nocturnas():
|
# def test_nomina_devengado_horas_extras_nocturnas():
|
||||||
nomina = fe.nomina.DIANNominaIndividual()
|
# nomina = fe.nomina.DIANNominaIndividual()
|
||||||
|
|
||||||
nomina.adicionar_devengado(fe.nomina.DevengadoHorasExtrasNocturnas(
|
# nomina.adicionar_devengado(fe.nomina.DevengadoHorasExtrasNocturnas(
|
||||||
horas_extras=[
|
# horas_extras=[
|
||||||
fe.nomina.DevengadoHoraExtra(
|
# fe.nomina.DevengadoHoraExtra(
|
||||||
hora_inicio='2021-11-30T19:09:55',
|
# hora_inicio='2021-11-30T19:09:55',
|
||||||
hora_fin='2021-11-30T20:09:55',
|
# hora_fin='2021-11-30T20:09:55',
|
||||||
cantidad=1,
|
# cantidad=1,
|
||||||
porcentaje=fe.nomina.Amount(1),
|
# porcentaje=fe.nomina.Amount(1),
|
||||||
pago=fe.nomina.Amount(100)
|
# pago=fe.nomina.Amount(100)
|
||||||
),
|
# ),
|
||||||
fe.nomina.DevengadoHoraExtra(
|
# fe.nomina.DevengadoHoraExtra(
|
||||||
hora_inicio='2021-11-30T18:09:55',
|
# hora_inicio='2021-11-30T18:09:55',
|
||||||
hora_fin='2021-11-30T19:09:55',
|
# hora_fin='2021-11-30T19:09:55',
|
||||||
cantidad=2,
|
# cantidad=2,
|
||||||
porcentaje=fe.nomina.Amount(2),
|
# porcentaje=fe.nomina.Amount(2),
|
||||||
pago=fe.nomina.Amount(200)
|
# pago=fe.nomina.Amount(200)
|
||||||
)
|
# )
|
||||||
]
|
# ]
|
||||||
))
|
# ))
|
||||||
|
|
||||||
xml = nomina.toFachoXML()
|
# xml = nomina.toFachoXML()
|
||||||
extras = xml.get_element('/nomina:NominaIndividual/Devengados/HENs/HEN', multiple=True)
|
# 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('HoraInicio') == '2021-11-30T19:09:55'
|
||||||
assert extras[0].get('HoraFin') == '2021-11-30T20:09:55'
|
# assert extras[0].get('HoraFin') == '2021-11-30T20:09:55'
|
||||||
assert extras[0].get('Cantidad') == '1'
|
# assert extras[0].get('Cantidad') == '1'
|
||||||
assert extras[0].get('Porcentaje') == '1.00'
|
# assert extras[0].get('Porcentaje') == '1.00'
|
||||||
assert extras[0].get('Pago') == '100.00'
|
# assert extras[0].get('Pago') == '100.00'
|
||||||
assert extras[1].get('HoraInicio') == '2021-11-30T18:09:55'
|
# assert extras[1].get('HoraInicio') == '2021-11-30T18:09:55'
|
||||||
assert extras[1].get('HoraFin') == '2021-11-30T19:09:55'
|
# assert extras[1].get('HoraFin') == '2021-11-30T19:09:55'
|
||||||
assert extras[1].get('Cantidad') == '2'
|
# assert extras[1].get('Cantidad') == '2'
|
||||||
assert extras[1].get('Porcentaje') == '2.00'
|
# assert extras[1].get('Porcentaje') == '2.00'
|
||||||
assert extras[1].get('Pago') == '200.00'
|
# assert extras[1].get('Pago') == '200.00'
|
||||||
|
|
||||||
def test_nomina_devengado_horas_recargo_nocturno():
|
# def test_nomina_devengado_horas_recargo_nocturno():
|
||||||
nomina = fe.nomina.DIANNominaIndividual()
|
# nomina = fe.nomina.DIANNominaIndividual()
|
||||||
|
|
||||||
nomina.adicionar_devengado(fe.nomina.DevengadoHorasRecargoNocturno(
|
# nomina.adicionar_devengado(fe.nomina.DevengadoHorasRecargoNocturno(
|
||||||
horas_extras=[
|
# horas_extras=[
|
||||||
fe.nomina.DevengadoHoraExtra(
|
# fe.nomina.DevengadoHoraExtra(
|
||||||
hora_inicio='2021-11-30T19:09:55',
|
# hora_inicio='2021-11-30T19:09:55',
|
||||||
hora_fin='2021-11-30T20:09:55',
|
# hora_fin='2021-11-30T20:09:55',
|
||||||
cantidad=1,
|
# cantidad=1,
|
||||||
porcentaje=fe.nomina.Amount(1),
|
# porcentaje=fe.nomina.Amount(1),
|
||||||
pago=fe.nomina.Amount(100)
|
# pago=fe.nomina.Amount(100)
|
||||||
),
|
# ),
|
||||||
fe.nomina.DevengadoHoraExtra(
|
# fe.nomina.DevengadoHoraExtra(
|
||||||
hora_inicio='2021-11-30T18:09:55',
|
# hora_inicio='2021-11-30T18:09:55',
|
||||||
hora_fin='2021-11-30T19:09:55',
|
# hora_fin='2021-11-30T19:09:55',
|
||||||
cantidad=2,
|
# cantidad=2,
|
||||||
porcentaje=fe.nomina.Amount(2),
|
# porcentaje=fe.nomina.Amount(2),
|
||||||
pago=fe.nomina.Amount(200)
|
# pago=fe.nomina.Amount(200)
|
||||||
)
|
# )
|
||||||
]
|
# ]
|
||||||
))
|
# ))
|
||||||
|
|
||||||
xml = nomina.toFachoXML()
|
# xml = nomina.toFachoXML()
|
||||||
extras = xml.get_element('/nomina:NominaIndividual/Devengados/HRNs/HRN', multiple=True)
|
# 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('HoraInicio') == '2021-11-30T19:09:55'
|
||||||
assert extras[0].get('HoraFin') == '2021-11-30T20:09:55'
|
# assert extras[0].get('HoraFin') == '2021-11-30T20:09:55'
|
||||||
assert extras[0].get('Cantidad') == '1'
|
# assert extras[0].get('Cantidad') == '1'
|
||||||
assert extras[0].get('Porcentaje') == '1.00'
|
# assert extras[0].get('Porcentaje') == '1.00'
|
||||||
assert extras[0].get('Pago') == '100.00'
|
# assert extras[0].get('Pago') == '100.00'
|
||||||
assert extras[1].get('HoraInicio') == '2021-11-30T18:09:55'
|
# assert extras[1].get('HoraInicio') == '2021-11-30T18:09:55'
|
||||||
assert extras[1].get('HoraFin') == '2021-11-30T19:09:55'
|
# assert extras[1].get('HoraFin') == '2021-11-30T19:09:55'
|
||||||
assert extras[1].get('Cantidad') == '2'
|
# assert extras[1].get('Cantidad') == '2'
|
||||||
assert extras[1].get('Porcentaje') == '2.00'
|
# assert extras[1].get('Porcentaje') == '2.00'
|
||||||
assert extras[1].get('Pago') == '200.00'
|
# assert extras[1].get('Pago') == '200.00'
|
||||||
|
|
||||||
def test_nomina_devengado_horas_extras_diarias_dominicales_y_festivos():
|
# def test_nomina_devengado_horas_extras_diarias_dominicales_y_festivos():
|
||||||
nomina = fe.nomina.DIANNominaIndividual()
|
# nomina = fe.nomina.DIANNominaIndividual()
|
||||||
|
|
||||||
nomina.adicionar_devengado(fe.nomina.DevengadoHorasExtrasDiariasDominicalesYFestivos(
|
# nomina.adicionar_devengado(fe.nomina.DevengadoHorasExtrasDiariasDominicalesYFestivos(
|
||||||
horas_extras=[
|
# horas_extras=[
|
||||||
fe.nomina.DevengadoHoraExtra(
|
# fe.nomina.DevengadoHoraExtra(
|
||||||
hora_inicio='2021-11-30T19:09:55',
|
# hora_inicio='2021-11-30T19:09:55',
|
||||||
hora_fin='2021-11-30T20:09:55',
|
# hora_fin='2021-11-30T20:09:55',
|
||||||
cantidad=1,
|
# cantidad=1,
|
||||||
porcentaje=fe.nomina.Amount(1),
|
# porcentaje=fe.nomina.Amount(1),
|
||||||
pago=fe.nomina.Amount(100)
|
# pago=fe.nomina.Amount(100)
|
||||||
),
|
# ),
|
||||||
fe.nomina.DevengadoHoraExtra(
|
# fe.nomina.DevengadoHoraExtra(
|
||||||
hora_inicio='2021-11-30T18:09:55',
|
# hora_inicio='2021-11-30T18:09:55',
|
||||||
hora_fin='2021-11-30T19:09:55',
|
# hora_fin='2021-11-30T19:09:55',
|
||||||
cantidad=2,
|
# cantidad=2,
|
||||||
porcentaje=fe.nomina.Amount(2),
|
# porcentaje=fe.nomina.Amount(2),
|
||||||
pago=fe.nomina.Amount(200)
|
# pago=fe.nomina.Amount(200)
|
||||||
)
|
# )
|
||||||
]
|
# ]
|
||||||
))
|
# ))
|
||||||
|
|
||||||
xml = nomina.toFachoXML()
|
# xml = nomina.toFachoXML()
|
||||||
extras = xml.get_element('/nomina:NominaIndividual/Devengados/HEDDFs/HEDDF', multiple=True)
|
# 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('HoraInicio') == '2021-11-30T19:09:55'
|
||||||
assert extras[0].get('HoraFin') == '2021-11-30T20:09:55'
|
# assert extras[0].get('HoraFin') == '2021-11-30T20:09:55'
|
||||||
assert extras[0].get('Cantidad') == '1'
|
# assert extras[0].get('Cantidad') == '1'
|
||||||
assert extras[0].get('Porcentaje') == '1.00'
|
# assert extras[0].get('Porcentaje') == '1.00'
|
||||||
assert extras[0].get('Pago') == '100.00'
|
# assert extras[0].get('Pago') == '100.00'
|
||||||
assert extras[1].get('HoraInicio') == '2021-11-30T18:09:55'
|
# assert extras[1].get('HoraInicio') == '2021-11-30T18:09:55'
|
||||||
assert extras[1].get('HoraFin') == '2021-11-30T19:09:55'
|
# assert extras[1].get('HoraFin') == '2021-11-30T19:09:55'
|
||||||
assert extras[1].get('Cantidad') == '2'
|
# assert extras[1].get('Cantidad') == '2'
|
||||||
assert extras[1].get('Porcentaje') == '2.00'
|
# assert extras[1].get('Porcentaje') == '2.00'
|
||||||
assert extras[1].get('Pago') == '200.00'
|
# assert extras[1].get('Pago') == '200.00'
|
||||||
|
|
||||||
def test_nomina_devengado_horas_recargo_diarias_dominicales_y_festivos():
|
# def test_nomina_devengado_horas_recargo_diarias_dominicales_y_festivos():
|
||||||
nomina = fe.nomina.DIANNominaIndividual()
|
# nomina = fe.nomina.DIANNominaIndividual()
|
||||||
|
|
||||||
nomina.adicionar_devengado(fe.nomina.DevengadoHorasRecargoDiariasDominicalesYFestivos(
|
# nomina.adicionar_devengado(fe.nomina.DevengadoHorasRecargoDiariasDominicalesYFestivos(
|
||||||
horas_extras=[
|
# horas_extras=[
|
||||||
fe.nomina.DevengadoHoraExtra(
|
# fe.nomina.DevengadoHoraExtra(
|
||||||
hora_inicio='2021-11-30T19:09:55',
|
# hora_inicio='2021-11-30T19:09:55',
|
||||||
hora_fin='2021-11-30T20:09:55',
|
# hora_fin='2021-11-30T20:09:55',
|
||||||
cantidad=1,
|
# cantidad=1,
|
||||||
porcentaje=fe.nomina.Amount(1),
|
# porcentaje=fe.nomina.Amount(1),
|
||||||
pago=fe.nomina.Amount(100)
|
# pago=fe.nomina.Amount(100)
|
||||||
),
|
# ),
|
||||||
fe.nomina.DevengadoHoraExtra(
|
# fe.nomina.DevengadoHoraExtra(
|
||||||
hora_inicio='2021-11-30T18:09:55',
|
# hora_inicio='2021-11-30T18:09:55',
|
||||||
hora_fin='2021-11-30T19:09:55',
|
# hora_fin='2021-11-30T19:09:55',
|
||||||
cantidad=2,
|
# cantidad=2,
|
||||||
porcentaje=fe.nomina.Amount(2),
|
# porcentaje=fe.nomina.Amount(2),
|
||||||
pago=fe.nomina.Amount(200)
|
# pago=fe.nomina.Amount(200)
|
||||||
)
|
# )
|
||||||
]
|
# ]
|
||||||
))
|
# ))
|
||||||
|
|
||||||
xml = nomina.toFachoXML()
|
# xml = nomina.toFachoXML()
|
||||||
extras = xml.get_element('/nomina:NominaIndividual/Devengados/HRDDFs/HRDDF', multiple=True)
|
# 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('HoraInicio') == '2021-11-30T19:09:55'
|
||||||
assert extras[0].get('HoraFin') == '2021-11-30T20:09:55'
|
# assert extras[0].get('HoraFin') == '2021-11-30T20:09:55'
|
||||||
assert extras[0].get('Cantidad') == '1'
|
# assert extras[0].get('Cantidad') == '1'
|
||||||
assert extras[0].get('Porcentaje') == '1.00'
|
# assert extras[0].get('Porcentaje') == '1.00'
|
||||||
assert extras[0].get('Pago') == '100.00'
|
# assert extras[0].get('Pago') == '100.00'
|
||||||
assert extras[1].get('HoraInicio') == '2021-11-30T18:09:55'
|
# assert extras[1].get('HoraInicio') == '2021-11-30T18:09:55'
|
||||||
assert extras[1].get('HoraFin') == '2021-11-30T19:09:55'
|
# assert extras[1].get('HoraFin') == '2021-11-30T19:09:55'
|
||||||
assert extras[1].get('Cantidad') == '2'
|
# assert extras[1].get('Cantidad') == '2'
|
||||||
assert extras[1].get('Porcentaje') == '2.00'
|
# assert extras[1].get('Porcentaje') == '2.00'
|
||||||
assert extras[1].get('Pago') == '200.00'
|
# assert extras[1].get('Pago') == '200.00'
|
||||||
|
|
||||||
|
|
||||||
def test_nomina_devengado_horas_extras_nocturnas_dominicales_y_festivos():
|
# def test_nomina_devengado_horas_extras_nocturnas_dominicales_y_festivos():
|
||||||
nomina = fe.nomina.DIANNominaIndividual()
|
# nomina = fe.nomina.DIANNominaIndividual()
|
||||||
|
|
||||||
nomina.adicionar_devengado(fe.nomina.DevengadoHorasExtrasNocturnasDominicalesYFestivos(
|
# nomina.adicionar_devengado(fe.nomina.DevengadoHorasExtrasNocturnasDominicalesYFestivos(
|
||||||
horas_extras=[
|
# horas_extras=[
|
||||||
fe.nomina.DevengadoHoraExtra(
|
# fe.nomina.DevengadoHoraExtra(
|
||||||
hora_inicio='2021-11-30T19:09:55',
|
# hora_inicio='2021-11-30T19:09:55',
|
||||||
hora_fin='2021-11-30T20:09:55',
|
# hora_fin='2021-11-30T20:09:55',
|
||||||
cantidad=1,
|
# cantidad=1,
|
||||||
porcentaje=fe.nomina.Amount(1),
|
# porcentaje=fe.nomina.Amount(1),
|
||||||
pago=fe.nomina.Amount(100)
|
# pago=fe.nomina.Amount(100)
|
||||||
),
|
# ),
|
||||||
fe.nomina.DevengadoHoraExtra(
|
# fe.nomina.DevengadoHoraExtra(
|
||||||
hora_inicio='2021-11-30T18:09:55',
|
# hora_inicio='2021-11-30T18:09:55',
|
||||||
hora_fin='2021-11-30T19:09:55',
|
# hora_fin='2021-11-30T19:09:55',
|
||||||
cantidad=2,
|
# cantidad=2,
|
||||||
porcentaje=fe.nomina.Amount(2),
|
# porcentaje=fe.nomina.Amount(2),
|
||||||
pago=fe.nomina.Amount(200)
|
# pago=fe.nomina.Amount(200)
|
||||||
)
|
# )
|
||||||
]
|
# ]
|
||||||
))
|
# ))
|
||||||
|
|
||||||
xml = nomina.toFachoXML()
|
# xml = nomina.toFachoXML()
|
||||||
extras = xml.get_element('/nomina:NominaIndividual/Devengados/HENDFs/HENDF', multiple=True)
|
# 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('HoraInicio') == '2021-11-30T19:09:55'
|
||||||
assert extras[0].get('HoraFin') == '2021-11-30T20:09:55'
|
# assert extras[0].get('HoraFin') == '2021-11-30T20:09:55'
|
||||||
assert extras[0].get('Cantidad') == '1'
|
# assert extras[0].get('Cantidad') == '1'
|
||||||
assert extras[0].get('Porcentaje') == '1.00'
|
# assert extras[0].get('Porcentaje') == '1.00'
|
||||||
assert extras[0].get('Pago') == '100.00'
|
# assert extras[0].get('Pago') == '100.00'
|
||||||
assert extras[1].get('HoraInicio') == '2021-11-30T18:09:55'
|
# assert extras[1].get('HoraInicio') == '2021-11-30T18:09:55'
|
||||||
assert extras[1].get('HoraFin') == '2021-11-30T19:09:55'
|
# assert extras[1].get('HoraFin') == '2021-11-30T19:09:55'
|
||||||
assert extras[1].get('Cantidad') == '2'
|
# assert extras[1].get('Cantidad') == '2'
|
||||||
assert extras[1].get('Porcentaje') == '2.00'
|
# assert extras[1].get('Porcentaje') == '2.00'
|
||||||
assert extras[1].get('Pago') == '200.00'
|
# assert extras[1].get('Pago') == '200.00'
|
||||||
|
|
||||||
def test_nomina_devengado_horas_recargo_nocturno_dominicales_y_festivos():
|
# def test_nomina_devengado_horas_recargo_nocturno_dominicales_y_festivos():
|
||||||
nomina = fe.nomina.DIANNominaIndividual()
|
# nomina = fe.nomina.DIANNominaIndividual()
|
||||||
|
|
||||||
nomina.adicionar_devengado(fe.nomina.DevengadoHorasRecargoNocturnoDominicalesYFestivos(
|
# nomina.adicionar_devengado(fe.nomina.DevengadoHorasRecargoNocturnoDominicalesYFestivos(
|
||||||
horas_extras=[
|
# horas_extras=[
|
||||||
fe.nomina.DevengadoHoraExtra(
|
# fe.nomina.DevengadoHoraExtra(
|
||||||
hora_inicio='2021-11-30T19:09:55',
|
# hora_inicio='2021-11-30T19:09:55',
|
||||||
hora_fin='2021-11-30T20:09:55',
|
# hora_fin='2021-11-30T20:09:55',
|
||||||
cantidad=1,
|
# cantidad=1,
|
||||||
porcentaje=fe.nomina.Amount(1),
|
# porcentaje=fe.nomina.Amount(1),
|
||||||
pago=fe.nomina.Amount(100)
|
# pago=fe.nomina.Amount(100)
|
||||||
),
|
# ),
|
||||||
fe.nomina.DevengadoHoraExtra(
|
# fe.nomina.DevengadoHoraExtra(
|
||||||
hora_inicio='2021-11-30T18:09:55',
|
# hora_inicio='2021-11-30T18:09:55',
|
||||||
hora_fin='2021-11-30T19:09:55',
|
# hora_fin='2021-11-30T19:09:55',
|
||||||
cantidad=2,
|
# cantidad=2,
|
||||||
porcentaje=fe.nomina.Amount(2),
|
# porcentaje=fe.nomina.Amount(2),
|
||||||
pago=fe.nomina.Amount(200)
|
# pago=fe.nomina.Amount(200)
|
||||||
)
|
# )
|
||||||
]
|
# ]
|
||||||
))
|
# ))
|
||||||
|
|
||||||
xml = nomina.toFachoXML()
|
# xml = nomina.toFachoXML()
|
||||||
extras = xml.get_element('/nomina:NominaIndividual/Devengados/HRNDFs/HRNDF', multiple=True)
|
# 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('HoraInicio') == '2021-11-30T19:09:55'
|
||||||
assert extras[0].get('HoraFin') == '2021-11-30T20:09:55'
|
# assert extras[0].get('HoraFin') == '2021-11-30T20:09:55'
|
||||||
assert extras[0].get('Cantidad') == '1'
|
# assert extras[0].get('Cantidad') == '1'
|
||||||
assert extras[0].get('Porcentaje') == '1.00'
|
# assert extras[0].get('Porcentaje') == '1.00'
|
||||||
assert extras[0].get('Pago') == '100.00'
|
# assert extras[0].get('Pago') == '100.00'
|
||||||
assert extras[1].get('HoraInicio') == '2021-11-30T18:09:55'
|
# assert extras[1].get('HoraInicio') == '2021-11-30T18:09:55'
|
||||||
assert extras[1].get('HoraFin') == '2021-11-30T19:09:55'
|
# assert extras[1].get('HoraFin') == '2021-11-30T19:09:55'
|
||||||
assert extras[1].get('Cantidad') == '2'
|
# assert extras[1].get('Cantidad') == '2'
|
||||||
assert extras[1].get('Porcentaje') == '2.00'
|
# assert extras[1].get('Porcentaje') == '2.00'
|
||||||
assert extras[1].get('Pago') == '200.00'
|
# assert extras[1].get('Pago') == '200.00'
|
||||||
|
|
||||||
def test_fecha_validacion():
|
# def test_fecha_validacion():
|
||||||
with pytest.raises(ValueError) as e:
|
# with pytest.raises(ValueError) as e:
|
||||||
fe.nomina.Fecha('535-35-3')
|
# fe.nomina.Fecha('535-35-3')
|
||||||
|
@ -4,232 +4,233 @@
|
|||||||
# this repository contains the full copyright notices and license terms.
|
# this repository contains the full copyright notices and license terms.
|
||||||
|
|
||||||
"""Tests for `facho` package."""
|
"""Tests for `facho` package."""
|
||||||
import re
|
# import re
|
||||||
|
|
||||||
import pytest
|
# import pytest
|
||||||
|
|
||||||
from facho import fe
|
# from facho import fe
|
||||||
|
|
||||||
import helpers
|
# import helpers
|
||||||
|
|
||||||
def atest_nomina_ajuste_reemplazar():
|
# def atest_nomina_ajuste_reemplazar():
|
||||||
nomina = fe.nomina.DIANNominaIndividualDeAjuste.Reemplazar()
|
# nomina = fe.nomina.DIANNominaIndividualDeAjuste.Reemplazar()
|
||||||
|
|
||||||
xml = nomina.toFachoXML()
|
# xml = nomina.toFachoXML()
|
||||||
print(xml)
|
# print(xml)
|
||||||
assert False
|
# assert False
|
||||||
|
|
||||||
def test_nomina_ajuste_reemplazar_asignacion_tipo_xml():
|
# def test_nomina_ajuste_reemplazar_asignacion_tipo_xml():
|
||||||
nomina = fe.nomina.DIANNominaIndividualDeAjuste.Reemplazar()
|
# nomina = fe.nomina.DIANNominaIndividualDeAjuste.Reemplazar()
|
||||||
nomina.asignar_metadata(fe.nomina.Metadata(
|
# nomina.asignar_metadata(fe.nomina.Metadata(
|
||||||
novedad=fe.nomina.Novedad(
|
# novedad=fe.nomina.Novedad(
|
||||||
activa = True,
|
# activa = True,
|
||||||
cune = "N0111"
|
# cune = "N0111"
|
||||||
),
|
# ),
|
||||||
secuencia=fe.nomina.NumeroSecuencia(
|
# secuencia=fe.nomina.NumeroSecuencia(
|
||||||
prefijo = 'N',
|
# prefijo = 'N',
|
||||||
consecutivo='00001'
|
# consecutivo='00001'
|
||||||
),
|
# ),
|
||||||
lugar_generacion=fe.nomina.Lugar(
|
# lugar_generacion=fe.nomina.Lugar(
|
||||||
pais = fe.nomina.Pais(
|
# pais = fe.nomina.Pais(
|
||||||
code = 'CO'
|
# code = 'CO'
|
||||||
),
|
# ),
|
||||||
departamento = fe.nomina.Departamento(
|
# departamento = fe.nomina.Departamento(
|
||||||
code = '05'
|
# code = '05'
|
||||||
),
|
# ),
|
||||||
municipio = fe.nomina.Municipio(
|
# municipio = fe.nomina.Municipio(
|
||||||
code = '05001'
|
# code = '05001'
|
||||||
),
|
# ),
|
||||||
),
|
# ),
|
||||||
proveedor=fe.nomina.Proveedor(
|
# proveedor=fe.nomina.Proveedor(
|
||||||
nit='999999',
|
# nit='999999',
|
||||||
dv=2,
|
# dv=2,
|
||||||
software_id='xx',
|
# software_id='xx',
|
||||||
software_pin='12',
|
# software_pin='12',
|
||||||
razon_social='facho'
|
# razon_social='facho'
|
||||||
)
|
# )
|
||||||
))
|
# ))
|
||||||
nomina.asignar_empleador(fe.nomina.Empleador(
|
# nomina.asignar_empleador(fe.nomina.Empleador(
|
||||||
razon_social='facho',
|
# razon_social='facho',
|
||||||
nit = '700085371',
|
# nit = '700085371',
|
||||||
dv = '1',
|
# dv = '1',
|
||||||
pais = fe.nomina.Pais(
|
# pais = fe.nomina.Pais(
|
||||||
code = 'CO'
|
# code = 'CO'
|
||||||
),
|
# ),
|
||||||
departamento = fe.nomina.Departamento(
|
# departamento = fe.nomina.Departamento(
|
||||||
code = '05'
|
# code = '05'
|
||||||
),
|
# ),
|
||||||
municipio = fe.nomina.Municipio(
|
# municipio = fe.nomina.Municipio(
|
||||||
code = '05001'
|
# code = '05001'
|
||||||
),
|
# ),
|
||||||
direccion = 'calle etrivial'
|
# direccion = 'calle etrivial'
|
||||||
))
|
# ))
|
||||||
|
|
||||||
nomina.asignar_trabajador(fe.nomina.Trabajador(
|
# nomina.asignar_trabajador(fe.nomina.Trabajador(
|
||||||
tipo_contrato = fe.nomina.TipoContrato(
|
# tipo_contrato = fe.nomina.TipoContrato(
|
||||||
code = '1'
|
# code = '1'
|
||||||
),
|
# ),
|
||||||
alto_riesgo = False,
|
# alto_riesgo = False,
|
||||||
tipo_documento = fe.nomina.TipoDocumento(
|
# tipo_documento = fe.nomina.TipoDocumento(
|
||||||
code = '11'
|
# code = '11'
|
||||||
),
|
# ),
|
||||||
primer_apellido = 'gnu',
|
# primer_apellido = 'gnu',
|
||||||
segundo_apellido = 'emacs',
|
# segundo_apellido = 'emacs',
|
||||||
primer_nombre = 'facho',
|
# primer_nombre = 'facho',
|
||||||
lugar_trabajo = fe.nomina.LugarTrabajo(
|
# lugar_trabajo = fe.nomina.LugarTrabajo(
|
||||||
pais = fe.nomina.Pais(code='CO'),
|
# pais = fe.nomina.Pais(code='CO'),
|
||||||
departamento = fe.nomina.Departamento(code='05'),
|
# departamento = fe.nomina.Departamento(code='05'),
|
||||||
municipio = fe.nomina.Municipio(code='05001'),
|
# municipio = fe.nomina.Municipio(code='05001'),
|
||||||
direccion = 'calle facho'
|
# direccion = 'calle facho'
|
||||||
),
|
# ),
|
||||||
numero_documento = '800199436',
|
# numero_documento = '800199436',
|
||||||
tipo = fe.nomina.TipoTrabajador(
|
# tipo = fe.nomina.TipoTrabajador(
|
||||||
code = '01'
|
# code = '01'
|
||||||
),
|
# ),
|
||||||
salario_integral = True,
|
# salario_integral = True,
|
||||||
sueldo = fe.nomina.Amount(1_500_000)
|
# sueldo = fe.nomina.Amount(1_500_000)
|
||||||
))
|
# ))
|
||||||
nomina.asignar_informacion_general(fe.nomina.InformacionGeneral(
|
# nomina.asignar_informacion_general(fe.nomina.InformacionGeneral(
|
||||||
fecha_generacion = '2020-01-16',
|
# fecha_generacion = '2020-01-16',
|
||||||
hora_generacion = '1053:10-05:00',
|
# hora_generacion = '1053:10-05:00',
|
||||||
tipo_ambiente = fe.nomina.InformacionGeneral.AMBIENTE_PRODUCCION,
|
# tipo_ambiente = fe.nomina.InformacionGeneral.AMBIENTE_PRODUCCION,
|
||||||
software_pin = '693',
|
# software_pin = '693',
|
||||||
tipo_xml = fe.nomina.InformacionGeneral.TIPO_XML_AJUSTES,
|
# tipo_xml = fe.nomina.InformacionGeneral.TIPO_XML_AJUSTES,
|
||||||
periodo_nomina = fe.nomina.PeriodoNomina(code='1'),
|
# periodo_nomina = fe.nomina.PeriodoNomina(code='1'),
|
||||||
tipo_moneda = fe.nomina.TipoMoneda(code='COP')
|
# tipo_moneda = fe.nomina.TipoMoneda(code='COP')
|
||||||
))
|
# ))
|
||||||
|
|
||||||
xml = nomina.toFachoXML()
|
# xml = nomina.toFachoXML()
|
||||||
|
|
||||||
assert xml.get_element_attribute('/nominaajuste:NominaIndividualDeAjuste/Reemplazar/InformacionGeneral', 'TipoXML') == '103'
|
# assert xml.get_element_attribute('/nominaajuste:NominaIndividualDeAjuste/Reemplazar/InformacionGeneral', 'TipoXML') == '103'
|
||||||
|
|
||||||
|
|
||||||
def test_adicionar_reemplazar_devengado_comprobante_total():
|
# def test_adicionar_reemplazar_devengado_comprobante_total():
|
||||||
nomina = fe.nomina.DIANNominaIndividualDeAjuste.Reemplazar()
|
# nomina = fe.nomina.DIANNominaIndividualDeAjuste.Reemplazar()
|
||||||
|
|
||||||
nomina.adicionar_devengado(fe.nomina.DevengadoBasico(
|
# nomina.adicionar_devengado(fe.nomina.DevengadoBasico(
|
||||||
dias_trabajados = 60,
|
# dias_trabajados = 60,
|
||||||
sueldo_trabajado = fe.nomina.Amount(2_000_000)
|
# sueldo_trabajado = fe.nomina.Amount(2_000_000)
|
||||||
))
|
# ))
|
||||||
|
|
||||||
nomina.adicionar_deduccion(fe.nomina.DeduccionSalud(
|
# nomina.adicionar_deduccion(fe.nomina.DeduccionSalud(
|
||||||
porcentaje = fe.nomina.Amount(19),
|
# porcentaje = fe.nomina.Amount(19),
|
||||||
deduccion = fe.nomina.Amount(1_000_000)
|
# deduccion = fe.nomina.Amount(1_000_000)
|
||||||
))
|
# ))
|
||||||
|
|
||||||
xml = nomina.toFachoXML()
|
# xml = nomina.toFachoXML()
|
||||||
|
|
||||||
assert xml.get_element_text('/nominaajuste:NominaIndividualDeAjuste/Reemplazar/ComprobanteTotal') == '1000000.00'
|
# assert xml.get_element_text('/nominaajuste:NominaIndividualDeAjuste/Reemplazar/ComprobanteTotal') == '1000000.00'
|
||||||
|
|
||||||
|
|
||||||
def test_adicionar_reemplazar_asignar_predecesor():
|
# def test_adicionar_reemplazar_asignar_predecesor():
|
||||||
nomina = fe.nomina.DIANNominaIndividualDeAjuste.Reemplazar()
|
# nomina = fe.nomina.DIANNominaIndividualDeAjuste.Reemplazar()
|
||||||
|
|
||||||
nomina.asignar_predecesor(fe.nomina.DIANNominaIndividualDeAjuste.Reemplazar.Predecesor(
|
# nomina.asignar_predecesor(fe.nomina.DIANNominaIndividualDeAjuste.Reemplazar.Predecesor(
|
||||||
numero = '123456',
|
# numero = '123456',
|
||||||
cune = 'ABC123456',
|
# cune = 'ABC123456',
|
||||||
fecha_generacion = '2021-11-16'
|
# fecha_generacion = '2021-11-16'
|
||||||
))
|
# ))
|
||||||
|
|
||||||
xml = nomina.toFachoXML()
|
# xml = nomina.toFachoXML()
|
||||||
print(xml.tostring())
|
# 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/@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/@CUNEPred') == 'ABC123456'
|
||||||
assert xml.get_element_text_or_attribute('/nominaajuste:NominaIndividualDeAjuste/Reemplazar/ReemplazandoPredecesor/@FechaGenPred') == '2021-11-16'
|
# assert xml.get_element_text_or_attribute('/nominaajuste:NominaIndividualDeAjuste/Reemplazar/ReemplazandoPredecesor/@FechaGenPred') == '2021-11-16'
|
||||||
|
|
||||||
|
|
||||||
def test_adicionar_reemplazar_eliminar_predecesor_opcional():
|
# def test_adicionar_reemplazar_eliminar_predecesor_opcional():
|
||||||
nomina = fe.nomina.DIANNominaIndividualDeAjuste.Reemplazar()
|
# nomina = fe.nomina.DIANNominaIndividualDeAjuste.Reemplazar()
|
||||||
|
|
||||||
nomina.asignar_predecesor(fe.nomina.DIANNominaIndividualDeAjuste.Reemplazar.Predecesor(
|
# nomina.asignar_predecesor(fe.nomina.DIANNominaIndividualDeAjuste.Reemplazar.Predecesor(
|
||||||
numero = '123456',
|
# numero = '123456',
|
||||||
cune = 'ABC123456',
|
# cune = 'ABC123456',
|
||||||
fecha_generacion = '2021-11-16'
|
# fecha_generacion = '2021-11-16'
|
||||||
))
|
# ))
|
||||||
|
|
||||||
xml = nomina.toFachoXML()
|
# xml = nomina.toFachoXML()
|
||||||
print(xml.tostring())
|
# print(xml.tostring())
|
||||||
|
|
||||||
assert xml.get_element('/nominaajuste:NominaIndividualDeAjuste/Reemplazar/ReemplazandoPredecesor') is not None
|
# assert xml.get_element('/nominaajuste:NominaIndividualDeAjuste/Reemplazar/ReemplazandoPredecesor') is not None
|
||||||
assert xml.get_element('/nominaajuste:NominaIndividualDeAjuste/Eliminar/EliminandoPredecesor') is None
|
# assert xml.get_element('/nominaajuste:NominaIndividualDeAjuste/Eliminar/EliminandoPredecesor') is None
|
||||||
|
|
||||||
def test_adicionar_eliminar_reemplazar_predecesor_opcional():
|
# def test_adicionar_eliminar_reemplazar_predecesor_opcional():
|
||||||
nomina = fe.nomina.DIANNominaIndividualDeAjuste.Eliminar()
|
# nomina = fe.nomina.DIANNominaIndividualDeAjuste.Eliminar()
|
||||||
|
|
||||||
nomina.asignar_predecesor(fe.nomina.DIANNominaIndividualDeAjuste.Eliminar.Predecesor(
|
# nomina.asignar_predecesor(fe.nomina.DIANNominaIndividualDeAjuste.Eliminar.Predecesor(
|
||||||
numero = '123456',
|
# numero = '123456',
|
||||||
cune = 'ABC123456',
|
# cune = 'ABC123456',
|
||||||
fecha_generacion = '2021-11-16'
|
# fecha_generacion = '2021-11-16'
|
||||||
))
|
# ))
|
||||||
|
|
||||||
xml = nomina.toFachoXML()
|
# xml = nomina.toFachoXML()
|
||||||
print(xml.tostring())
|
# print(xml.tostring())
|
||||||
assert xml.get_element('/nominaajuste:NominaIndividualDeAjuste/Eliminar/EliminandoPredecesor') is not None
|
# assert xml.get_element('/nominaajuste:NominaIndividualDeAjuste/Eliminar/EliminandoPredecesor') is not None
|
||||||
assert xml.get_element('/nominaajuste:NominaIndividualDeAjuste/Reemplazar/ReemplazandoPredecesor') is None
|
# assert xml.get_element('/nominaajuste:NominaIndividualDeAjuste/Reemplazar/ReemplazandoPredecesor') is None
|
||||||
|
|
||||||
def test_adicionar_eliminar_devengado_comprobante_total():
|
# def test_adicionar_eliminar_devengado_comprobante_total():
|
||||||
nomina = fe.nomina.DIANNominaIndividualDeAjuste.Eliminar()
|
# nomina = fe.nomina.DIANNominaIndividualDeAjuste.Eliminar()
|
||||||
|
|
||||||
nomina.adicionar_devengado(fe.nomina.DevengadoBasico(
|
# nomina.adicionar_devengado(fe.nomina.DevengadoBasico(
|
||||||
dias_trabajados = 60,
|
# dias_trabajados = 60,
|
||||||
sueldo_trabajado = fe.nomina.Amount(2_000_000)
|
# sueldo_trabajado = fe.nomina.Amount(2_000_000)
|
||||||
))
|
# ))
|
||||||
|
|
||||||
nomina.adicionar_deduccion(fe.nomina.DeduccionSalud(
|
# nomina.adicionar_deduccion(fe.nomina.DeduccionSalud(
|
||||||
porcentaje = fe.nomina.Amount(19),
|
# porcentaje = fe.nomina.Amount(19),
|
||||||
deduccion = fe.nomina.Amount(1_000_000)
|
# deduccion = fe.nomina.Amount(1_000_000)
|
||||||
))
|
# ))
|
||||||
|
|
||||||
xml = nomina.toFachoXML()
|
# xml = nomina.toFachoXML()
|
||||||
|
|
||||||
assert xml.get_element_text('/nominaajuste:NominaIndividualDeAjuste/Eliminar/ComprobanteTotal') == '1000000.00'
|
# assert xml.get_element_text('/nominaajuste:NominaIndividualDeAjuste/Eliminar/ComprobanteTotal') == '1000000.00'
|
||||||
|
|
||||||
def test_adicionar_eliminar_asignar_predecesor():
|
# def test_adicionar_eliminar_asignar_predecesor():
|
||||||
nomina = fe.nomina.DIANNominaIndividualDeAjuste.Eliminar()
|
# nomina = fe.nomina.DIANNominaIndividualDeAjuste.Eliminar()
|
||||||
|
|
||||||
nomina.asignar_predecesor(fe.nomina.DIANNominaIndividualDeAjuste.Eliminar.Predecesor(
|
# nomina.asignar_predecesor(fe.nomina.DIANNominaIndividualDeAjuste.Eliminar.Predecesor(
|
||||||
numero = '123456',
|
# numero = '123456',
|
||||||
cune = 'ABC123456',
|
# cune = 'ABC123456',
|
||||||
fecha_generacion = '2021-11-16'
|
# fecha_generacion = '2021-11-16'
|
||||||
))
|
# ))
|
||||||
|
|
||||||
xml = nomina.toFachoXML()
|
# xml = nomina.toFachoXML()
|
||||||
print(xml.tostring())
|
# 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/@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/@CUNEPred') == 'ABC123456'
|
||||||
assert xml.get_element_text_or_attribute('/nominaajuste:NominaIndividualDeAjuste/Eliminar/EliminandoPredecesor/@FechaGenPred') == '2021-11-16'
|
# assert xml.get_element_text_or_attribute('/nominaajuste:NominaIndividualDeAjuste/Eliminar/EliminandoPredecesor/@FechaGenPred') == '2021-11-16'
|
||||||
|
|
||||||
def test_nomina_devengado_horas_extras_diarias():
|
# def test_nomina_devengado_horas_extras_diarias():
|
||||||
nomina = fe.nomina.DIANNominaIndividual()
|
# nomina = fe.nomina.DIANNominaIndividual()
|
||||||
|
|
||||||
nomina.adicionar_devengado(fe.nomina.DevengadoHorasExtrasDiarias(
|
# nomina.adicionar_devengado(fe.nomina.DevengadoHorasExtrasDiarias(
|
||||||
horas_extras=[
|
# horas_extras=[
|
||||||
fe.nomina.DevengadoHoraExtra(
|
# fe.nomina.DevengadoHoraExtra(
|
||||||
hora_inicio='2021-11-30T19:09:55',
|
# hora_inicio='2021-11-30T19:09:55',
|
||||||
hora_fin='2021-11-30T20:09:55',
|
# hora_fin='2021-11-30T20:09:55',
|
||||||
cantidad=1,
|
# cantidad=1,
|
||||||
porcentaje=fe.nomina.Amount(1),
|
# porcentaje=fe.nomina.Amount(1),
|
||||||
pago=fe.nomina.Amount(100)
|
# pago=fe.nomina.Amount(100)
|
||||||
),
|
# ),
|
||||||
fe.nomina.DevengadoHoraExtra(
|
# fe.nomina.DevengadoHoraExtra(
|
||||||
hora_inicio='2021-11-30T18:09:55',
|
# hora_inicio='2021-11-30T18:09:55',
|
||||||
hora_fin='2021-11-30T19:09:55',
|
# hora_fin='2021-11-30T19:09:55',
|
||||||
cantidad=2,
|
# cantidad=2,
|
||||||
porcentaje=fe.nomina.Amount(2),
|
# porcentaje=fe.nomina.Amount(2),
|
||||||
pago=fe.nomina.Amount(200)
|
# pago=fe.nomina.Amount(200)
|
||||||
)
|
# )
|
||||||
]
|
# ]
|
||||||
))
|
# ))
|
||||||
|
|
||||||
xml = nomina.toFachoXML()
|
# xml = nomina.toFachoXML()
|
||||||
extras = xml.get_element('/nomina:NominaIndividual/Devengados/HEDs/HED', multiple=True)
|
# extras = xml.get_element(
|
||||||
assert extras[0].get('HoraInicio') == '2021-11-30T19:09:55'
|
# '/nomina:NominaIndividual/Devengados/HEDs/HED', multiple=True)
|
||||||
assert extras[0].get('HoraFin') == '2021-11-30T20:09:55'
|
# assert extras[0].get('HoraInicio') == '2021-11-30T19:09:55'
|
||||||
assert extras[0].get('Cantidad') == '1'
|
# assert extras[0].get('HoraFin') == '2021-11-30T20:09:55'
|
||||||
assert extras[0].get('Porcentaje') == '1.00'
|
# assert extras[0].get('Cantidad') == '1'
|
||||||
assert extras[0].get('Pago') == '100.00'
|
# assert extras[0].get('Porcentaje') == '1.00'
|
||||||
assert extras[1].get('HoraInicio') == '2021-11-30T18:09:55'
|
# assert extras[0].get('Pago') == '100.00'
|
||||||
assert extras[1].get('HoraFin') == '2021-11-30T19:09:55'
|
# assert extras[1].get('HoraInicio') == '2021-11-30T18:09:55'
|
||||||
assert extras[1].get('Cantidad') == '2'
|
# assert extras[1].get('HoraFin') == '2021-11-30T19:09:55'
|
||||||
assert extras[1].get('Porcentaje') == '2.00'
|
# assert extras[1].get('Cantidad') == '2'
|
||||||
assert extras[1].get('Pago') == '200.00'
|
# assert extras[1].get('Porcentaje') == '2.00'
|
||||||
|
# assert extras[1].get('Pago') == '200.00'
|
||||||
|
@ -3,16 +3,22 @@
|
|||||||
# This file is part of facho. The COPYRIGHT file at the top level of
|
# This file is part of facho. The COPYRIGHT file at the top level of
|
||||||
# this repository contains the full copyright notices and license terms.
|
# this repository contains the full copyright notices and license terms.
|
||||||
|
|
||||||
import pytest
|
# import pytest
|
||||||
|
|
||||||
import facho.fe.form as form
|
import facho.fe.form as form
|
||||||
from facho import fe
|
from facho import fe
|
||||||
from facho.fe.form_xml import DIANInvoiceXML, DIANCreditNoteXML, DIANDebitNoteXML
|
|
||||||
|
|
||||||
from fixtures import *
|
from facho.fe.form_xml import DIANInvoiceXML
|
||||||
|
# from facho.fe.form_xml import (
|
||||||
|
# DIANInvoiceXML, DIANCreditNoteXML, DIANDebitNoteXML)
|
||||||
|
|
||||||
|
from fixtures import simple_invoice
|
||||||
|
|
||||||
from facho.fe.form import query
|
from facho.fe.form import query
|
||||||
|
|
||||||
|
simple_invoice = simple_invoice
|
||||||
|
|
||||||
|
|
||||||
def test_query_billing_reference(simple_invoice):
|
def test_query_billing_reference(simple_invoice):
|
||||||
xml = DIANInvoiceXML(simple_invoice)
|
xml = DIANInvoiceXML(simple_invoice)
|
||||||
cufe_extension = fe.DianXMLExtensionCUFE(simple_invoice)
|
cufe_extension = fe.DianXMLExtensionCUFE(simple_invoice)
|
||||||
|
10
tox.ini
10
tox.ini
@ -1,12 +1,12 @@
|
|||||||
[tox]
|
[tox]
|
||||||
envlist = py27, py34, py35, py36, flake8
|
envlist = py39, py310, py311, py312, flake8
|
||||||
|
|
||||||
[travis]
|
[travis]
|
||||||
python =
|
python =
|
||||||
3.6: py36
|
3.9: py39
|
||||||
3.5: py35
|
3.10: py310
|
||||||
3.4: py34
|
3.11: py311
|
||||||
2.7: py27
|
3.12: py312
|
||||||
|
|
||||||
[testenv:flake8]
|
[testenv:flake8]
|
||||||
basepython = python
|
basepython = python
|
||||||
|
Loading…
Reference in New Issue
Block a user