Compare commits
No commits in common. "main" and "documento_soporte" have entirely different histories.
main
...
documento_
1
.gitignore
vendored
1
.gitignore
vendored
@ -215,4 +215,3 @@ tags
|
||||
pyvenv.cfg
|
||||
.venv
|
||||
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
|
||||
|
||||
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.10 python3.10-distutils python3.10-dev \
|
||||
python3.11 python3.11-distutils python3.11-dev \
|
||||
python3.12 python3-setuptools python3.12-dev \
|
||||
wget \
|
||||
ca-certificates
|
||||
|
||||
RUN wget https://bootstrap.pypa.io/get-pip.py \
|
||||
&& python3.9 get-pip.py pip==23.2.1 --break-system-packages \
|
||||
&& python3.10 get-pip.py pip==23.2.1 --break-system-packages \
|
||||
&& python3.11 get-pip.py pip==23.2.1 --break-system-packages \
|
||||
&& python3.12 get-pip.py pip==23.2.1 --break-system-packages \
|
||||
&& python3.7 get-pip.py pip==22.2.2 \
|
||||
&& python3.8 get-pip.py pip==22.2.2 \
|
||||
&& python3.9 get-pip.py pip==22.2.2 \
|
||||
&& python3.10 get-pip.py pip==22.2.2 \
|
||||
&& rm get-pip.py
|
||||
|
||||
RUN apt-get install -y --no-install-recommends \
|
||||
@ -27,14 +27,14 @@ RUN apt-get install -y --no-install-recommends \
|
||||
build-essential \
|
||||
zip
|
||||
|
||||
RUN python3.7 --version
|
||||
RUN python3.8 --version
|
||||
RUN python3.9 --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.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 --break-system-packages
|
||||
RUN pip3 install tox pytest
|
||||
|
@ -15,7 +15,7 @@ dev-shell:
|
||||
docker run --rm -ti -v "$(PWD):/app" -w /app --name facho-cli facho bash
|
||||
|
||||
test:
|
||||
docker run -t -v $(PWD):/app -w /app facho sh -c 'cd /app; python3.12 setup.py test'
|
||||
docker run -t -v $(PWD):/app -w /app facho sh -c 'cd /app; python3.7 setup.py test'
|
||||
|
||||
tox:
|
||||
docker run -it -v $(PWD)/:/app -w /app facho tox
|
||||
|
@ -14,7 +14,7 @@ from facho.fe import fe
|
||||
from datetime import datetime, date
|
||||
|
||||
# Datos del fomulario del SET de pruebas
|
||||
INVOICE_AUTHORIZATION = '181360000001' # Número suministrado por la Dian en el momento de la creación del SET de Pruebas
|
||||
INVOICE_AUTHORIZATION = '181360000001' #Número suministrado por la Dian en el momento de la creación del SET de Pruebas
|
||||
ID_SOFTWARE = '57bcb6d1-c591-5a90-b80a-cb030ec91440' #Id suministrado por la Dian en el momento de la creación del SET de Pruebas
|
||||
PIN = '19642' #Número creado por la empresa para poder crear el SET de pruebas
|
||||
CLAVE_TECNICA = 'fc9eac422eba16e21ffd8c5f94b3f30a6e38162d' ##Id suministrado por la Dian en el momento de la creación del SET de Pruebas
|
||||
@ -36,7 +36,6 @@ def extensions(inv):
|
||||
'SETP', 990000000, 995000000)#del SET de pruebas
|
||||
return [security_code, authorization_provider, cufe, software_provider, inv_authorization]
|
||||
|
||||
|
||||
def invoice():
|
||||
# factura de venta nacional
|
||||
inv = form.Invoice('01')
|
||||
@ -50,17 +49,16 @@ def invoice():
|
||||
inv.set_operation_type('10')
|
||||
inv.set_supplier(form.Party(
|
||||
legal_name = 'Nombre registrado de la empresa',
|
||||
name='Nombre comercial o él mismo nombre registrado',
|
||||
ident=form.PartyIdentification(
|
||||
'nit_empresa', 'digito_verificación', '31'),
|
||||
name = 'Nombre comercial o él mismo nombre registrado',
|
||||
ident = form.PartyIdentification('nit_empresa', 'digito_verificación', '31'),
|
||||
# obligaciones del contribuyente ver DIAN:FAK26
|
||||
responsability_code=form.Responsability(['ZZ', 'O-14', 'O-48']),
|
||||
responsability_code = form.Responsability(['O-07', 'O-14', 'O-48']),
|
||||
# ver DIAN:FAJ28
|
||||
responsability_regime_code='48',
|
||||
responsability_regime_code = '48',
|
||||
# tipo de organizacion juridica ver DIAN:6.2.3
|
||||
organization_code='1',
|
||||
email="correoempresa@correoempresa.correo",
|
||||
address=form.Address(
|
||||
organization_code = '1',
|
||||
email = "correoempresa@correoempresa.correo",
|
||||
address = form.Address(
|
||||
'', '', form.City('05001', 'Medellín'),
|
||||
form.Country('CO', 'Colombia'),
|
||||
form.CountrySubentity('05', 'Antioquia')),
|
||||
@ -78,43 +76,42 @@ def invoice():
|
||||
'', '', form.City('05001', 'Medellín'),
|
||||
form.Country('CO', 'Colombia'),
|
||||
form.CountrySubentity('05', 'Antioquia')),
|
||||
# tax_scheme = form.TaxScheme('01', 'IVA')
|
||||
#tax_scheme = form.TaxScheme('01', 'IVA')
|
||||
))
|
||||
# asignar metodo de pago
|
||||
inv.set_payment_mean(form.PaymentMean(
|
||||
# metodo de pago ver DIAN:3.4.1
|
||||
id='1',
|
||||
# codigocorrespondientealmediodepagoverDIAN:3.4.2
|
||||
code='20',
|
||||
# fechadevencimientodelafactura
|
||||
due_at=datetime.now(),
|
||||
# identificadornumerico
|
||||
payment_id='2'
|
||||
id = '1',
|
||||
# codigo correspondiente al medio de pago ver DIAN:3.4.2
|
||||
code = '20',
|
||||
# fecha de vencimiento de la factura
|
||||
due_at = datetime.now(),
|
||||
# identificador numerico
|
||||
payment_id = '2'
|
||||
))
|
||||
# adicionar una linea al documento
|
||||
inv.add_invoice_line(
|
||||
form.InvoiceLine(
|
||||
quantity=form.Quantity(int(20.5), '94'),
|
||||
# item general de codigo 999
|
||||
description='productO3',
|
||||
sitem=form.StandardItem('test', 9999),
|
||||
price=form.Price(
|
||||
# precio base del item (sin iva)
|
||||
amount=form.Amount(200.00),
|
||||
# ver DIAN:6.3.5.1
|
||||
type_code='01',
|
||||
type='x'
|
||||
),
|
||||
tax=form.TaxTotal(
|
||||
subtotals=[
|
||||
form.TaxSubTotal(
|
||||
percent=19.00,
|
||||
scheme=form.TaxScheme('01')
|
||||
)]
|
||||
)
|
||||
))
|
||||
inv.add_invoice_line(form.InvoiceLine(
|
||||
quantity = form.Quantity(int(20.5), '94'),
|
||||
# item general de codigo 999
|
||||
description = 'productO3',
|
||||
item = form.StandardItem('test', 9999),
|
||||
price = form.Price(
|
||||
# precio base del item (sin iva)
|
||||
amount = form.Amount(200.00),
|
||||
# ver DIAN:6.3.5.1
|
||||
type_code = '01',
|
||||
type = 'x'
|
||||
),
|
||||
tax = form.TaxTotal(
|
||||
subtotals = [
|
||||
form.TaxSubTotal(
|
||||
percent = 19.00,
|
||||
scheme=form.TaxScheme('01')
|
||||
)
|
||||
]
|
||||
)
|
||||
))
|
||||
return inv
|
||||
|
||||
|
||||
def document_xml():
|
||||
return form_xml.DIANInvoiceXML
|
||||
|
@ -1,127 +1,109 @@
|
||||
# importar libreria de modelos
|
||||
from facho import fe, form_xml
|
||||
import facho.fe.form as form
|
||||
import datetime
|
||||
|
||||
PRIVATE_KEY_PATH = 'ruta a mi llave privada'
|
||||
PRIVATE_PASSPHRASE = 'clave de la llave privada'
|
||||
import facho.fe.form_xml
|
||||
|
||||
PRIVATE_KEY_PATH='ruta a mi llave privada'
|
||||
PRIVATE_PASSPHRASE='clave de la llave privada'
|
||||
|
||||
# consultar las extensiones necesarias
|
||||
def extensions(inv):
|
||||
security_code = fe.DianXMLExtensionSoftwareSecurityCode(
|
||||
'id software', 'pin', inv.invoice_ident)
|
||||
security_code = fe.DianXMLExtensionSoftwareSecurityCode('id software', 'pin', inv.invoice_ident)
|
||||
authorization_provider = fe.DianXMLExtensionAuthorizationProvider()
|
||||
cufe = fe.DianXMLExtensionCUFE(
|
||||
inv, fe.DianXMLExtensionCUFE.AMBIENTE_PRUEBAS,
|
||||
'clave tecnica')
|
||||
cufe = fe.DianXMLExtensionCUFE(inv, fe.DianXMLExtensionCUFE.AMBIENTE_PRUEBAS,
|
||||
'clave tecnica')
|
||||
nit = form.PartyIdentification('nit', '5', '31')
|
||||
software_provider = fe.DianXMLExtensionSoftwareProvider(
|
||||
nit, nit.dv, 'id software')
|
||||
inv_authorization = fe.DianXMLExtensionInvoiceAuthorization(
|
||||
'invoice autorization',
|
||||
datetime(2019, 1, 19),
|
||||
datetime(2030, 1, 19),
|
||||
'SETP', 990000001, 995000000)
|
||||
return [
|
||||
security_code,
|
||||
authorization_provider,
|
||||
cufe, software_provider,
|
||||
inv_authorization
|
||||
]
|
||||
|
||||
software_provider = fe.DianXMLExtensionSoftwareProvider(nit, nit.dv, 'id software')
|
||||
inv_authorization = fe.DianXMLExtensionInvoiceAuthorization('invoice autorization',
|
||||
datetime(2019, 1, 19),
|
||||
datetime(2030, 1, 19),
|
||||
'SETP', 990000001, 995000000)
|
||||
return [security_code, authorization_provider, cufe, software_provider, inv_authorization]
|
||||
|
||||
# generar documento desde modelo a ruta indicada
|
||||
def generate_document(invoice, filepath):
|
||||
xml = form_xml.DIANInvoiceXML(invoice)
|
||||
for extension in extensions(invoice):
|
||||
xml.add_extension(extension)
|
||||
form_xml.utils.DIANWriteSigned(
|
||||
xml, filepath, PRIVATE_KEY_PATH, PRIVATE_PASSPHRASE, True)
|
||||
|
||||
form_xml.utils.DIANWriteSigned(xml, filepath, PRIVATE_KEY_PATH, PRIVATE_PASSPHRASE, True)
|
||||
|
||||
# Modelars las facturas
|
||||
# ...
|
||||
|
||||
# factura de venta nacional
|
||||
inv = form.NationalSalesInvoice()
|
||||
|
||||
# asignar periodo de facturacion
|
||||
inv.set_period(datetime.now(), datetime.now())
|
||||
|
||||
# asignar fecha de emision de la factura
|
||||
|
||||
inv.set_issue(datetime.now())
|
||||
# asignar prefijo y numero del documento
|
||||
|
||||
inv.set_ident('SETP990003033')
|
||||
# asignar tipo de operacion ver DIAN:6.1.5
|
||||
inv.set_operation_type('10')
|
||||
|
||||
# asignar proveedor
|
||||
inv.set_supplier(form.Party(
|
||||
legal_name='FACHOSOS',
|
||||
name='FACHOSOS',
|
||||
ident=form.PartyIdentification('900579212', '5', '31'),
|
||||
legal_name = 'FACHO SOS',
|
||||
name = 'FACHO SOS',
|
||||
ident = form.PartyIdentification('900579212', '5', '31'),
|
||||
# obligaciones del contribuyente ver DIAN:FAK26
|
||||
responsability_code=form.Responsability(['ZZ', 'O-09', 'O-14', 'O-48']),
|
||||
responsability_code = form.Responsability(['O-07', 'O-09', 'O-14', 'O-48']),
|
||||
# ver DIAN:FAJ28
|
||||
responsability_regime_code='48',
|
||||
responsability_regime_code = '48',
|
||||
# tipo de organizacion juridica ver DIAN:6.2.3
|
||||
organization_code='1',
|
||||
email="sdds@sd.com",
|
||||
address=form.Address(
|
||||
name='',
|
||||
street='',
|
||||
city=form.City('05001', 'Medellín'),
|
||||
country=form.Country('CO', 'Colombia'),
|
||||
countrysubentity=form.CountrySubentity('05', 'Antioquia'))
|
||||
organization_code = '1',
|
||||
email = "sdds@sd.com",
|
||||
address = form.Address(
|
||||
name = '',
|
||||
street = '',
|
||||
city = form.City('05001', 'Medellín'),
|
||||
country = form.Country('CO', 'Colombia'),
|
||||
countrysubentity = form.CountrySubentity('05', 'Antioquia'))
|
||||
))
|
||||
|
||||
inv.set_customer(form.Party(
|
||||
legal_name='facho-customer',
|
||||
name='facho-customer',
|
||||
ident=form.PartyIdentification('999999999', '', '13'),
|
||||
responsability_code=form.Responsability(['R-99-PN']),
|
||||
responsability_regime_code='49',
|
||||
organization_code='2',
|
||||
email="sdds@sd.com",
|
||||
address=form.Address(
|
||||
name='',
|
||||
street='',
|
||||
city=form.City('05001', 'Medellín'),
|
||||
country=form.Country('CO', 'Colombia'),
|
||||
countrysubentity=form.CountrySubentity('05', 'Antioquia'))
|
||||
legal_name = 'facho-customer',
|
||||
name = 'facho-customer',
|
||||
ident = form.PartyIdentification('999999999', '', '13'),
|
||||
responsability_code = form.Responsability(['R-99-PN']),
|
||||
responsability_regime_code = '49',
|
||||
organization_code = '2',
|
||||
email = "sdds@sd.com",
|
||||
address = form.Address(
|
||||
name = '',
|
||||
street = '',
|
||||
city = form.City('05001', 'Medellín'),
|
||||
country = form.Country('CO', 'Colombia'),
|
||||
countrysubentity = form.CountrySubentity('05', 'Antioquia'))
|
||||
))
|
||||
# asignar metodo de pago
|
||||
inv.set_payment_mean(form.PaymentMean(
|
||||
# metodo de pago ver DIAN:3.4.1
|
||||
id='1',
|
||||
id = '1',
|
||||
# codigo correspondiente al medio de pago ver DIAN:3.4.2
|
||||
code='10',
|
||||
code = '10',
|
||||
# fecha de vencimiento de la factura
|
||||
due_at=datetime.now(),
|
||||
due_at = datetime.now(),
|
||||
|
||||
# identificador numerico
|
||||
payment_id='1'
|
||||
payment_id = '1'
|
||||
))
|
||||
# adicionar una linea al documento
|
||||
inv.add_invoice_line(form.InvoiceLine(
|
||||
quantity=form.Quantity(1, '94'),
|
||||
description='producto facho',
|
||||
quantity = form.Quantity(1, '94'),
|
||||
description = 'producto facho',
|
||||
# item general de codigo 999
|
||||
item=form.StandardItem('test', 9999),
|
||||
price=form.Price(
|
||||
item = form.StandardItem('test', 9999),
|
||||
price = form.Price(
|
||||
# precio base del tiem
|
||||
amount=form.Amount(100.00),
|
||||
amount = form.Amount(100.00),
|
||||
# ver DIAN:6.3.5.1
|
||||
type_code='01',
|
||||
type='x'
|
||||
type_code = '01',
|
||||
type = 'x'
|
||||
),
|
||||
tax=form.TaxTotal(
|
||||
subtotals=[
|
||||
tax = form.TaxTotal(
|
||||
subtotals = [
|
||||
form.TaxSubTotal(
|
||||
percent=19.00,
|
||||
)]
|
||||
percent = 19.00,
|
||||
)
|
||||
]
|
||||
)
|
||||
))
|
||||
|
||||
|
@ -76,7 +76,7 @@
|
||||
<cac:PartyTaxScheme>
|
||||
<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:TaxLevelCode listName="48">ZZ;O-09;O-14;O-48</cbc:TaxLevelCode>
|
||||
<cbc:TaxLevelCode listName="48">O-07;O-09;O-14;O-48</cbc:TaxLevelCode>
|
||||
<cac:TaxScheme/>
|
||||
</cac:PartyTaxScheme>
|
||||
<cac:Contact>
|
||||
|
@ -96,5 +96,27 @@
|
||||
<SimpleValue>Exclusivo en referencias a documentos (elementos DocumentReference)</SimpleValue>
|
||||
</Value>
|
||||
</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>
|
||||
</gc:CodeList>
|
||||
|
@ -62,12 +62,28 @@
|
||||
<SimpleValue>Régimen simple de tributación</SimpleValue>
|
||||
</Value>
|
||||
</Row>
|
||||
<Row>
|
||||
<Row>
|
||||
<Value ColumnRef="code">
|
||||
<SimpleValue>ZZ</SimpleValue>
|
||||
<SimpleValue>O-48</SimpleValue>
|
||||
</Value>
|
||||
<Value ColumnRef="name">
|
||||
<SimpleValue>No aplica</SimpleValue>
|
||||
<SimpleValue>Impuesto sobre las ventas – IVA</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>
|
||||
</Row>
|
||||
</SimpleCodeList>
|
||||
|
118
facho/fe/fe.py
118
facho/fe/fe.py
@ -1,5 +1,6 @@
|
||||
# This file is part of facho. The COPYRIGHT file at the top level of
|
||||
# this repository contains the full copyright notices and license terms.
|
||||
|
||||
from ..facho import FachoXML, FachoXMLExtension, LXMLBuilder
|
||||
import uuid
|
||||
import xmlsig
|
||||
@ -7,16 +8,13 @@ import xades
|
||||
from datetime import datetime
|
||||
import OpenSSL
|
||||
import zipfile
|
||||
# import warnings
|
||||
import warnings
|
||||
import hashlib
|
||||
from contextlib import contextmanager
|
||||
from .data.dian import codelist
|
||||
from . import form
|
||||
from collections import defaultdict
|
||||
# from pathlib import Path
|
||||
from dateutil import tz
|
||||
|
||||
from cryptography.hazmat.primitives.serialization import pkcs12
|
||||
from pathlib import Path
|
||||
|
||||
AMBIENTE_PRUEBAS = codelist.TipoAmbiente.by_name('Pruebas')['code']
|
||||
AMBIENTE_PRODUCCION = codelist.TipoAmbiente.by_name('Producción')['code']
|
||||
@ -32,7 +30,7 @@ SCHEME_AGENCY_ATTRS = {
|
||||
POLICY_ID = 'https://facturaelectronica.dian.gov.co/politicadefirma/v2/politicadefirmav2.pdf'
|
||||
POLICY_NAME = u'Política de firma para facturas electrónicas de la República de Colombia.'
|
||||
|
||||
Bogota = tz.gettz('America/Bogota')
|
||||
|
||||
# NAMESPACES = {
|
||||
# 'atd': 'urn:oasis:names:specification:ubl:schema:xsd:AttachedDocument-2',
|
||||
# 'nomina': 'dian:gov:co:facturaelectronica:NominaIndividual',
|
||||
@ -57,8 +55,6 @@ Bogota = tz.gettz('America/Bogota')
|
||||
|
||||
|
||||
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',
|
||||
'cac': 'urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2',
|
||||
'cbc': 'urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2',
|
||||
@ -72,11 +68,11 @@ NAMESPACES = {
|
||||
}
|
||||
|
||||
|
||||
|
||||
def fe_from_string(document: str) -> FachoXML:
|
||||
return FeXML.from_string(document)
|
||||
|
||||
|
||||
# from contextlib import contextmanager
|
||||
from contextlib import contextmanager
|
||||
@contextmanager
|
||||
def mock_xades_policy():
|
||||
from mock import patch
|
||||
@ -108,25 +104,22 @@ class FeXML(FachoXML):
|
||||
|
||||
def tostring(self, **kw):
|
||||
# MACHETE(bit4bit) la DIAN espera que la etiqueta raiz no este en un namespace
|
||||
urn_oasis = {
|
||||
'AttachedDocument': 'urn:oasis:names:specification:ubl:schema:xsd:AttachedDocument-2',
|
||||
'Invoice': 'urn:oasis:names:specification:ubl:schema:xsd:Invoice-2',
|
||||
'CreditNote': 'urn:oasis:names:specification:ubl:schema:xsd:CreditNote-2',
|
||||
'ApplicationResponse': 'urn:oasis:names:specification:ubl:schema:xsd:ApplicationResponse-2'
|
||||
}
|
||||
|
||||
root_namespace = self.root_namespace()
|
||||
root_localname = self.root_localname()
|
||||
xmlns_name = {v: k for k, v in NAMESPACES.items()}[root_namespace]
|
||||
|
||||
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)\
|
||||
.replace(xmlns_name + ':', '')\
|
||||
.replace('xmlns:'+xmlns_name, 'xmlns')\
|
||||
.replace(root_namespace, urn_oasis[root_localname])
|
||||
.replace(root_namespace, urn_oasis)
|
||||
|
||||
|
||||
class DianXMLExtensionCUDFE(FachoXMLExtension):
|
||||
def __init__(self, invoice, tipo_ambiente=AMBIENTE_PRUEBAS):
|
||||
|
||||
def __init__(self, invoice, tipo_ambiente = AMBIENTE_PRUEBAS):
|
||||
self.tipo_ambiente = tipo_ambiente
|
||||
self.invoice = invoice
|
||||
|
||||
@ -164,12 +157,10 @@ class DianXMLExtensionCUDFE(FachoXMLExtension):
|
||||
|
||||
# #DIAN 1.8.-2021: FAD03
|
||||
# fachoxml.set_element('./cbc:ProfileID', 'DIAN 2.1: Factura Electrónica de Venta')
|
||||
fachoxml.set_element(
|
||||
'./cbc:ProfileExecutionID', self._tipo_ambiente_int())
|
||||
fachoxml.set_element('./cbc:ProfileExecutionID', self._tipo_ambiente_int())
|
||||
#DIAN 1.7.-2020: FAB36
|
||||
fachoxml.set_element(
|
||||
'./ext:UBLExtensions/ext:UBLExtension/ext:ExtensionContent/sts:DianExtensions/sts:QRCode',
|
||||
self._get_qrcode(cufe))
|
||||
fachoxml.set_element('./ext:UBLExtensions/ext:UBLExtension/ext:ExtensionContent/sts:DianExtensions/sts:QRCode',
|
||||
self._get_qrcode(cufe))
|
||||
|
||||
def issue_time(self, datetime_):
|
||||
return datetime_.strftime('%H:%M:%S-05:00')
|
||||
@ -185,8 +176,7 @@ class DianXMLExtensionCUDFE(FachoXMLExtension):
|
||||
build_vars['HoraFac'] = self.issue_time(invoice.invoice_issue)
|
||||
# PAG 601
|
||||
build_vars['ValorBruto'] = invoice.invoice_legal_monetary_total.line_extension_amount
|
||||
build_vars['ValorTotalPagar'
|
||||
] = invoice.invoice_legal_monetary_total.payable_amount
|
||||
build_vars['ValorTotalPagar'] = invoice.invoice_legal_monetary_total.payable_amount
|
||||
ValorImpuestoPara = defaultdict(lambda: form.Amount(0.0))
|
||||
build_vars['CodImpuesto1'] = '01'
|
||||
build_vars['CodImpuesto2'] = '04'
|
||||
@ -216,8 +206,7 @@ class DianXMLExtensionCUDFE(FachoXMLExtension):
|
||||
|
||||
|
||||
class DianXMLExtensionCUFE(DianXMLExtensionCUDFE):
|
||||
def __init__(
|
||||
self, invoice, clave_tecnica='', tipo_ambiente=AMBIENTE_PRUEBAS):
|
||||
def __init__(self, invoice, clave_tecnica = '', tipo_ambiente = AMBIENTE_PRUEBAS):
|
||||
self.tipo_ambiente = tipo_ambiente
|
||||
self.clave_tecnica = clave_tecnica
|
||||
self.invoice = invoice
|
||||
@ -253,7 +242,6 @@ class DianXMLExtensionCUFE(DianXMLExtensionCUDFE):
|
||||
'%d' % build_vars['TipoAmb'],
|
||||
]
|
||||
|
||||
|
||||
class DianXMLExtensionCUDE(DianXMLExtensionCUDFE):
|
||||
def __init__(self, invoice, software_pin, tipo_ambiente = AMBIENTE_PRUEBAS):
|
||||
self.tipo_ambiente = tipo_ambiente
|
||||
@ -291,7 +279,6 @@ class DianXMLExtensionCUDE(DianXMLExtensionCUDFE):
|
||||
'%d' % build_vars['TipoAmb'],
|
||||
]
|
||||
|
||||
|
||||
class DianXMLExtensionCUDS(DianXMLExtensionCUDFE):
|
||||
def __init__(self, invoice, software_pin, tipo_ambiente = AMBIENTE_PRUEBAS):
|
||||
self.tipo_ambiente = tipo_ambiente
|
||||
@ -325,7 +312,6 @@ class DianXMLExtensionCUDS(DianXMLExtensionCUDFE):
|
||||
'%d' % build_vars['TipoAmb'],
|
||||
]
|
||||
|
||||
|
||||
class DianXMLExtensionSoftwareProvider(FachoXMLExtension):
|
||||
# RESOLUCION 0004: pagina 108
|
||||
|
||||
@ -335,8 +321,7 @@ class DianXMLExtensionSoftwareProvider(FachoXMLExtension):
|
||||
self.id_software = id_software
|
||||
|
||||
def build(self, fexml):
|
||||
software_provider = fexml.fragment(
|
||||
'./ext:UBLExtensions/ext:UBLExtension/ext:ExtensionContent/sts:DianExtensions/sts:SoftwareProvider')
|
||||
software_provider = fexml.fragment('./ext:UBLExtensions/ext:UBLExtension/ext:ExtensionContent/sts:DianExtensions/sts:SoftwareProvider')
|
||||
provider_id_attrs = SCHEME_AGENCY_ATTRS.copy()
|
||||
provider_id_attrs.update({'schemeID': self.dv})
|
||||
#DIAN 1.7.-2020: FAB23
|
||||
@ -366,6 +351,7 @@ class DianXMLExtensionSoftwareSecurityCode(FachoXMLExtension):
|
||||
|
||||
|
||||
class DianXMLExtensionSigner:
|
||||
|
||||
def __init__(self, pkcs12_path, passphrase=None, localpolicy=True):
|
||||
self._pkcs12_data = open(pkcs12_path, 'rb').read()
|
||||
self._passphrase = None
|
||||
@ -376,6 +362,7 @@ class DianXMLExtensionSigner:
|
||||
@classmethod
|
||||
def from_bytes(cls, data, passphrase=None, localpolicy=True):
|
||||
self = cls.__new__(cls)
|
||||
|
||||
self._pkcs12_data = data
|
||||
self._passphrase = None
|
||||
self._localpolicy = localpolicy
|
||||
@ -385,9 +372,7 @@ class DianXMLExtensionSigner:
|
||||
return self
|
||||
|
||||
def _element_extension_content(self, fachoxml):
|
||||
return fachoxml.builder.xpath(
|
||||
fachoxml.root,
|
||||
'./ext:UBLExtensions/ext:UBLExtension[2]/ext:ExtensionContent')
|
||||
return fachoxml.builder.xpath(fachoxml.root, './ext:UBLExtensions/ext:UBLExtension[2]/ext:ExtensionContent')
|
||||
|
||||
def sign_xml_string(self, document):
|
||||
xml = LXMLBuilder.from_string(document)
|
||||
@ -409,6 +394,7 @@ class DianXMLExtensionSigner:
|
||||
)
|
||||
xml.append(signature)
|
||||
|
||||
|
||||
ref = xmlsig.template.add_reference(
|
||||
signature, xmlsig.constants.TransformSha256, uri="", name="xmldsig-%s-ref0" % (id_uuid)
|
||||
)
|
||||
@ -416,16 +402,14 @@ class DianXMLExtensionSigner:
|
||||
|
||||
id_keyinfo = "xmldsig-%s-KeyInfo" % (id_uuid)
|
||||
xmlsig.template.add_reference(
|
||||
signature, xmlsig.constants.TransformSha256, uri="#%s" % (
|
||||
id_keyinfo), name="xmldsig-%s-ref1" % (id_uuid),
|
||||
signature, xmlsig.constants.TransformSha256, uri="#%s" % (id_keyinfo), name="xmldsig-%s-ref1" % (id_uuid),
|
||||
)
|
||||
ki = xmlsig.template.ensure_key_info(signature, name=id_keyinfo)
|
||||
data = xmlsig.template.add_x509_data(ki)
|
||||
xmlsig.template.x509_data_add_certificate(data)
|
||||
xmlsig.template.add_key_value(ki)
|
||||
|
||||
qualifying = xades.template.create_qualifying_properties(
|
||||
signature, 'XadesObjects', 'xades')
|
||||
qualifying = xades.template.create_qualifying_properties(signature, 'XadesObjects', 'xades')
|
||||
xades.utils.ensure_id(qualifying)
|
||||
|
||||
id_props = "xmldsig-%s-signedprops" % (id_uuid)
|
||||
@ -433,12 +417,10 @@ class DianXMLExtensionSigner:
|
||||
signature, xmlsig.constants.TransformSha256, uri="#%s" % (id_props),
|
||||
uri_type="http://uri.etsi.org/01903#SignedProperties"
|
||||
)
|
||||
xmlsig.template.add_transform(
|
||||
props_ref, xmlsig.constants.TransformInclC14N)
|
||||
xmlsig.template.add_transform(props_ref, xmlsig.constants.TransformInclC14N)
|
||||
|
||||
# TODO assert with http://www.sic.gov.co/hora-legal-colombiana
|
||||
props = xades.template.create_signed_properties(
|
||||
qualifying, name=id_props, datetime=datetime.now(tz=Bogota))
|
||||
props = xades.template.create_signed_properties(qualifying, name=id_props, datetime=datetime.now())
|
||||
xades.template.add_claimed_role(props, "supplier")
|
||||
|
||||
policy = xades.policy.GenericPolicyId(
|
||||
@ -446,13 +428,9 @@ class DianXMLExtensionSigner:
|
||||
POLICY_NAME,
|
||||
xmlsig.constants.TransformSha256)
|
||||
ctx = xades.XAdESContext(policy)
|
||||
ctx.load_pkcs12(pkcs12.load_key_and_certificates(
|
||||
self._pkcs12_data,
|
||||
self._passphrase))
|
||||
ctx.load_pkcs12(OpenSSL.crypto.load_pkcs12(self._pkcs12_data,
|
||||
self._passphrase))
|
||||
|
||||
# ctx.load_pkcs12(OpenSSL.crypto.load_pkcs12(
|
||||
# self._pkcs12_data,
|
||||
# self._passphrase))
|
||||
if self._localpolicy:
|
||||
with mock_xades_policy():
|
||||
ctx.sign(signature)
|
||||
@ -460,7 +438,7 @@ class DianXMLExtensionSigner:
|
||||
else:
|
||||
ctx.sign(signature)
|
||||
ctx.verify(signature)
|
||||
# xmlsig take parent root
|
||||
#xmlsig take parent root
|
||||
xml.remove(signature)
|
||||
return signature
|
||||
|
||||
@ -476,21 +454,22 @@ class DianXMLExtensionAuthorizationProvider(FachoXMLExtension):
|
||||
def build(self, fexml):
|
||||
attrs = {'schemeID': '4', 'schemeName': '31'}
|
||||
attrs.update(SCHEME_AGENCY_ATTRS)
|
||||
|
||||
authorization_provider = fexml.fragment('./ext:UBLExtensions/ext:UBLExtension/ext:ExtensionContent/sts:DianExtensions/sts:AuthorizationProvider')
|
||||
authorization_provider.set_element('./sts:AuthorizationProviderID',
|
||||
'800197268',
|
||||
**attrs)
|
||||
|
||||
|
||||
|
||||
class DianXMLExtensionInvoiceSource(FachoXMLExtension):
|
||||
# CAB13
|
||||
def build(self, fexml):
|
||||
dian_path = '/fe:CreditNote/ext:UBLExtensions/ext:UBLExtension/ext:ExtensionContent/sts:DianExtensions/sts:InvoiceSource/cbc:IdentificationCode'
|
||||
fexml.set_element(
|
||||
dian_path, 'CO',
|
||||
listAgencyID="6",
|
||||
listAgencyName="United Nations Economic Commission for Europe",
|
||||
listSchemeURI="urn:oasis:names:specification:ubl:codelist:gc:CountryIdentificationCode-2.1")
|
||||
fexml.set_element(dian_path, 'CO',
|
||||
listAgencyID="6",
|
||||
listAgencyName="United Nations Economic Commission for Europe",
|
||||
listSchemeURI="urn:oasis:names:specification:ubl:codelist:gc:CountryIdentificationCode-2.1")
|
||||
|
||||
|
||||
class DianXMLExtensionInvoiceAuthorization(FachoXMLExtension):
|
||||
@ -520,15 +499,16 @@ class DianXMLExtensionInvoiceAuthorization(FachoXMLExtension):
|
||||
invoice_control.set_element('/sts:InvoiceControl/sts:AuthorizedInvoices/sts:To',
|
||||
self.to)
|
||||
|
||||
fexml.set_element(
|
||||
'./ext:UBLExtensions/ext:UBLExtension/ext:ExtensionContent/sts:DianExtensions/sts:InvoiceSource/cbc:IdentificationCode',
|
||||
'CO',
|
||||
# DIAN 1.7.-2020: FAB15
|
||||
listAgencyID="6",
|
||||
# DIAN 1.7.-2020: FAB16
|
||||
listAgencyName="United Nations Economic Commission for Europe",
|
||||
# DIAN 1.7.-2020: FAB17
|
||||
listSchemeURI="urn:oasis:names:specification:ubl:codelist:gc:CountryIdentificationCode-2.1")
|
||||
fexml.set_element('./ext:UBLExtensions/ext:UBLExtension/ext:ExtensionContent/sts:DianExtensions/sts:InvoiceSource/cbc:IdentificationCode',
|
||||
'CO',
|
||||
#DIAN 1.7.-2020: FAB15
|
||||
listAgencyID="6",
|
||||
#DIAN 1.7.-2020: FAB16
|
||||
listAgencyName="United Nations Economic Commission for Europe",
|
||||
#DIAN 1.7.-2020: FAB17
|
||||
listSchemeURI="urn:oasis:names:specification:ubl:codelist:gc:CountryIdentificationCode-2.1"
|
||||
)
|
||||
|
||||
|
||||
|
||||
class DianZIP:
|
||||
@ -537,8 +517,7 @@ class DianZIP:
|
||||
MAX_FILES = 50
|
||||
|
||||
def __init__(self, file_like):
|
||||
self.zipfile = zipfile.ZipFile(
|
||||
file_like, mode='w', compression=zipfile.ZIP_DEFLATED)
|
||||
self.zipfile = zipfile.ZipFile(file_like, mode='w', compression=zipfile.ZIP_DEFLATED)
|
||||
self.num_files = 0
|
||||
|
||||
def add_xml(self, name, xml_data):
|
||||
@ -559,6 +538,7 @@ class DianZIP:
|
||||
def __enter__(self):
|
||||
"""
|
||||
Facilita el uso de esta manera:
|
||||
|
||||
f = open('xxx', 'rb')
|
||||
with DianZIP(f) as zip:
|
||||
zip.add_invoice_xml('name', 'data xml')
|
||||
@ -581,7 +561,7 @@ class DianXMLExtensionSignerVerifier:
|
||||
def verify_string(self, document):
|
||||
# Obtener FachoXML
|
||||
xml = LXMLBuilder.from_string(document)
|
||||
fachoxml = FachoXML(xml, nsmap=NAMESPACES)
|
||||
fachoxml = FachoXML(xml,nsmap=NAMESPACES)
|
||||
|
||||
# Obtener Signature
|
||||
signature = fachoxml.builder.xpath(fachoxml.root, '//ds:Signature')
|
||||
|
@ -1,14 +1,12 @@
|
||||
# This file is part of facho. The COPYRIGHT file at the top level of
|
||||
# this repository contains the full copyright notices and license terms.
|
||||
|
||||
# import hashlib
|
||||
# from functools import reduce
|
||||
# import copy
|
||||
|
||||
import hashlib
|
||||
from functools import reduce
|
||||
import copy
|
||||
import dataclasses
|
||||
from dataclasses import dataclass, field
|
||||
from dataclasses import dataclass
|
||||
from datetime import datetime, date
|
||||
# from collections import defaultdict
|
||||
from collections import defaultdict
|
||||
import decimal
|
||||
from decimal import Decimal
|
||||
import typing
|
||||
@ -16,11 +14,9 @@ from ..data.dian import codelist
|
||||
|
||||
DECIMAL_PRECISION = 6
|
||||
|
||||
|
||||
class AmountCurrencyError(TypeError):
|
||||
pass
|
||||
|
||||
|
||||
@dataclass
|
||||
class Currency:
|
||||
code: str
|
||||
@ -31,7 +27,6 @@ class Currency:
|
||||
def __str__(self):
|
||||
return self.code
|
||||
|
||||
|
||||
class Collection:
|
||||
|
||||
def __init__(self, array):
|
||||
@ -48,7 +43,6 @@ class Collection:
|
||||
def sum(self):
|
||||
return sum(self.array)
|
||||
|
||||
|
||||
class AmountCollection(Collection):
|
||||
|
||||
def sum(self):
|
||||
@ -57,13 +51,10 @@ class AmountCollection(Collection):
|
||||
total += v
|
||||
return total
|
||||
|
||||
|
||||
class Amount:
|
||||
def __init__(
|
||||
self, amount: typing.Union[int, float, str, "Amount"],
|
||||
currency: Currency = Currency('COP')):
|
||||
def __init__(self, amount: int or float or str or Amount, currency: Currency = Currency('COP')):
|
||||
|
||||
# DIAN 1.7.-2020: 1.2.3.1
|
||||
#DIAN 1.7.-2020: 1.2.3.1
|
||||
if isinstance(amount, Amount):
|
||||
if amount < Amount(0.0):
|
||||
raise ValueError('amount must be positive >= 0')
|
||||
@ -74,11 +65,9 @@ class Amount:
|
||||
if float(amount) < 0:
|
||||
raise ValueError('amount must be positive >= 0')
|
||||
|
||||
self.amount = Decimal(
|
||||
amount, decimal.Context(
|
||||
prec=DECIMAL_PRECISION,
|
||||
# DIAN 1.7.-2020: 1.2.1.1
|
||||
rounding=decimal.ROUND_HALF_EVEN))
|
||||
self.amount = Decimal(amount, decimal.Context(prec=DECIMAL_PRECISION,
|
||||
#DIAN 1.7.-2020: 1.2.1.1
|
||||
rounding=decimal.ROUND_HALF_EVEN ))
|
||||
self.currency = currency
|
||||
|
||||
def fromNumber(self, val):
|
||||
@ -101,8 +90,7 @@ class Amount:
|
||||
def __eq__(self, other):
|
||||
if not self.is_same_currency(other):
|
||||
raise AmountCurrencyError()
|
||||
return round(self.amount, DECIMAL_PRECISION) == round(
|
||||
other.amount, DECIMAL_PRECISION)
|
||||
return round(self.amount, DECIMAL_PRECISION) == round(other.amount, DECIMAL_PRECISION)
|
||||
|
||||
def _cast(self, val):
|
||||
if type(val) in [int, float]:
|
||||
@ -134,7 +122,7 @@ class Amount:
|
||||
|
||||
def truncate_as_string(self, prec):
|
||||
parts = str(self.float()).split('.', 1)
|
||||
return '%s.%s' % (parts[0], parts[1][0:prec].ljust(prec, '0'))
|
||||
return '%s.%s' % (parts[0], parts[1][0:prec].ljust(prec,'0'))
|
||||
|
||||
def float(self):
|
||||
return float(round(self.amount, DECIMAL_PRECISION))
|
||||
@ -163,7 +151,6 @@ class Quantity:
|
||||
def __repr__(self):
|
||||
return str(self)
|
||||
|
||||
|
||||
@dataclass
|
||||
class Item:
|
||||
scheme_name: str
|
||||
@ -201,7 +188,6 @@ class Country:
|
||||
raise ValueError("code [%s] not found" % (self.code))
|
||||
self.name = codelist.Paises[self.code]['name']
|
||||
|
||||
|
||||
@dataclass
|
||||
class CountrySubentity:
|
||||
code: str
|
||||
@ -212,7 +198,6 @@ class CountrySubentity:
|
||||
raise ValueError("code [%s] not found" % (self.code))
|
||||
self.name = codelist.Departamento[self.code]['name']
|
||||
|
||||
|
||||
@dataclass
|
||||
class City:
|
||||
code: str
|
||||
@ -223,22 +208,18 @@ class City:
|
||||
raise ValueError("code [%s] not found" % (self.code))
|
||||
self.name = codelist.Municipio[self.code]['name']
|
||||
|
||||
|
||||
@dataclass
|
||||
class PostalZone:
|
||||
code: str = ''
|
||||
|
||||
|
||||
@dataclass
|
||||
class Address:
|
||||
name: str
|
||||
street: str = ''
|
||||
city: City = field(default_factory=lambda: City('05001'))
|
||||
country: Country = field(default_factory=lambda: Country('CO'))
|
||||
countrysubentity: CountrySubentity = field(
|
||||
default_factory=lambda: CountrySubentity('05'))
|
||||
postalzone: PostalZone = field(default_factory=lambda: PostalZone(''))
|
||||
|
||||
city: City = City('05001')
|
||||
country: Country = Country('CO')
|
||||
countrysubentity: CountrySubentity = CountrySubentity('05')
|
||||
postalzone: PostalZone = PostalZone('')
|
||||
|
||||
@dataclass
|
||||
class PartyIdentification:
|
||||
@ -259,7 +240,6 @@ class PartyIdentification:
|
||||
if self.type_fiscal not in codelist.TipoIdFiscal:
|
||||
raise ValueError("type_fiscal [%s] not found" % (self.type_fiscal))
|
||||
|
||||
|
||||
@dataclass
|
||||
class Responsability:
|
||||
codes: list
|
||||
@ -289,7 +269,6 @@ class TaxScheme:
|
||||
raise ValueError("code not found")
|
||||
self.name = codelist.TipoImpuesto[self.code]['name']
|
||||
|
||||
|
||||
@dataclass
|
||||
class Party:
|
||||
name: str
|
||||
@ -297,10 +276,10 @@ class Party:
|
||||
responsability_code: typing.List[Responsability]
|
||||
responsability_regime_code: str
|
||||
organization_code: str
|
||||
tax_scheme: TaxScheme = field(default_factory=lambda: TaxScheme('01'))
|
||||
tax_scheme: TaxScheme = TaxScheme('01')
|
||||
|
||||
phone: str = ''
|
||||
address: Address = field(default_factory=lambda: Address(''))
|
||||
address: Address = Address('')
|
||||
email: str = ''
|
||||
legal_name: str = ''
|
||||
legal_company_ident: str = ''
|
||||
@ -328,7 +307,7 @@ class TaxScheme:
|
||||
class TaxSubTotal:
|
||||
percent: float
|
||||
scheme: typing.Optional[TaxScheme] = None
|
||||
tax_amount: Amount = field(default_factory=lambda: Amount(0.0))
|
||||
tax_amount: Amount = Amount(0.0)
|
||||
|
||||
def calculate(self, invline):
|
||||
if self.percent is not None:
|
||||
@ -338,8 +317,8 @@ class TaxSubTotal:
|
||||
@dataclass
|
||||
class TaxTotal:
|
||||
subtotals: list
|
||||
tax_amount: Amount = field(default_factory=lambda: Amount(0.0))
|
||||
taxable_amount: Amount = field(default_factory=lambda: Amount(0.0))
|
||||
tax_amount: Amount = Amount(0.0)
|
||||
taxable_amount: Amount = Amount(0.0)
|
||||
|
||||
def calculate(self, invline):
|
||||
self.taxable_amount = invline.total_amount
|
||||
@ -355,23 +334,21 @@ class TaxTotalOmit(TaxTotal):
|
||||
def calculate(self, invline):
|
||||
pass
|
||||
|
||||
|
||||
@dataclass
|
||||
class WithholdingTaxSubTotal:
|
||||
percent: float
|
||||
scheme: typing.Optional[TaxScheme] = None
|
||||
tax_amount: Amount = field(default_factory=lambda: Amount(0.0))
|
||||
tax_amount: Amount = Amount(0.0)
|
||||
|
||||
def calculate(self, invline):
|
||||
if self.percent is not None:
|
||||
self.tax_amount = invline.total_amount * Amount(self.percent / 100)
|
||||
|
||||
|
||||
@dataclass
|
||||
class WithholdingTaxTotal:
|
||||
subtotals: list
|
||||
tax_amount: Amount = field(default_factory=lambda: Amount(0.0))
|
||||
taxable_amount: Amount = field(default_factory=lambda: Amount(0.0))
|
||||
tax_amount: Amount = Amount(0.0)
|
||||
taxable_amount: Amount = Amount(0.0)
|
||||
|
||||
def calculate(self, invline):
|
||||
self.taxable_amount = invline.total_amount
|
||||
@ -380,7 +357,6 @@ class WithholdingTaxTotal:
|
||||
subtax.calculate(invline)
|
||||
self.tax_amount += subtax.tax_amount
|
||||
|
||||
|
||||
class WithholdingTaxTotalOmit(WithholdingTaxTotal):
|
||||
def __init__(self):
|
||||
super().__init__([])
|
||||
@ -388,7 +364,6 @@ class WithholdingTaxTotalOmit(WithholdingTaxTotal):
|
||||
def calculate(self, invline):
|
||||
pass
|
||||
|
||||
|
||||
@dataclass
|
||||
class Price:
|
||||
amount: Amount
|
||||
@ -404,7 +379,6 @@ class Price:
|
||||
|
||||
self.amount *= self.quantity
|
||||
|
||||
|
||||
@dataclass
|
||||
class PaymentMean:
|
||||
DEBIT = '01'
|
||||
@ -422,9 +396,8 @@ class PaymentMean:
|
||||
|
||||
@dataclass
|
||||
class PrePaidPayment:
|
||||
# DIAN 1.7.-2020: FBD03
|
||||
paid_amount: Amount = field(default_factory=lambda: Amount(0.0))
|
||||
|
||||
#DIAN 1.7.-2020: FBD03
|
||||
paid_amount: Amount = Amount(0.0)
|
||||
|
||||
@dataclass
|
||||
class BillingResponse:
|
||||
@ -432,7 +405,6 @@ class BillingResponse:
|
||||
code: str
|
||||
description: str
|
||||
|
||||
|
||||
class SupportDocumentCreditNoteResponse(BillingResponse):
|
||||
"""
|
||||
ReferenceID: Identifica la sección del Documento
|
||||
@ -442,13 +414,13 @@ class SupportDocumentCreditNoteResponse(BillingResponse):
|
||||
"""
|
||||
|
||||
|
||||
|
||||
@dataclass
|
||||
class BillingReference:
|
||||
ident: str
|
||||
uuid: str
|
||||
date: date
|
||||
|
||||
|
||||
class CreditNoteDocumentReference(BillingReference):
|
||||
"""
|
||||
ident: Prefijo + Numero de la factura relacionada
|
||||
@ -456,7 +428,6 @@ class CreditNoteDocumentReference(BillingReference):
|
||||
date: fecha de emision de la factura relacionada
|
||||
"""
|
||||
|
||||
|
||||
class DebitNoteDocumentReference(BillingReference):
|
||||
"""
|
||||
ident: Prefijo + Numero de la factura relacionada
|
||||
@ -464,7 +435,6 @@ class DebitNoteDocumentReference(BillingReference):
|
||||
date: fecha de emision de la factura relacionada
|
||||
"""
|
||||
|
||||
|
||||
class InvoiceDocumentReference(BillingReference):
|
||||
"""
|
||||
ident: Prefijo + Numero de la nota credito relacionada
|
||||
@ -472,7 +442,6 @@ class InvoiceDocumentReference(BillingReference):
|
||||
date: fecha de emision de la nota credito relacionada
|
||||
"""
|
||||
|
||||
|
||||
@dataclass
|
||||
class AllowanceChargeReason:
|
||||
code: str
|
||||
@ -485,26 +454,22 @@ class AllowanceChargeReason:
|
||||
|
||||
@dataclass
|
||||
class AllowanceCharge:
|
||||
# DIAN 1.7.-2020: FAQ03
|
||||
#DIAN 1.7.-2020: FAQ03
|
||||
charge_indicator: bool = True
|
||||
amount: Amount = field(default_factory=lambda: Amount(0.0))
|
||||
amount: Amount = Amount(0.0)
|
||||
reason: AllowanceChargeReason = None
|
||||
|
||||
# Valor Base para calcular el descuento o el cargo
|
||||
base_amount: typing.Optional[Amount] = field(
|
||||
default_factory=lambda: Amount(0.0))
|
||||
#Valor Base para calcular el descuento o el cargo
|
||||
base_amount: typing.Optional[Amount] = Amount(0.0)
|
||||
|
||||
# Porcentaje: Porcentaje que aplicar.
|
||||
multiplier_factor_numeric: Amount = field(
|
||||
default_factory=lambda: Amount(1.0))
|
||||
multiplier_factor_numeric: Amount = Amount(1.0)
|
||||
|
||||
def isCharge(self):
|
||||
charge_indicator = self.charge_indicator is True
|
||||
return charge_indicator
|
||||
return self.charge_indicator == True
|
||||
|
||||
def isDiscount(self):
|
||||
charge_indicator = self.charge_indicator is False
|
||||
return charge_indicator
|
||||
return self.charge_indicator == False
|
||||
|
||||
def asCharge(self):
|
||||
self.charge_indicator = True
|
||||
@ -518,13 +483,11 @@ class AllowanceCharge:
|
||||
def set_base_amount(self, amount):
|
||||
self.base_amount = amount
|
||||
|
||||
|
||||
class AllowanceChargeAsDiscount(AllowanceCharge):
|
||||
def __init__(self, amount: Amount = Amount(0.0)):
|
||||
self.charge_indicator = False
|
||||
self.amount = amount
|
||||
|
||||
|
||||
@dataclass
|
||||
class InvoiceLine:
|
||||
# RESOLUCION 0004: pagina 155
|
||||
@ -538,8 +501,7 @@ class InvoiceLine:
|
||||
# de subtotal
|
||||
tax: typing.Optional[TaxTotal]
|
||||
withholding: typing.Optional[WithholdingTaxTotal]
|
||||
allowance_charge: typing.List[AllowanceCharge] = dataclasses.field(
|
||||
default_factory=list)
|
||||
allowance_charge: typing.List[AllowanceCharge] = dataclasses.field(default_factory=list)
|
||||
|
||||
def add_allowance_charge(self, charge):
|
||||
if not isinstance(charge, AllowanceCharge):
|
||||
@ -604,19 +566,18 @@ class InvoiceLine:
|
||||
if self.withholding is None:
|
||||
self.withholding = WithholdingTaxTotalOmit()
|
||||
|
||||
|
||||
@dataclass
|
||||
class LegalMonetaryTotal:
|
||||
line_extension_amount: Amount = field(default_factory=lambda: Amount(0.0))
|
||||
tax_exclusive_amount: Amount = field(default_factory=lambda: Amount(0.0))
|
||||
tax_inclusive_amount: Amount = field(default_factory=lambda: Amount(0.0))
|
||||
charge_total_amount: Amount = field(default_factory=lambda: Amount(0.0))
|
||||
allowance_total_amount: Amount = field(default_factory=lambda: Amount(0.0))
|
||||
payable_amount: Amount = field(default_factory=lambda: Amount(0.0))
|
||||
prepaid_amount: Amount = field(default_factory=lambda: Amount(0.0))
|
||||
line_extension_amount: Amount = Amount(0.0)
|
||||
tax_exclusive_amount: Amount = Amount(0.0)
|
||||
tax_inclusive_amount: Amount = Amount(0.0)
|
||||
charge_total_amount: Amount = Amount(0.0)
|
||||
allowance_total_amount: Amount = Amount(0.0)
|
||||
payable_amount: Amount = Amount(0.0)
|
||||
prepaid_amount: Amount = Amount(0.0)
|
||||
|
||||
def calculate(self):
|
||||
# DIAN 1.7.-2020: FAU14
|
||||
#DIAN 1.7.-2020: FAU14
|
||||
self.payable_amount = \
|
||||
self.tax_inclusive_amount \
|
||||
+ self.allowance_total_amount \
|
||||
@ -624,29 +585,26 @@ class LegalMonetaryTotal:
|
||||
- self.prepaid_amount
|
||||
|
||||
|
||||
|
||||
class NationalSalesInvoiceDocumentType(str):
|
||||
def __str__(self):
|
||||
# 6.1.3
|
||||
return '01'
|
||||
|
||||
|
||||
class CreditNoteDocumentType(str):
|
||||
def __str__(self):
|
||||
# 6.1.3
|
||||
return '91'
|
||||
|
||||
|
||||
class DebitNoteDocumentType(str):
|
||||
def __str__(self):
|
||||
# 6.1.3
|
||||
return '92'
|
||||
|
||||
|
||||
class CreditNoteSupportDocumentType(str):
|
||||
def __str__(self):
|
||||
return '95'
|
||||
|
||||
|
||||
class Invoice:
|
||||
def __init__(self, type_code: str):
|
||||
if str(type_code) not in codelist.TipoDocumento:
|
||||
@ -692,8 +650,7 @@ class Invoice:
|
||||
if len(prefix) <= 4:
|
||||
self.invoice_ident_prefix = prefix
|
||||
else:
|
||||
raise ValueError(
|
||||
'ident prefix failed to get, expected 0 to 4 chars')
|
||||
raise ValueError('ident prefix failed to get, expected 0 to 4 chars')
|
||||
|
||||
def set_ident(self, ident: str):
|
||||
"""
|
||||
@ -746,6 +703,7 @@ class Invoice:
|
||||
def set_discrepancy_response(self, billing_response: BillingResponse):
|
||||
self.invoice_discrepancy_response = billing_response
|
||||
|
||||
|
||||
def accept(self, visitor):
|
||||
visitor.visit_payment_mean(self.invoice_payment_mean)
|
||||
visitor.visit_customer(self.invoice_customer)
|
||||
@ -757,34 +715,29 @@ class Invoice:
|
||||
|
||||
def _calculate_legal_monetary_total(self):
|
||||
for invline in self.invoice_lines:
|
||||
self.invoice_legal_monetary_total.line_extension_amount +=\
|
||||
invline.total_amount
|
||||
self.invoice_legal_monetary_total.tax_exclusive_amount +=\
|
||||
invline.total_tax_exclusive_amount
|
||||
# DIAN 1.7.-2020: FAU6
|
||||
self.invoice_legal_monetary_total.tax_inclusive_amount +=\
|
||||
invline.total_tax_inclusive_amount
|
||||
self.invoice_legal_monetary_total.line_extension_amount += invline.total_amount
|
||||
self.invoice_legal_monetary_total.tax_exclusive_amount += invline.total_tax_exclusive_amount
|
||||
#DIAN 1.7.-2020: FAU6
|
||||
self.invoice_legal_monetary_total.tax_inclusive_amount += invline.total_tax_inclusive_amount
|
||||
|
||||
# DIAN 1.7.-2020: FAU08
|
||||
self.invoice_legal_monetary_total.allowance_total_amount =\
|
||||
AmountCollection(self.invoice_allowance_charge)\
|
||||
#DIAN 1.7.-2020: FAU08
|
||||
self.invoice_legal_monetary_total.allowance_total_amount = AmountCollection(self.invoice_allowance_charge)\
|
||||
.filter(lambda charge: charge.isDiscount())\
|
||||
.map(lambda charge: charge.amount)\
|
||||
.sum()
|
||||
|
||||
# DIAN 1.7.-2020: FAU10
|
||||
self.invoice_legal_monetary_total.charge_total_amount =\
|
||||
AmountCollection(self.invoice_allowance_charge)\
|
||||
#DIAN 1.7.-2020: FAU10
|
||||
self.invoice_legal_monetary_total.charge_total_amount = AmountCollection(self.invoice_allowance_charge)\
|
||||
.filter(lambda charge: charge.isCharge())\
|
||||
.map(lambda charge: charge.amount)\
|
||||
.sum()
|
||||
|
||||
# DIAN 1.7.-2020: FAU12
|
||||
self.invoice_legal_monetary_total.prepaid_amount = AmountCollection(
|
||||
self.invoice_prepaid_payment).map(
|
||||
lambda paid: paid.paid_amount).sum()
|
||||
#DIAN 1.7.-2020: FAU12
|
||||
self.invoice_legal_monetary_total.prepaid_amount = AmountCollection(self.invoice_prepaid_payment)\
|
||||
.map(lambda paid: paid.paid_amount)\
|
||||
.sum()
|
||||
|
||||
# DIAN 1.7.-2020: FAU14
|
||||
#DIAN 1.7.-2020: FAU14
|
||||
self.invoice_legal_monetary_total.calculate()
|
||||
|
||||
def _refresh_charges_base_amount(self):
|
||||
@ -792,13 +745,11 @@ class Invoice:
|
||||
for invline in self.invoice_lines:
|
||||
if invline.allowance_charge:
|
||||
# TODO actualmente solo uno de los cargos es permitido
|
||||
raise ValueError(
|
||||
'allowance charge in invoice exclude invoice line')
|
||||
raise ValueError('allowance charge in invoice exclude invoice line')
|
||||
|
||||
# cargos a nivel de factura
|
||||
for charge in self.invoice_allowance_charge:
|
||||
charge.set_base_amount(
|
||||
self.invoice_legal_monetary_total.line_extension_amount)
|
||||
charge.set_base_amount(self.invoice_legal_monetary_total.line_extension_amount)
|
||||
|
||||
def calculate(self):
|
||||
for invline in self.invoice_lines:
|
||||
@ -806,7 +757,6 @@ class Invoice:
|
||||
self._calculate_legal_monetary_total()
|
||||
self._refresh_charges_base_amount()
|
||||
|
||||
|
||||
class NationalSalesInvoice(Invoice):
|
||||
def __init__(self):
|
||||
super().__init__(NationalSalesInvoiceDocumentType())
|
||||
@ -851,15 +801,12 @@ class DebitNote(Invoice):
|
||||
if not self.invoice_ident_prefix:
|
||||
self.invoice_ident_prefix = self.invoice_ident[0:6]
|
||||
|
||||
|
||||
class SupportDocument(Invoice):
|
||||
pass
|
||||
|
||||
|
||||
class SupportDocumentCreditNote(SupportDocument):
|
||||
def __init__(
|
||||
self, invoice_document_reference: BillingReference,
|
||||
invoice_discrepancy_response: BillingResponse):
|
||||
def __init__(self, invoice_document_reference: BillingReference,
|
||||
invoice_discrepancy_response: BillingResponse):
|
||||
super().__init__(CreditNoteSupportDocumentType())
|
||||
|
||||
if not isinstance(invoice_document_reference, BillingReference):
|
||||
|
@ -2,7 +2,6 @@ from .invoice import *
|
||||
from .credit_note import *
|
||||
from .debit_note import *
|
||||
from .utils import *
|
||||
from .attached_document import *
|
||||
from .support_document import *
|
||||
from .support_document_credit_note import *
|
||||
from .attached_document import *
|
||||
from .application_response import *
|
||||
|
@ -1,177 +0,0 @@
|
||||
from .. import fe
|
||||
|
||||
__all__ = ['ApplicationResponse']
|
||||
|
||||
|
||||
class ApplicationResponse:
|
||||
|
||||
def __init__(self, invoice, tag_document='ApplicationResponse'):
|
||||
self.schema =\
|
||||
'urn:oasis:names:specification:ubl:schema:xsd:ApplicationResponse-2'
|
||||
self.tag_document = tag_document
|
||||
self.invoice = invoice
|
||||
self.fexml = fe.FeXML(
|
||||
self.tag_document, self.schema)
|
||||
self.application_response = self.application_response()
|
||||
|
||||
def application_response(self):
|
||||
# DIAN 1.9.-2023: AE02
|
||||
self.fexml.set_element(
|
||||
'./cbc:UBLVersionID', 'UBL 2.1')
|
||||
|
||||
# DIAN 1.9.-2023: AE03
|
||||
self.fexml.set_element(
|
||||
'./cbc:CustomizationID', 'Documentos adjuntos')
|
||||
|
||||
# DIAN 1.9.-2023: AE04
|
||||
self.fexml.set_element(
|
||||
'./cbc:ProfileID', 'DIAN 2.1')
|
||||
|
||||
# DIAN 1.9.-2023: AE04a
|
||||
self.fexml.set_element(
|
||||
'./cbc:ProfileExecutionID', '1')
|
||||
|
||||
self.fexml.set_element(
|
||||
'./cbc:ID', '1')
|
||||
|
||||
self.fexml.set_element(
|
||||
'./cbc:UUID', '1', schemeName="CUDE-SHA384")
|
||||
|
||||
self.fexml.set_element(
|
||||
'./cbc:IssueDate',
|
||||
self.invoice.invoice_issue.strftime('%Y-%m-%d'))
|
||||
|
||||
# DIAN 1.9.-2023: AE06
|
||||
self.fexml.set_element(
|
||||
'./cbc:IssueTime', self.invoice.invoice_issue.strftime(
|
||||
'%H:%M:%S-05:00'))
|
||||
|
||||
self.set_sender_party()
|
||||
self.set_receiver_party()
|
||||
self.set_document_response()
|
||||
|
||||
def set_sender_party(self):
|
||||
# DIAN 1.9.-2023: AE09
|
||||
self.fexml.placeholder_for(
|
||||
'./cac:SenderParty')
|
||||
# DIAN 1.9.-2023: AE10
|
||||
self.fexml.placeholder_for(
|
||||
'./cac:SenderParty/cac:PartyTaxScheme')
|
||||
# DIAN 1.9.-2023: AE11
|
||||
self.fexml.set_element(
|
||||
'./cac:SenderParty/cac:PartyTaxScheme/cbc:RegistrationName',
|
||||
self.invoice.invoice_supplier.name)
|
||||
# DIAN 1.9.-2023: AE12
|
||||
# DIAN 1.9.-2023: AE13
|
||||
# DIAN 1.9.-2023: AE14
|
||||
# DIAN 1.9.-2023: AE15
|
||||
self.fexml.set_element(
|
||||
'./cac:SenderParty/cac:PartyTaxScheme/cbc:CompanyID',
|
||||
self.invoice.invoice_supplier.ident,
|
||||
schemeAgencyID='195',
|
||||
schemeID=self.invoice.invoice_supplier.ident.dv,
|
||||
schemeName=self.invoice.invoice_supplier.ident.type_fiscal)
|
||||
|
||||
# DIAN 1.9.-2023: AE16
|
||||
self.fexml.set_element(
|
||||
'./cac:SenderParty/cac:PartyTaxScheme/cbc:TaxLevelCode',
|
||||
self.invoice.invoice_supplier.responsability_code)
|
||||
|
||||
# DIAN 1.9.-2023: AE18
|
||||
self.fexml.placeholder_for(
|
||||
'./cac:SenderParty/cac:PartyTaxScheme/cac:TaxScheme')
|
||||
|
||||
# DIAN 1.9.-2023: AE19
|
||||
self.fexml.set_element(
|
||||
'./cac:SenderParty/cac:PartyTaxScheme/cac:TaxScheme/cbc:ID',
|
||||
self.invoice.invoice_supplier.tax_scheme.code)
|
||||
|
||||
# DIAN 1.9.-2023: AE20
|
||||
self.fexml.set_element(
|
||||
'./cac:SenderParty/cac:PartyTaxScheme/cac:TaxScheme/cbc:Name',
|
||||
self.invoice.invoice_supplier.tax_scheme.name)
|
||||
|
||||
def set_receiver_party(self):
|
||||
# DIAN 1.9.-2023: AE21
|
||||
self.fexml.placeholder_for(
|
||||
'./cac:ReceiverParty')
|
||||
# DIAN 1.9.-2023: AE22
|
||||
self.fexml.placeholder_for(
|
||||
'./cac:ReceiverParty/cac:PartyTaxScheme')
|
||||
# DIAN 1.9.-2023: AE23
|
||||
self.fexml.set_element(
|
||||
'./cac:ReceiverParty/cac:PartyTaxScheme/cbc:RegistrationName',
|
||||
self.invoice.invoice_customer.name)
|
||||
# DIAN 1.9.-2023: AE24
|
||||
# DIAN 1.9.-2023: AE25
|
||||
# DIAN 1.9.-2023: AE26
|
||||
# DIAN 1.9.-2023: AE27
|
||||
self.fexml.set_element(
|
||||
'./cac:ReceiverParty/cac:PartyTaxScheme/cbc:CompanyID',
|
||||
self.invoice.invoice_customer.ident,
|
||||
schemeAgencyID='195',
|
||||
schemeID=self.invoice.invoice_customer.ident.dv,
|
||||
schemeName=self.invoice.invoice_customer.ident.type_fiscal)
|
||||
# DIAN 1.9.-2023: AE28
|
||||
self.fexml.set_element(
|
||||
'./cac:ReceiverParty/cac:PartyTaxScheme/cbc:TaxLevelCode',
|
||||
self.invoice.invoice_customer.responsability_code)
|
||||
# DIAN 1.9.-2023: AE30
|
||||
self.fexml.placeholder_for(
|
||||
'./cac:ReceiverParty/cac:PartyTaxScheme/cac:TaxScheme')
|
||||
# DIAN 1.9.-2023: AE31
|
||||
self.fexml.set_element(
|
||||
'./cac:ReceiverParty/cac:PartyTaxScheme/cac:TaxScheme/cbc:ID',
|
||||
self.invoice.invoice_customer.tax_scheme.code)
|
||||
# DIAN 1.9.-2023: AE32
|
||||
self.fexml.set_element(
|
||||
'./cac:ReceiverParty/cac:PartyTaxScheme/cac:TaxScheme/cbc:Name',
|
||||
self.invoice.invoice_customer.tax_scheme.name)
|
||||
|
||||
def set_document_response(self):
|
||||
self.fexml.placeholder_for(
|
||||
'./cac:DocumentResponse')
|
||||
self.fexml.placeholder_for(
|
||||
'./cac:DocumentResponse/cac:Response')
|
||||
self.fexml.set_element(
|
||||
'./cac:DocumentResponse/cac:Response/cbc:ResponseCode',
|
||||
'02')
|
||||
self.fexml.set_element(
|
||||
'./cac:DocumentResponse/cac:Response/cbc:Description',
|
||||
'Documento validado por la DIAN')
|
||||
self.set_documnent_reference()
|
||||
|
||||
def set_documnent_reference(self):
|
||||
self.fexml.placeholder_for(
|
||||
'./cac:DocumentResponse/cac:DocumentReference')
|
||||
self.fexml.set_element(
|
||||
'./cac:DocumentResponse/cac:DocumentReference/cbc:ID',
|
||||
'FESS19566058')
|
||||
self.fexml.set_element(
|
||||
'./cac:DocumentResponse/cac:DocumentReference/cbc:UUID',
|
||||
'f51ee529aabd19d10e39444f2f593b94d56d5885fbf433faf718d53a7e968f64bf54a6ee43c6a2df842771b54a6aae1a',
|
||||
schemeName="CUFE-SHA384")
|
||||
self.set_response_lines()
|
||||
|
||||
def set_response_lines(self):
|
||||
lines = [{
|
||||
'LineID': '1',
|
||||
'ResponseCode': '0000',
|
||||
'Description': '0',
|
||||
}]
|
||||
|
||||
for line in lines:
|
||||
self.fexml.set_element(
|
||||
'./cac:DocumentResponse/cac:LineResponse/cac:LineReference/cbc:LineID', line[
|
||||
'LineID'])
|
||||
self.fexml.set_element(
|
||||
'./cac:DocumentResponse/cac:LineResponse/cac:Response/cbc:ResponseCode', line[
|
||||
'ResponseCode'])
|
||||
self.fexml.set_element(
|
||||
'./cac:DocumentResponse/cac:LineResponse/cac:Response/cbc:Description', line[
|
||||
'Description'])
|
||||
|
||||
|
||||
|
||||
def toFachoXML(self):
|
||||
return self.fexml
|
@ -1,227 +1,14 @@
|
||||
from .. import fe
|
||||
from .application_response import ApplicationResponse
|
||||
|
||||
__all__ = ['AttachedDocument']
|
||||
|
||||
|
||||
class AttachedDocument():
|
||||
|
||||
def __init__(self, invoice, DIANInvoiceXML, id):
|
||||
self.schema =\
|
||||
'urn:oasis:names:specification:ubl:schema:xsd:AttachedDocument-2'
|
||||
self.id = id
|
||||
self.invoice = invoice
|
||||
self.DIANInvoiceXML = DIANInvoiceXML
|
||||
self.attached_document_invoice = self.attached_document_invoice()
|
||||
|
||||
def attached_document_invoice(self):
|
||||
self.fexml = fe.FeXML(
|
||||
'AttachedDocument', self.schema)
|
||||
|
||||
# DIAN 1.9.-2023: AE02
|
||||
self.fexml.set_element(
|
||||
'./cbc:UBLVersionID', 'UBL 2.1')
|
||||
|
||||
# DIAN 1.9.-2023: AE03
|
||||
self.fexml.set_element(
|
||||
'./cbc:CustomizationID', 'Documentos adjuntos')
|
||||
|
||||
# DIAN 1.9.-2023: AE04
|
||||
self.fexml.set_element(
|
||||
'./cbc:ProfileID', 'Factura Electrónica de Venta')
|
||||
|
||||
# DIAN 1.9.-2023: AE04a
|
||||
self.fexml.set_element(
|
||||
'./cbc:ProfileExecutionID', '1')
|
||||
|
||||
# DIAN 1.9.-2023: AE04b
|
||||
self.fexml.set_element(
|
||||
'./cbc:ID', self.id)
|
||||
|
||||
# DIAN 1.9.-2023: AE05
|
||||
self.fexml.set_element(
|
||||
'./cbc:IssueDate',
|
||||
self.invoice.invoice_issue.strftime('%Y-%m-%d'))
|
||||
|
||||
# DIAN 1.9.-2023: AE06
|
||||
self.fexml.set_element(
|
||||
'./cbc:IssueTime', self.invoice.invoice_issue.strftime(
|
||||
'%H:%M:%S-05:00'))
|
||||
|
||||
# DIAN 1.9.-2023: AE08
|
||||
self.fexml.set_element(
|
||||
'./cbc:DocumentType', 'Contenedor de Factura Electrónica')
|
||||
|
||||
# DIAN 1.9.-2023: AE08a
|
||||
self.fexml.set_element(
|
||||
'./cbc:ParentDocumentID', self.invoice.invoice_ident)
|
||||
|
||||
# DIAN 1.9.-2023: AE09
|
||||
self.set_sender_party()
|
||||
|
||||
# DIAN 1.9.-2023: AE20
|
||||
self.set_receiver_party()
|
||||
# DIAN 1.9.-2023: AE33
|
||||
self.set_attachment()
|
||||
self.set_parent_document_line_reference()
|
||||
|
||||
def set_sender_party(self):
|
||||
# DIAN 1.9.-2023: AE09
|
||||
self.fexml.placeholder_for(
|
||||
'./cac:SenderParty')
|
||||
# DIAN 1.9.-2023: AE10
|
||||
self.fexml.placeholder_for(
|
||||
'./cac:SenderParty/cac:PartyTaxScheme')
|
||||
# DIAN 1.9.-2023: AE11
|
||||
self.fexml.set_element(
|
||||
'./cac:SenderParty/cac:PartyTaxScheme/cbc:RegistrationName',
|
||||
self.invoice.invoice_supplier.name)
|
||||
# DIAN 1.9.-2023: AE12
|
||||
# DIAN 1.9.-2023: AE13
|
||||
# DIAN 1.9.-2023: AE14
|
||||
# DIAN 1.9.-2023: AE15
|
||||
self.fexml.set_element(
|
||||
'./cac:SenderParty/cac:PartyTaxScheme/cbc:CompanyID',
|
||||
self.invoice.invoice_supplier.ident,
|
||||
schemeAgencyID='195',
|
||||
schemeID=self.invoice.invoice_supplier.ident.dv,
|
||||
schemeName=self.invoice.invoice_supplier.ident.type_fiscal)
|
||||
|
||||
# DIAN 1.9.-2023: AE16
|
||||
self.fexml.set_element(
|
||||
'./cac:SenderParty/cac:PartyTaxScheme/cbc:TaxLevelCode',
|
||||
self.invoice.invoice_supplier.responsability_code)
|
||||
|
||||
# DIAN 1.9.-2023: AE18
|
||||
self.fexml.placeholder_for(
|
||||
'./cac:SenderParty/cac:PartyTaxScheme/cac:TaxScheme')
|
||||
|
||||
# DIAN 1.9.-2023: AE19
|
||||
self.fexml.set_element(
|
||||
'./cac:SenderParty/cac:PartyTaxScheme/cac:TaxScheme/cbc:ID',
|
||||
self.invoice.invoice_supplier.tax_scheme.code)
|
||||
|
||||
# DIAN 1.9.-2023: AE20
|
||||
self.fexml.set_element(
|
||||
'./cac:SenderParty/cac:PartyTaxScheme/cac:TaxScheme/cbc:Name',
|
||||
self.invoice.invoice_supplier.tax_scheme.name)
|
||||
|
||||
def set_receiver_party(self):
|
||||
# DIAN 1.9.-2023: AE21
|
||||
self.fexml.placeholder_for(
|
||||
'./cac:ReceiverParty')
|
||||
# DIAN 1.9.-2023: AE22
|
||||
self.fexml.placeholder_for(
|
||||
'./cac:ReceiverParty/cac:PartyTaxScheme')
|
||||
# DIAN 1.9.-2023: AE23
|
||||
self.fexml.set_element(
|
||||
'./cac:ReceiverParty/cac:PartyTaxScheme/cbc:RegistrationName',
|
||||
self.invoice.invoice_customer.name)
|
||||
# DIAN 1.9.-2023: AE24
|
||||
# DIAN 1.9.-2023: AE25
|
||||
# DIAN 1.9.-2023: AE26
|
||||
# DIAN 1.9.-2023: AE27
|
||||
self.fexml.set_element(
|
||||
'./cac:ReceiverParty/cac:PartyTaxScheme/cbc:CompanyID',
|
||||
self.invoice.invoice_customer.ident,
|
||||
schemeAgencyID='195',
|
||||
schemeID=self.invoice.invoice_customer.ident.dv,
|
||||
schemeName=self.invoice.invoice_customer.ident.type_fiscal)
|
||||
# DIAN 1.9.-2023: AE28
|
||||
self.fexml.set_element(
|
||||
'./cac:ReceiverParty/cac:PartyTaxScheme/cbc:TaxLevelCode',
|
||||
self.invoice.invoice_customer.responsability_code)
|
||||
# DIAN 1.9.-2023: AE30
|
||||
self.fexml.placeholder_for(
|
||||
'./cac:ReceiverParty/cac:PartyTaxScheme/cac:TaxScheme')
|
||||
# DIAN 1.9.-2023: AE31
|
||||
self.fexml.set_element(
|
||||
'./cac:ReceiverParty/cac:PartyTaxScheme/cac:TaxScheme/cbc:ID',
|
||||
self.invoice.invoice_customer.tax_scheme.code)
|
||||
# DIAN 1.9.-2023: AE32
|
||||
self.fexml.set_element(
|
||||
'./cac:ReceiverParty/cac:PartyTaxScheme/cac:TaxScheme/cbc:Name',
|
||||
self.invoice.invoice_customer.tax_scheme.name)
|
||||
|
||||
def set_attachment(self):
|
||||
# DIAN 1.9.-2023: AE33
|
||||
self.fexml.placeholder_for(
|
||||
'./cac:Attachment')
|
||||
# DIAN 1.9.-2023: AE34
|
||||
self.fexml.placeholder_for(
|
||||
'./cac:Attachment/cac:ExternalReference')
|
||||
# DIAN 1.9.-2023: AE35
|
||||
self.fexml.set_element(
|
||||
'./cac:Attachment/cac:ExternalReference/cbc:MimeCode',
|
||||
'text/xml')
|
||||
# DIAN 1.9.-2023: AE36
|
||||
self.fexml.set_element(
|
||||
'./cac:Attachment/cac:ExternalReference/cbc:EncodingCode',
|
||||
'UTF-8')
|
||||
# DIAN 1.9.-2023: AE37
|
||||
self.fexml.set_element(
|
||||
'./cac:Attachment/cac:ExternalReference/cbc:Description',
|
||||
self._build_attachment(self.DIANInvoiceXML)
|
||||
)
|
||||
|
||||
def set_parent_document_line_reference(self):
|
||||
self.fexml.placeholder_for(
|
||||
'./cac:ParentDocumentLineReference')
|
||||
self.fexml.set_element(
|
||||
'./cac:ParentDocumentLineReference/cbc:LineID', 1)
|
||||
self.fexml.placeholder_for(
|
||||
'./cac:ParentDocumentLineReference/cac:DocumentReference')
|
||||
self.fexml.set_element(
|
||||
'./cac:ParentDocumentLineReference/cac:DocumentReference/cbc:ID',
|
||||
'1234')
|
||||
self.fexml.set_element(
|
||||
'./cac:ParentDocumentLineReference/cac:DocumentReference/cbc:UUID',
|
||||
'1234',
|
||||
schemeName="CUFE-SHA384")
|
||||
self.fexml.set_element(
|
||||
'./cac:ParentDocumentLineReference/cac:DocumentReference/cbc:IssueDate',
|
||||
'2024-11-28')
|
||||
self.fexml.set_element(
|
||||
'./cac:ParentDocumentLineReference/cac:DocumentReference/cbc:DocumentType',
|
||||
'ApplicationResponse')
|
||||
self.fexml.placeholder_for(
|
||||
'./cac:ParentDocumentLineReference/cac:DocumentReference/cac:Attachment')
|
||||
self.fexml.set_element(
|
||||
'./cac:ParentDocumentLineReference/cac:DocumentReference/cac:Attachment/cac:ExternalReference/cbc:MimeCode',
|
||||
'text/xml')
|
||||
self.fexml.set_element(
|
||||
'./cac:ParentDocumentLineReference/cac:DocumentReference/cac:Attachment/cac:ExternalReference/cbc:EncodingCode',
|
||||
'UTF-8')
|
||||
|
||||
application_response = ApplicationResponse(
|
||||
self.invoice).toFachoXML()
|
||||
|
||||
self.fexml.set_element(
|
||||
'./cac:ParentDocumentLineReference/cac:DocumentReference/cac:Attachment/cac:ExternalReference/cbc:Description',
|
||||
self._build_attachment(application_response))
|
||||
self.fexml.placeholder_for(
|
||||
'./cac:ParentDocumentLineReference/cac:DocumentReference/cac:ResultOfVerification')
|
||||
self.fexml.set_element(
|
||||
'./cac:ParentDocumentLineReference/cac:DocumentReference/cac:ResultOfVerification/cbc:ValidatorID',
|
||||
'Unidad Especial Dirección de Impuestos y Aduanas Nacionales')
|
||||
self.fexml.set_element(
|
||||
'./cac:ParentDocumentLineReference/cac:DocumentReference/cac:ResultOfVerification/cbc:ValidationResultCode',
|
||||
'02')
|
||||
self.fexml.set_element(
|
||||
'./cac:ParentDocumentLineReference/cac:DocumentReference/cac:ResultOfVerification/cbc:ValidationDate',
|
||||
'2024-11-28')
|
||||
self.fexml.set_element(
|
||||
'./cac:ParentDocumentLineReference/cac:DocumentReference/cac:ResultOfVerification/cbc:ValidationTime',
|
||||
'10:35:11-05:00')
|
||||
|
||||
def _build_attachment(self, DIANInvoiceXML):
|
||||
document = (
|
||||
'<?xml version="1.0" encoding="UTF-8" standalone="no"?>'
|
||||
) + DIANInvoiceXML.tostring()
|
||||
attachment = "<![CDATA[{}]]>".format(
|
||||
document)
|
||||
|
||||
return attachment
|
||||
def __init__(self, id):
|
||||
schema = 'urn:oasis:names:specification:ubl:schema:xsd:AttachedDocument-2'
|
||||
self.fexml = fe.FeXML('AttachedDocument', schema)
|
||||
self.fexml.set_element('./cbc:ID', id)
|
||||
|
||||
def toFachoXML(self):
|
||||
return self.fexml
|
||||
|
||||
|
@ -1,10 +1,9 @@
|
||||
# from .. import fe
|
||||
# from ..form import *
|
||||
from .. import fe
|
||||
from ..form import *
|
||||
from .invoice import DIANInvoiceXML
|
||||
|
||||
__all__ = ['DIANCreditNoteXML']
|
||||
|
||||
|
||||
class DIANCreditNoteXML(DIANInvoiceXML):
|
||||
"""
|
||||
DianInvoiceXML mapea objeto form.Invoice a XML segun
|
||||
|
@ -1,10 +1,9 @@
|
||||
# from .. import fe
|
||||
# from ..form import *
|
||||
from .. import fe
|
||||
from ..form import *
|
||||
from .invoice import DIANInvoiceXML
|
||||
|
||||
__all__ = ['DIANDebitNoteXML']
|
||||
|
||||
|
||||
class DIANDebitNoteXML(DIANInvoiceXML):
|
||||
"""
|
||||
DianInvoiceXML mapea objeto form.Invoice a XML segun
|
||||
@ -20,24 +19,19 @@ class DIANDebitNoteXML(DIANInvoiceXML):
|
||||
def tag_document_concilied(fexml):
|
||||
return 'Debited'
|
||||
|
||||
# DIAN 1.7.-2020: DAU03
|
||||
#DIAN 1.7.-2020: DAU03
|
||||
def set_legal_monetary(fexml, invoice):
|
||||
fexml.set_element_amount(
|
||||
'./cac:RequestedMonetaryTotal/cbc:LineExtensionAmount',
|
||||
invoice.invoice_legal_monetary_total.line_extension_amount)
|
||||
fexml.set_element_amount('./cac:RequestedMonetaryTotal/cbc:LineExtensionAmount',
|
||||
invoice.invoice_legal_monetary_total.line_extension_amount)
|
||||
|
||||
fexml.set_element_amount(
|
||||
'./cac:RequestedMonetaryTotal/cbc:TaxExclusiveAmount',
|
||||
invoice.invoice_legal_monetary_total.tax_exclusive_amount)
|
||||
fexml.set_element_amount('./cac:RequestedMonetaryTotal/cbc:TaxExclusiveAmount',
|
||||
invoice.invoice_legal_monetary_total.tax_exclusive_amount)
|
||||
|
||||
fexml.set_element_amount(
|
||||
'./cac:RequestedMonetaryTotal/cbc:TaxInclusiveAmount',
|
||||
invoice.invoice_legal_monetary_total.tax_inclusive_amount)
|
||||
fexml.set_element_amount('./cac:RequestedMonetaryTotal/cbc:TaxInclusiveAmount',
|
||||
invoice.invoice_legal_monetary_total.tax_inclusive_amount)
|
||||
|
||||
fexml.set_element_amount(
|
||||
'./cac:RequestedMonetaryTotal/cbc:ChargeTotalAmount',
|
||||
invoice.invoice_legal_monetary_total.charge_total_amount)
|
||||
fexml.set_element_amount('./cac:RequestedMonetaryTotal/cbc:ChargeTotalAmount',
|
||||
invoice.invoice_legal_monetary_total.charge_total_amount)
|
||||
|
||||
fexml.set_element_amount(
|
||||
'./cac:RequestedMonetaryTotal/cbc:PayableAmount',
|
||||
invoice.invoice_legal_monetary_total.payable_amount)
|
||||
fexml.set_element_amount('./cac:RequestedMonetaryTotal/cbc:PayableAmount',
|
||||
invoice.invoice_legal_monetary_total.payable_amount)
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,263 +1,219 @@
|
||||
from .. import fe
|
||||
from ..form import (
|
||||
Amount, DebitNoteDocumentReference, CreditNoteDocumentReference,
|
||||
InvoiceDocumentReference, TaxTotalOmit, WithholdingTaxTotalOmit
|
||||
)
|
||||
|
||||
from collections import defaultdict
|
||||
from datetime import datetime
|
||||
# from .attached_document import *
|
||||
from ..form import *
|
||||
from datetime import datetime, date
|
||||
from .attached_document import *
|
||||
|
||||
__all__ = ['DIANSupportDocumentXML']
|
||||
|
||||
|
||||
class DIANSupportDocumentXML(fe.FeXML):
|
||||
"""
|
||||
DianSupportDocumentXML mapea objeto form.Invoice a XML segun
|
||||
lo indicado para él Documento soporte en adquisiciones efectuadas con sujetos no obligados a expedir factura de venta o documento equivalente.
|
||||
"""
|
||||
|
||||
def __init__(self, invoice, tag_document='Invoice'):
|
||||
def __init__(self, invoice, tag_document = 'Invoice'):
|
||||
super().__init__(tag_document, 'http://www.dian.gov.co/contratos/facturaelectronica/v1')
|
||||
|
||||
# DIAN 1.1.-2021: DSAB03
|
||||
# DIAN 1.1.-2021: NSAB03
|
||||
self.placeholder_for(
|
||||
'./ext:UBLExtensions/ext:UBLExtension/ext:ExtensionContent/sts:DianExtensions/sts:InvoiceControl')
|
||||
#DIAN 1.1.-2021: DSAB03
|
||||
#DIAN 1.1.-2021: NSAB03
|
||||
self.placeholder_for('./ext:UBLExtensions/ext:UBLExtension/ext:ExtensionContent/sts:DianExtensions/sts:InvoiceControl')
|
||||
|
||||
# DIAN 1.1.-2021: DSAB13
|
||||
# DIAN 1.1.-2021: NSAB13
|
||||
self.placeholder_for(
|
||||
'./ext:UBLExtensions/ext:UBLExtension/ext:ExtensionContent/sts:DianExtensions/sts:InvoiceSource')
|
||||
#DIAN 1.1.-2021: DSAB13
|
||||
#DIAN 1.1.-2021: NSAB13
|
||||
self.placeholder_for('./ext:UBLExtensions/ext:UBLExtension/ext:ExtensionContent/sts:DianExtensions/sts:InvoiceSource')
|
||||
|
||||
# DIAN 1.1.-2021: DSAB18
|
||||
# DIAN 1.1.-2021: NSAB18
|
||||
self.placeholder_for(
|
||||
'./ext:UBLExtensions/ext:UBLExtension/ext:ExtensionContent/sts:DianExtensions/sts:SoftwareProvider')
|
||||
#DIAN 1.1.-2021: DSAB18
|
||||
#DIAN 1.1.-2021: NSAB18
|
||||
self.placeholder_for('./ext:UBLExtensions/ext:UBLExtension/ext:ExtensionContent/sts:DianExtensions/sts:SoftwareProvider')
|
||||
|
||||
# DIAN 1.1.-2021: DSAB27
|
||||
# DIAN 1.1.-2021: NSAB27
|
||||
self.placeholder_for(
|
||||
'./ext:UBLExtensions/ext:UBLExtension/ext:ExtensionContent/sts:DianExtensions/sts:SoftwareSecurityCode')
|
||||
#DIAN 1.1.-2021: DSAB27
|
||||
#DIAN 1.1.-2021: NSAB27
|
||||
self.placeholder_for('./ext:UBLExtensions/ext:UBLExtension/ext:ExtensionContent/sts:DianExtensions/sts:SoftwareSecurityCode')
|
||||
|
||||
# DIAN 1.1.-2021: DSAB30 DSAB31
|
||||
# DIAN 1.1.-2021: NSAB30 NSAB31
|
||||
self.placeholder_for(
|
||||
'./ext:UBLExtensions/ext:UBLExtension/ext:ExtensionContent/sts:DianExtensions/sts:AuthorizationProvider/sts:AuthorizationProviderID')
|
||||
#DIAN 1.1.-2021: DSAB30 DSAB31
|
||||
#DIAN 1.1.-2021: NSAB30 NSAB31
|
||||
self.placeholder_for('./ext:UBLExtensions/ext:UBLExtension/ext:ExtensionContent/sts:DianExtensions/sts:AuthorizationProvider/sts:AuthorizationProviderID')
|
||||
|
||||
# ZE02 se requiere existencia para firmar
|
||||
# DIAN 1.1.-2021: DSAA02 DSAB01
|
||||
# DIAN 1.1.-2021: NSAA02 NSAB01
|
||||
ublextension = self.fragment(
|
||||
'./ext:UBLExtensions/ext:UBLExtension', append=True)
|
||||
# DIAN 1.1.-2021: DSAB02
|
||||
# DIAN 1.1.-2021: NSAB02
|
||||
extcontent = ublextension.find_or_create_element(
|
||||
'/ext:UBLExtension/ext:ExtensionContent')
|
||||
#ZE02 se requiere existencia para firmar
|
||||
#DIAN 1.1.-2021: DSAA02 DSAB01
|
||||
#DIAN 1.1.-2021: NSAA02 NSAB01
|
||||
ublextension = self.fragment('./ext:UBLExtensions/ext:UBLExtension', append=True)
|
||||
#DIAN 1.1.-2021: DSAB02
|
||||
#DIAN 1.1.-2021: NSAB02
|
||||
extcontent = ublextension.find_or_create_element('/ext:UBLExtension/ext:ExtensionContent')
|
||||
self.attach_invoice(invoice)
|
||||
|
||||
def set_supplier(fexml, invoice):
|
||||
# DIAN 1.1.-2021: DSAJ01
|
||||
# DIAN 1.1.-2021: NSAB01
|
||||
#DIAN 1.1.-2021: DSAJ01
|
||||
#DIAN 1.1.-2021: NSAB01
|
||||
fexml.placeholder_for('./cac:AccountingSupplierParty')
|
||||
|
||||
# DIAN 1.1.-2021: DSAJ02
|
||||
# DIAN 1.1.-2021: NSAJ02
|
||||
fexml.set_element(
|
||||
'./cac:AccountingSupplierParty/cbc:AdditionalAccountID',
|
||||
invoice.invoice_supplier.organization_code)
|
||||
#DIAN 1.1.-2021: DSAJ02
|
||||
#DIAN 1.1.-2021: NSAJ02
|
||||
fexml.set_element('./cac:AccountingSupplierParty/cbc:AdditionalAccountID',
|
||||
invoice.invoice_supplier.organization_code)
|
||||
|
||||
# DIAN 1.1.-2021: DSAJ07 DSAJ08
|
||||
# DIAN 1.1.-2021: NSAJ07 NSAJ08
|
||||
fexml.placeholder_for(
|
||||
'./cac:AccountingSupplierParty/cac:Party/cac:PhysicalLocation/cac:Address')
|
||||
#DIAN 1.1.-2021: DSAJ07 DSAJ08
|
||||
#DIAN 1.1.-2021: NSAJ07 NSAJ08
|
||||
fexml.placeholder_for('./cac:AccountingSupplierParty/cac:Party/cac:PhysicalLocation/cac:Address')
|
||||
|
||||
# DIAN 1.1.-2021: DSAJ09
|
||||
# DIAN 1.1.-2021: NSAJ09
|
||||
fexml.set_element(
|
||||
'./cac:AccountingSupplierParty/cac:Party/cac:PhysicalLocation/cac:Address/cbc:ID',
|
||||
invoice.invoice_supplier.address.city.code)
|
||||
#DIAN 1.1.-2021: DSAJ09
|
||||
#DIAN 1.1.-2021: NSAJ09
|
||||
fexml.set_element('./cac:AccountingSupplierParty/cac:Party/cac:PhysicalLocation/cac:Address/cbc:ID',
|
||||
invoice.invoice_supplier.address.city.code)
|
||||
|
||||
# DIAN 1.1.-2021: DSAJ10
|
||||
# DIAN 1.1.-2021: NSAJ10
|
||||
fexml.set_element(
|
||||
'./cac:AccountingSupplierParty/cac:Party/cac:PhysicalLocation/cac:Address/cbc:CityName',
|
||||
invoice.invoice_supplier.address.city.name)
|
||||
#DIAN 1.1.-2021: DSAJ10
|
||||
#DIAN 1.1.-2021: NSAJ10
|
||||
fexml.set_element('./cac:AccountingSupplierParty/cac:Party/cac:PhysicalLocation/cac:Address/cbc:CityName',
|
||||
invoice.invoice_supplier.address.city.name)
|
||||
|
||||
# DIAN 1.1.-2021: DSAJ73
|
||||
# DIAN 1.1.-2021: NSAJ73
|
||||
fexml.set_element(
|
||||
'./cac:AccountingSupplierParty/cac:Party/cac:PhysicalLocation/cac:Address/cbc:PostalZone',
|
||||
invoice.invoice_supplier.address.postalzone.code)
|
||||
#DIAN 1.1.-2021: DSAJ73
|
||||
#DIAN 1.1.-2021: NSAJ73
|
||||
fexml.set_element('./cac:AccountingSupplierParty/cac:Party/cac:PhysicalLocation/cac:Address/cbc:PostalZone',
|
||||
invoice.invoice_supplier.address.postalzone.code)
|
||||
|
||||
# DIAN 1.1.-2021: DSAJ11
|
||||
# DIAN 1.1.-2021: NSAJ11
|
||||
fexml.set_element(
|
||||
'./cac:AccountingSupplierParty/cac:Party/cac:PhysicalLocation/cac:Address/cbc:CountrySubentity',
|
||||
invoice.invoice_supplier.address.countrysubentity.name)
|
||||
#DIAN 1.1.-2021: DSAJ11
|
||||
#DIAN 1.1.-2021: NSAJ11
|
||||
fexml.set_element('./cac:AccountingSupplierParty/cac:Party/cac:PhysicalLocation/cac:Address/cbc:CountrySubentity',
|
||||
invoice.invoice_supplier.address.countrysubentity.name)
|
||||
|
||||
# DIAN 1.1.-2021: DSAJ12
|
||||
# DIAN 1.1.-2021: NSAJ12
|
||||
fexml.set_element(
|
||||
'./cac:AccountingSupplierParty/cac:Party/cac:PhysicalLocation/cac:Address/cbc:CountrySubentityCode',
|
||||
invoice.invoice_supplier.address.countrysubentity.code)
|
||||
# DIAN 1.1.-2021: NSAJ13 NSAJ14
|
||||
fexml.set_element(
|
||||
'./cac:AccountingSupplierParty/cac:Party/cac:PhysicalLocation/cac:Address/cac:AddressLine/cbc:Line',
|
||||
invoice.invoice_supplier.address.street)
|
||||
#DIAN 1.1.-2021: DSAJ12
|
||||
#DIAN 1.1.-2021: NSAJ12
|
||||
fexml.set_element('./cac:AccountingSupplierParty/cac:Party/cac:PhysicalLocation/cac:Address/cbc:CountrySubentityCode',
|
||||
invoice.invoice_supplier.address.countrysubentity.code)
|
||||
#DIAN 1.1.-2021: NSAJ13 NSAJ14
|
||||
fexml.set_element('./cac:AccountingSupplierParty/cac:Party/cac:PhysicalLocation/cac:Address/cac:AddressLine/cbc:Line',
|
||||
invoice.invoice_supplier.address.street)
|
||||
|
||||
# DIAN 1.1.-2021: DSAJ15 DSAJ16
|
||||
# DIAN 1.1.-2021: NSAJ15 NSAJ16
|
||||
fexml.set_element(
|
||||
'./cac:AccountingSupplierParty/cac:Party/cac:PhysicalLocation/cac:Address/cac:Country/cbc:IdentificationCode',
|
||||
invoice.invoice_supplier.address.country.code)
|
||||
#DIAN 1.1.-2021: DSAJ15 DSAJ16
|
||||
#DIAN 1.1.-2021: NSAJ15 NSAJ16
|
||||
fexml.set_element('./cac:AccountingSupplierParty/cac:Party/cac:PhysicalLocation/cac:Address/cac:Country/cbc:IdentificationCode',
|
||||
invoice.invoice_supplier.address.country.code)
|
||||
|
||||
# DIAN 1.1.-2021: DSAJ17
|
||||
# DIAN 1.1.-2021: NSAJ17
|
||||
#DIAN 1.1.-2021: DSAJ17
|
||||
#DIAN 1.1.-2021: NSAJ17
|
||||
fexml.set_element('./cac:AccountingSupplierParty/cac:Party/cac:PhysicalLocation/cac:Address/cac:Country/cbc:Name',
|
||||
invoice.invoice_supplier.address.country.name,
|
||||
# DIAN 1.1.-2021: DSAJ18
|
||||
# # DIAN 1.1.-2021: NSAJ18
|
||||
languageID='es')
|
||||
#DIAN 1.1.-2021: DSAJ18
|
||||
#DIAN 1.1.-2021: NSAJ18
|
||||
languageID = 'es')
|
||||
|
||||
|
||||
supplier_company_id_attrs = fe.SCHEME_AGENCY_ATTRS.copy()
|
||||
supplier_company_id_attrs.update(
|
||||
{
|
||||
'schemeID': invoice.invoice_supplier.ident.dv,
|
||||
'schemeName': invoice.invoice_supplier.ident.type_fiscal})
|
||||
supplier_company_id_attrs.update({'schemeID': invoice.invoice_supplier.ident.dv,
|
||||
'schemeName': invoice.invoice_supplier.ident.type_fiscal})
|
||||
|
||||
# DIAN 1.1.-2021: DSAJ19
|
||||
# DIAN 1.1.-2021: NSAJ19
|
||||
fexml.placeholder_for(
|
||||
'./cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme')
|
||||
#DIAN 1.1.-2021: DSAJ19
|
||||
#DIAN 1.1.-2021: NSAJ19
|
||||
fexml.placeholder_for('./cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme')
|
||||
|
||||
# DIAN 1.1.-2021: DSAJ20
|
||||
# DIAN 1.1.-2021: NSAJ20
|
||||
fexml.set_element(
|
||||
'./cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme/cbc:RegistrationName',
|
||||
invoice.invoice_supplier.legal_name)
|
||||
#DIAN 1.1.-2021: DSAJ20
|
||||
#DIAN 1.1.-2021: NSAJ20
|
||||
fexml.set_element('./cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme/cbc:RegistrationName',
|
||||
invoice.invoice_supplier.legal_name)
|
||||
|
||||
# DIAN 1.1.-2021: DSAJ21
|
||||
# DIAN 1.1.-2021: NSAJ21
|
||||
fexml.set_element(
|
||||
'./cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme/cbc:CompanyID',
|
||||
invoice.invoice_supplier.ident,
|
||||
# DIAN 1.1.-2021: DSAJ22 DSAJ23 DSAJ24 DSAJ25
|
||||
# DIAN 1.1.-2021: NSAJ22 NSAJ23 NSAJ24 NSAJ25
|
||||
**supplier_company_id_attrs)
|
||||
#DIAN 1.1.-2021: DSAJ21
|
||||
#DIAN 1.1.-2021: NSAJ21
|
||||
fexml.set_element('./cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme/cbc:CompanyID',
|
||||
invoice.invoice_supplier.ident,
|
||||
#DIAN 1.1.-2021: DSAJ22 DSAJ23 DSAJ24 DSAJ25
|
||||
#DIAN 1.1.-2021: NSAJ22 NSAJ23 NSAJ24 NSAJ25
|
||||
**supplier_company_id_attrs)
|
||||
|
||||
# DIAN 1.1.-2021: DSAJ26
|
||||
# DIAN 1.1.-2021: NSAJ26
|
||||
fexml.set_element(
|
||||
'./cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme/cbc:TaxLevelCode',
|
||||
invoice.invoice_supplier.responsability_code,
|
||||
listName=invoice.invoice_supplier.responsability_regime_code)
|
||||
#DIAN 1.1.-2021: DSAJ26
|
||||
#DIAN 1.1.-2021: NSAJ26
|
||||
fexml.set_element('./cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme/cbc:TaxLevelCode',
|
||||
invoice.invoice_supplier.responsability_code,
|
||||
listName=invoice.invoice_supplier.responsability_regime_code)
|
||||
|
||||
# DIAN 1.1.-2021: DSAJ39
|
||||
# DIAN 1.1.-2021: NSAJ39
|
||||
fexml.placeholder_for(
|
||||
'./cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme/cac:TaxScheme')
|
||||
#DIAN 1.1.-2021: DSAJ39
|
||||
#DIAN 1.1.-2021: NSAJ39
|
||||
fexml.placeholder_for('./cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme/cac:TaxScheme')
|
||||
|
||||
# DIAN 1.1.-2021: DSAJ40
|
||||
# DIAN 1.1.-2021: NSAJ40
|
||||
fexml.set_element(
|
||||
'./cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme/cac:TaxScheme/cbc:ID',
|
||||
invoice.invoice_customer.tax_scheme.code)
|
||||
#DIAN 1.1.-2021: DSAJ40
|
||||
#DIAN 1.1.-2021: NSAJ40
|
||||
fexml.set_element('./cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme/cac:TaxScheme/cbc:ID',
|
||||
invoice.invoice_customer.tax_scheme.code)
|
||||
|
||||
#DIAN 1.1.-2021: DSAJ41
|
||||
#DIAN 1.1.-2021: NSAJ41
|
||||
fexml.set_element('./cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme/cac:TaxScheme/cbc:Name',
|
||||
invoice.invoice_customer.tax_scheme.name)
|
||||
|
||||
# DIAN 1.1.-2021: DSAJ41
|
||||
# DIAN 1.1.-2021: NSAJ41
|
||||
fexml.set_element(
|
||||
'./cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme/cac:TaxScheme/cbc:Name',
|
||||
invoice.invoice_customer.tax_scheme.name)
|
||||
|
||||
def set_customer(fexml, invoice):
|
||||
# DIAN 1.1.-2021: DSAK01
|
||||
# DIAN 1.1.-2021: NSAK01
|
||||
#DIAN 1.1.-2021: DSAK01
|
||||
#DIAN 1.1.-2021: NSAK01
|
||||
fexml.placeholder_for('./cac:AccountingCustomerParty')
|
||||
|
||||
# DIAN 1.1.-2021: DSAK02
|
||||
# DIAN 1.1.-2021: NSAK02
|
||||
fexml.set_element(
|
||||
'./cac:AccountingCustomerParty/cbc:AdditionalAccountID',
|
||||
invoice.invoice_customer.organization_code)
|
||||
#DIAN 1.1.-2021: DSAK02
|
||||
#DIAN 1.1.-2021: NSAK02
|
||||
fexml.set_element('./cac:AccountingCustomerParty/cbc:AdditionalAccountID',
|
||||
invoice.invoice_customer.organization_code)
|
||||
|
||||
# DIAN 1.1.-2021: DSAK03
|
||||
# DIAN 1.1.-2021: NSAK03
|
||||
#DIAN 1.1.-2021: DSAK03
|
||||
#DIAN 1.1.-2021: NSAK03
|
||||
fexml.placeholder_for('./cac:AccountingCustomerParty/cac:Party')
|
||||
|
||||
# DIAN 1.1.-2021: DSAK19
|
||||
# DIAN 1.1.-2021: NSAK19
|
||||
fexml.placeholder_for(
|
||||
'./cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme')
|
||||
#DIAN 1.1.-2021: DSAK19
|
||||
#DIAN 1.1.-2021: NSAK19
|
||||
fexml.placeholder_for('./cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme')
|
||||
|
||||
# DIAN 1.1.-2021: DSAK20
|
||||
# DIAN 1.1.-2021: NSAK20
|
||||
fexml.set_element(
|
||||
'./cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cbc:RegistrationName',
|
||||
invoice.invoice_customer.legal_name)
|
||||
#DIAN 1.1.-2021: DSAK20
|
||||
#DIAN 1.1.-2021: NSAK20
|
||||
fexml.set_element('./cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cbc:RegistrationName',
|
||||
invoice.invoice_customer.legal_name)
|
||||
|
||||
customer_company_id_attrs = fe.SCHEME_AGENCY_ATTRS.copy()
|
||||
customer_company_id_attrs.update(
|
||||
{
|
||||
'schemeID': invoice.invoice_customer.ident.dv,
|
||||
'schemeName': invoice.invoice_customer.ident.type_fiscal})
|
||||
customer_company_id_attrs.update({'schemeID': invoice.invoice_customer.ident.dv,
|
||||
'schemeName': invoice.invoice_customer.ident.type_fiscal})
|
||||
|
||||
# DIAN 1.1.-2021: DSAK21
|
||||
# DIAN 1.1.-2021: NSAK21
|
||||
fexml.set_element(
|
||||
'./cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cbc:CompanyID',
|
||||
invoice.invoice_customer.ident,
|
||||
# DIAN 1.1.-2021: DSAK22 DSAK23 DSAK24 DSAK25
|
||||
# DIAN 1.1.-2021: NSAK22 NSAK23 NSAK24 NSAK25
|
||||
**customer_company_id_attrs)
|
||||
#DIAN 1.1.-2021: DSAK21
|
||||
#DIAN 1.1.-2021: NSAK21
|
||||
fexml.set_element('./cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cbc:CompanyID',
|
||||
invoice.invoice_customer.ident,
|
||||
#DIAN 1.1.-2021: DSAK22 DSAK23 DSAK24 DSAK25
|
||||
#DIAN 1.1.-2021: NSAK22 NSAK23 NSAK24 NSAK25
|
||||
**customer_company_id_attrs)
|
||||
|
||||
# DIAN 1.1.-2021: DSAK26
|
||||
# DIAN 1.1.-2021: NSAK26
|
||||
fexml.set_element(
|
||||
'./cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cbc:TaxLevelCode',
|
||||
invoice.invoice_customer.responsability_code)
|
||||
#DIAN 1.1.-2021: DSAK26
|
||||
#DIAN 1.1.-2021: NSAK26
|
||||
fexml.set_element('./cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cbc:TaxLevelCode',
|
||||
invoice.invoice_customer.responsability_code)
|
||||
|
||||
# DIAN 1.1.-2021: DSAK39
|
||||
# DIAN 1.1.-2021: NSAK39
|
||||
fexml.placeholder_for(
|
||||
'./cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cac:TaxScheme')
|
||||
#DIAN 1.1.-2021: DSAK39
|
||||
#DIAN 1.1.-2021: NSAK39
|
||||
fexml.placeholder_for('./cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cac:TaxScheme')
|
||||
|
||||
# DIAN 1.1.-2021: DSAK40
|
||||
# DIAN 1.1.-2021: NSAK40
|
||||
fexml.set_element(
|
||||
'./cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cac:TaxScheme/cbc:ID',
|
||||
invoice.invoice_customer.tax_scheme.code)
|
||||
#DIAN 1.1.-2021: DSAK40
|
||||
#DIAN 1.1.-2021: NSAK40
|
||||
fexml.set_element('./cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cac:TaxScheme/cbc:ID',
|
||||
invoice.invoice_customer.tax_scheme.code)
|
||||
|
||||
#DIAN 1.1.-2021: DSAK41
|
||||
#DIAN 1.1.-2021: NSAK41
|
||||
fexml.set_element('./cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cac:TaxScheme/cbc:Name',
|
||||
invoice.invoice_customer.tax_scheme.name)
|
||||
|
||||
# DIAN 1.1.-2021: DSAK41
|
||||
# DIAN 1.1.-2021: NSAK41
|
||||
fexml.set_element(
|
||||
'./cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cac:TaxScheme/cbc:Name',
|
||||
invoice.invoice_customer.tax_scheme.name)
|
||||
|
||||
def set_payment_mean(fexml, invoice):
|
||||
payment_mean = invoice.invoice_payment_mean
|
||||
|
||||
# DIAN 1.1.-2021: DSAN01 DSAN02
|
||||
# DIAN 1.1.-2021: NSAN02 NSAN02
|
||||
#DIAN 1.1.-2021: DSAN01 DSAN02
|
||||
#DIAN 1.1.-2021: NSAN02 NSAN02
|
||||
fexml.set_element('./cac:PaymentMeans/cbc:ID', payment_mean.id)
|
||||
|
||||
# DIAN 1.1.-2021: DSAN03
|
||||
# DIAN 1.1.-2021: NSAN03
|
||||
fexml.set_element(
|
||||
'./cac:PaymentMeans/cbc:PaymentMeansCode',
|
||||
payment_mean.code)
|
||||
#DIAN 1.1.-2021: DSAN03
|
||||
#DIAN 1.1.-2021: NSAN03
|
||||
fexml.set_element('./cac:PaymentMeans/cbc:PaymentMeansCode', payment_mean.code)
|
||||
|
||||
# DIAN 1.1.-2021: DSAN04
|
||||
# DIAN 1.1.-2021: NSAN04
|
||||
fexml.set_element(
|
||||
'./cac:PaymentMeans/cbc:PaymentDueDate',
|
||||
payment_mean.due_at.strftime('%Y-%m-%d'))
|
||||
#DIAN 1.1.-2021: DSAN04
|
||||
#DIAN 1.1.-2021: NSAN04
|
||||
fexml.set_element('./cac:PaymentMeans/cbc:PaymentDueDate', payment_mean.due_at.strftime('%Y-%m-%d'))
|
||||
|
||||
# DIAN 1.1.-2021: DSAN05
|
||||
# DIAN 1.1.-2021: NSAN05
|
||||
fexml.set_element(
|
||||
'./cac:PaymentMeans/cbc:PaymentID',
|
||||
payment_mean.payment_id)
|
||||
#DIAN 1.1.-2021: DSAN05
|
||||
#DIAN 1.1.-2021: NSAN05
|
||||
fexml.set_element('./cac:PaymentMeans/cbc:PaymentID', payment_mean.payment_id)
|
||||
|
||||
def set_element_amount_for(fexml, xml, xpath, amount):
|
||||
if not isinstance(amount, Amount):
|
||||
@ -272,47 +228,40 @@ class DIANSupportDocumentXML(fe.FeXML):
|
||||
fexml.set_element(xpath, amount, currencyID=amount.currency.code)
|
||||
|
||||
def set_legal_monetary(fexml, invoice):
|
||||
# DIAN 1.1.-2021: DSAU01 DSAU02 DSAU03
|
||||
# DIAN 1.1.-2021: NSAU01 NSAU02 NSAU03
|
||||
fexml.set_element_amount(
|
||||
'./cac:LegalMonetaryTotal/cbc:LineExtensionAmount',
|
||||
invoice.invoice_legal_monetary_total.line_extension_amount)
|
||||
#DIAN 1.1.-2021: DSAU01 DSAU02 DSAU03
|
||||
#DIAN 1.1.-2021: NSAU01 NSAU02 NSAU03
|
||||
fexml.set_element_amount('./cac:LegalMonetaryTotal/cbc:LineExtensionAmount',
|
||||
invoice.invoice_legal_monetary_total.line_extension_amount)
|
||||
|
||||
# DIAN 1.1.-2021: DSAU04 DSAU05
|
||||
# DIAN 1.1.-2021: NSAU04 NSAU05
|
||||
fexml.set_element_amount(
|
||||
'./cac:LegalMonetaryTotal/cbc:TaxExclusiveAmount',
|
||||
invoice.invoice_legal_monetary_total.tax_exclusive_amount)
|
||||
#DIAN 1.1.-2021: DSAU04 DSAU05
|
||||
#DIAN 1.1.-2021: NSAU04 NSAU05
|
||||
fexml.set_element_amount('./cac:LegalMonetaryTotal/cbc:TaxExclusiveAmount',
|
||||
invoice.invoice_legal_monetary_total.tax_exclusive_amount)
|
||||
|
||||
# DIAN 1.1.-2021: DSAU06 DSAU07
|
||||
# DIAN 1.1.-2021: NSAU06 DSAU07
|
||||
fexml.set_element_amount(
|
||||
'./cac:LegalMonetaryTotal/cbc:TaxInclusiveAmount',
|
||||
invoice.invoice_legal_monetary_total.tax_inclusive_amount)
|
||||
#DIAN 1.1.-2021: DSAU06 DSAU07
|
||||
#DIAN 1.1.-2021: NSAU06 DSAU07
|
||||
fexml.set_element_amount('./cac:LegalMonetaryTotal/cbc:TaxInclusiveAmount',
|
||||
invoice.invoice_legal_monetary_total.tax_inclusive_amount)
|
||||
|
||||
# DIAN 1.1.-2021: DSAU10 DSAU11
|
||||
# DIAN 1.1.-2021: NSAU10 DSAU11
|
||||
fexml.set_element_amount(
|
||||
'./cac:LegalMonetaryTotal/cbc:ChargeTotalAmount',
|
||||
invoice.invoice_legal_monetary_total.charge_total_amount)
|
||||
#DIAN 1.1.-2021: DSAU10 DSAU11
|
||||
#DIAN 1.1.-2021: NSAU10 DSAU11
|
||||
fexml.set_element_amount('./cac:LegalMonetaryTotal/cbc:ChargeTotalAmount',
|
||||
invoice.invoice_legal_monetary_total.charge_total_amount)
|
||||
|
||||
#DIAN 1.1.-2021: DSAU14 DSAU15
|
||||
#DIAN 1.1.-2021: NSAU14 DSAU15
|
||||
fexml.set_element_amount('./cac:LegalMonetaryTotal/cbc:PayableAmount',
|
||||
invoice.invoice_legal_monetary_total.payable_amount)
|
||||
|
||||
# DIAN 1.1.-2021: DSAU14 DSAU15
|
||||
# DIAN 1.1.-2021: NSAU14 DSAU15
|
||||
fexml.set_element_amount(
|
||||
'./cac:LegalMonetaryTotal/cbc:PayableAmount',
|
||||
invoice.invoice_legal_monetary_total.payable_amount)
|
||||
|
||||
def _set_invoice_document_reference(fexml, reference):
|
||||
fexml._do_set_billing_reference(
|
||||
reference, 'cac:InvoiceDocumentReference')
|
||||
fexml._do_set_billing_reference(reference, 'cac:InvoiceDocumentReference')
|
||||
|
||||
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):
|
||||
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):
|
||||
|
||||
@ -321,15 +270,13 @@ class DIANSupportDocumentXML(fe.FeXML):
|
||||
else:
|
||||
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)
|
||||
fexml.set_element(
|
||||
'./cac:BillingReference/cac:InvoiceDocumentReference/cbc:UUID',
|
||||
reference.uuid,
|
||||
schemeName=schemeName)
|
||||
fexml.set_element(
|
||||
'./cac:BillingReference/cac:InvoiceDocumentReference/cbc:IssueDate',
|
||||
reference.date.strftime("%Y-%m-%d"))
|
||||
fexml.set_element('./cac:BillingReference/cac:InvoiceDocumentReference/cbc:UUID',
|
||||
reference.uuid,
|
||||
schemeName=schemeName)
|
||||
fexml.set_element('./cac:BillingReference/cac:InvoiceDocumentReference/cbc:IssueDate',
|
||||
reference.date.strftime("%Y-%m-%d"))
|
||||
|
||||
def set_billing_reference(fexml, invoice):
|
||||
reference = invoice.invoice_billing_reference
|
||||
@ -357,11 +304,11 @@ class DIANSupportDocumentXML(fe.FeXML):
|
||||
return fexml._set_invoice_document_reference(reference)
|
||||
|
||||
fexml.set_element('./cac:DiscrepancyResponse/cbc:ReferenceID',
|
||||
reference.id)
|
||||
reference.id)
|
||||
fexml.set_element('./cac:DiscrepancyResponse/cbc:ResponseCode',
|
||||
reference.code)
|
||||
reference.code)
|
||||
fexml.set_element('./cac:DiscrepancyResponse/cbc:Description',
|
||||
reference.description)
|
||||
reference.description)
|
||||
|
||||
def set_invoice_totals(fexml, invoice):
|
||||
tax_amount_for = defaultdict(lambda: defaultdict(lambda: Amount(0.0)))
|
||||
@ -372,64 +319,60 @@ class DIANSupportDocumentXML(fe.FeXML):
|
||||
for invoice_line in invoice.invoice_lines:
|
||||
for subtotal in invoice_line.tax.subtotals:
|
||||
if subtotal.scheme is not None:
|
||||
tax_amount_for[
|
||||
subtotal.scheme.code][
|
||||
'tax_amount'] += subtotal.tax_amount
|
||||
tax_amount_for[subtotal.scheme.code][
|
||||
'taxable_amount'] += invoice_line.taxable_amount
|
||||
tax_amount_for[subtotal.scheme.code]['tax_amount'] += subtotal.tax_amount
|
||||
tax_amount_for[subtotal.scheme.code]['taxable_amount'] += invoice_line.taxable_amount
|
||||
|
||||
# MACHETE ojo InvoiceLine.tax pasar a Invoice
|
||||
percent_for[subtotal.scheme.code] = subtotal.percent
|
||||
|
||||
total_tax_amount += subtotal.tax_amount
|
||||
|
||||
|
||||
if total_tax_amount != Amount(0.0):
|
||||
fexml.placeholder_for('./cac:TaxTotal')
|
||||
fexml.set_element_amount('./cac:TaxTotal/cbc:TaxAmount',
|
||||
total_tax_amount)
|
||||
total_tax_amount)
|
||||
|
||||
|
||||
for index, item in enumerate(tax_amount_for.items()):
|
||||
cod_impuesto, amount_of = item
|
||||
next_append = index > 0
|
||||
|
||||
# DIAN 1.7.-2020: FAS01
|
||||
#DIAN 1.7.-2020: FAS01
|
||||
line = fexml.fragment('./cac:TaxTotal', append=next_append)
|
||||
# DIAN 1.7.-2020: FAU06
|
||||
#DIAN 1.7.-2020: FAU06
|
||||
tax_amount = amount_of['tax_amount']
|
||||
fexml.set_element_amount_for(line,
|
||||
'/cac:TaxTotal/cbc:TaxAmount',
|
||||
tax_amount)
|
||||
|
||||
# DIAN 1.7.-2020: FAS05
|
||||
fexml.set_element_amount_for(
|
||||
line,
|
||||
'/cac:TaxTotal/cac:TaxSubtotal/cbc:TaxableAmount',
|
||||
amount_of['taxable_amount'])
|
||||
#DIAN 1.7.-2020: FAS05
|
||||
fexml.set_element_amount_for(line,
|
||||
'/cac:TaxTotal/cac:TaxSubtotal/cbc:TaxableAmount',
|
||||
amount_of['taxable_amount'])
|
||||
|
||||
# DIAN 1.7.-2020: FAU06
|
||||
fexml.set_element_amount_for(
|
||||
line,
|
||||
'/cac:TaxTotal/cac:TaxSubtotal/cbc:TaxAmount',
|
||||
amount_of['tax_amount'])
|
||||
#DIAN 1.7.-2020: FAU06
|
||||
fexml.set_element_amount_for(line,
|
||||
'/cac:TaxTotal/cac:TaxSubtotal/cbc:TaxAmount',
|
||||
amount_of['tax_amount'])
|
||||
|
||||
# DIAN 1.7.-2020: FAS07
|
||||
#DIAN 1.7.-2020: FAS07
|
||||
if percent_for[cod_impuesto]:
|
||||
line.set_element('/cac:TaxTotal/cac:TaxSubtotal/cbc:Percent',
|
||||
percent_for[cod_impuesto])
|
||||
|
||||
if percent_for[cod_impuesto]:
|
||||
line.set_element(
|
||||
'/cac:TaxTotal/cac:TaxSubtotal/cac:TaxCategory/cbc:Percent',
|
||||
percent_for[cod_impuesto])
|
||||
|
||||
line.set_element(
|
||||
'/cac:TaxTotal/cac:TaxSubtotal/cac:TaxCategory/cac:TaxScheme/cbc:ID',
|
||||
cod_impuesto)
|
||||
line.set_element(
|
||||
'/cac:TaxTotal/cac:TaxSubtotal/cac:TaxCategory/cac:TaxScheme/cbc:Name', 'IVA')
|
||||
if percent_for[cod_impuesto]:
|
||||
line.set_element('/cac:TaxTotal/cac:TaxSubtotal/cac:TaxCategory/cbc:Percent',
|
||||
percent_for[cod_impuesto])
|
||||
|
||||
line.set_element('/cac:TaxTotal/cac:TaxSubtotal/cac:TaxCategory/cac:TaxScheme/cbc:ID',
|
||||
cod_impuesto)
|
||||
line.set_element('/cac:TaxTotal/cac:TaxSubtotal/cac:TaxCategory/cac:TaxScheme/cbc:Name',
|
||||
'IVA')
|
||||
|
||||
|
||||
# abstract method
|
||||
|
||||
def tag_document(fexml):
|
||||
return 'Invoice'
|
||||
|
||||
@ -441,83 +384,50 @@ class DIANSupportDocumentXML(fe.FeXML):
|
||||
fexml.set_element_amount_for(line,
|
||||
'./cac:WithholdingTaxTotal/cbc:TaxAmount',
|
||||
invoice_line.withholding_amount)
|
||||
# DIAN 1.7.-2020: FAX05
|
||||
fexml.set_element_amount_for(
|
||||
line,
|
||||
'./cac:WithholdingTaxTotal/cac:TaxSubtotal/cbc:TaxableAmount',
|
||||
invoice_line.withholding_taxable_amount)
|
||||
#DIAN 1.7.-2020: FAX05
|
||||
fexml.set_element_amount_for(line,
|
||||
'./cac:WithholdingTaxTotal/cac:TaxSubtotal/cbc:TaxableAmount',
|
||||
invoice_line.withholding_taxable_amount)
|
||||
|
||||
for subtotal in invoice_line.withholding.subtotals:
|
||||
line.set_element(
|
||||
'./cac:WithholdingTaxTotal/cac:TaxSubtotal/cbc:TaxAmount',
|
||||
subtotal.tax_amount,
|
||||
currencyID='COP')
|
||||
line.set_element('./cac:WithholdingTaxTotal/cac:TaxSubtotal/cbc:TaxAmount', subtotal.tax_amount, currencyID='COP')
|
||||
|
||||
if subtotal.percent is not None:
|
||||
line.set_element(
|
||||
'./cac:WithholdingTaxTotal/cac:TaxSubtotal/cac:TaxCategory/cbc:Percent',
|
||||
'%0.2f' %
|
||||
round(
|
||||
subtotal.percent,
|
||||
2))
|
||||
line.set_element('./cac:WithholdingTaxTotal/cac:TaxSubtotal/cac:TaxCategory/cbc:Percent', '%0.2f' % round(subtotal.percent, 2))
|
||||
|
||||
if subtotal.scheme is not None:
|
||||
# DIAN 1.7.-2020: FAX15
|
||||
line.set_element(
|
||||
'./cac:WithholdingTaxTotal/cac:TaxSubtotal/cac:TaxCategory/cac:TaxScheme/cbc:ID',
|
||||
subtotal.scheme.code)
|
||||
line.set_element(
|
||||
'./cac:WithholdingTaxTotal/cac:TaxSubtotal/cac:TaxCategory/cac:TaxScheme/cbc:Name',
|
||||
subtotal.scheme.name)
|
||||
#DIAN 1.7.-2020: FAX15
|
||||
line.set_element('./cac:WithholdingTaxTotal/cac:TaxSubtotal/cac:TaxCategory/cac:TaxScheme/cbc:ID', subtotal.scheme.code)
|
||||
line.set_element('./cac:WithholdingTaxTotal/cac:TaxSubtotal/cac:TaxCategory/cac:TaxScheme/cbc:Name', subtotal.scheme.name)
|
||||
|
||||
def set_invoice_line_tax(fexml, line, invoice_line):
|
||||
fexml.set_element_amount_for(line,
|
||||
'./cac:TaxTotal/cbc:TaxAmount',
|
||||
invoice_line.tax_amount)
|
||||
|
||||
# DIAN 1.7.-2020: FAX05
|
||||
fexml.set_element_amount_for(
|
||||
line,
|
||||
'./cac:TaxTotal/cac:TaxSubtotal/cbc:TaxableAmount',
|
||||
invoice_line.taxable_amount)
|
||||
#DIAN 1.7.-2020: FAX05
|
||||
fexml.set_element_amount_for(line,
|
||||
'./cac:TaxTotal/cac:TaxSubtotal/cbc:TaxableAmount',
|
||||
invoice_line.taxable_amount)
|
||||
for subtotal in invoice_line.tax.subtotals:
|
||||
line.set_element(
|
||||
'./cac:TaxTotal/cac:TaxSubtotal/cbc:TaxAmount',
|
||||
subtotal.tax_amount,
|
||||
currencyID='COP')
|
||||
line.set_element('./cac:TaxTotal/cac:TaxSubtotal/cbc:TaxAmount', subtotal.tax_amount, currencyID='COP')
|
||||
|
||||
if subtotal.percent is not None:
|
||||
line.set_element(
|
||||
'./cac:TaxTotal/cac:TaxSubtotal/cac:TaxCategory/cbc:Percent',
|
||||
'%0.2f' %
|
||||
round(
|
||||
subtotal.percent,
|
||||
2))
|
||||
line.set_element('./cac:TaxTotal/cac:TaxSubtotal/cac:TaxCategory/cbc:Percent', '%0.2f' % round(subtotal.percent, 2))
|
||||
|
||||
if subtotal.scheme is not None:
|
||||
# DIAN 1.7.-2020: FAX15
|
||||
line.set_element(
|
||||
'./cac:TaxTotal/cac:TaxSubtotal/cac:TaxCategory/cac:TaxScheme/cbc:ID',
|
||||
subtotal.scheme.code)
|
||||
line.set_element(
|
||||
'./cac:TaxTotal/cac:TaxSubtotal/cac:TaxCategory/cac:TaxScheme/cbc:Name',
|
||||
subtotal.scheme.name)
|
||||
#DIAN 1.7.-2020: FAX15
|
||||
line.set_element('./cac:TaxTotal/cac:TaxSubtotal/cac:TaxCategory/cac:TaxScheme/cbc:ID', subtotal.scheme.code)
|
||||
line.set_element('./cac:TaxTotal/cac:TaxSubtotal/cac:TaxCategory/cac:TaxScheme/cbc:Name', subtotal.scheme.name)
|
||||
|
||||
def set_invoice_lines(fexml, invoice):
|
||||
next_append = False
|
||||
for index, invoice_line in enumerate(invoice.invoice_lines):
|
||||
line = fexml.fragment(
|
||||
'./cac:%sLine' %
|
||||
(fexml.tag_document()),
|
||||
append=next_append)
|
||||
line = fexml.fragment('./cac:%sLine' % (fexml.tag_document()), append=next_append)
|
||||
next_append = True
|
||||
|
||||
line.set_element('./cbc:ID', index + 1)
|
||||
line.set_element(
|
||||
'./cbc:%sQuantity' %
|
||||
(fexml.tag_document_concilied()),
|
||||
invoice_line.quantity,
|
||||
unitCode='NAR')
|
||||
line.set_element('./cbc:%sQuantity' % (fexml.tag_document_concilied()), invoice_line.quantity, unitCode = 'NAR')
|
||||
fexml.set_element_amount_for(line,
|
||||
'./cbc:LineExtensionAmount',
|
||||
invoice_line.total_amount)
|
||||
@ -525,69 +435,53 @@ class DIANSupportDocumentXML(fe.FeXML):
|
||||
period = line.fragment('./cac:InvoicePeriod')
|
||||
period.set_element('./cbc:StartDate',
|
||||
datetime.now().strftime('%Y-%m-%d'))
|
||||
period.set_element(
|
||||
'./cbc:DescriptionCode', '1')
|
||||
period.set_element('./cbc:DescriptionCode',
|
||||
'1')
|
||||
period.set_element('./cbc:Description',
|
||||
'Por operación')
|
||||
|
||||
if not isinstance(invoice_line.tax, TaxTotalOmit):
|
||||
fexml.set_invoice_line_tax(line, invoice_line)
|
||||
|
||||
if not isinstance(
|
||||
invoice_line.withholding,
|
||||
WithholdingTaxTotalOmit):
|
||||
if not isinstance(invoice_line.withholding, WithholdingTaxTotalOmit):
|
||||
fexml.set_invoice_line_withholding(line, invoice_line)
|
||||
|
||||
line.set_element(
|
||||
'./cac:Item/cbc:Description',
|
||||
invoice_line.item.description)
|
||||
|
||||
line.set_element(
|
||||
'./cac:Item/cac:StandardItemIdentification/cbc:ID',
|
||||
invoice_line.item.id,
|
||||
schemeID=invoice_line.item.scheme_id,
|
||||
schemeName=invoice_line.item.scheme_name,
|
||||
schemeAgencyID=invoice_line.item.scheme_agency_id)
|
||||
line.set_element('./cac:Item/cbc:Description', invoice_line.item.description)
|
||||
|
||||
line.set_element(
|
||||
'./cac:Price/cbc:PriceAmount',
|
||||
invoice_line.price.amount,
|
||||
currencyID=invoice_line.price.amount.currency.code)
|
||||
# DIAN 1.7.-2020: FBB04
|
||||
line.set_element('./cac:Item/cac:StandardItemIdentification/cbc:ID',
|
||||
invoice_line.item.id,
|
||||
schemeID=invoice_line.item.scheme_id,
|
||||
schemeName=invoice_line.item.scheme_name,
|
||||
schemeAgencyID=invoice_line.item.scheme_agency_id)
|
||||
|
||||
line.set_element('./cac:Price/cbc:PriceAmount', invoice_line.price.amount, currencyID=invoice_line.price.amount.currency.code)
|
||||
#DIAN 1.7.-2020: FBB04
|
||||
line.set_element('./cac:Price/cbc:BaseQuantity',
|
||||
invoice_line.quantity,
|
||||
unitCode=invoice_line.quantity.code)
|
||||
|
||||
for idx, charge in enumerate(invoice_line.allowance_charge):
|
||||
next_append_charge = idx > 0
|
||||
fexml.append_allowance_charge(
|
||||
line, index + 1, charge, append=next_append_charge)
|
||||
fexml.append_allowance_charge(line, index + 1, charge, append=next_append_charge)
|
||||
|
||||
def set_allowance_charge(fexml, invoice):
|
||||
for idx, charge in enumerate(invoice.invoice_allowance_charge):
|
||||
next_append = idx > 0
|
||||
fexml.append_allowance_charge(
|
||||
fexml, idx + 1, charge, append=next_append)
|
||||
fexml.append_allowance_charge(fexml, idx + 1, charge, append=next_append)
|
||||
|
||||
def append_allowance_charge(fexml, parent, idx, charge, append=False):
|
||||
line = parent.fragment('./cac:AllowanceCharge', append=append)
|
||||
# DIAN 1.7.-2020: FAQ02
|
||||
line.set_element('./cbc:ID', idx)
|
||||
# DIAN 1.7.-2020: FAQ03
|
||||
line.set_element('./cbc:ChargeIndicator',
|
||||
str(charge.charge_indicator).lower())
|
||||
if charge.reason:
|
||||
line.set_element(
|
||||
'./cbc:AllowanceChargeReasonCode',
|
||||
charge.reason.code)
|
||||
line.set_element(
|
||||
'./cbc:allowanceChargeReason',
|
||||
charge.reason.reason)
|
||||
line.set_element('./cbc:MultiplierFactorNumeric',
|
||||
str(round(charge.multiplier_factor_numeric, 2)))
|
||||
fexml.set_element_amount_for(line, './cbc:Amount', charge.amount)
|
||||
fexml.set_element_amount_for(
|
||||
line, './cbc:BaseAmount', charge.base_amount)
|
||||
line = parent.fragment('./cac:AllowanceCharge', append=append)
|
||||
#DIAN 1.7.-2020: FAQ02
|
||||
line.set_element('./cbc:ID', idx)
|
||||
#DIAN 1.7.-2020: FAQ03
|
||||
line.set_element('./cbc:ChargeIndicator', str(charge.charge_indicator).lower())
|
||||
if charge.reason:
|
||||
line.set_element('./cbc:AllowanceChargeReasonCode', charge.reason.code)
|
||||
line.set_element('./cbc:allowanceChargeReason', charge.reason.reason)
|
||||
line.set_element('./cbc:MultiplierFactorNumeric', str(round(charge.multiplier_factor_numeric, 2)))
|
||||
fexml.set_element_amount_for(line, './cbc:Amount', charge.amount)
|
||||
fexml.set_element_amount_for(line, './cbc:BaseAmount', charge.base_amount)
|
||||
|
||||
def attach_invoice(fexml, invoice):
|
||||
"""adiciona etiquetas a FEXML y retorna FEXML
|
||||
@ -595,38 +489,26 @@ class DIANSupportDocumentXML(fe.FeXML):
|
||||
|
||||
fexml.placeholder_for('./ext:UBLExtensions')
|
||||
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:ProfileExecutionID')
|
||||
fexml.set_element('./cbc:ID', invoice.invoice_ident)
|
||||
fexml.placeholder_for('./cbc:UUID')
|
||||
fexml.set_element('./cbc:DocumentCurrencyCode', 'COP')
|
||||
fexml.set_element(
|
||||
'./cbc:IssueDate',
|
||||
invoice.invoice_issue.strftime('%Y-%m-%d'))
|
||||
# DIAN 1.7.-2020: FAD10
|
||||
fexml.set_element(
|
||||
'./cbc:IssueTime',
|
||||
invoice.invoice_issue.strftime('%H:%M:%S-05:00'))
|
||||
fexml.set_element(
|
||||
'./cbc:%sTypeCode' %
|
||||
(fexml.tag_document()),
|
||||
invoice.invoice_type_code,
|
||||
listAgencyID='195',
|
||||
listAgencyName='No matching global declaration available for the validation root',
|
||||
listURI='http://www.dian.gov.co')
|
||||
fexml.set_element('./cbc:IssueDate', invoice.invoice_issue.strftime('%Y-%m-%d'))
|
||||
#DIAN 1.7.-2020: FAD10
|
||||
fexml.set_element('./cbc:IssueTime', invoice.invoice_issue.strftime('%H:%M:%S-05:00'))
|
||||
fexml.set_element('./cbc:%sTypeCode' % (fexml.tag_document()),
|
||||
invoice.invoice_type_code,
|
||||
listAgencyID='195',
|
||||
listAgencyName='No matching global declaration available for the validation root',
|
||||
listURI='http://www.dian.gov.co')
|
||||
fexml.set_element('./cbc:LineCountNumeric', len(invoice.invoice_lines))
|
||||
fexml.set_element(
|
||||
'./cac:%sPeriod/cbc:StartDate' %
|
||||
(fexml.tag_document()),
|
||||
invoice.invoice_period_start.strftime('%Y-%m-%d'))
|
||||
fexml.set_element('./cac:%sPeriod/cbc: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_element('./cac:%sPeriod/cbc:EndDate' % (fexml.tag_document()),
|
||||
invoice.invoice_period_end.strftime('%Y-%m-%d'))
|
||||
|
||||
fexml.customize(invoice)
|
||||
|
||||
|
@ -1,10 +1,9 @@
|
||||
# from .. import fe
|
||||
# from ..form import *
|
||||
from .. import fe
|
||||
from ..form import *
|
||||
from .support_document import DIANSupportDocumentXML
|
||||
|
||||
__all__ = ['DIANSupportDocumentCreditNoteXML']
|
||||
|
||||
|
||||
class DIANSupportDocumentCreditNoteXML(DIANSupportDocumentXML):
|
||||
"""
|
||||
DianInvoiceXML mapea objeto form.Invoice a XML segun
|
||||
@ -12,14 +11,11 @@ class DIANSupportDocumentCreditNoteXML(DIANSupportDocumentXML):
|
||||
"""
|
||||
|
||||
def __init__(self, invoice):
|
||||
super(
|
||||
DIANSupportDocumentCreditNoteXML,
|
||||
self).__init__(
|
||||
invoice,
|
||||
'CreditNote')
|
||||
super(DIANSupportDocumentCreditNoteXML, self).__init__(invoice, 'CreditNote')
|
||||
|
||||
def tag_document(fexml):
|
||||
return 'CreditNote'
|
||||
|
||||
def tag_document_concilied(fexml):
|
||||
return 'Credited'
|
||||
|
||||
|
@ -2,30 +2,18 @@ from .. import fe
|
||||
|
||||
__all__ = ['DIANWrite', 'DIANWriteSigned']
|
||||
|
||||
|
||||
def DIANWrite(xml, filename):
|
||||
document = xml.tostring(xml_declaration=True, encoding='UTF-8')
|
||||
with open(filename, 'w') as f:
|
||||
f.write(document)
|
||||
|
||||
|
||||
def DIANWriteSigned(
|
||||
xml,
|
||||
filename,
|
||||
private_key,
|
||||
passphrase,
|
||||
use_cache_policy=False,
|
||||
dian_signer=None):
|
||||
document = xml.tostring(
|
||||
xml_declaration=True,
|
||||
encoding='UTF-8').encode('utf-8')
|
||||
def DIANWriteSigned(xml, filename, private_key, passphrase, use_cache_policy=False, dian_signer=None):
|
||||
document = xml.tostring(xml_declaration=True, encoding='UTF-8').encode('utf-8')
|
||||
if dian_signer is None:
|
||||
dian_signer = fe.DianXMLExtensionSigner
|
||||
|
||||
signer = dian_signer(
|
||||
private_key,
|
||||
passphrase=passphrase,
|
||||
localpolicy=use_cache_policy)
|
||||
signer = dian_signer(private_key, passphrase=passphrase, localpolicy=use_cache_policy)
|
||||
|
||||
with open(filename, 'w') as f:
|
||||
f.write(signer.sign_xml_string(document))
|
||||
|
@ -1,4 +1,4 @@
|
||||
from dataclasses import dataclass, field
|
||||
from dataclasses import dataclass
|
||||
|
||||
from ..amount import Amount
|
||||
|
||||
@ -29,7 +29,7 @@ class Trabajador:
|
||||
|
||||
codigo_trabajador: str = None
|
||||
otros_nombres: str = None
|
||||
sub_tipo: SubTipoTrabajador = field(default_factory=lambda: SubTipoTrabajador(code='00'))
|
||||
sub_tipo: SubTipoTrabajador = SubTipoTrabajador(code='00')
|
||||
|
||||
def apply(self, fragment):
|
||||
fragment.set_attributes('./Trabajador',
|
||||
|
@ -1,17 +0,0 @@
|
||||
attrs==22.1.0
|
||||
distlib==0.3.6
|
||||
filelock==3.8.0
|
||||
iniconfig==1.1.1
|
||||
packaging==21.3
|
||||
platformdirs==2.5.2
|
||||
pluggy==1.0.0
|
||||
py==1.11.0
|
||||
pyparsing==3.0.9
|
||||
pytest==7.1.3
|
||||
semantic-version==2.10.0
|
||||
setuptools-rust==1.5.2
|
||||
six==1.16.0
|
||||
tomli==2.0.1
|
||||
tox==3.26.0
|
||||
typing_extensions==4.4.0
|
||||
virtualenv==20.16.5
|
@ -22,4 +22,4 @@ exclude = docs
|
||||
test = pytest
|
||||
|
||||
[tool:pytest]
|
||||
addopts = --ignore=setup.py
|
||||
collect_ignore = ['setup.py']
|
||||
|
26
setup.py
26
setup.py
@ -13,22 +13,6 @@ with open('README.rst') as readme_file:
|
||||
with open('HISTORY.rst') as history_file:
|
||||
history = history_file.read()
|
||||
|
||||
requirements = ['Click>=8.1.7',
|
||||
'zeep==4.2.1',
|
||||
'lxml==5.2.2',
|
||||
'cryptography==3.3.2',
|
||||
'pyOpenSSL==20.0.1',
|
||||
'xmlsig==0.1.7',
|
||||
'xades==1.0.0',
|
||||
'xmlsec==1.3.14',
|
||||
'python-dateutil==2.9.0.post0',
|
||||
# usamos esta dependencia en runtime
|
||||
# para forzar uso de policy_id de archivo local
|
||||
'mock>=5.1.0',
|
||||
'xmlschema>=3.0.0']
|
||||
|
||||
"""
|
||||
Listado de Versiones Anteriores
|
||||
requirements = ['Click>=6.0',
|
||||
'zeep==4.0.0',
|
||||
'lxml==4.6.3',
|
||||
@ -42,8 +26,6 @@ requirements = ['Click>=6.0',
|
||||
'mock>=2.0.0',
|
||||
'xmlschema>=1.8']
|
||||
|
||||
"""
|
||||
|
||||
setup_requirements = ['pytest-runner', ]
|
||||
|
||||
test_requirements = ['pytest', ]
|
||||
@ -57,10 +39,10 @@ setup(
|
||||
'License :: OSI Approved :: GNU General Public License v3 (GPLv3)',
|
||||
'Natural Language :: English',
|
||||
'Programming Language :: Python :: 3',
|
||||
'Programming Language :: Python :: 3.9',
|
||||
'Programming Language :: Python :: 3.10',
|
||||
'Programming Language :: Python :: 3.11',
|
||||
'Programming Language :: Python :: 3.12',
|
||||
'Programming Language :: Python :: 3.4',
|
||||
'Programming Language :: Python :: 3.5',
|
||||
'Programming Language :: Python :: 3.6',
|
||||
'Programming Language :: Python :: 3.7',
|
||||
],
|
||||
description="Facturacion Electronica Colombia",
|
||||
entry_points={
|
||||
|
@ -1 +0,0 @@
|
||||
907e4444decc9e59c160a2fb3b6659b33dc5b632a5008922b9a62f83f757b1c448e47f5867f2b50dbdb96f48c7681168
|
@ -4,32 +4,30 @@ from datetime import datetime
|
||||
|
||||
@pytest.fixture
|
||||
def simple_debit_note_without_lines():
|
||||
inv = form.DebitNote(form.InvoiceDocumentReference(
|
||||
'1234', 'xx', datetime.now()))
|
||||
inv = form.DebitNote(form.InvoiceDocumentReference('1234', 'xx', datetime.now()))
|
||||
inv.set_period(datetime.now(), datetime.now())
|
||||
inv.set_issue(datetime.now())
|
||||
inv.set_ident('ABC123')
|
||||
inv.set_operation_type('30')
|
||||
inv.set_payment_mean(form.PaymentMean(
|
||||
form.PaymentMean.DEBIT, '41', datetime.now(), '1234'))
|
||||
inv.set_payment_mean(form.PaymentMean(form.PaymentMean.DEBIT, '41', datetime.now(), '1234'))
|
||||
inv.set_supplier(form.Party(
|
||||
name='facho-supplier',
|
||||
ident=form.PartyIdentification('123', '', '31'),
|
||||
responsability_code = form.Responsability(['ZZ']),
|
||||
responsability_regime_code='48',
|
||||
organization_code='1',
|
||||
address=form.Address(
|
||||
name = 'facho-supplier',
|
||||
ident = form.PartyIdentification('123','', '31'),
|
||||
responsability_code = form.Responsability(['O-07']),
|
||||
responsability_regime_code = '48',
|
||||
organization_code = '1',
|
||||
address = form.Address(
|
||||
'', '', form.City('05001', 'Medellín'),
|
||||
form.Country('CO', 'Colombia'),
|
||||
form.CountrySubentity('05', 'Antioquia'))
|
||||
))
|
||||
inv.set_customer(form.Party(
|
||||
name='facho-customer',
|
||||
ident=form.PartyIdentification('321', '', '31'),
|
||||
responsability_code=form.Responsability(['ZZ']),
|
||||
responsability_regime_code='48',
|
||||
organization_code='1',
|
||||
address=form.Address(
|
||||
name = 'facho-customer',
|
||||
ident = form.PartyIdentification('321', '', '31'),
|
||||
responsability_code = form.Responsability(['O-07']),
|
||||
responsability_regime_code = '48',
|
||||
organization_code = '1',
|
||||
address = form.Address(
|
||||
'', '', form.City('05001', 'Medellín'),
|
||||
form.Country('CO', 'Colombia'),
|
||||
form.CountrySubentity('05', 'Antioquia'))
|
||||
@ -47,7 +45,7 @@ def simple_credit_note_without_lines():
|
||||
inv.set_supplier(form.Party(
|
||||
name = 'facho-supplier',
|
||||
ident = form.PartyIdentification('123','', '31'),
|
||||
responsability_code = form.Responsability(['ZZ']),
|
||||
responsability_code = form.Responsability(['O-07']),
|
||||
responsability_regime_code = '48',
|
||||
organization_code = '1',
|
||||
address = form.Address(
|
||||
@ -58,7 +56,7 @@ def simple_credit_note_without_lines():
|
||||
inv.set_customer(form.Party(
|
||||
name = 'facho-customer',
|
||||
ident = form.PartyIdentification('321', '', '31'),
|
||||
responsability_code = form.Responsability(['ZZ']),
|
||||
responsability_code = form.Responsability(['O-07']),
|
||||
responsability_regime_code = '48',
|
||||
organization_code = '1',
|
||||
address = form.Address(
|
||||
@ -79,7 +77,7 @@ def simple_invoice_without_lines():
|
||||
inv.set_supplier(form.Party(
|
||||
name = 'facho-supplier',
|
||||
ident = form.PartyIdentification('123','', '31'),
|
||||
responsability_code = form.Responsability(['ZZ']),
|
||||
responsability_code = form.Responsability(['O-07']),
|
||||
responsability_regime_code = '48',
|
||||
organization_code = '1',
|
||||
address = form.Address(
|
||||
@ -90,7 +88,7 @@ def simple_invoice_without_lines():
|
||||
inv.set_customer(form.Party(
|
||||
name = 'facho-customer',
|
||||
ident = form.PartyIdentification('321', '', '31'),
|
||||
responsability_code = form.Responsability(['ZZ']),
|
||||
responsability_code = form.Responsability(['O-07']),
|
||||
responsability_regime_code = '48',
|
||||
organization_code = '1',
|
||||
address = form.Address(
|
||||
@ -100,7 +98,6 @@ def simple_invoice_without_lines():
|
||||
))
|
||||
return inv
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def simple_invoice():
|
||||
inv = form.NationalSalesInvoice()
|
||||
@ -112,7 +109,7 @@ def simple_invoice():
|
||||
inv.set_supplier(form.Party(
|
||||
name = 'facho-supplier',
|
||||
ident = form.PartyIdentification('123','', '31'),
|
||||
responsability_code = form.Responsability(['ZZ']),
|
||||
responsability_code = form.Responsability(['O-07']),
|
||||
responsability_regime_code = '48',
|
||||
organization_code = '1',
|
||||
address = form.Address(
|
||||
@ -123,7 +120,7 @@ def simple_invoice():
|
||||
inv.set_customer(form.Party(
|
||||
name = 'facho-customer',
|
||||
ident = form.PartyIdentification('321','', '31'),
|
||||
responsability_code = form.Responsability(['ZZ']),
|
||||
responsability_code = form.Responsability(['O-07']),
|
||||
responsability_regime_code = '48',
|
||||
organization_code = '1',
|
||||
address = form.Address(
|
||||
@ -131,20 +128,19 @@ def simple_invoice():
|
||||
form.Country('CO', 'Colombia'),
|
||||
form.CountrySubentity('05', 'Antioquia'))
|
||||
))
|
||||
|
||||
inv.add_invoice_line(form.InvoiceLine(
|
||||
quantity=form.Quantity(1, '94'),
|
||||
description='productofacho',
|
||||
item=form.StandardItem(9999),
|
||||
price=form.Price(form.Amount(100.0),'01',''),
|
||||
tax=form.TaxTotal(
|
||||
tax_amount=form.Amount(0.0),
|
||||
taxable_amount=form.Amount(0.0),
|
||||
subtotals=[
|
||||
quantity = form.Quantity(1, '94'),
|
||||
description = 'producto facho',
|
||||
item = form.StandardItem( 9999),
|
||||
price = form.Price(form.Amount(100.0), '01', ''),
|
||||
tax = form.TaxTotal(
|
||||
tax_amount = form.Amount(0.0),
|
||||
taxable_amount = form.Amount(0.0),
|
||||
subtotals = [
|
||||
form.TaxSubTotal(
|
||||
percent=19.0,
|
||||
)]),
|
||||
withholding=form.WithholdingTaxTotal(
|
||||
subtotals=[])
|
||||
percent = 19.0,
|
||||
)
|
||||
]
|
||||
)
|
||||
))
|
||||
return inv
|
||||
|
@ -1,21 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
# This file is part of facho. The COPYRIGHT file at the top level of
|
||||
# this repository contains the full copyright notices and license terms.
|
||||
import pytest
|
||||
from facho.fe import form_xml
|
||||
from fixtures import simple_invoice
|
||||
|
||||
simple_invoice = simple_invoice
|
||||
|
||||
|
||||
def test_application_response(simple_invoice):
|
||||
|
||||
doc = form_xml.ApplicationResponse(simple_invoice)
|
||||
xml = doc.toFachoXML()
|
||||
|
||||
with open("application_response.xml", "w") as fh:
|
||||
fh.write(xml.tostring())
|
||||
# raise Exception(xml.tostring())
|
||||
# assert xml.get_element_text(
|
||||
# './apr:ApplicationResponse')
|
@ -2,124 +2,16 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# This file is part of facho. The COPYRIGHT file at the top level of
|
||||
# this repository contains the full copyright notices and license terms.
|
||||
# from datetime import datetime
|
||||
from datetime import datetime
|
||||
|
||||
import pytest
|
||||
from facho.fe import form_xml
|
||||
from datetime import datetime
|
||||
|
||||
import helpers
|
||||
from fixtures import simple_invoice
|
||||
|
||||
simple_invoice = simple_invoice
|
||||
|
||||
def test_xml_with_required_elements(simple_invoice):
|
||||
|
||||
xml_header = '<?xml version="1.0" encoding="UTF-8" standalone="no"?>'
|
||||
DIANInvoiceXML = form_xml.DIANInvoiceXML(
|
||||
simple_invoice)
|
||||
|
||||
doc = form_xml.AttachedDocument(
|
||||
simple_invoice,
|
||||
DIANInvoiceXML,
|
||||
id='123')
|
||||
def test_xml_with_required_elements():
|
||||
doc = form_xml.AttachedDocument(id='123')
|
||||
|
||||
xml = doc.toFachoXML()
|
||||
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,26 +4,21 @@
|
||||
# this repository contains the full copyright notices and license terms.
|
||||
|
||||
"""Tests for `facho` package."""
|
||||
from facho.fe.data.dian import codelist
|
||||
|
||||
import pytest
|
||||
from facho.fe.data.dian import codelist
|
||||
|
||||
def test_tiporesponsabilidad():
|
||||
assert codelist.TipoResponsabilidad.short_name == 'TipoResponsabilidad'
|
||||
assert codelist.TipoResponsabilidad.by_name(
|
||||
'Autorretenedor')['name'] == 'Autorretenedor'
|
||||
|
||||
assert codelist.TipoResponsabilidad.by_name('Autorretenedor')['name'] == 'Autorretenedor'
|
||||
|
||||
def test_tipoorganizacion():
|
||||
assert codelist.TipoOrganizacion.short_name == 'TipoOrganizacion'
|
||||
assert codelist.TipoOrganizacion.by_name(
|
||||
'Persona Natural')['name'] == 'Persona Natural'
|
||||
|
||||
assert codelist.TipoOrganizacion.by_name('Persona Natural')['name'] == 'Persona Natural'
|
||||
|
||||
def test_tipodocumento():
|
||||
assert codelist.TipoDocumento.short_name == 'TipoDocumento'
|
||||
assert codelist.TipoDocumento.by_name(
|
||||
'Factura electrónica de Venta')['code'] == '01'
|
||||
|
||||
assert codelist.TipoDocumento.by_name('Factura de Venta Nacional')['code'] == '01'
|
||||
|
||||
def test_departamento():
|
||||
assert codelist.Departamento['05']['name'] == 'Antioquia'
|
||||
|
@ -5,15 +5,15 @@
|
||||
from datetime import datetime
|
||||
|
||||
import pytest
|
||||
|
||||
from facho import fe
|
||||
|
||||
|
||||
import helpers
|
||||
|
||||
|
||||
def test_xmlsigned_build(monkeypatch):
|
||||
# openssl req -x509 -sha256 -nodes -subj "/CN=test" -days 1 -newkey rsa:2048 -keyout example.key -out example.pem
|
||||
# openssl pkcs12 -export -out example.p12 -inkey example.key -in example.pem
|
||||
#openssl req -x509 -sha256 -nodes -subj "/CN=test" -days 1 -newkey rsa:2048 -keyout example.key -out example.pem
|
||||
#openssl pkcs12 -export -out example.p12 -inkey example.key -in example.pem
|
||||
signer = fe.DianXMLExtensionSigner('./tests/example.p12')
|
||||
|
||||
xml = fe.FeXML('Invoice',
|
||||
@ -116,17 +116,3 @@ def test_xml_sign_dian_using_bytes(monkeypatch):
|
||||
|
||||
xmlsigned = signer.sign_xml_string(xmlstring)
|
||||
assert "Signature" in xmlsigned
|
||||
|
||||
def test_xml_signature_timestamp(monkeypatch):
|
||||
xml = fe.FeXML(
|
||||
'Invoice',
|
||||
'http://www.dian.gov.co/contratos/facturaelectronica/v1')
|
||||
xml.find_or_create_element(
|
||||
'/fe:Invoice/ext:UBLExtensions/ext:UBLExtension/ext:ExtensionContent')
|
||||
ublextension = xml.fragment(
|
||||
'/fe:Invoice/ext:UBLExtensions/ext:UBLExtension', append=True)
|
||||
ublextension.find_or_create_element(
|
||||
'/ext:UBLExtension/ext:ExtensionContent')
|
||||
xmlstring = xml.tostring()
|
||||
signer = fe.DianXMLExtensionSigner('./tests/example.p12')
|
||||
xmlsigned = signer.sign_xml_string(xmlstring)
|
||||
|
@ -5,47 +5,24 @@
|
||||
|
||||
"""Tests for `facho` package."""
|
||||
|
||||
import pytest
|
||||
from datetime import datetime
|
||||
import io
|
||||
import zipfile
|
||||
|
||||
import facho.fe.form as form
|
||||
from facho import fe
|
||||
from facho.fe.form_xml import (
|
||||
DIANInvoiceXML, DIANCreditNoteXML, DIANDebitNoteXML)
|
||||
|
||||
from fixtures import (
|
||||
simple_invoice,
|
||||
simple_invoice_without_lines,
|
||||
simple_credit_note_without_lines,
|
||||
simple_debit_note_without_lines)
|
||||
|
||||
try:
|
||||
CUDE_ = open("./tests/cude.txt", 'r').read().strip()
|
||||
except FileNotFoundError:
|
||||
raise Exception("Archivo Cude No encontrado")
|
||||
|
||||
CUFE_ = (
|
||||
'8bb918b19ba22a694f1da'
|
||||
'11c643b5e9de39adf60311c'
|
||||
'f179179e9b33381030bcd4c3c'
|
||||
'3f156c506ed5908f9276f5bd9b4')
|
||||
|
||||
simple_invoice = simple_invoice
|
||||
simple_invoice_without_lines = simple_invoice_without_lines
|
||||
simple_credit_note_without_lines = simple_credit_note_without_lines
|
||||
simple_debit_note_without_lines = simple_debit_note_without_lines
|
||||
from facho.fe.form_xml import DIANInvoiceXML, DIANCreditNoteXML, DIANDebitNoteXML
|
||||
|
||||
from fixtures import *
|
||||
|
||||
def test_invoicesimple_build(simple_invoice):
|
||||
xml = DIANInvoiceXML(simple_invoice)
|
||||
|
||||
supplier_name = xml.get_element_text(
|
||||
'/fe:Invoice/cac:AccountingSupplierParty/cac:Party/cac:PartyName/cbc:Name')
|
||||
supplier_name = xml.get_element_text('/fe:Invoice/cac:AccountingSupplierParty/cac:Party/cac:PartyName/cbc:Name')
|
||||
assert supplier_name == simple_invoice.invoice_supplier.name
|
||||
|
||||
customer_name = xml.get_element_text(
|
||||
'/fe:Invoice/cac:AccountingCustomerParty/cac:Party/cac:PartyName/cbc:Name')
|
||||
customer_name = xml.get_element_text('/fe:Invoice/cac:AccountingCustomerParty/cac:Party/cac:PartyName/cbc:Name')
|
||||
assert customer_name == simple_invoice.invoice_customer.name
|
||||
|
||||
|
||||
@ -65,11 +42,9 @@ def test_invoicesimple_xml_signed(monkeypatch, simple_invoice):
|
||||
print(xml.tostring())
|
||||
xml.add_extension(signer)
|
||||
|
||||
elem = xml.get_element(
|
||||
'/fe:Invoice/ext:UBLExtensions/ext:UBLExtension[2]/ext:ExtensionContent/ds:Signature')
|
||||
elem = xml.get_element('/fe:Invoice/ext:UBLExtensions/ext:UBLExtension[2]/ext:ExtensionContent/ds:Signature')
|
||||
assert elem.text is not None
|
||||
|
||||
|
||||
def test_invoicesimple_zip(simple_invoice):
|
||||
xml_invoice = DIANInvoiceXML(simple_invoice)
|
||||
|
||||
@ -91,34 +66,26 @@ def test_invoicesimple_zip(simple_invoice):
|
||||
|
||||
def test_bug_cbcid_empty_on_invoice_line(simple_invoice):
|
||||
xml_invoice = DIANInvoiceXML(simple_invoice)
|
||||
cbc_id = xml_invoice.get_element_text(
|
||||
'/fe:Invoice/cac:InvoiceLine[1]/cbc:ID', format_=int)
|
||||
cbc_id = xml_invoice.get_element_text('/fe:Invoice/cac:InvoiceLine[1]/cbc:ID', format_=int)
|
||||
assert cbc_id == 1
|
||||
|
||||
|
||||
def test_invoice_line_count_numeric(simple_invoice):
|
||||
xml_invoice = DIANInvoiceXML(simple_invoice)
|
||||
count = xml_invoice.get_element_text(
|
||||
'/fe:Invoice/cbc:LineCountNumeric', format_=int)
|
||||
count = xml_invoice.get_element_text('/fe:Invoice/cbc:LineCountNumeric', format_=int)
|
||||
assert count == len(simple_invoice.invoice_lines)
|
||||
|
||||
|
||||
def test_invoice_profileexecutionid(simple_invoice):
|
||||
xml_invoice = DIANInvoiceXML(simple_invoice)
|
||||
cufe_extension = fe.DianXMLExtensionCUFE(simple_invoice)
|
||||
xml_invoice.add_extension(cufe_extension)
|
||||
id_ = xml_invoice.get_element_text(
|
||||
'/fe:Invoice/cbc:ProfileExecutionID', format_=int)
|
||||
id_ = xml_invoice.get_element_text('/fe:Invoice/cbc:ProfileExecutionID', format_=int)
|
||||
assert id_ == 2
|
||||
|
||||
|
||||
def test_invoice_invoice_type_code(simple_invoice):
|
||||
xml_invoice = DIANInvoiceXML(simple_invoice)
|
||||
id_ = xml_invoice.get_element_text(
|
||||
'/fe:Invoice/cbc:InvoiceTypeCode', format_=int)
|
||||
id_ = xml_invoice.get_element_text('/fe:Invoice/cbc:InvoiceTypeCode', format_=int)
|
||||
assert id_ == 1
|
||||
|
||||
|
||||
def test_invoice_totals(simple_invoice_without_lines):
|
||||
simple_invoice = simple_invoice_without_lines
|
||||
simple_invoice.invoice_ident = '323200000129'
|
||||
@ -126,50 +93,39 @@ def test_invoice_totals(simple_invoice_without_lines):
|
||||
simple_invoice.invoice_supplier.ident = '700085371'
|
||||
simple_invoice.invoice_customer.ident = '800199436'
|
||||
simple_invoice.add_invoice_line(form.InvoiceLine(
|
||||
quantity=form.Quantity(1, '94'),
|
||||
description='producto',
|
||||
item=form.StandardItem(9999),
|
||||
price=form.Price(form.Amount(1_500_000), '01', ''),
|
||||
tax=form.TaxTotal(
|
||||
subtotals=[
|
||||
quantity = form.Quantity(1, '94'),
|
||||
description = 'producto',
|
||||
item = form.StandardItem(9999),
|
||||
price = form.Price(form.Amount(1_500_000), '01', ''),
|
||||
tax = form.TaxTotal(
|
||||
subtotals = [
|
||||
form.TaxSubTotal(
|
||||
scheme=form.TaxScheme('01'),
|
||||
percent=19.0
|
||||
)]),
|
||||
withholding=form.WithholdingTaxTotal(
|
||||
subtotals=[])
|
||||
scheme = form.TaxScheme('01'),
|
||||
percent = 19.0
|
||||
)])
|
||||
))
|
||||
simple_invoice.calculate()
|
||||
assert 1 == len(simple_invoice.invoice_lines)
|
||||
assert form.Amount(1_500_000) == (
|
||||
simple_invoice.invoice_legal_monetary_total.line_extension_amount)
|
||||
assert form.Amount(1_785_000) == (
|
||||
simple_invoice.invoice_legal_monetary_total.payable_amount)
|
||||
|
||||
assert form.Amount(1_500_000) == simple_invoice.invoice_legal_monetary_total.line_extension_amount
|
||||
assert form.Amount(1_785_000) == simple_invoice.invoice_legal_monetary_total.payable_amount
|
||||
|
||||
def test_invoice_cufe(simple_invoice_without_lines):
|
||||
simple_invoice = simple_invoice_without_lines
|
||||
simple_invoice.invoice_ident = '323200000129'
|
||||
simple_invoice.invoice_issue = datetime.strptime(
|
||||
'2019-01-16 10:53:10-05:00', '%Y-%m-%d %H:%M:%S%z')
|
||||
simple_invoice.invoice_supplier.ident = form.PartyIdentification(
|
||||
'700085371', '5', '31')
|
||||
simple_invoice.invoice_customer.ident = form.PartyIdentification(
|
||||
'800199436', '5', '31')
|
||||
simple_invoice.invoice_issue = datetime.strptime('2019-01-16 10:53:10-05:00', '%Y-%m-%d %H:%M:%S%z')
|
||||
simple_invoice.invoice_supplier.ident = form.PartyIdentification('700085371', '5', '31')
|
||||
simple_invoice.invoice_customer.ident = form.PartyIdentification('800199436', '5', '31')
|
||||
simple_invoice.add_invoice_line(form.InvoiceLine(
|
||||
quantity=form.Quantity(
|
||||
1.00, '94'),
|
||||
description='producto',
|
||||
item=form.StandardItem(111),
|
||||
price=form.Price(form.Amount(1_500_000), '01', ''),
|
||||
tax=form.TaxTotal(
|
||||
subtotals=[
|
||||
quantity = form.Quantity(1.00, '94'),
|
||||
description = 'producto',
|
||||
item = form.StandardItem(111),
|
||||
price = form.Price(form.Amount(1_500_000), '01', ''),
|
||||
tax = form.TaxTotal(
|
||||
subtotals = [
|
||||
form.TaxSubTotal(
|
||||
scheme=form.TaxScheme('01'),
|
||||
percent=19.0
|
||||
)]),
|
||||
withholding=form.WithholdingTaxTotal(
|
||||
subtotals=[])
|
||||
scheme = form.TaxScheme('01'),
|
||||
percent = 19.0
|
||||
)])
|
||||
))
|
||||
|
||||
simple_invoice.calculate()
|
||||
@ -177,86 +133,65 @@ def test_invoice_cufe(simple_invoice_without_lines):
|
||||
|
||||
cufe_extension = fe.DianXMLExtensionCUFE(
|
||||
simple_invoice,
|
||||
tipo_ambiente=fe.AMBIENTE_PRODUCCION,
|
||||
clave_tecnica='693ff6f2a553c3646a063436fd4dd9ded0311471'
|
||||
tipo_ambiente = fe.AMBIENTE_PRODUCCION,
|
||||
clave_tecnica = '693ff6f2a553c3646a063436fd4dd9ded0311471'
|
||||
)
|
||||
formatVars = cufe_extension.formatVars()
|
||||
|
||||
# NumFac
|
||||
#NumFac
|
||||
assert formatVars[0] == '323200000129', "NumFac"
|
||||
|
||||
# FecFac
|
||||
#FecFac
|
||||
assert formatVars[1] == '2019-01-16', "FecFac"
|
||||
|
||||
# HoraFac
|
||||
#HoraFac
|
||||
assert formatVars[2] == '10:53:10-05:00', "HoraFac"
|
||||
|
||||
# ValorBruto
|
||||
#ValorBruto
|
||||
assert formatVars[3] == '1500000.00', "ValorBruto"
|
||||
|
||||
# CodImpuesto1
|
||||
#CodImpuesto1
|
||||
assert formatVars[4] == '01', "CodImpuesto1"
|
||||
|
||||
# ValorImpuesto1
|
||||
#ValorImpuesto1
|
||||
assert formatVars[5] == '285000.00', "ValorImpuesto1"
|
||||
|
||||
# CodImpuesto2
|
||||
#CodImpuesto2
|
||||
assert formatVars[6] == '04', "CodImpuesto2"
|
||||
|
||||
# ValorImpuesto2
|
||||
#ValorImpuesto2
|
||||
assert formatVars[7] == '0.00', "ValorImpuesto2"
|
||||
|
||||
# CodImpuesto3
|
||||
#CodImpuesto3
|
||||
assert formatVars[8] == '03', "CodImpuesto3"
|
||||
|
||||
# ValorImpuesto3
|
||||
#ValorImpuesto3
|
||||
assert formatVars[9] == '0.00', "ValorImpuesto3"
|
||||
|
||||
# ValTotFac
|
||||
#ValTotFac
|
||||
assert formatVars[10] == '1785000.00', "ValTotFac"
|
||||
|
||||
# NitOFE
|
||||
#NitOFE
|
||||
assert formatVars[11] == '700085371', "NitOFE"
|
||||
|
||||
# NumAdq
|
||||
#NumAdq
|
||||
assert formatVars[12] == '800199436', "NumAdq"
|
||||
|
||||
# ClTec
|
||||
#ClTec
|
||||
assert formatVars[13] == '693ff6f2a553c3646a063436fd4dd9ded0311471', "ClTec"
|
||||
|
||||
# TipoAmbiente
|
||||
#TipoAmbiente
|
||||
assert formatVars[14] == '1', "TipoAmbiente"
|
||||
|
||||
xml_invoice.add_extension(cufe_extension)
|
||||
cufe = xml_invoice.get_element_text('/fe:Invoice/cbc:UUID')
|
||||
# RESOLUCION 004: pagina 689
|
||||
assert cufe == CUFE_
|
||||
assert cufe == '8bb918b19ba22a694f1da11c643b5e9de39adf60311cf179179e9b33381030bcd4c3c3f156c506ed5908f9276f5bd9b4'
|
||||
|
||||
|
||||
|
||||
def test_credit_note_cude(simple_credit_note_without_lines):
|
||||
simple_invoice = simple_credit_note_without_lines
|
||||
simple_invoice.invoice_ident = '8110007871'
|
||||
simple_invoice.invoice_issue = datetime.strptime(
|
||||
'2019-01-12 07:00:00-05:00', '%Y-%m-%d %H:%M:%S%z')
|
||||
simple_invoice.invoice_supplier.ident = form.PartyIdentification(
|
||||
'900373076', '5', '31')
|
||||
simple_invoice.invoice_customer.ident = form.PartyIdentification(
|
||||
'8355990', '5', '31')
|
||||
simple_invoice.invoice_issue = datetime.strptime('2019-01-12 07:00:00-05:00', '%Y-%m-%d %H:%M:%S%z')
|
||||
simple_invoice.invoice_supplier.ident = form.PartyIdentification('900373076', '5', '31')
|
||||
simple_invoice.invoice_customer.ident = form.PartyIdentification('8355990', '5', '31')
|
||||
simple_invoice.add_invoice_line(form.InvoiceLine(
|
||||
quantity=form.Quantity(
|
||||
1, '94'),
|
||||
description='producto',
|
||||
item=form.StandardItem(111),
|
||||
price=form.Price(
|
||||
form.Amount(5_000), '01', ''),
|
||||
tax=form.TaxTotal(
|
||||
subtotals=[
|
||||
quantity = form.Quantity(1, '94'),
|
||||
description = 'producto',
|
||||
item = form.StandardItem(111),
|
||||
price = form.Price(form.Amount(5_000), '01', ''),
|
||||
tax = form.TaxTotal(
|
||||
subtotals = [
|
||||
form.TaxSubTotal(
|
||||
scheme=form.TaxScheme('01'),
|
||||
percent=19.0
|
||||
)]),
|
||||
withholding=form.WithholdingTaxTotal(
|
||||
subtotals=[])
|
||||
scheme = form.TaxScheme('01'),
|
||||
percent = 19.0
|
||||
)])
|
||||
))
|
||||
|
||||
simple_invoice.calculate()
|
||||
@ -265,39 +200,33 @@ def test_credit_note_cude(simple_credit_note_without_lines):
|
||||
cude_extension = fe.DianXMLExtensionCUDE(
|
||||
simple_invoice,
|
||||
'12301',
|
||||
tipo_ambiente=fe.AMBIENTE_PRODUCCION,
|
||||
tipo_ambiente = fe.AMBIENTE_PRODUCCION,
|
||||
)
|
||||
|
||||
xml_invoice.add_extension(cude_extension)
|
||||
cude = xml_invoice.get_element_text('/fe:CreditNote/cbc:UUID')
|
||||
# pag 612
|
||||
|
||||
assert cude == CUDE_
|
||||
assert cude == '907e4444decc9e59c160a2fb3b6659b33dc5b632a5008922b9a62f83f757b1c448e47f5867f2b50dbdb96f48c7681168'
|
||||
|
||||
|
||||
# pag 614
|
||||
def test_debit_note_cude(simple_debit_note_without_lines):
|
||||
simple_invoice = simple_debit_note_without_lines
|
||||
simple_invoice.invoice_ident = 'ND1001'
|
||||
simple_invoice.invoice_issue = datetime.strptime(
|
||||
'2019-01-18 10:58:00-05:00', '%Y-%m-%d %H:%M:%S%z')
|
||||
simple_invoice.invoice_supplier.ident = form.PartyIdentification(
|
||||
'900197264', '5', '31')
|
||||
simple_invoice.invoice_customer.ident = form.PartyIdentification(
|
||||
'10254102', '5', '31')
|
||||
simple_invoice.invoice_issue = datetime.strptime('2019-01-18 10:58:00-05:00', '%Y-%m-%d %H:%M:%S%z')
|
||||
simple_invoice.invoice_supplier.ident = form.PartyIdentification('900197264', '5', '31')
|
||||
simple_invoice.invoice_customer.ident = form.PartyIdentification('10254102', '5', '31')
|
||||
simple_invoice.add_invoice_line(form.InvoiceLine(
|
||||
quantity=form.Quantity(1, '94'),
|
||||
description='producto',
|
||||
item=form.StandardItem(111),
|
||||
price=form.Price(form.Amount(30_000), '01', ''),
|
||||
tax=form.TaxTotal(
|
||||
subtotals=[
|
||||
quantity = form.Quantity(1, '94'),
|
||||
description = 'producto',
|
||||
item = form.StandardItem(111),
|
||||
price = form.Price(form.Amount(30_000), '01', ''),
|
||||
tax = form.TaxTotal(
|
||||
subtotals = [
|
||||
form.TaxSubTotal(
|
||||
scheme=form.TaxScheme('04'),
|
||||
percent=8.0
|
||||
)]),
|
||||
withholding=form.WithholdingTaxTotal(
|
||||
subtotals=[])
|
||||
scheme = form.TaxScheme('04'),
|
||||
percent = 8.0
|
||||
)])
|
||||
))
|
||||
|
||||
simple_invoice.calculate()
|
||||
@ -306,7 +235,7 @@ def test_debit_note_cude(simple_debit_note_without_lines):
|
||||
cude_extension = fe.DianXMLExtensionCUDE(
|
||||
simple_invoice,
|
||||
'10201',
|
||||
tipo_ambiente=fe.AMBIENTE_PRUEBAS,
|
||||
tipo_ambiente = fe.AMBIENTE_PRUEBAS,
|
||||
)
|
||||
build_vars = cude_extension.buildVars()
|
||||
assert build_vars['NumFac'] == 'ND1001'
|
||||
@ -322,7 +251,8 @@ def test_debit_note_cude(simple_debit_note_without_lines):
|
||||
assert build_vars['Software-PIN'] == '10201'
|
||||
assert build_vars['TipoAmb'] == 2
|
||||
|
||||
cude_composicion = "".join(cude_extension.formatVars())
|
||||
|
||||
cude_composicion = "".join(cude_extension.formatVars())
|
||||
assert cude_composicion == 'ND10012019-01-1810:58:00-05:0030000.00010.00042400.00030.0032400.0090019726410254102102012'
|
||||
|
||||
xml_invoice.add_extension(cude_extension)
|
||||
|
@ -6,117 +6,91 @@
|
||||
"""Tests for `facho` package."""
|
||||
|
||||
import pytest
|
||||
# from datetime import datetime
|
||||
# import io
|
||||
# import zipfile
|
||||
from datetime import datetime
|
||||
import io
|
||||
import zipfile
|
||||
|
||||
import facho.fe.form as form
|
||||
# from facho import fe
|
||||
|
||||
from fixtures import (
|
||||
simple_invoice,
|
||||
simple_invoice_without_lines,
|
||||
simple_credit_note_without_lines,
|
||||
simple_debit_note_without_lines)
|
||||
|
||||
simple_invoice = simple_invoice
|
||||
simple_invoice_without_lines = simple_invoice_without_lines
|
||||
simple_credit_note_without_lines = simple_credit_note_without_lines
|
||||
simple_debit_note_without_lines = simple_debit_note_without_lines
|
||||
|
||||
from facho import fe
|
||||
|
||||
def test_invoice_legalmonetary():
|
||||
inv = form.NationalSalesInvoice()
|
||||
inv.add_invoice_line(form.InvoiceLine(
|
||||
quantity=form.Quantity(1, '94'),
|
||||
description='producto facho',
|
||||
item=form.StandardItem(9999),
|
||||
price=form.Price(
|
||||
amount=form.Amount(100.0),
|
||||
type_code='01',
|
||||
type='x'
|
||||
quantity = form.Quantity(1, '94'),
|
||||
description = 'producto facho',
|
||||
item = form.StandardItem(9999),
|
||||
price = form.Price(
|
||||
amount = form.Amount(100.0),
|
||||
type_code = '01',
|
||||
type = 'x'
|
||||
),
|
||||
tax=form.TaxTotal(
|
||||
subtotals=[
|
||||
tax = form.TaxTotal(
|
||||
subtotals = [
|
||||
form.TaxSubTotal(
|
||||
percent=19.0,
|
||||
)]),
|
||||
withholding=form.WithholdingTaxTotal(
|
||||
subtotals=[])
|
||||
percent = 19.0,
|
||||
)
|
||||
]
|
||||
)
|
||||
))
|
||||
|
||||
inv.calculate()
|
||||
assert inv.invoice_legal_monetary_total.line_extension_amount == (
|
||||
form.Amount(100.0))
|
||||
assert inv.invoice_legal_monetary_total.tax_exclusive_amount == (
|
||||
form.Amount(100.0))
|
||||
assert inv.invoice_legal_monetary_total.tax_inclusive_amount == (
|
||||
form.Amount(119.0))
|
||||
assert inv.invoice_legal_monetary_total.charge_total_amount == (
|
||||
form.Amount(0.0))
|
||||
|
||||
assert inv.invoice_legal_monetary_total.line_extension_amount == form.Amount(100.0)
|
||||
assert inv.invoice_legal_monetary_total.tax_exclusive_amount == form.Amount(100.0)
|
||||
assert inv.invoice_legal_monetary_total.tax_inclusive_amount == form.Amount(119.0)
|
||||
assert inv.invoice_legal_monetary_total.charge_total_amount == form.Amount(0.0)
|
||||
|
||||
def test_allowancecharge_as_discount():
|
||||
discount = form.AllowanceChargeAsDiscount(amount=form.Amount(1000.0))
|
||||
|
||||
assert discount.isDiscount()
|
||||
|
||||
assert discount.isDiscount() == True
|
||||
|
||||
def test_FAU10():
|
||||
inv = form.NationalSalesInvoice()
|
||||
inv.add_invoice_line(form.InvoiceLine(
|
||||
quantity=form.Quantity(1, '94'),
|
||||
description='productofacho',
|
||||
item=form.StandardItem(9999),
|
||||
price=form.Price(
|
||||
amount=form.Amount(100.0),
|
||||
type_code='01',
|
||||
type='x'
|
||||
quantity = form.Quantity(1, '94'),
|
||||
description = 'producto facho',
|
||||
item = form.StandardItem(9999),
|
||||
price = form.Price(
|
||||
amount = form.Amount(100.0),
|
||||
type_code = '01',
|
||||
type = 'x'
|
||||
),
|
||||
tax=form.TaxTotal(
|
||||
subtotals=[
|
||||
tax = form.TaxTotal(
|
||||
subtotals = [
|
||||
form.TaxSubTotal(
|
||||
percent=19.0)]),
|
||||
withholding=form.WithholdingTaxTotal(
|
||||
subtotals=[])
|
||||
percent = 19.0,
|
||||
)
|
||||
]
|
||||
)
|
||||
))
|
||||
|
||||
inv.add_allowance_charge(form.AllowanceCharge(amount=form.Amount(19.0)))
|
||||
|
||||
inv.calculate()
|
||||
assert inv.invoice_legal_monetary_total.line_extension_amount == (
|
||||
form.Amount(100.0))
|
||||
assert inv.invoice_legal_monetary_total.tax_exclusive_amount == (
|
||||
form.Amount(100.0))
|
||||
assert inv.invoice_legal_monetary_total.tax_inclusive_amount == (
|
||||
form.Amount(119.0))
|
||||
assert inv.invoice_legal_monetary_total.charge_total_amount == (
|
||||
form.Amount(19.0))
|
||||
assert inv.invoice_legal_monetary_total.line_extension_amount == form.Amount(100.0)
|
||||
assert inv.invoice_legal_monetary_total.tax_exclusive_amount == form.Amount(100.0)
|
||||
assert inv.invoice_legal_monetary_total.tax_inclusive_amount == form.Amount(119.0)
|
||||
assert inv.invoice_legal_monetary_total.charge_total_amount == form.Amount(19.0)
|
||||
|
||||
|
||||
def test_FAU14():
|
||||
inv = form.NationalSalesInvoice()
|
||||
inv.add_invoice_line(form.InvoiceLine(
|
||||
quantity=form.Quantity(1, '94'),
|
||||
description='productofacho',
|
||||
item=form.StandardItem(9999),
|
||||
price=form.Price(
|
||||
amount=form.Amount(100.0),
|
||||
type_code='01',
|
||||
type='x'
|
||||
quantity = form.Quantity(1, '94'),
|
||||
description = 'producto facho',
|
||||
item = form.StandardItem(9999),
|
||||
price = form.Price(
|
||||
amount = form.Amount(100.0),
|
||||
type_code = '01',
|
||||
type = 'x'
|
||||
),
|
||||
tax=form.TaxTotal(
|
||||
subtotals=[
|
||||
tax = form.TaxTotal(
|
||||
subtotals = [
|
||||
form.TaxSubTotal(
|
||||
percent=19.0,
|
||||
)]),
|
||||
withholding=form.WithholdingTaxTotal(
|
||||
subtotals=[])
|
||||
percent = 19.0,
|
||||
)
|
||||
]
|
||||
)
|
||||
))
|
||||
inv.add_allowance_charge(form.AllowanceCharge(
|
||||
amount=form.Amount(19.0)))
|
||||
inv.add_prepaid_payment(form.PrePaidPayment(
|
||||
paid_amount=form.Amount(50.0)))
|
||||
inv.add_allowance_charge(form.AllowanceCharge(amount=form.Amount(19.0)))
|
||||
inv.add_prepaid_payment(form.PrePaidPayment(paid_amount = form.Amount(50.0)))
|
||||
inv.calculate()
|
||||
|
||||
wants = form.Amount(119.0 + 19.0 - 50.0)
|
||||
@ -126,42 +100,38 @@ def test_FAU14():
|
||||
|
||||
def test_invalid_tipo_operacion_nota_debito():
|
||||
reference = form.InvoiceDocumentReference(
|
||||
ident='11111',
|
||||
uuid='21312312',
|
||||
date='2020-05-05'
|
||||
ident = '11111',
|
||||
uuid = '21312312',
|
||||
date = '2020-05-05'
|
||||
)
|
||||
inv = form.DebitNote(reference)
|
||||
with pytest.raises(ValueError):
|
||||
inv.set_operation_type(22)
|
||||
|
||||
|
||||
def test_valid_tipo_operacion_nota_debito():
|
||||
reference = form.InvoiceDocumentReference(
|
||||
ident='11111',
|
||||
uuid='21312312',
|
||||
date='2020-05-05'
|
||||
ident = '11111',
|
||||
uuid = '21312312',
|
||||
date = '2020-05-05'
|
||||
)
|
||||
inv = form.DebitNote(reference)
|
||||
inv.set_operation_type('30')
|
||||
|
||||
|
||||
def test_invalid_tipo_operacion_nota_credito():
|
||||
reference = form.InvoiceDocumentReference(
|
||||
ident='11111',
|
||||
uuid='21312312',
|
||||
date='2020-05-05'
|
||||
ident = '11111',
|
||||
uuid = '21312312',
|
||||
date = '2020-05-05'
|
||||
)
|
||||
|
||||
inv = form.DebitNote(reference)
|
||||
with pytest.raises(ValueError):
|
||||
inv.set_operation_type('990')
|
||||
|
||||
|
||||
def test_valid_tipo_operacion_nota_credito():
|
||||
reference = form.InvoiceDocumentReference(
|
||||
ident='11111',
|
||||
uuid='21312312',
|
||||
date='2020-05-05'
|
||||
ident = '11111',
|
||||
uuid = '21312312',
|
||||
date = '2020-05-05'
|
||||
)
|
||||
inv = form.CreditNote(reference)
|
||||
inv.set_operation_type('20')
|
||||
@ -171,52 +141,41 @@ def test_quantity():
|
||||
quantity1 = form.Quantity(10, '94')
|
||||
assert quantity1 * form.Amount(3) == form.Amount(30)
|
||||
|
||||
|
||||
def test_invoice_line_quantity_without_taxes():
|
||||
line = form.InvoiceLine(
|
||||
quantity=form.Quantity(10, '94'),
|
||||
description='',
|
||||
item=form.StandardItem('test', 9999),
|
||||
price=form.Price(
|
||||
amount=form.Amount(30.00),
|
||||
type_code='01',
|
||||
type='x'),
|
||||
tax=form.TaxTotal(subtotals=[]),
|
||||
withholding=form.WithholdingTaxTotal(
|
||||
subtotals=[])
|
||||
)
|
||||
quantity = form.Quantity(10, '94'),
|
||||
description = '',
|
||||
item = form.StandardItem('test', 9999),
|
||||
price = form.Price(
|
||||
amount = form.Amount(30.00),
|
||||
type_code = '01',
|
||||
type = 'x'
|
||||
),
|
||||
tax = form.TaxTotal(subtotals=[]))
|
||||
line.calculate()
|
||||
assert line.total_amount == form.Amount(300)
|
||||
assert line.tax_amount == form.Amount(0)
|
||||
|
||||
|
||||
def test_invoice_legalmonetary_with_taxes():
|
||||
inv = form.NationalSalesInvoice()
|
||||
inv.add_invoice_line(form.InvoiceLine(
|
||||
quantity=form.Quantity(1, '94'),
|
||||
description='productofacho',
|
||||
item=form.StandardItem(9999),
|
||||
price=form.Price(
|
||||
amount=form.Amount(100.0),
|
||||
type_code='01',
|
||||
type='x'
|
||||
quantity = form.Quantity(1, '94'),
|
||||
description = 'producto facho',
|
||||
item = form.StandardItem(9999),
|
||||
price = form.Price(
|
||||
amount = form.Amount(100.0),
|
||||
type_code = '01',
|
||||
type = 'x'
|
||||
),
|
||||
tax=form.TaxTotal(subtotals=[]),
|
||||
withholding=form.WithholdingTaxTotal(
|
||||
subtotals=[])
|
||||
tax = form.TaxTotal(subtotals=[])
|
||||
))
|
||||
inv.calculate()
|
||||
|
||||
assert inv.invoice_legal_monetary_total.line_extension_amount == (
|
||||
form.Amount(100.0))
|
||||
assert inv.invoice_legal_monetary_total.tax_exclusive_amount == (
|
||||
form.Amount(100.0))
|
||||
assert inv.invoice_legal_monetary_total.tax_inclusive_amount == (
|
||||
form.Amount(100.0))
|
||||
assert inv.invoice_legal_monetary_total.charge_total_amount == (
|
||||
form.Amount(0.0))
|
||||
assert inv.invoice_legal_monetary_total.payable_amount == (
|
||||
form.Amount(100.0))
|
||||
assert inv.invoice_legal_monetary_total.line_extension_amount == form.Amount(100.0)
|
||||
assert inv.invoice_legal_monetary_total.tax_exclusive_amount == form.Amount(100.0)
|
||||
assert inv.invoice_legal_monetary_total.tax_inclusive_amount == form.Amount(100.0)
|
||||
assert inv.invoice_legal_monetary_total.charge_total_amount == form.Amount(0.0)
|
||||
assert inv.invoice_legal_monetary_total.payable_amount == form.Amount(100.0)
|
||||
|
||||
|
||||
def test_invoice_ident_prefix_automatic_invalid():
|
||||
@ -224,7 +183,6 @@ def test_invoice_ident_prefix_automatic_invalid():
|
||||
with pytest.raises(ValueError):
|
||||
inv.set_ident('SETPQJQJ1234567')
|
||||
|
||||
|
||||
def test_invoice_ident_prefix_automatic():
|
||||
inv = form.NationalSalesInvoice()
|
||||
inv.set_ident('SETP1234567')
|
||||
@ -242,15 +200,13 @@ def test_invoice_ident_prefix_automatic():
|
||||
inv.set_ident('1234567')
|
||||
assert inv.invoice_ident_prefix == ''
|
||||
|
||||
|
||||
def test_invoice_ident_prefix_manual():
|
||||
inv = form.NationalSalesInvoice()
|
||||
inv.set_ident('SETP1234567')
|
||||
inv.set_ident_prefix('SETA')
|
||||
assert inv.invoice_ident_prefix == 'SETA'
|
||||
|
||||
|
||||
def test_invoice_ident_prefix_automatic_debit():
|
||||
inv = form.DebitNote(form.BillingReference('', '', ''))
|
||||
inv = form.DebitNote(form.BillingReference('','',''))
|
||||
inv.set_ident('ABCDEF1234567')
|
||||
assert inv.invoice_ident_prefix == 'ABCDEF'
|
||||
|
@ -6,24 +6,13 @@
|
||||
"""Tests for `facho` package."""
|
||||
|
||||
import pytest
|
||||
# from datetime import datetime
|
||||
from datetime import datetime
|
||||
import copy
|
||||
|
||||
from facho.fe import form
|
||||
from facho.fe import form_xml
|
||||
# from fixtures import *
|
||||
|
||||
from fixtures import (
|
||||
simple_invoice,
|
||||
simple_invoice_without_lines,
|
||||
simple_credit_note_without_lines,
|
||||
simple_debit_note_without_lines)
|
||||
|
||||
simple_invoice = simple_invoice
|
||||
simple_invoice_without_lines = simple_invoice_without_lines
|
||||
simple_credit_note_without_lines = simple_credit_note_without_lines
|
||||
simple_debit_note_without_lines = simple_debit_note_without_lines
|
||||
|
||||
from fixtures import *
|
||||
|
||||
def test_import_DIANInvoiceXML():
|
||||
try:
|
||||
@ -38,82 +27,70 @@ def test_import_DIANDebitNoteXML():
|
||||
except AttributeError:
|
||||
pytest.fail("unexpected not found")
|
||||
|
||||
|
||||
def test_import_DIANCreditNoteXML():
|
||||
try:
|
||||
form_xml.DIANCreditNoteXML
|
||||
except AttributeError:
|
||||
pytest.fail("unexpected not found")
|
||||
|
||||
def test_allowance_charge_in_invoice(simple_invoice_without_lines):
|
||||
inv = copy.copy(simple_invoice_without_lines)
|
||||
inv.add_invoice_line(form.InvoiceLine(
|
||||
quantity = form.Quantity(1, '94'),
|
||||
description = 'producto facho',
|
||||
item = form.StandardItem(9999),
|
||||
price = form.Price(
|
||||
amount = form.Amount(100.0),
|
||||
type_code = '01',
|
||||
type = 'x'
|
||||
),
|
||||
tax = form.TaxTotal(
|
||||
subtotals = [
|
||||
form.TaxSubTotal(
|
||||
percent = 19.0,
|
||||
)
|
||||
]
|
||||
)
|
||||
))
|
||||
inv.add_allowance_charge(form.AllowanceCharge(amount=form.Amount(19.0)))
|
||||
inv.calculate()
|
||||
|
||||
# def test_allowance_charge_in_invoice(simple_invoice_without_lines):
|
||||
# inv = copy.copy(simple_invoice_without_lines)
|
||||
# inv.add_invoice_line(form.InvoiceLine(
|
||||
# quantity=form.Quantity(1, '94'),
|
||||
# description='productofacho',
|
||||
# item=form.StandardItem(9999),
|
||||
# price=form.Price(
|
||||
# amount=form.Amount(100.0),
|
||||
# type_code='01',
|
||||
# type='x'
|
||||
# ),
|
||||
# tax=form.TaxTotal(
|
||||
# subtotals=[
|
||||
# form.TaxSubTotal(
|
||||
# percent=19.0,
|
||||
# )]),
|
||||
# withholding=form.WithholdingTaxTotal(
|
||||
# subtotals=[])
|
||||
# ))
|
||||
xml = form_xml.DIANInvoiceXML(inv)
|
||||
assert xml.get_element_text('./cac:AllowanceCharge/cbc:ID') == '1'
|
||||
assert xml.get_element_text('./cac:AllowanceCharge/cbc:ChargeIndicator') == 'true'
|
||||
assert xml.get_element_text('./cac:AllowanceCharge/cbc:Amount') == '19.0'
|
||||
assert xml.get_element_text('./cac:AllowanceCharge/cbc:BaseAmount') == '100.0'
|
||||
|
||||
# inv.add_allowance_charge(form.AllowanceCharge(amount=form.Amount(19.0)))
|
||||
# inv.calculate()
|
||||
def test_allowance_charge_in_invoice_line(simple_invoice_without_lines):
|
||||
inv = copy.copy(simple_invoice_without_lines)
|
||||
inv.add_invoice_line(form.InvoiceLine(
|
||||
quantity = form.Quantity(1, '94'),
|
||||
description = 'producto facho',
|
||||
item = form.StandardItem(9999),
|
||||
price = form.Price(
|
||||
amount = form.Amount(100.0),
|
||||
type_code = '01',
|
||||
type = 'x'
|
||||
),
|
||||
tax = form.TaxTotal(
|
||||
subtotals = [
|
||||
form.TaxSubTotal(
|
||||
percent = 19.0,
|
||||
)
|
||||
]
|
||||
),
|
||||
allowance_charge = [
|
||||
form.AllowanceChargeAsDiscount(amount=form.Amount(10.0))
|
||||
]
|
||||
))
|
||||
inv.calculate()
|
||||
|
||||
# xml = form_xml.DIANInvoiceXML(inv)
|
||||
# assert xml.get_element_text('./cac:AllowanceCharge/cbc:ID') == '1'
|
||||
# assert xml.get_element_text(
|
||||
# './cac:AllowanceCharge/cbc:ChargeIndicator') == 'true'
|
||||
# assert xml.get_element_text(
|
||||
# './cac:AllowanceCharge/cbc:Amount') == '19.0'
|
||||
# assert xml.get_element_text(
|
||||
# './cac:AllowanceCharge/cbc:BaseAmount') == '100.0'
|
||||
# se aplico descuento
|
||||
assert inv.invoice_legal_monetary_total.line_extension_amount == form.Amount(90.0)
|
||||
|
||||
xml = form_xml.DIANInvoiceXML(inv)
|
||||
|
||||
# def test_allowance_charge_in_invoice_line(simple_invoice_without_lines):
|
||||
# inv = copy.copy(simple_invoice_without_lines)
|
||||
# inv.add_invoice_line(form.InvoiceLine(
|
||||
# quantity=form.Quantity(1, '94'),
|
||||
# description='producto facho',
|
||||
# item=form.StandardItem(9999),
|
||||
# price=form.Price(
|
||||
# amount=form.Amount(100.0),
|
||||
# type_code='01',
|
||||
# type='x'
|
||||
# ),
|
||||
# tax=form.TaxTotal(
|
||||
# subtotals=[
|
||||
# form.TaxSubTotal(
|
||||
# percent=19.0,
|
||||
# )]),
|
||||
# withholding=form.WithholdingTaxTotal(
|
||||
# subtotals=[]),
|
||||
# allowance_charge=[
|
||||
# form.AllowanceChargeAsDiscount(amount=form.Amount(10.0))
|
||||
# ]
|
||||
# ))
|
||||
# inv.calculate()
|
||||
|
||||
# # se aplico descuento
|
||||
# assert inv.invoice_legal_monetary_total.line_extension_amount == (
|
||||
# form.Amount(90.0))
|
||||
|
||||
# xml = form_xml.DIANInvoiceXML(inv)
|
||||
|
||||
# with pytest.raises(AttributeError):
|
||||
# assert xml.get_element_text(
|
||||
# '/fe:Invoice/cac:AllowanceCharge/cbc:ID') == '1'
|
||||
# xml.get_element_text(
|
||||
# '/fe:Invoice/cac:InvoiceLine/cac:AllowanceCharge/cbc:ID') == '1'
|
||||
# xml.get_element_text(
|
||||
# '/fe:Invoice/cac:InvoiceLine/cac:AllowanceCharge/cbc:BaseAmount'
|
||||
# ) == '100.0'
|
||||
with pytest.raises(AttributeError):
|
||||
assert xml.get_element_text('/fe:Invoice/cac:AllowanceCharge/cbc:ID') == '1'
|
||||
xml.get_element_text('/fe:Invoice/cac:InvoiceLine/cac:AllowanceCharge/cbc:ID') == '1'
|
||||
xml.get_element_text('/fe:Invoice/cac:InvoiceLine/cac:AllowanceCharge/cbc:BaseAmount') == '100.0'
|
||||
|
@ -4,467 +4,467 @@
|
||||
# this repository contains the full copyright notices and license terms.
|
||||
|
||||
"""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):
|
||||
# for error in errors:
|
||||
# if str(error) == msg:
|
||||
# return True
|
||||
def assert_error(errors, msg):
|
||||
for error in errors:
|
||||
if str(error) == msg:
|
||||
return True
|
||||
|
||||
# raise "wants error: %s" % (msg)
|
||||
raise "wants error: %s" % (msg)
|
||||
|
||||
# def test_adicionar_devengado_Basico():
|
||||
# nomina = fe.nomina.DIANNominaIndividual()
|
||||
def test_adicionar_devengado_Basico():
|
||||
nomina = fe.nomina.DIANNominaIndividual()
|
||||
|
||||
# nomina.adicionar_devengado(fe.nomina.DevengadoBasico(
|
||||
# dias_trabajados = 30,
|
||||
# sueldo_trabajado = fe.nomina.Amount(1_000_000)
|
||||
# ))
|
||||
nomina.adicionar_devengado(fe.nomina.DevengadoBasico(
|
||||
dias_trabajados = 30,
|
||||
sueldo_trabajado = fe.nomina.Amount(1_000_000)
|
||||
))
|
||||
|
||||
# xml = nomina.toFachoXML()
|
||||
# assert xml.get_element_attribute('/nomina:NominaIndividual/Devengados/Basico', 'DiasTrabajados') == '30'
|
||||
# assert xml.get_element_attribute('/nomina:NominaIndividual/Devengados/Basico', 'SueldoTrabajado') == '1000000.00'
|
||||
xml = nomina.toFachoXML()
|
||||
assert xml.get_element_attribute('/nomina:NominaIndividual/Devengados/Basico', 'DiasTrabajados') == '30'
|
||||
assert xml.get_element_attribute('/nomina:NominaIndividual/Devengados/Basico', 'SueldoTrabajado') == '1000000.00'
|
||||
|
||||
# def test_adicionar_devengado_transporte():
|
||||
# nomina = fe.nomina.DIANNominaIndividual()
|
||||
def test_adicionar_devengado_transporte():
|
||||
nomina = fe.nomina.DIANNominaIndividual()
|
||||
|
||||
# nomina.adicionar_devengado(fe.nomina.DevengadoTransporte(
|
||||
# auxilio_transporte = fe.nomina.Amount(2_000_000)
|
||||
# ))
|
||||
nomina.adicionar_devengado(fe.nomina.DevengadoTransporte(
|
||||
auxilio_transporte = fe.nomina.Amount(2_000_000)
|
||||
))
|
||||
|
||||
# xml = nomina.toFachoXML()
|
||||
xml = nomina.toFachoXML()
|
||||
|
||||
# assert xml.get_element_attribute('/nomina:NominaIndividual/Devengados/Transporte', 'AuxilioTransporte') == '2000000.0'
|
||||
assert xml.get_element_attribute('/nomina:NominaIndividual/Devengados/Transporte', 'AuxilioTransporte') == '2000000.0'
|
||||
|
||||
# def test_adicionar_devengado_comprobante_total():
|
||||
# nomina = fe.nomina.DIANNominaIndividual()
|
||||
def test_adicionar_devengado_comprobante_total():
|
||||
nomina = fe.nomina.DIANNominaIndividual()
|
||||
|
||||
# nomina.adicionar_devengado(fe.nomina.DevengadoBasico(
|
||||
# dias_trabajados = 60,
|
||||
# sueldo_trabajado = fe.nomina.Amount(2_000_000)
|
||||
# ))
|
||||
nomina.adicionar_devengado(fe.nomina.DevengadoBasico(
|
||||
dias_trabajados = 60,
|
||||
sueldo_trabajado = fe.nomina.Amount(2_000_000)
|
||||
))
|
||||
|
||||
# nomina.adicionar_deduccion(fe.nomina.DeduccionSalud(
|
||||
# porcentaje = fe.nomina.Amount(19),
|
||||
# deduccion = fe.nomina.Amount(1_000_000)
|
||||
# ))
|
||||
nomina.adicionar_deduccion(fe.nomina.DeduccionSalud(
|
||||
porcentaje = fe.nomina.Amount(19),
|
||||
deduccion = fe.nomina.Amount(1_000_000)
|
||||
))
|
||||
|
||||
|
||||
# xml = nomina.toFachoXML()
|
||||
|
||||
# assert xml.get_element_text('/nomina:NominaIndividual/ComprobanteTotal') == '1000000.00'
|
||||
|
||||
# def test_adicionar_devengado_comprobante_total_cero():
|
||||
# nomina = fe.nomina.DIANNominaIndividual()
|
||||
|
||||
# nomina.adicionar_devengado(fe.nomina.DevengadoBasico(
|
||||
# dias_trabajados = 60,
|
||||
# sueldo_trabajado = fe.nomina.Amount(1_000_000)
|
||||
# ))
|
||||
|
||||
# nomina.adicionar_deduccion(fe.nomina.DeduccionSalud(
|
||||
# porcentaje = fe.nomina.Amount(19),
|
||||
# deduccion = fe.nomina.Amount(1_000_000)
|
||||
# ))
|
||||
|
||||
# xml = nomina.toFachoXML()
|
||||
|
||||
# assert xml.get_element_text('/nomina:NominaIndividual/ComprobanteTotal') == '0.00'
|
||||
|
||||
# def test_adicionar_devengado_transporte_muchos():
|
||||
# nomina = fe.nomina.DIANNominaIndividual()
|
||||
|
||||
# nomina.adicionar_devengado(fe.nomina.DevengadoTransporte(
|
||||
# auxilio_transporte = fe.nomina.Amount(2_000_000)
|
||||
# ))
|
||||
|
||||
# nomina.adicionar_devengado(fe.nomina.DevengadoTransporte(
|
||||
# auxilio_transporte = fe.nomina.Amount(3_000_000)
|
||||
# ))
|
||||
|
||||
# xml = nomina.toFachoXML()
|
||||
# print(xml)
|
||||
# assert xml.get_element_text('/nomina:NominaIndividual/DevengadosTotal') == '5000000.00'
|
||||
|
||||
# def test_adicionar_deduccion_salud():
|
||||
# nomina = fe.nomina.DIANNominaIndividual()
|
||||
|
||||
# nomina.adicionar_devengado(fe.nomina.DevengadoBasico(
|
||||
# dias_trabajados = 60,
|
||||
# sueldo_trabajado = fe.nomina.Amount(1000)
|
||||
# ))
|
||||
|
||||
# nomina.adicionar_deduccion(fe.nomina.DeduccionSalud(
|
||||
# porcentaje = fe.nomina.Amount(19),
|
||||
# deduccion = fe.nomina.Amount(1000)
|
||||
# ))
|
||||
|
||||
# xml = nomina.toFachoXML()
|
||||
# print(xml)
|
||||
# assert xml.get_element_text('/nomina:NominaIndividual/DeduccionesTotal') == '1000.00'
|
||||
|
||||
# def test_nomina_obligatorios_segun_anexo_tecnico():
|
||||
# nomina = fe.nomina.DIANNominaIndividual()
|
||||
|
||||
# errors = nomina.validate()
|
||||
|
||||
# assert_error(errors, 'se requiere Periodo')
|
||||
# assert_error(errors, 'se requiere DevengadoBasico')
|
||||
# assert_error(errors, 'se requiere DeduccionSalud')
|
||||
# assert_error(errors, 'se requiere DeduccionFondoPension')
|
||||
|
||||
# def test_nomina_xml():
|
||||
# nomina = fe.nomina.DIANNominaIndividual()
|
||||
|
||||
# nomina.asignar_metadata(fe.nomina.Metadata(
|
||||
# novedad=fe.nomina.Novedad(
|
||||
# activa = True,
|
||||
# cune = "N0111"
|
||||
# ),
|
||||
# secuencia=fe.nomina.NumeroSecuencia(
|
||||
# prefijo = 'N',
|
||||
# consecutivo='00001'
|
||||
# ),
|
||||
# lugar_generacion=fe.nomina.Lugar(
|
||||
# pais = fe.nomina.Pais(
|
||||
# code = 'CO'
|
||||
# ),
|
||||
# departamento = fe.nomina.Departamento(
|
||||
# code = '05'
|
||||
# ),
|
||||
# municipio = fe.nomina.Municipio(
|
||||
# code = '05001'
|
||||
# ),
|
||||
# ),
|
||||
# proveedor=fe.nomina.Proveedor(
|
||||
# nit='999999',
|
||||
# dv=2,
|
||||
# software_id='xx',
|
||||
# software_pin='12',
|
||||
# razon_social='facho'
|
||||
# )
|
||||
# ))
|
||||
|
||||
# nomina.asignar_informacion_general(fe.nomina.InformacionGeneral(
|
||||
# fecha_generacion = '2020-01-16',
|
||||
# hora_generacion = '1053:10-05:00',
|
||||
# tipo_ambiente = fe.nomina.InformacionGeneral.AMBIENTE_PRODUCCION,
|
||||
# software_pin = '693',
|
||||
# tipo_xml = fe.nomina.InformacionGeneral.TIPO_XML_NORMAL,
|
||||
# periodo_nomina = fe.nomina.PeriodoNomina(code='1'),
|
||||
# tipo_moneda = fe.nomina.TipoMoneda(code='COP')
|
||||
# ))
|
||||
|
||||
# nomina.asignar_empleador(fe.nomina.Empleador(
|
||||
# razon_social='facho',
|
||||
# nit = '700085371',
|
||||
# dv = '1',
|
||||
# pais = fe.nomina.Pais(
|
||||
# code = 'CO'
|
||||
# ),
|
||||
# departamento = fe.nomina.Departamento(
|
||||
# code = '05'
|
||||
# ),
|
||||
# municipio = fe.nomina.Municipio(
|
||||
# code = '05001'
|
||||
# ),
|
||||
# direccion = 'calle etrivial'
|
||||
# ))
|
||||
|
||||
# nomina.asignar_trabajador(fe.nomina.Trabajador(
|
||||
# tipo_contrato = fe.nomina.TipoContrato(
|
||||
# code = '1'
|
||||
# ),
|
||||
# alto_riesgo = False,
|
||||
# tipo_documento = fe.nomina.TipoDocumento(
|
||||
# code = '11'
|
||||
# ),
|
||||
# primer_apellido = 'gnu',
|
||||
# segundo_apellido = 'emacs',
|
||||
# primer_nombre = 'facho',
|
||||
# lugar_trabajo = fe.nomina.LugarTrabajo(
|
||||
# pais = fe.nomina.Pais(code='CO'),
|
||||
# departamento = fe.nomina.Departamento(code='05'),
|
||||
# municipio = fe.nomina.Municipio(code='05001'),
|
||||
# direccion = 'calle facho'
|
||||
# ),
|
||||
# numero_documento = '800199436',
|
||||
# tipo = fe.nomina.TipoTrabajador(
|
||||
# code = '01'
|
||||
# ),
|
||||
# salario_integral = True,
|
||||
# sueldo = fe.nomina.Amount(1_500_000)
|
||||
# ))
|
||||
|
||||
# nomina.adicionar_devengado(fe.nomina.DevengadoBasico(
|
||||
# dias_trabajados = 60,
|
||||
# sueldo_trabajado = fe.nomina.Amount(3_500_000)
|
||||
# ))
|
||||
|
||||
# nomina.adicionar_deduccion(fe.nomina.DeduccionSalud(
|
||||
# porcentaje = fe.nomina.Amount(19),
|
||||
# deduccion = fe.nomina.Amount(1_000_000)
|
||||
# ))
|
||||
|
||||
# xml = nomina.toFachoXML()
|
||||
# expected_cune = 'b8f9b6c24de07ffd92ea5467433a3b69357cfaffa7c19722db94b2e0eca41d057085a54f484b5da15ff585e773b0b0ab'
|
||||
# assert xml.get_element_attribute('/nomina:NominaIndividual/InformacionGeneral', 'CUNE') == expected_cune
|
||||
# assert xml.get_element_attribute('/nomina:NominaIndividual/InformacionGeneral', 'TipoXML') == '102'
|
||||
# assert xml.get_element_text_or_attribute('/nomina:NominaIndividual/NumeroSecuenciaXML/@Numero') == 'N00001'
|
||||
# assert xml.get_element_text_or_attribute('/nomina:NominaIndividual/NumeroSecuenciaXML/@Consecutivo') == '00001'
|
||||
# assert xml.get_element_text_or_attribute('/nomina:NominaIndividual/LugarGeneracionXML/@Pais') == 'CO'
|
||||
# assert xml.get_element_text_or_attribute('/nomina:NominaIndividual/LugarGeneracionXML/@DepartamentoEstado') == '05'
|
||||
# assert xml.get_element_text_or_attribute('/nomina:NominaIndividual/LugarGeneracionXML/@MunicipioCiudad') == '05001'
|
||||
# assert xml.get_element_text_or_attribute('/nomina:NominaIndividual/ProveedorXML/@NIT') == '999999'
|
||||
# assert xml.get_element_text_or_attribute('/nomina:NominaIndividual/ProveedorXML/@DV') == '2'
|
||||
# assert xml.get_element_text_or_attribute('/nomina:NominaIndividual/ProveedorXML/@SoftwareID') == 'xx'
|
||||
# assert xml.get_element_text_or_attribute('/nomina:NominaIndividual/ProveedorXML/@SoftwareSC') is not None
|
||||
# assert xml.get_element_text_or_attribute('/nomina:NominaIndividual/CodigoQR') == f"https://catalogo-vpfe.dian.gov.co/document/searchqr?documentkey={expected_cune}"
|
||||
# assert xml.get_element_text_or_attribute('/nomina:NominaIndividual/Empleador/@NIT') == '700085371'
|
||||
# assert xml.get_element_text_or_attribute('/nomina:NominaIndividual/Trabajador/@NumeroDocumento') == '800199436'
|
||||
# assert xml.get_element_text_or_attribute('/nomina:NominaIndividual/Novedad') == 'True'
|
||||
# assert xml.get_element_text_or_attribute('/nomina:NominaIndividual/Novedad/@CUNENov') == 'N0111'
|
||||
|
||||
# # confirmar el namespace
|
||||
# assert 'xmlns="dian:gov:co:facturaelectronica:NominaIndividual"' in xml.tostring()
|
||||
|
||||
# def test_asignar_pago():
|
||||
# nomina = fe.nomina.DIANNominaIndividual()
|
||||
# nomina.asignar_pago(fe.nomina.Pago(
|
||||
# forma = fe.nomina.FormaPago(code='1'),
|
||||
# metodo = fe.nomina.MetodoPago(code='1')
|
||||
# ))
|
||||
|
||||
# def test_nomina_xmlsign(monkeypatch):
|
||||
# nomina = fe.nomina.DIANNominaIndividual()
|
||||
# xml = nomina.toFachoXML()
|
||||
|
||||
# signer = fe.nomina.DianXMLExtensionSigner('./tests/example.p12')
|
||||
# xml.add_extension(signer)
|
||||
|
||||
# print(xml.tostring())
|
||||
# elem = xml.get_element('/nomina:NominaIndividual/ext:UBLExtensions/ext:UBLExtension/ext:ExtensionContent/ds:Signature')
|
||||
# assert elem is not None
|
||||
|
||||
|
||||
|
||||
# def test_nomina_devengado_horas_extras_nocturnas():
|
||||
# nomina = fe.nomina.DIANNominaIndividual()
|
||||
|
||||
# nomina.adicionar_devengado(fe.nomina.DevengadoHorasExtrasNocturnas(
|
||||
# horas_extras=[
|
||||
# fe.nomina.DevengadoHoraExtra(
|
||||
# hora_inicio='2021-11-30T19:09:55',
|
||||
# hora_fin='2021-11-30T20:09:55',
|
||||
# cantidad=1,
|
||||
# porcentaje=fe.nomina.Amount(1),
|
||||
# pago=fe.nomina.Amount(100)
|
||||
# ),
|
||||
# fe.nomina.DevengadoHoraExtra(
|
||||
# hora_inicio='2021-11-30T18:09:55',
|
||||
# hora_fin='2021-11-30T19:09:55',
|
||||
# cantidad=2,
|
||||
# porcentaje=fe.nomina.Amount(2),
|
||||
# pago=fe.nomina.Amount(200)
|
||||
# )
|
||||
# ]
|
||||
# ))
|
||||
|
||||
# xml = nomina.toFachoXML()
|
||||
# extras = xml.get_element('/nomina:NominaIndividual/Devengados/HENs/HEN', multiple=True)
|
||||
# assert extras[0].get('HoraInicio') == '2021-11-30T19:09:55'
|
||||
# assert extras[0].get('HoraFin') == '2021-11-30T20:09:55'
|
||||
# assert extras[0].get('Cantidad') == '1'
|
||||
# assert extras[0].get('Porcentaje') == '1.00'
|
||||
# assert extras[0].get('Pago') == '100.00'
|
||||
# assert extras[1].get('HoraInicio') == '2021-11-30T18:09:55'
|
||||
# assert extras[1].get('HoraFin') == '2021-11-30T19:09:55'
|
||||
# assert extras[1].get('Cantidad') == '2'
|
||||
# assert extras[1].get('Porcentaje') == '2.00'
|
||||
# assert extras[1].get('Pago') == '200.00'
|
||||
|
||||
# def test_nomina_devengado_horas_recargo_nocturno():
|
||||
# nomina = fe.nomina.DIANNominaIndividual()
|
||||
|
||||
# nomina.adicionar_devengado(fe.nomina.DevengadoHorasRecargoNocturno(
|
||||
# horas_extras=[
|
||||
# fe.nomina.DevengadoHoraExtra(
|
||||
# hora_inicio='2021-11-30T19:09:55',
|
||||
# hora_fin='2021-11-30T20:09:55',
|
||||
# cantidad=1,
|
||||
# porcentaje=fe.nomina.Amount(1),
|
||||
# pago=fe.nomina.Amount(100)
|
||||
# ),
|
||||
# fe.nomina.DevengadoHoraExtra(
|
||||
# hora_inicio='2021-11-30T18:09:55',
|
||||
# hora_fin='2021-11-30T19:09:55',
|
||||
# cantidad=2,
|
||||
# porcentaje=fe.nomina.Amount(2),
|
||||
# pago=fe.nomina.Amount(200)
|
||||
# )
|
||||
# ]
|
||||
# ))
|
||||
|
||||
# xml = nomina.toFachoXML()
|
||||
# extras = xml.get_element('/nomina:NominaIndividual/Devengados/HRNs/HRN', multiple=True)
|
||||
# assert extras[0].get('HoraInicio') == '2021-11-30T19:09:55'
|
||||
# assert extras[0].get('HoraFin') == '2021-11-30T20:09:55'
|
||||
# assert extras[0].get('Cantidad') == '1'
|
||||
# assert extras[0].get('Porcentaje') == '1.00'
|
||||
# assert extras[0].get('Pago') == '100.00'
|
||||
# assert extras[1].get('HoraInicio') == '2021-11-30T18:09:55'
|
||||
# assert extras[1].get('HoraFin') == '2021-11-30T19:09:55'
|
||||
# assert extras[1].get('Cantidad') == '2'
|
||||
# assert extras[1].get('Porcentaje') == '2.00'
|
||||
# assert extras[1].get('Pago') == '200.00'
|
||||
|
||||
# def test_nomina_devengado_horas_extras_diarias_dominicales_y_festivos():
|
||||
# nomina = fe.nomina.DIANNominaIndividual()
|
||||
|
||||
# nomina.adicionar_devengado(fe.nomina.DevengadoHorasExtrasDiariasDominicalesYFestivos(
|
||||
# horas_extras=[
|
||||
# fe.nomina.DevengadoHoraExtra(
|
||||
# hora_inicio='2021-11-30T19:09:55',
|
||||
# hora_fin='2021-11-30T20:09:55',
|
||||
# cantidad=1,
|
||||
# porcentaje=fe.nomina.Amount(1),
|
||||
# pago=fe.nomina.Amount(100)
|
||||
# ),
|
||||
# fe.nomina.DevengadoHoraExtra(
|
||||
# hora_inicio='2021-11-30T18:09:55',
|
||||
# hora_fin='2021-11-30T19:09:55',
|
||||
# cantidad=2,
|
||||
# porcentaje=fe.nomina.Amount(2),
|
||||
# pago=fe.nomina.Amount(200)
|
||||
# )
|
||||
# ]
|
||||
# ))
|
||||
|
||||
# xml = nomina.toFachoXML()
|
||||
# extras = xml.get_element('/nomina:NominaIndividual/Devengados/HEDDFs/HEDDF', multiple=True)
|
||||
# assert extras[0].get('HoraInicio') == '2021-11-30T19:09:55'
|
||||
# assert extras[0].get('HoraFin') == '2021-11-30T20:09:55'
|
||||
# assert extras[0].get('Cantidad') == '1'
|
||||
# assert extras[0].get('Porcentaje') == '1.00'
|
||||
# assert extras[0].get('Pago') == '100.00'
|
||||
# assert extras[1].get('HoraInicio') == '2021-11-30T18:09:55'
|
||||
# assert extras[1].get('HoraFin') == '2021-11-30T19:09:55'
|
||||
# assert extras[1].get('Cantidad') == '2'
|
||||
# assert extras[1].get('Porcentaje') == '2.00'
|
||||
# assert extras[1].get('Pago') == '200.00'
|
||||
|
||||
# def test_nomina_devengado_horas_recargo_diarias_dominicales_y_festivos():
|
||||
# nomina = fe.nomina.DIANNominaIndividual()
|
||||
|
||||
# nomina.adicionar_devengado(fe.nomina.DevengadoHorasRecargoDiariasDominicalesYFestivos(
|
||||
# horas_extras=[
|
||||
# fe.nomina.DevengadoHoraExtra(
|
||||
# hora_inicio='2021-11-30T19:09:55',
|
||||
# hora_fin='2021-11-30T20:09:55',
|
||||
# cantidad=1,
|
||||
# porcentaje=fe.nomina.Amount(1),
|
||||
# pago=fe.nomina.Amount(100)
|
||||
# ),
|
||||
# fe.nomina.DevengadoHoraExtra(
|
||||
# hora_inicio='2021-11-30T18:09:55',
|
||||
# hora_fin='2021-11-30T19:09:55',
|
||||
# cantidad=2,
|
||||
# porcentaje=fe.nomina.Amount(2),
|
||||
# pago=fe.nomina.Amount(200)
|
||||
# )
|
||||
# ]
|
||||
# ))
|
||||
|
||||
# xml = nomina.toFachoXML()
|
||||
# extras = xml.get_element('/nomina:NominaIndividual/Devengados/HRDDFs/HRDDF', multiple=True)
|
||||
# assert extras[0].get('HoraInicio') == '2021-11-30T19:09:55'
|
||||
# assert extras[0].get('HoraFin') == '2021-11-30T20:09:55'
|
||||
# assert extras[0].get('Cantidad') == '1'
|
||||
# assert extras[0].get('Porcentaje') == '1.00'
|
||||
# assert extras[0].get('Pago') == '100.00'
|
||||
# assert extras[1].get('HoraInicio') == '2021-11-30T18:09:55'
|
||||
# assert extras[1].get('HoraFin') == '2021-11-30T19:09:55'
|
||||
# assert extras[1].get('Cantidad') == '2'
|
||||
# assert extras[1].get('Porcentaje') == '2.00'
|
||||
# assert extras[1].get('Pago') == '200.00'
|
||||
|
||||
|
||||
# def test_nomina_devengado_horas_extras_nocturnas_dominicales_y_festivos():
|
||||
# nomina = fe.nomina.DIANNominaIndividual()
|
||||
|
||||
# nomina.adicionar_devengado(fe.nomina.DevengadoHorasExtrasNocturnasDominicalesYFestivos(
|
||||
# horas_extras=[
|
||||
# fe.nomina.DevengadoHoraExtra(
|
||||
# hora_inicio='2021-11-30T19:09:55',
|
||||
# hora_fin='2021-11-30T20:09:55',
|
||||
# cantidad=1,
|
||||
# porcentaje=fe.nomina.Amount(1),
|
||||
# pago=fe.nomina.Amount(100)
|
||||
# ),
|
||||
# fe.nomina.DevengadoHoraExtra(
|
||||
# hora_inicio='2021-11-30T18:09:55',
|
||||
# hora_fin='2021-11-30T19:09:55',
|
||||
# cantidad=2,
|
||||
# porcentaje=fe.nomina.Amount(2),
|
||||
# pago=fe.nomina.Amount(200)
|
||||
# )
|
||||
# ]
|
||||
# ))
|
||||
|
||||
# xml = nomina.toFachoXML()
|
||||
# extras = xml.get_element('/nomina:NominaIndividual/Devengados/HENDFs/HENDF', multiple=True)
|
||||
# assert extras[0].get('HoraInicio') == '2021-11-30T19:09:55'
|
||||
# assert extras[0].get('HoraFin') == '2021-11-30T20:09:55'
|
||||
# assert extras[0].get('Cantidad') == '1'
|
||||
# assert extras[0].get('Porcentaje') == '1.00'
|
||||
# assert extras[0].get('Pago') == '100.00'
|
||||
# assert extras[1].get('HoraInicio') == '2021-11-30T18:09:55'
|
||||
# assert extras[1].get('HoraFin') == '2021-11-30T19:09:55'
|
||||
# assert extras[1].get('Cantidad') == '2'
|
||||
# assert extras[1].get('Porcentaje') == '2.00'
|
||||
# assert extras[1].get('Pago') == '200.00'
|
||||
|
||||
# def test_nomina_devengado_horas_recargo_nocturno_dominicales_y_festivos():
|
||||
# nomina = fe.nomina.DIANNominaIndividual()
|
||||
|
||||
# nomina.adicionar_devengado(fe.nomina.DevengadoHorasRecargoNocturnoDominicalesYFestivos(
|
||||
# horas_extras=[
|
||||
# fe.nomina.DevengadoHoraExtra(
|
||||
# hora_inicio='2021-11-30T19:09:55',
|
||||
# hora_fin='2021-11-30T20:09:55',
|
||||
# cantidad=1,
|
||||
# porcentaje=fe.nomina.Amount(1),
|
||||
# pago=fe.nomina.Amount(100)
|
||||
# ),
|
||||
# fe.nomina.DevengadoHoraExtra(
|
||||
# hora_inicio='2021-11-30T18:09:55',
|
||||
# hora_fin='2021-11-30T19:09:55',
|
||||
# cantidad=2,
|
||||
# porcentaje=fe.nomina.Amount(2),
|
||||
# pago=fe.nomina.Amount(200)
|
||||
# )
|
||||
# ]
|
||||
# ))
|
||||
|
||||
# xml = nomina.toFachoXML()
|
||||
# extras = xml.get_element('/nomina:NominaIndividual/Devengados/HRNDFs/HRNDF', multiple=True)
|
||||
# assert extras[0].get('HoraInicio') == '2021-11-30T19:09:55'
|
||||
# assert extras[0].get('HoraFin') == '2021-11-30T20:09:55'
|
||||
# assert extras[0].get('Cantidad') == '1'
|
||||
# assert extras[0].get('Porcentaje') == '1.00'
|
||||
# assert extras[0].get('Pago') == '100.00'
|
||||
# assert extras[1].get('HoraInicio') == '2021-11-30T18:09:55'
|
||||
# assert extras[1].get('HoraFin') == '2021-11-30T19:09:55'
|
||||
# assert extras[1].get('Cantidad') == '2'
|
||||
# assert extras[1].get('Porcentaje') == '2.00'
|
||||
# assert extras[1].get('Pago') == '200.00'
|
||||
|
||||
# def test_fecha_validacion():
|
||||
# with pytest.raises(ValueError) as e:
|
||||
# fe.nomina.Fecha('535-35-3')
|
||||
|
||||
xml = nomina.toFachoXML()
|
||||
|
||||
assert xml.get_element_text('/nomina:NominaIndividual/ComprobanteTotal') == '1000000.00'
|
||||
|
||||
def test_adicionar_devengado_comprobante_total_cero():
|
||||
nomina = fe.nomina.DIANNominaIndividual()
|
||||
|
||||
nomina.adicionar_devengado(fe.nomina.DevengadoBasico(
|
||||
dias_trabajados = 60,
|
||||
sueldo_trabajado = fe.nomina.Amount(1_000_000)
|
||||
))
|
||||
|
||||
nomina.adicionar_deduccion(fe.nomina.DeduccionSalud(
|
||||
porcentaje = fe.nomina.Amount(19),
|
||||
deduccion = fe.nomina.Amount(1_000_000)
|
||||
))
|
||||
|
||||
xml = nomina.toFachoXML()
|
||||
|
||||
assert xml.get_element_text('/nomina:NominaIndividual/ComprobanteTotal') == '0.00'
|
||||
|
||||
def test_adicionar_devengado_transporte_muchos():
|
||||
nomina = fe.nomina.DIANNominaIndividual()
|
||||
|
||||
nomina.adicionar_devengado(fe.nomina.DevengadoTransporte(
|
||||
auxilio_transporte = fe.nomina.Amount(2_000_000)
|
||||
))
|
||||
|
||||
nomina.adicionar_devengado(fe.nomina.DevengadoTransporte(
|
||||
auxilio_transporte = fe.nomina.Amount(3_000_000)
|
||||
))
|
||||
|
||||
xml = nomina.toFachoXML()
|
||||
print(xml)
|
||||
assert xml.get_element_text('/nomina:NominaIndividual/DevengadosTotal') == '5000000.00'
|
||||
|
||||
def test_adicionar_deduccion_salud():
|
||||
nomina = fe.nomina.DIANNominaIndividual()
|
||||
|
||||
nomina.adicionar_devengado(fe.nomina.DevengadoBasico(
|
||||
dias_trabajados = 60,
|
||||
sueldo_trabajado = fe.nomina.Amount(1000)
|
||||
))
|
||||
|
||||
nomina.adicionar_deduccion(fe.nomina.DeduccionSalud(
|
||||
porcentaje = fe.nomina.Amount(19),
|
||||
deduccion = fe.nomina.Amount(1000)
|
||||
))
|
||||
|
||||
xml = nomina.toFachoXML()
|
||||
print(xml)
|
||||
assert xml.get_element_text('/nomina:NominaIndividual/DeduccionesTotal') == '1000.00'
|
||||
|
||||
def test_nomina_obligatorios_segun_anexo_tecnico():
|
||||
nomina = fe.nomina.DIANNominaIndividual()
|
||||
|
||||
errors = nomina.validate()
|
||||
|
||||
assert_error(errors, 'se requiere Periodo')
|
||||
assert_error(errors, 'se requiere DevengadoBasico')
|
||||
assert_error(errors, 'se requiere DeduccionSalud')
|
||||
assert_error(errors, 'se requiere DeduccionFondoPension')
|
||||
|
||||
def test_nomina_xml():
|
||||
nomina = fe.nomina.DIANNominaIndividual()
|
||||
|
||||
nomina.asignar_metadata(fe.nomina.Metadata(
|
||||
novedad=fe.nomina.Novedad(
|
||||
activa = True,
|
||||
cune = "N0111"
|
||||
),
|
||||
secuencia=fe.nomina.NumeroSecuencia(
|
||||
prefijo = 'N',
|
||||
consecutivo='00001'
|
||||
),
|
||||
lugar_generacion=fe.nomina.Lugar(
|
||||
pais = fe.nomina.Pais(
|
||||
code = 'CO'
|
||||
),
|
||||
departamento = fe.nomina.Departamento(
|
||||
code = '05'
|
||||
),
|
||||
municipio = fe.nomina.Municipio(
|
||||
code = '05001'
|
||||
),
|
||||
),
|
||||
proveedor=fe.nomina.Proveedor(
|
||||
nit='999999',
|
||||
dv=2,
|
||||
software_id='xx',
|
||||
software_pin='12',
|
||||
razon_social='facho'
|
||||
)
|
||||
))
|
||||
|
||||
nomina.asignar_informacion_general(fe.nomina.InformacionGeneral(
|
||||
fecha_generacion = '2020-01-16',
|
||||
hora_generacion = '1053:10-05:00',
|
||||
tipo_ambiente = fe.nomina.InformacionGeneral.AMBIENTE_PRODUCCION,
|
||||
software_pin = '693',
|
||||
tipo_xml = fe.nomina.InformacionGeneral.TIPO_XML_NORMAL,
|
||||
periodo_nomina = fe.nomina.PeriodoNomina(code='1'),
|
||||
tipo_moneda = fe.nomina.TipoMoneda(code='COP')
|
||||
))
|
||||
|
||||
nomina.asignar_empleador(fe.nomina.Empleador(
|
||||
razon_social='facho',
|
||||
nit = '700085371',
|
||||
dv = '1',
|
||||
pais = fe.nomina.Pais(
|
||||
code = 'CO'
|
||||
),
|
||||
departamento = fe.nomina.Departamento(
|
||||
code = '05'
|
||||
),
|
||||
municipio = fe.nomina.Municipio(
|
||||
code = '05001'
|
||||
),
|
||||
direccion = 'calle etrivial'
|
||||
))
|
||||
|
||||
nomina.asignar_trabajador(fe.nomina.Trabajador(
|
||||
tipo_contrato = fe.nomina.TipoContrato(
|
||||
code = '1'
|
||||
),
|
||||
alto_riesgo = False,
|
||||
tipo_documento = fe.nomina.TipoDocumento(
|
||||
code = '11'
|
||||
),
|
||||
primer_apellido = 'gnu',
|
||||
segundo_apellido = 'emacs',
|
||||
primer_nombre = 'facho',
|
||||
lugar_trabajo = fe.nomina.LugarTrabajo(
|
||||
pais = fe.nomina.Pais(code='CO'),
|
||||
departamento = fe.nomina.Departamento(code='05'),
|
||||
municipio = fe.nomina.Municipio(code='05001'),
|
||||
direccion = 'calle facho'
|
||||
),
|
||||
numero_documento = '800199436',
|
||||
tipo = fe.nomina.TipoTrabajador(
|
||||
code = '01'
|
||||
),
|
||||
salario_integral = True,
|
||||
sueldo = fe.nomina.Amount(1_500_000)
|
||||
))
|
||||
|
||||
nomina.adicionar_devengado(fe.nomina.DevengadoBasico(
|
||||
dias_trabajados = 60,
|
||||
sueldo_trabajado = fe.nomina.Amount(3_500_000)
|
||||
))
|
||||
|
||||
nomina.adicionar_deduccion(fe.nomina.DeduccionSalud(
|
||||
porcentaje = fe.nomina.Amount(19),
|
||||
deduccion = fe.nomina.Amount(1_000_000)
|
||||
))
|
||||
|
||||
xml = nomina.toFachoXML()
|
||||
expected_cune = 'b8f9b6c24de07ffd92ea5467433a3b69357cfaffa7c19722db94b2e0eca41d057085a54f484b5da15ff585e773b0b0ab'
|
||||
assert xml.get_element_attribute('/nomina:NominaIndividual/InformacionGeneral', 'CUNE') == expected_cune
|
||||
assert xml.get_element_attribute('/nomina:NominaIndividual/InformacionGeneral', 'TipoXML') == '102'
|
||||
assert xml.get_element_text_or_attribute('/nomina:NominaIndividual/NumeroSecuenciaXML/@Numero') == 'N00001'
|
||||
assert xml.get_element_text_or_attribute('/nomina:NominaIndividual/NumeroSecuenciaXML/@Consecutivo') == '00001'
|
||||
assert xml.get_element_text_or_attribute('/nomina:NominaIndividual/LugarGeneracionXML/@Pais') == 'CO'
|
||||
assert xml.get_element_text_or_attribute('/nomina:NominaIndividual/LugarGeneracionXML/@DepartamentoEstado') == '05'
|
||||
assert xml.get_element_text_or_attribute('/nomina:NominaIndividual/LugarGeneracionXML/@MunicipioCiudad') == '05001'
|
||||
assert xml.get_element_text_or_attribute('/nomina:NominaIndividual/ProveedorXML/@NIT') == '999999'
|
||||
assert xml.get_element_text_or_attribute('/nomina:NominaIndividual/ProveedorXML/@DV') == '2'
|
||||
assert xml.get_element_text_or_attribute('/nomina:NominaIndividual/ProveedorXML/@SoftwareID') == 'xx'
|
||||
assert xml.get_element_text_or_attribute('/nomina:NominaIndividual/ProveedorXML/@SoftwareSC') is not None
|
||||
assert xml.get_element_text_or_attribute('/nomina:NominaIndividual/CodigoQR') == f"https://catalogo-vpfe.dian.gov.co/document/searchqr?documentkey={expected_cune}"
|
||||
assert xml.get_element_text_or_attribute('/nomina:NominaIndividual/Empleador/@NIT') == '700085371'
|
||||
assert xml.get_element_text_or_attribute('/nomina:NominaIndividual/Trabajador/@NumeroDocumento') == '800199436'
|
||||
assert xml.get_element_text_or_attribute('/nomina:NominaIndividual/Novedad') == 'True'
|
||||
assert xml.get_element_text_or_attribute('/nomina:NominaIndividual/Novedad/@CUNENov') == 'N0111'
|
||||
|
||||
# confirmar el namespace
|
||||
assert 'xmlns="dian:gov:co:facturaelectronica:NominaIndividual"' in xml.tostring()
|
||||
|
||||
def test_asignar_pago():
|
||||
nomina = fe.nomina.DIANNominaIndividual()
|
||||
nomina.asignar_pago(fe.nomina.Pago(
|
||||
forma = fe.nomina.FormaPago(code='1'),
|
||||
metodo = fe.nomina.MetodoPago(code='1')
|
||||
))
|
||||
|
||||
def test_nomina_xmlsign(monkeypatch):
|
||||
nomina = fe.nomina.DIANNominaIndividual()
|
||||
xml = nomina.toFachoXML()
|
||||
|
||||
signer = fe.nomina.DianXMLExtensionSigner('./tests/example.p12')
|
||||
xml.add_extension(signer)
|
||||
|
||||
print(xml.tostring())
|
||||
elem = xml.get_element('/nomina:NominaIndividual/ext:UBLExtensions/ext:UBLExtension/ext:ExtensionContent/ds:Signature')
|
||||
assert elem is not None
|
||||
|
||||
|
||||
|
||||
def test_nomina_devengado_horas_extras_nocturnas():
|
||||
nomina = fe.nomina.DIANNominaIndividual()
|
||||
|
||||
nomina.adicionar_devengado(fe.nomina.DevengadoHorasExtrasNocturnas(
|
||||
horas_extras=[
|
||||
fe.nomina.DevengadoHoraExtra(
|
||||
hora_inicio='2021-11-30T19:09:55',
|
||||
hora_fin='2021-11-30T20:09:55',
|
||||
cantidad=1,
|
||||
porcentaje=fe.nomina.Amount(1),
|
||||
pago=fe.nomina.Amount(100)
|
||||
),
|
||||
fe.nomina.DevengadoHoraExtra(
|
||||
hora_inicio='2021-11-30T18:09:55',
|
||||
hora_fin='2021-11-30T19:09:55',
|
||||
cantidad=2,
|
||||
porcentaje=fe.nomina.Amount(2),
|
||||
pago=fe.nomina.Amount(200)
|
||||
)
|
||||
]
|
||||
))
|
||||
|
||||
xml = nomina.toFachoXML()
|
||||
extras = xml.get_element('/nomina:NominaIndividual/Devengados/HENs/HEN', multiple=True)
|
||||
assert extras[0].get('HoraInicio') == '2021-11-30T19:09:55'
|
||||
assert extras[0].get('HoraFin') == '2021-11-30T20:09:55'
|
||||
assert extras[0].get('Cantidad') == '1'
|
||||
assert extras[0].get('Porcentaje') == '1.00'
|
||||
assert extras[0].get('Pago') == '100.00'
|
||||
assert extras[1].get('HoraInicio') == '2021-11-30T18:09:55'
|
||||
assert extras[1].get('HoraFin') == '2021-11-30T19:09:55'
|
||||
assert extras[1].get('Cantidad') == '2'
|
||||
assert extras[1].get('Porcentaje') == '2.00'
|
||||
assert extras[1].get('Pago') == '200.00'
|
||||
|
||||
def test_nomina_devengado_horas_recargo_nocturno():
|
||||
nomina = fe.nomina.DIANNominaIndividual()
|
||||
|
||||
nomina.adicionar_devengado(fe.nomina.DevengadoHorasRecargoNocturno(
|
||||
horas_extras=[
|
||||
fe.nomina.DevengadoHoraExtra(
|
||||
hora_inicio='2021-11-30T19:09:55',
|
||||
hora_fin='2021-11-30T20:09:55',
|
||||
cantidad=1,
|
||||
porcentaje=fe.nomina.Amount(1),
|
||||
pago=fe.nomina.Amount(100)
|
||||
),
|
||||
fe.nomina.DevengadoHoraExtra(
|
||||
hora_inicio='2021-11-30T18:09:55',
|
||||
hora_fin='2021-11-30T19:09:55',
|
||||
cantidad=2,
|
||||
porcentaje=fe.nomina.Amount(2),
|
||||
pago=fe.nomina.Amount(200)
|
||||
)
|
||||
]
|
||||
))
|
||||
|
||||
xml = nomina.toFachoXML()
|
||||
extras = xml.get_element('/nomina:NominaIndividual/Devengados/HRNs/HRN', multiple=True)
|
||||
assert extras[0].get('HoraInicio') == '2021-11-30T19:09:55'
|
||||
assert extras[0].get('HoraFin') == '2021-11-30T20:09:55'
|
||||
assert extras[0].get('Cantidad') == '1'
|
||||
assert extras[0].get('Porcentaje') == '1.00'
|
||||
assert extras[0].get('Pago') == '100.00'
|
||||
assert extras[1].get('HoraInicio') == '2021-11-30T18:09:55'
|
||||
assert extras[1].get('HoraFin') == '2021-11-30T19:09:55'
|
||||
assert extras[1].get('Cantidad') == '2'
|
||||
assert extras[1].get('Porcentaje') == '2.00'
|
||||
assert extras[1].get('Pago') == '200.00'
|
||||
|
||||
def test_nomina_devengado_horas_extras_diarias_dominicales_y_festivos():
|
||||
nomina = fe.nomina.DIANNominaIndividual()
|
||||
|
||||
nomina.adicionar_devengado(fe.nomina.DevengadoHorasExtrasDiariasDominicalesYFestivos(
|
||||
horas_extras=[
|
||||
fe.nomina.DevengadoHoraExtra(
|
||||
hora_inicio='2021-11-30T19:09:55',
|
||||
hora_fin='2021-11-30T20:09:55',
|
||||
cantidad=1,
|
||||
porcentaje=fe.nomina.Amount(1),
|
||||
pago=fe.nomina.Amount(100)
|
||||
),
|
||||
fe.nomina.DevengadoHoraExtra(
|
||||
hora_inicio='2021-11-30T18:09:55',
|
||||
hora_fin='2021-11-30T19:09:55',
|
||||
cantidad=2,
|
||||
porcentaje=fe.nomina.Amount(2),
|
||||
pago=fe.nomina.Amount(200)
|
||||
)
|
||||
]
|
||||
))
|
||||
|
||||
xml = nomina.toFachoXML()
|
||||
extras = xml.get_element('/nomina:NominaIndividual/Devengados/HEDDFs/HEDDF', multiple=True)
|
||||
assert extras[0].get('HoraInicio') == '2021-11-30T19:09:55'
|
||||
assert extras[0].get('HoraFin') == '2021-11-30T20:09:55'
|
||||
assert extras[0].get('Cantidad') == '1'
|
||||
assert extras[0].get('Porcentaje') == '1.00'
|
||||
assert extras[0].get('Pago') == '100.00'
|
||||
assert extras[1].get('HoraInicio') == '2021-11-30T18:09:55'
|
||||
assert extras[1].get('HoraFin') == '2021-11-30T19:09:55'
|
||||
assert extras[1].get('Cantidad') == '2'
|
||||
assert extras[1].get('Porcentaje') == '2.00'
|
||||
assert extras[1].get('Pago') == '200.00'
|
||||
|
||||
def test_nomina_devengado_horas_recargo_diarias_dominicales_y_festivos():
|
||||
nomina = fe.nomina.DIANNominaIndividual()
|
||||
|
||||
nomina.adicionar_devengado(fe.nomina.DevengadoHorasRecargoDiariasDominicalesYFestivos(
|
||||
horas_extras=[
|
||||
fe.nomina.DevengadoHoraExtra(
|
||||
hora_inicio='2021-11-30T19:09:55',
|
||||
hora_fin='2021-11-30T20:09:55',
|
||||
cantidad=1,
|
||||
porcentaje=fe.nomina.Amount(1),
|
||||
pago=fe.nomina.Amount(100)
|
||||
),
|
||||
fe.nomina.DevengadoHoraExtra(
|
||||
hora_inicio='2021-11-30T18:09:55',
|
||||
hora_fin='2021-11-30T19:09:55',
|
||||
cantidad=2,
|
||||
porcentaje=fe.nomina.Amount(2),
|
||||
pago=fe.nomina.Amount(200)
|
||||
)
|
||||
]
|
||||
))
|
||||
|
||||
xml = nomina.toFachoXML()
|
||||
extras = xml.get_element('/nomina:NominaIndividual/Devengados/HRDDFs/HRDDF', multiple=True)
|
||||
assert extras[0].get('HoraInicio') == '2021-11-30T19:09:55'
|
||||
assert extras[0].get('HoraFin') == '2021-11-30T20:09:55'
|
||||
assert extras[0].get('Cantidad') == '1'
|
||||
assert extras[0].get('Porcentaje') == '1.00'
|
||||
assert extras[0].get('Pago') == '100.00'
|
||||
assert extras[1].get('HoraInicio') == '2021-11-30T18:09:55'
|
||||
assert extras[1].get('HoraFin') == '2021-11-30T19:09:55'
|
||||
assert extras[1].get('Cantidad') == '2'
|
||||
assert extras[1].get('Porcentaje') == '2.00'
|
||||
assert extras[1].get('Pago') == '200.00'
|
||||
|
||||
|
||||
def test_nomina_devengado_horas_extras_nocturnas_dominicales_y_festivos():
|
||||
nomina = fe.nomina.DIANNominaIndividual()
|
||||
|
||||
nomina.adicionar_devengado(fe.nomina.DevengadoHorasExtrasNocturnasDominicalesYFestivos(
|
||||
horas_extras=[
|
||||
fe.nomina.DevengadoHoraExtra(
|
||||
hora_inicio='2021-11-30T19:09:55',
|
||||
hora_fin='2021-11-30T20:09:55',
|
||||
cantidad=1,
|
||||
porcentaje=fe.nomina.Amount(1),
|
||||
pago=fe.nomina.Amount(100)
|
||||
),
|
||||
fe.nomina.DevengadoHoraExtra(
|
||||
hora_inicio='2021-11-30T18:09:55',
|
||||
hora_fin='2021-11-30T19:09:55',
|
||||
cantidad=2,
|
||||
porcentaje=fe.nomina.Amount(2),
|
||||
pago=fe.nomina.Amount(200)
|
||||
)
|
||||
]
|
||||
))
|
||||
|
||||
xml = nomina.toFachoXML()
|
||||
extras = xml.get_element('/nomina:NominaIndividual/Devengados/HENDFs/HENDF', multiple=True)
|
||||
assert extras[0].get('HoraInicio') == '2021-11-30T19:09:55'
|
||||
assert extras[0].get('HoraFin') == '2021-11-30T20:09:55'
|
||||
assert extras[0].get('Cantidad') == '1'
|
||||
assert extras[0].get('Porcentaje') == '1.00'
|
||||
assert extras[0].get('Pago') == '100.00'
|
||||
assert extras[1].get('HoraInicio') == '2021-11-30T18:09:55'
|
||||
assert extras[1].get('HoraFin') == '2021-11-30T19:09:55'
|
||||
assert extras[1].get('Cantidad') == '2'
|
||||
assert extras[1].get('Porcentaje') == '2.00'
|
||||
assert extras[1].get('Pago') == '200.00'
|
||||
|
||||
def test_nomina_devengado_horas_recargo_nocturno_dominicales_y_festivos():
|
||||
nomina = fe.nomina.DIANNominaIndividual()
|
||||
|
||||
nomina.adicionar_devengado(fe.nomina.DevengadoHorasRecargoNocturnoDominicalesYFestivos(
|
||||
horas_extras=[
|
||||
fe.nomina.DevengadoHoraExtra(
|
||||
hora_inicio='2021-11-30T19:09:55',
|
||||
hora_fin='2021-11-30T20:09:55',
|
||||
cantidad=1,
|
||||
porcentaje=fe.nomina.Amount(1),
|
||||
pago=fe.nomina.Amount(100)
|
||||
),
|
||||
fe.nomina.DevengadoHoraExtra(
|
||||
hora_inicio='2021-11-30T18:09:55',
|
||||
hora_fin='2021-11-30T19:09:55',
|
||||
cantidad=2,
|
||||
porcentaje=fe.nomina.Amount(2),
|
||||
pago=fe.nomina.Amount(200)
|
||||
)
|
||||
]
|
||||
))
|
||||
|
||||
xml = nomina.toFachoXML()
|
||||
extras = xml.get_element('/nomina:NominaIndividual/Devengados/HRNDFs/HRNDF', multiple=True)
|
||||
assert extras[0].get('HoraInicio') == '2021-11-30T19:09:55'
|
||||
assert extras[0].get('HoraFin') == '2021-11-30T20:09:55'
|
||||
assert extras[0].get('Cantidad') == '1'
|
||||
assert extras[0].get('Porcentaje') == '1.00'
|
||||
assert extras[0].get('Pago') == '100.00'
|
||||
assert extras[1].get('HoraInicio') == '2021-11-30T18:09:55'
|
||||
assert extras[1].get('HoraFin') == '2021-11-30T19:09:55'
|
||||
assert extras[1].get('Cantidad') == '2'
|
||||
assert extras[1].get('Porcentaje') == '2.00'
|
||||
assert extras[1].get('Pago') == '200.00'
|
||||
|
||||
def test_fecha_validacion():
|
||||
with pytest.raises(ValueError) as e:
|
||||
fe.nomina.Fecha('535-35-3')
|
||||
|
@ -4,233 +4,232 @@
|
||||
# this repository contains the full copyright notices and license terms.
|
||||
|
||||
"""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():
|
||||
# nomina = fe.nomina.DIANNominaIndividualDeAjuste.Reemplazar()
|
||||
def atest_nomina_ajuste_reemplazar():
|
||||
nomina = fe.nomina.DIANNominaIndividualDeAjuste.Reemplazar()
|
||||
|
||||
# xml = nomina.toFachoXML()
|
||||
# print(xml)
|
||||
# assert False
|
||||
xml = nomina.toFachoXML()
|
||||
print(xml)
|
||||
assert False
|
||||
|
||||
# def test_nomina_ajuste_reemplazar_asignacion_tipo_xml():
|
||||
# nomina = fe.nomina.DIANNominaIndividualDeAjuste.Reemplazar()
|
||||
# nomina.asignar_metadata(fe.nomina.Metadata(
|
||||
# novedad=fe.nomina.Novedad(
|
||||
# activa = True,
|
||||
# cune = "N0111"
|
||||
# ),
|
||||
# secuencia=fe.nomina.NumeroSecuencia(
|
||||
# prefijo = 'N',
|
||||
# consecutivo='00001'
|
||||
# ),
|
||||
# lugar_generacion=fe.nomina.Lugar(
|
||||
# pais = fe.nomina.Pais(
|
||||
# code = 'CO'
|
||||
# ),
|
||||
# departamento = fe.nomina.Departamento(
|
||||
# code = '05'
|
||||
# ),
|
||||
# municipio = fe.nomina.Municipio(
|
||||
# code = '05001'
|
||||
# ),
|
||||
# ),
|
||||
# proveedor=fe.nomina.Proveedor(
|
||||
# nit='999999',
|
||||
# dv=2,
|
||||
# software_id='xx',
|
||||
# software_pin='12',
|
||||
# razon_social='facho'
|
||||
# )
|
||||
# ))
|
||||
# nomina.asignar_empleador(fe.nomina.Empleador(
|
||||
# razon_social='facho',
|
||||
# nit = '700085371',
|
||||
# dv = '1',
|
||||
# pais = fe.nomina.Pais(
|
||||
# code = 'CO'
|
||||
# ),
|
||||
# departamento = fe.nomina.Departamento(
|
||||
# code = '05'
|
||||
# ),
|
||||
# municipio = fe.nomina.Municipio(
|
||||
# code = '05001'
|
||||
# ),
|
||||
# direccion = 'calle etrivial'
|
||||
# ))
|
||||
def test_nomina_ajuste_reemplazar_asignacion_tipo_xml():
|
||||
nomina = fe.nomina.DIANNominaIndividualDeAjuste.Reemplazar()
|
||||
nomina.asignar_metadata(fe.nomina.Metadata(
|
||||
novedad=fe.nomina.Novedad(
|
||||
activa = True,
|
||||
cune = "N0111"
|
||||
),
|
||||
secuencia=fe.nomina.NumeroSecuencia(
|
||||
prefijo = 'N',
|
||||
consecutivo='00001'
|
||||
),
|
||||
lugar_generacion=fe.nomina.Lugar(
|
||||
pais = fe.nomina.Pais(
|
||||
code = 'CO'
|
||||
),
|
||||
departamento = fe.nomina.Departamento(
|
||||
code = '05'
|
||||
),
|
||||
municipio = fe.nomina.Municipio(
|
||||
code = '05001'
|
||||
),
|
||||
),
|
||||
proveedor=fe.nomina.Proveedor(
|
||||
nit='999999',
|
||||
dv=2,
|
||||
software_id='xx',
|
||||
software_pin='12',
|
||||
razon_social='facho'
|
||||
)
|
||||
))
|
||||
nomina.asignar_empleador(fe.nomina.Empleador(
|
||||
razon_social='facho',
|
||||
nit = '700085371',
|
||||
dv = '1',
|
||||
pais = fe.nomina.Pais(
|
||||
code = 'CO'
|
||||
),
|
||||
departamento = fe.nomina.Departamento(
|
||||
code = '05'
|
||||
),
|
||||
municipio = fe.nomina.Municipio(
|
||||
code = '05001'
|
||||
),
|
||||
direccion = 'calle etrivial'
|
||||
))
|
||||
|
||||
# nomina.asignar_trabajador(fe.nomina.Trabajador(
|
||||
# tipo_contrato = fe.nomina.TipoContrato(
|
||||
# code = '1'
|
||||
# ),
|
||||
# alto_riesgo = False,
|
||||
# tipo_documento = fe.nomina.TipoDocumento(
|
||||
# code = '11'
|
||||
# ),
|
||||
# primer_apellido = 'gnu',
|
||||
# segundo_apellido = 'emacs',
|
||||
# primer_nombre = 'facho',
|
||||
# lugar_trabajo = fe.nomina.LugarTrabajo(
|
||||
# pais = fe.nomina.Pais(code='CO'),
|
||||
# departamento = fe.nomina.Departamento(code='05'),
|
||||
# municipio = fe.nomina.Municipio(code='05001'),
|
||||
# direccion = 'calle facho'
|
||||
# ),
|
||||
# numero_documento = '800199436',
|
||||
# tipo = fe.nomina.TipoTrabajador(
|
||||
# code = '01'
|
||||
# ),
|
||||
# salario_integral = True,
|
||||
# sueldo = fe.nomina.Amount(1_500_000)
|
||||
# ))
|
||||
# nomina.asignar_informacion_general(fe.nomina.InformacionGeneral(
|
||||
# fecha_generacion = '2020-01-16',
|
||||
# hora_generacion = '1053:10-05:00',
|
||||
# tipo_ambiente = fe.nomina.InformacionGeneral.AMBIENTE_PRODUCCION,
|
||||
# software_pin = '693',
|
||||
# tipo_xml = fe.nomina.InformacionGeneral.TIPO_XML_AJUSTES,
|
||||
# periodo_nomina = fe.nomina.PeriodoNomina(code='1'),
|
||||
# tipo_moneda = fe.nomina.TipoMoneda(code='COP')
|
||||
# ))
|
||||
nomina.asignar_trabajador(fe.nomina.Trabajador(
|
||||
tipo_contrato = fe.nomina.TipoContrato(
|
||||
code = '1'
|
||||
),
|
||||
alto_riesgo = False,
|
||||
tipo_documento = fe.nomina.TipoDocumento(
|
||||
code = '11'
|
||||
),
|
||||
primer_apellido = 'gnu',
|
||||
segundo_apellido = 'emacs',
|
||||
primer_nombre = 'facho',
|
||||
lugar_trabajo = fe.nomina.LugarTrabajo(
|
||||
pais = fe.nomina.Pais(code='CO'),
|
||||
departamento = fe.nomina.Departamento(code='05'),
|
||||
municipio = fe.nomina.Municipio(code='05001'),
|
||||
direccion = 'calle facho'
|
||||
),
|
||||
numero_documento = '800199436',
|
||||
tipo = fe.nomina.TipoTrabajador(
|
||||
code = '01'
|
||||
),
|
||||
salario_integral = True,
|
||||
sueldo = fe.nomina.Amount(1_500_000)
|
||||
))
|
||||
nomina.asignar_informacion_general(fe.nomina.InformacionGeneral(
|
||||
fecha_generacion = '2020-01-16',
|
||||
hora_generacion = '1053:10-05:00',
|
||||
tipo_ambiente = fe.nomina.InformacionGeneral.AMBIENTE_PRODUCCION,
|
||||
software_pin = '693',
|
||||
tipo_xml = fe.nomina.InformacionGeneral.TIPO_XML_AJUSTES,
|
||||
periodo_nomina = fe.nomina.PeriodoNomina(code='1'),
|
||||
tipo_moneda = fe.nomina.TipoMoneda(code='COP')
|
||||
))
|
||||
|
||||
# xml = nomina.toFachoXML()
|
||||
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():
|
||||
# nomina = fe.nomina.DIANNominaIndividualDeAjuste.Reemplazar()
|
||||
def test_adicionar_reemplazar_devengado_comprobante_total():
|
||||
nomina = fe.nomina.DIANNominaIndividualDeAjuste.Reemplazar()
|
||||
|
||||
# nomina.adicionar_devengado(fe.nomina.DevengadoBasico(
|
||||
# dias_trabajados = 60,
|
||||
# sueldo_trabajado = fe.nomina.Amount(2_000_000)
|
||||
# ))
|
||||
nomina.adicionar_devengado(fe.nomina.DevengadoBasico(
|
||||
dias_trabajados = 60,
|
||||
sueldo_trabajado = fe.nomina.Amount(2_000_000)
|
||||
))
|
||||
|
||||
# nomina.adicionar_deduccion(fe.nomina.DeduccionSalud(
|
||||
# porcentaje = fe.nomina.Amount(19),
|
||||
# deduccion = fe.nomina.Amount(1_000_000)
|
||||
# ))
|
||||
nomina.adicionar_deduccion(fe.nomina.DeduccionSalud(
|
||||
porcentaje = fe.nomina.Amount(19),
|
||||
deduccion = fe.nomina.Amount(1_000_000)
|
||||
))
|
||||
|
||||
# xml = nomina.toFachoXML()
|
||||
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():
|
||||
# nomina = fe.nomina.DIANNominaIndividualDeAjuste.Reemplazar()
|
||||
def test_adicionar_reemplazar_asignar_predecesor():
|
||||
nomina = fe.nomina.DIANNominaIndividualDeAjuste.Reemplazar()
|
||||
|
||||
# nomina.asignar_predecesor(fe.nomina.DIANNominaIndividualDeAjuste.Reemplazar.Predecesor(
|
||||
# numero = '123456',
|
||||
# cune = 'ABC123456',
|
||||
# fecha_generacion = '2021-11-16'
|
||||
# ))
|
||||
nomina.asignar_predecesor(fe.nomina.DIANNominaIndividualDeAjuste.Reemplazar.Predecesor(
|
||||
numero = '123456',
|
||||
cune = 'ABC123456',
|
||||
fecha_generacion = '2021-11-16'
|
||||
))
|
||||
|
||||
# xml = nomina.toFachoXML()
|
||||
# print(xml.tostring())
|
||||
# assert xml.get_element_text_or_attribute('/nominaajuste:NominaIndividualDeAjuste/Reemplazar/ReemplazandoPredecesor/@NumeroPred') == '123456'
|
||||
# assert xml.get_element_text_or_attribute('/nominaajuste:NominaIndividualDeAjuste/Reemplazar/ReemplazandoPredecesor/@CUNEPred') == 'ABC123456'
|
||||
# assert xml.get_element_text_or_attribute('/nominaajuste:NominaIndividualDeAjuste/Reemplazar/ReemplazandoPredecesor/@FechaGenPred') == '2021-11-16'
|
||||
xml = nomina.toFachoXML()
|
||||
print(xml.tostring())
|
||||
assert xml.get_element_text_or_attribute('/nominaajuste:NominaIndividualDeAjuste/Reemplazar/ReemplazandoPredecesor/@NumeroPred') == '123456'
|
||||
assert xml.get_element_text_or_attribute('/nominaajuste:NominaIndividualDeAjuste/Reemplazar/ReemplazandoPredecesor/@CUNEPred') == 'ABC123456'
|
||||
assert xml.get_element_text_or_attribute('/nominaajuste:NominaIndividualDeAjuste/Reemplazar/ReemplazandoPredecesor/@FechaGenPred') == '2021-11-16'
|
||||
|
||||
|
||||
# def test_adicionar_reemplazar_eliminar_predecesor_opcional():
|
||||
# nomina = fe.nomina.DIANNominaIndividualDeAjuste.Reemplazar()
|
||||
def test_adicionar_reemplazar_eliminar_predecesor_opcional():
|
||||
nomina = fe.nomina.DIANNominaIndividualDeAjuste.Reemplazar()
|
||||
|
||||
# nomina.asignar_predecesor(fe.nomina.DIANNominaIndividualDeAjuste.Reemplazar.Predecesor(
|
||||
# numero = '123456',
|
||||
# cune = 'ABC123456',
|
||||
# fecha_generacion = '2021-11-16'
|
||||
# ))
|
||||
nomina.asignar_predecesor(fe.nomina.DIANNominaIndividualDeAjuste.Reemplazar.Predecesor(
|
||||
numero = '123456',
|
||||
cune = 'ABC123456',
|
||||
fecha_generacion = '2021-11-16'
|
||||
))
|
||||
|
||||
# xml = nomina.toFachoXML()
|
||||
# print(xml.tostring())
|
||||
xml = nomina.toFachoXML()
|
||||
print(xml.tostring())
|
||||
|
||||
# assert xml.get_element('/nominaajuste:NominaIndividualDeAjuste/Reemplazar/ReemplazandoPredecesor') is not None
|
||||
# assert xml.get_element('/nominaajuste:NominaIndividualDeAjuste/Eliminar/EliminandoPredecesor') is None
|
||||
assert xml.get_element('/nominaajuste:NominaIndividualDeAjuste/Reemplazar/ReemplazandoPredecesor') is not None
|
||||
assert xml.get_element('/nominaajuste:NominaIndividualDeAjuste/Eliminar/EliminandoPredecesor') is None
|
||||
|
||||
# def test_adicionar_eliminar_reemplazar_predecesor_opcional():
|
||||
# nomina = fe.nomina.DIANNominaIndividualDeAjuste.Eliminar()
|
||||
def test_adicionar_eliminar_reemplazar_predecesor_opcional():
|
||||
nomina = fe.nomina.DIANNominaIndividualDeAjuste.Eliminar()
|
||||
|
||||
# nomina.asignar_predecesor(fe.nomina.DIANNominaIndividualDeAjuste.Eliminar.Predecesor(
|
||||
# numero = '123456',
|
||||
# cune = 'ABC123456',
|
||||
# fecha_generacion = '2021-11-16'
|
||||
# ))
|
||||
nomina.asignar_predecesor(fe.nomina.DIANNominaIndividualDeAjuste.Eliminar.Predecesor(
|
||||
numero = '123456',
|
||||
cune = 'ABC123456',
|
||||
fecha_generacion = '2021-11-16'
|
||||
))
|
||||
|
||||
# xml = nomina.toFachoXML()
|
||||
# print(xml.tostring())
|
||||
# assert xml.get_element('/nominaajuste:NominaIndividualDeAjuste/Eliminar/EliminandoPredecesor') is not None
|
||||
# assert xml.get_element('/nominaajuste:NominaIndividualDeAjuste/Reemplazar/ReemplazandoPredecesor') is None
|
||||
xml = nomina.toFachoXML()
|
||||
print(xml.tostring())
|
||||
assert xml.get_element('/nominaajuste:NominaIndividualDeAjuste/Eliminar/EliminandoPredecesor') is not None
|
||||
assert xml.get_element('/nominaajuste:NominaIndividualDeAjuste/Reemplazar/ReemplazandoPredecesor') is None
|
||||
|
||||
# def test_adicionar_eliminar_devengado_comprobante_total():
|
||||
# nomina = fe.nomina.DIANNominaIndividualDeAjuste.Eliminar()
|
||||
def test_adicionar_eliminar_devengado_comprobante_total():
|
||||
nomina = fe.nomina.DIANNominaIndividualDeAjuste.Eliminar()
|
||||
|
||||
# nomina.adicionar_devengado(fe.nomina.DevengadoBasico(
|
||||
# dias_trabajados = 60,
|
||||
# sueldo_trabajado = fe.nomina.Amount(2_000_000)
|
||||
# ))
|
||||
nomina.adicionar_devengado(fe.nomina.DevengadoBasico(
|
||||
dias_trabajados = 60,
|
||||
sueldo_trabajado = fe.nomina.Amount(2_000_000)
|
||||
))
|
||||
|
||||
# nomina.adicionar_deduccion(fe.nomina.DeduccionSalud(
|
||||
# porcentaje = fe.nomina.Amount(19),
|
||||
# deduccion = fe.nomina.Amount(1_000_000)
|
||||
# ))
|
||||
nomina.adicionar_deduccion(fe.nomina.DeduccionSalud(
|
||||
porcentaje = fe.nomina.Amount(19),
|
||||
deduccion = fe.nomina.Amount(1_000_000)
|
||||
))
|
||||
|
||||
# xml = nomina.toFachoXML()
|
||||
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():
|
||||
# nomina = fe.nomina.DIANNominaIndividualDeAjuste.Eliminar()
|
||||
def test_adicionar_eliminar_asignar_predecesor():
|
||||
nomina = fe.nomina.DIANNominaIndividualDeAjuste.Eliminar()
|
||||
|
||||
# nomina.asignar_predecesor(fe.nomina.DIANNominaIndividualDeAjuste.Eliminar.Predecesor(
|
||||
# numero = '123456',
|
||||
# cune = 'ABC123456',
|
||||
# fecha_generacion = '2021-11-16'
|
||||
# ))
|
||||
nomina.asignar_predecesor(fe.nomina.DIANNominaIndividualDeAjuste.Eliminar.Predecesor(
|
||||
numero = '123456',
|
||||
cune = 'ABC123456',
|
||||
fecha_generacion = '2021-11-16'
|
||||
))
|
||||
|
||||
# xml = nomina.toFachoXML()
|
||||
# print(xml.tostring())
|
||||
# assert xml.get_element_text_or_attribute('/nominaajuste:NominaIndividualDeAjuste/Eliminar/EliminandoPredecesor/@NumeroPred') == '123456'
|
||||
# assert xml.get_element_text_or_attribute('/nominaajuste:NominaIndividualDeAjuste/Eliminar/EliminandoPredecesor/@CUNEPred') == 'ABC123456'
|
||||
# assert xml.get_element_text_or_attribute('/nominaajuste:NominaIndividualDeAjuste/Eliminar/EliminandoPredecesor/@FechaGenPred') == '2021-11-16'
|
||||
xml = nomina.toFachoXML()
|
||||
print(xml.tostring())
|
||||
assert xml.get_element_text_or_attribute('/nominaajuste:NominaIndividualDeAjuste/Eliminar/EliminandoPredecesor/@NumeroPred') == '123456'
|
||||
assert xml.get_element_text_or_attribute('/nominaajuste:NominaIndividualDeAjuste/Eliminar/EliminandoPredecesor/@CUNEPred') == 'ABC123456'
|
||||
assert xml.get_element_text_or_attribute('/nominaajuste:NominaIndividualDeAjuste/Eliminar/EliminandoPredecesor/@FechaGenPred') == '2021-11-16'
|
||||
|
||||
# def test_nomina_devengado_horas_extras_diarias():
|
||||
# nomina = fe.nomina.DIANNominaIndividual()
|
||||
def test_nomina_devengado_horas_extras_diarias():
|
||||
nomina = fe.nomina.DIANNominaIndividual()
|
||||
|
||||
# nomina.adicionar_devengado(fe.nomina.DevengadoHorasExtrasDiarias(
|
||||
# horas_extras=[
|
||||
# fe.nomina.DevengadoHoraExtra(
|
||||
# hora_inicio='2021-11-30T19:09:55',
|
||||
# hora_fin='2021-11-30T20:09:55',
|
||||
# cantidad=1,
|
||||
# porcentaje=fe.nomina.Amount(1),
|
||||
# pago=fe.nomina.Amount(100)
|
||||
# ),
|
||||
# fe.nomina.DevengadoHoraExtra(
|
||||
# hora_inicio='2021-11-30T18:09:55',
|
||||
# hora_fin='2021-11-30T19:09:55',
|
||||
# cantidad=2,
|
||||
# porcentaje=fe.nomina.Amount(2),
|
||||
# pago=fe.nomina.Amount(200)
|
||||
# )
|
||||
# ]
|
||||
# ))
|
||||
nomina.adicionar_devengado(fe.nomina.DevengadoHorasExtrasDiarias(
|
||||
horas_extras=[
|
||||
fe.nomina.DevengadoHoraExtra(
|
||||
hora_inicio='2021-11-30T19:09:55',
|
||||
hora_fin='2021-11-30T20:09:55',
|
||||
cantidad=1,
|
||||
porcentaje=fe.nomina.Amount(1),
|
||||
pago=fe.nomina.Amount(100)
|
||||
),
|
||||
fe.nomina.DevengadoHoraExtra(
|
||||
hora_inicio='2021-11-30T18:09:55',
|
||||
hora_fin='2021-11-30T19:09:55',
|
||||
cantidad=2,
|
||||
porcentaje=fe.nomina.Amount(2),
|
||||
pago=fe.nomina.Amount(200)
|
||||
)
|
||||
]
|
||||
))
|
||||
|
||||
# xml = nomina.toFachoXML()
|
||||
# extras = xml.get_element(
|
||||
# '/nomina:NominaIndividual/Devengados/HEDs/HED', multiple=True)
|
||||
# assert extras[0].get('HoraInicio') == '2021-11-30T19:09:55'
|
||||
# assert extras[0].get('HoraFin') == '2021-11-30T20:09:55'
|
||||
# assert extras[0].get('Cantidad') == '1'
|
||||
# assert extras[0].get('Porcentaje') == '1.00'
|
||||
# assert extras[0].get('Pago') == '100.00'
|
||||
# assert extras[1].get('HoraInicio') == '2021-11-30T18:09:55'
|
||||
# assert extras[1].get('HoraFin') == '2021-11-30T19:09:55'
|
||||
# assert extras[1].get('Cantidad') == '2'
|
||||
# assert extras[1].get('Porcentaje') == '2.00'
|
||||
# assert extras[1].get('Pago') == '200.00'
|
||||
xml = nomina.toFachoXML()
|
||||
extras = xml.get_element('/nomina:NominaIndividual/Devengados/HEDs/HED', multiple=True)
|
||||
assert extras[0].get('HoraInicio') == '2021-11-30T19:09:55'
|
||||
assert extras[0].get('HoraFin') == '2021-11-30T20:09:55'
|
||||
assert extras[0].get('Cantidad') == '1'
|
||||
assert extras[0].get('Porcentaje') == '1.00'
|
||||
assert extras[0].get('Pago') == '100.00'
|
||||
assert extras[1].get('HoraInicio') == '2021-11-30T18:09:55'
|
||||
assert extras[1].get('HoraFin') == '2021-11-30T19:09:55'
|
||||
assert extras[1].get('Cantidad') == '2'
|
||||
assert extras[1].get('Porcentaje') == '2.00'
|
||||
assert extras[1].get('Pago') == '200.00'
|
||||
|
@ -3,22 +3,16 @@
|
||||
# This file is part of facho. The COPYRIGHT file at the top level of
|
||||
# this repository contains the full copyright notices and license terms.
|
||||
|
||||
# import pytest
|
||||
import pytest
|
||||
|
||||
import facho.fe.form as form
|
||||
from facho import fe
|
||||
from facho.fe.form_xml import DIANInvoiceXML, DIANCreditNoteXML, DIANDebitNoteXML
|
||||
|
||||
from facho.fe.form_xml import DIANInvoiceXML
|
||||
# from facho.fe.form_xml import (
|
||||
# DIANInvoiceXML, DIANCreditNoteXML, DIANDebitNoteXML)
|
||||
|
||||
from fixtures import simple_invoice
|
||||
from fixtures import *
|
||||
|
||||
from facho.fe.form import query
|
||||
|
||||
simple_invoice = simple_invoice
|
||||
|
||||
|
||||
def test_query_billing_reference(simple_invoice):
|
||||
xml = DIANInvoiceXML(simple_invoice)
|
||||
cufe_extension = fe.DianXMLExtensionCUFE(simple_invoice)
|
||||
|
Loading…
Reference in New Issue
Block a user