Files
oc-facho/facho/fe/form_xml/invoice.py
aserrador 4a7c2e2947 Fix: Formateo PEP8 (79 cols) y limpieza flake8 en paquete facho
- Líneas >79 cols reajustadas en todo el paquete
- Fix F821: etree.cleanup_namespaces(security) en signature.py (era 'header', NameError)
- Fix bug cune_xpath -> xpath en nomina/informacion_general() (NameError)
- Elimina clase TaxScheme duplicada en form/__init__.py
- Imports explícitos (F403/F405) y __all__ en form_xml/invoice.py, nomina
- Elimina imports y variables muertas (F401/F841), E712 (== None -> is None)
2026-08-12 19:56:29 -05:00

944 lines
37 KiB
Python

from collections import defaultdict
from .. import fe
from ..form import (
Amount,
CreditNoteDocumentReference,
DebitNoteDocumentReference,
InvoiceDocumentReference,
TaxTotalOmit,
WithholdingTaxTotalOmit,
)
__all__ = ['DIANInvoiceXML']
class DIANInvoiceXML(fe.FeXML):
"""
DianInvoiceXML mapea objeto form.Invoice a XML segun
lo indicado para la facturacion electronica.
"""
def __init__(self, invoice, tag_document='Invoice'):
super().__init__(
tag_document,
'http://www.dian.gov.co/contratos/facturaelectronica/v1')
self.placeholder_for(
'./ext:UBLExtensions/ext:UBLExtension/ext:ExtensionContent/sts'
':DianExtensions/sts:InvoiceControl')
self.placeholder_for(
'./ext:UBLExtensions/ext:UBLExtension/ext:ExtensionContent/sts'
':DianExtensions/sts:InvoiceSource')
self.placeholder_for(
'./ext:UBLExtensions/ext:UBLExtension/ext:ExtensionContent/sts'
':DianExtensions/sts:SoftwareProvider')
self.placeholder_for(
'./ext:UBLExtensions/ext:UBLExtension/ext:ExtensionContent/sts'
':DianExtensions/sts:SoftwareSecurityCode')
self.placeholder_for(
'./ext:UBLExtensions/ext:UBLExtension/ext:ExtensionContent/'
'sts:DianExtensions/sts:AuthorizationProvider/'
'sts:AuthorizationProviderID')
# ZE02 se requiere existencia para firmar
ublextension = self.fragment(
'./ext:UBLExtensions/ext:UBLExtension', append=True)
ublextension.find_or_create_element(
'/ext:UBLExtension/ext:ExtensionContent')
self.attach_invoice(invoice)
def set_supplier(fexml, invoice):
fexml.placeholder_for('./cac:AccountingSupplierParty')
# DIAN 1.7.-2020: CAJ02
# DIAN 1.7.-2020: FAJ02
fexml.set_element(
'./cac:AccountingSupplierParty/cbc:AdditionalAccountID',
invoice.invoice_supplier.organization_code)
# DIAN 1.7.-2020: CAJ06
# DIAN 1.7.-2020: FAJ06
fexml.set_element(
'./cac:AccountingSupplierParty/cac:Party/cac:PartyName/cbc:Name',
invoice.invoice_supplier.name)
# DIAN 1.7.-2020: CAJ07, CAJ08
# DIAN 1.7.-2020: FAJ07
fexml.placeholder_for(
'./cac:AccountingSupplierParty/cac:Party/cac:PhysicalLocation/cac'
':Address')
# DIAN 1.7.-2020: FAJ08
# DIAN 1.7.-2020: CAJ09
fexml.set_element(
'./cac:AccountingSupplierParty/cac:Party/cac:PhysicalLocation/cac'
':Address/cbc:ID',
invoice.invoice_supplier.address.city.code)
# DIAN 1.7.-2020: FAJ09
# DIAN 1.7.-2020: CAJ10
fexml.set_element(
'./cac:AccountingSupplierParty/cac:Party/cac:PhysicalLocation/cac'
':Address/cbc:CityName',
invoice.invoice_supplier.address.city.name)
# DIAN 1.7.-2020: FAJ11
# DIAN 1.7.-2020: CAJ11
fexml.set_element(
'./cac:AccountingSupplierParty/cac:Party/cac:PhysicalLocation/cac'
':Address/cbc:CountrySubentity',
invoice.invoice_supplier.address.countrysubentity.name)
# DIAN 1.7.-2020: FAJ12
# DIAN 1.7.-2020: CAJ12
fexml.set_element(
'./cac:AccountingSupplierParty/cac:Party/cac:PhysicalLocation/cac'
':Address/cbc:CountrySubentityCode',
invoice.invoice_supplier.address.countrysubentity.code)
# DIAN 1.7.-2020: FAJ14
# DIAN 1.7.-2020: CAJ13, CAJ14
fexml.set_element(
'./cac:AccountingSupplierParty/cac:Party/cac:PhysicalLocation/cac'
':Address/cac:AddressLine/cbc:Line',
invoice.invoice_supplier.address.street)
# DIAN 1.7.-2020: FAJ16
# DIAN 1.7.-2020: CAJ16, CAJ16
fexml.set_element(
'./cac:AccountingSupplierParty/cac:Party/cac:PhysicalLocation/cac'
':Address/cac:Country/cbc:IdentificationCode',
invoice.invoice_supplier.address.country.code)
# DIAN 1.7.-2020: FAJ17
# DIAN 1.7.-2020: CAJ17
fexml.set_element(
'./cac:AccountingSupplierParty/cac:Party/cac:PhysicalLocation/cac'
':Address/cac:Country/cbc:Name',
invoice.invoice_supplier.address.country.name,
# DIAN 1.7.-2020: FAJ18
languageID='es')
supplier_company_id_attrs = fe.SCHEME_AGENCY_ATTRS.copy()
supplier_company_id_attrs.update(
{
'schemeID': invoice.invoice_supplier.ident.dv,
'schemeName': invoice.invoice_supplier.ident.type_fiscal})
# DIAN 1.7.-2020: FAJ19
# DIAN 1.7.-2020: CAJ19
fexml.placeholder_for(
'./cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme')
# DIAN 1.7.-2020: FAJ20
# DIAN 1.7.-2020: CAJ20
fexml.set_element(
'./cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme/cbc'
':RegistrationName',
invoice.invoice_supplier.legal_name)
# DIAN 1.7.-2020: FAJ21
# DIAN 1.7.-2020: CAJ21
fexml.set_element(
'./cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme/cbc'
':CompanyID',
invoice.invoice_supplier.ident,
# DIAN 1.7.-2020: FAJ22,FAJ23,FAJ24,FAJ25
**supplier_company_id_attrs)
fexml.set_element(
'./cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme/cbc'
':TaxLevelCode',
# DIAN 1.7.-2020: FAJ26
# DIAN 1.7.-2020: CAJ26
invoice.invoice_supplier.responsability_code,
# DIAN 1.7.-2020: FAJ27
# DIAN 1.7.-2020: CAJ27
listName=invoice.invoice_supplier.responsability_regime_code)
# DIAN 1.7.-2020: FAJ28
# DIAN 1.7.-2020: CAJ28
fexml.placeholder_for(
'./cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme/cac'
':RegistrationAddress')
# DIAN 1.7.-2020: FAJ29
# DIAN 1.7.-2020: CAJ29
fexml.set_element(
'./cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme/cac'
':RegistrationAddress/cbc:ID',
invoice.invoice_supplier.address.city.code)
# DIAN 1.7.-2020: FAJ30
# DIAN 1.7.-2020: CAJ30
fexml.set_element(
'./cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme/cac'
':RegistrationAddress/cbc:CityName',
invoice.invoice_supplier.address.city.name)
# DIAN 1.7.-2020: FAJ31
# DIAN 1.7.-2020: CAJ31
fexml.set_element(
'./cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme/cac'
':RegistrationAddress/cbc:CountrySubentity',
invoice.invoice_supplier.address.countrysubentity.name)
# DIAN 1.7.-2020: FAJ32
# DIAN 1.7.-2020: CAJ32
fexml.set_element(
'./cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme/cac'
':RegistrationAddress/cbc:CountrySubentityCode',
invoice.invoice_supplier.address.countrysubentity.code)
# DIAN 1.7.-2020: FAJ33,FAJ34
# DIAN 1.7.-2020: CAJ33,CAJ34
fexml.set_element(
'./cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme/cac'
':RegistrationAddress/cac:AddressLine/cbc:Line',
invoice.invoice_supplier.address.street)
# DIAN 1.7.-2020: FAJ35,FAJ36
# DIAN 1.7.-2020: CAJ35,CAJ36
fexml.set_element(
'./cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme/cac'
':RegistrationAddress/cac:Country/cbc:IdentificationCode',
invoice.invoice_supplier.address.country.code)
# DIAN 1.7.-2020: FAJ37,FAJ38
# DIAN 1.7.-2020: CAJ37,CAJ38
fexml.set_element(
'./cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme/cac'
':RegistrationAddress/cac:Country/cbc:Name',
invoice.invoice_supplier.address.country.name,
languageID='es')
# DIAN 1.7.-2020: FAJ39
# DIAN 1.7.-2020: CAJ39
fexml.placeholder_for(
'./cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme/cac'
':TaxScheme')
# DIAN 1.7.-2020: CAJ40
# DIAN 1.7.-2020: FAJ40
fexml.set_element(
'./cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme/cac'
':TaxScheme/cbc:ID',
invoice.invoice_customer.tax_scheme.code)
# DIAN 1.7.-2020: CAJ41
fexml.set_element(
'./cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme/cac'
':TaxScheme/cbc:Name',
invoice.invoice_customer.tax_scheme.name)
# DIAN 1.7.-2020: FAJ42
# DIAN 1.7.-2020: CAJ42
fexml.placeholder_for(
'./cac:AccountingSupplierParty/cac:Party/cac:PartyLegalEntity')
# DIAN 1.7.-2020: FAJ43
# DIAN 1.7.-2020: CAJ43
fexml.set_element(
'./cac:AccountingSupplierParty/cac:Party/cac:PartyLegalEntity/cbc'
':RegistrationName',
invoice.invoice_supplier.legal_name)
# DIAN 1.7.-2020: FAJ44,FAJ45,FAJ46,FAJ47,FAJ48
# DIAN 1.7.-2020: CAJ44,CAJ45,CAJ46,CAJ47,CAJ48
fexml.set_element(
'./cac:AccountingSupplierParty/cac:Party/cac:PartyLegalEntity/cbc'
':CompanyID',
invoice.invoice_supplier.ident,
**supplier_company_id_attrs)
# DIAN 1.7.-2020: FAJ49
# DIAN 1.7.-2020: CAJ49
fexml.placeholder_for(
'./cac:AccountingSupplierParty/cac:Party/cac:PartyLegalEntity/cac'
':CorporateRegistrationScheme')
# DIAN 1.7.-2020: FAJ50
# DIAN 1.7.-2020: CAJ50
fexml.set_element(
'./cac:AccountingSupplierParty/cac:Party/cac:PartyLegalEntity/cac'
':CorporateRegistrationScheme/cbc:ID',
invoice.invoice_ident_prefix)
# DIAN 1.7.-2020: CAJ67
fexml.placeholder_for(
'./cac:AccountingSupplierParty/cac:Party/cac:Contact')
# DIAN 1.7.-2020: FAJ71
# DIAN 1.7.-2020: CAJ71
fexml.set_element(
'./cac:AccountingSupplierParty/cac:Party/cac:Contact/cbc'
':ElectronicMail',
invoice.invoice_supplier.email)
def set_customer(fexml, invoice):
fexml.placeholder_for('./cac:AccountingCustomerParty')
fexml.set_element(
'./cac:AccountingCustomerParty/cbc:AdditionalAccountID',
invoice.invoice_customer.organization_code)
fexml.set_element(
'./cac:AccountingCustomerParty/cac:Party/cac:PartyIdentification'
'/cbc:ID',
invoice.invoice_customer.ident)
fexml.set_element(
'./cac:AccountingCustomerParty/cac:Party/cac:PartyName/cbc:Name',
invoice.invoice_customer.name)
fexml.placeholder_for(
'./cac:AccountingCustomerParty/cac:Party/cac:PhysicalLocation')
customer_company_id_attrs = fe.SCHEME_AGENCY_ATTRS.copy()
# DIAN 1.7.-2020: FAK25
# DIAN 1.7.-2020: CAK25
customer_company_id_attrs.update(
{
'schemeID': invoice.invoice_customer.ident.dv,
'schemeName': invoice.invoice_customer.ident.type_fiscal})
# DIAN 1.7.-2020: FAK07
# DIAN 1.7.-2020: CAK07
fexml.placeholder_for(
'./cac:AccountingCustomerParty/cac:Party/cac:PhysicalLocation/cac'
':Address')
# DIAN 1.7.-2020: FAK08
# DIAN 1.7.-2020: CAK08
fexml.set_element(
'./cac:AccountingCustomerParty/cac:Party/cac:PhysicalLocation/cac'
':Address/cbc:ID',
invoice.invoice_customer.address.city.code)
# DIAN 1.7.-2020: FAK09
# DIAN 1.7.-2020: CAK09
fexml.set_element(
'./cac:AccountingCustomerParty/cac:Party/cac:PhysicalLocation/cac'
':Address/cbc:CityName',
invoice.invoice_customer.address.city.name)
# DIAN 1.7.-2020: FAK11
# DIAN 1.7.-2020: CAK11
fexml.set_element(
'./cac:AccountingCustomerParty/cac:Party/cac:PhysicalLocation/cac'
':Address/cbc:CountrySubentity',
invoice.invoice_customer.address.countrysubentity.name)
# DIAN 1.7.-2020: FAK12
# DIAN 1.7.-2020: CAK12
fexml.set_element(
'./cac:AccountingCustomerParty/cac:Party/cac:PhysicalLocation/cac'
':Address/cbc:CountrySubentityCode',
invoice.invoice_customer.address.countrysubentity.code)
# DIAN 1.7.-2020: CAK13, CAK14
fexml.set_element(
'./cac:AccountingCustomerParty/cac:Party/cac:PhysicalLocation/cac'
':Address/cac:AddressLine/cbc:Line',
invoice.invoice_customer.address.street)
# DIAN 1.7.-2020: CAK16
# DIAN 1.7.-2020: FAK16
fexml.set_element(
'./cac:AccountingCustomerParty/cac:Party/cac:PhysicalLocation/cac'
':Address/cac:Country/cbc:IdentificationCode',
invoice.invoice_customer.address.country.code)
# DIAN 1.7.-2020: FAK17
# DIAN 1.7.-2020: CAK17
fexml.set_element(
'./cac:AccountingCustomerParty/cac:Party/cac:PhysicalLocation/cac'
':Address/cac:Country/cbc:Name',
invoice.invoice_customer.address.country.name,
# DIAN 1.7.-2020: FAK18
# DIAN 1.7.-2020: CAK18
languageID='es')
# DIAN 1.7.-2020: FAK17,FAK19
# DIAN 1.7.-2020: CAK19
fexml.placeholder_for(
'./cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme')
# DIAN 1.7.-2020: FAK17,FAK20
# DIAN 1.7.-2020: CAK20
fexml.set_element(
'./cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cbc'
':RegistrationName',
invoice.invoice_customer.legal_name)
# DIAN 1.7.-2020: CAK21
# DIAN 1.7.-2020: FAK21
fexml.set_element(
'./cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cbc'
':CompanyID',
invoice.invoice_customer.ident,
# DIAN 1.7.-2020: CAK22, CAK23, CAK24, CAK25
**customer_company_id_attrs)
# DIAN 1.7.-2020: CAK26
fexml.set_element(
'./cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cbc'
':TaxLevelCode',
# DIAN 1.7.-2020: FAK26
invoice.invoice_customer.responsability_code,
# DIAN 1.7.-2020: FAK27
# DIAN 1.7.-2020: CAK27
listName=invoice.invoice_customer.responsability_regime_code)
# DIAN 1.7.-2020: FAK28
# DIAN 1.7.-2020: CAK28
fexml.placeholder_for(
'./cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cac'
':RegistrationAddress')
# DIAN 1.7.-2020: FAK29
# DIAN 1.7.-2020: CAK29
fexml.set_element(
'./cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cac'
':RegistrationAddress/cbc:ID',
invoice.invoice_customer.address.city.code)
# DIAN 1.7.-2020: FAK30
# DIAN 1.7.-2020: CAK30
fexml.set_element(
'./cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cac'
':RegistrationAddress/cbc:CityName',
invoice.invoice_customer.address.city.name)
# DIAN 1.7.-2020: FAK31
# DIAN 1.7.-2020: CAK31
fexml.set_element(
'./cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cac'
':RegistrationAddress/cbc:CountrySubentity',
invoice.invoice_customer.address.countrysubentity.name)
# DIAN 1.7.-2020: FAK32
# DIAN 1.7.-2020: CAK32
fexml.set_element(
'./cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cac'
':RegistrationAddress/cbc:CountrySubentityCode',
invoice.invoice_customer.address.countrysubentity.code)
# DIAN 1.7.-2020: FAK33
# DIAN 1.7.-2020: CAK33
fexml.placeholder_for(
'./cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cac'
':RegistrationAddress/cac:AddressLine')
# DIAN 1.7.-2020: FAK34
# DIAN 1.7.-2020: CAK34
fexml.set_element(
'./cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cac'
':RegistrationAddress/cac:AddressLine/cbc:Line',
invoice.invoice_customer.address.street)
# DIAN 1.7.-2020: CAK35
# DIAN 1.7.-2020: FAK35
fexml.placeholder_for(
'./cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cac'
':RegistrationAddress/cac:Country')
# DIAN 1.7.-2020: CAK36
# DIAN 1.7.-2020: FAK36
fexml.set_element(
'./cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cac'
':RegistrationAddress/cac:Country/cbc:IdentificationCode',
invoice.invoice_customer.address.country.code)
# DIAN 1.7.-2020: CAK37
# DIAN 1.7.-2020: FAK37
fexml.set_element(
'./cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cac'
':RegistrationAddress/cac:Country/cbc:Name',
invoice.invoice_customer.address.country.name)
# DIAN 1.7.-2020: FAK38
# DIAN 1.7.-2020: CAK38
fexml.set_element(
'./cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cac'
':RegistrationAddress/cac:Country/cbc:IdentificationCode',
invoice.invoice_customer.address.country.code,
languageID='es')
# DIAN 1.7.-2020: CAK39
# DIAN 1.7.-2020: FAK39
fexml.placeholder_for(
'./cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cac'
':TaxScheme')
# DIAN 1.7.-2020: CAK40 Machete Construir Validación
# DIAN 1.7.-2020: FAK40
fexml.set_element(
'./cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cac'
':TaxScheme/cbc:ID',
invoice.invoice_customer.tax_scheme.code)
# DIAN 1.7.-2020: FAK41
# DIAN 1.7.-2020: CAK41 Machete Construir Validación
fexml.set_element(
'./cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cac'
':TaxScheme/cbc:Name',
invoice.invoice_customer.tax_scheme.name)
# DIAN 1.7.-2020: FAK42
# DIAN 1.7.-2020: CAK42
fexml.placeholder_for(
'./cac:AccountingCustomerParty/cac:Party/cac:PartyLegalEntity')
# DIAN 1.7.-2020: FAK43
# DIAN 1.7.-2020: CAK43
fexml.set_element(
'./cac:AccountingCustomerParty/cac:Party/cac:PartyLegalEntity/cbc'
':RegistrationName',
invoice.invoice_customer.legal_name)
# DIAN 1.7.-2020: CAK44
# DIAN 1.7.-2020: FAK44,FAK45,FAK46,FAK47,FAK48
fexml.set_element(
'./cac:AccountingCustomerParty/cac:Party/cac:PartyLegalEntity/cbc'
':CompanyID',
invoice.invoice_customer.ident,
**customer_company_id_attrs)
fexml.placeholder_for(
'./cac:AccountingCustomerParty/cac:Party/cac:PartyLegalEntity')
# DIAN 1.7.-2020: FAK55
# DIAN 1.7.-2020: CAK51, CAK55
fexml.set_element(
'./cac:AccountingCustomerParty/cac:Party/cac:Contact/cbc'
':ElectronicMail',
invoice.invoice_customer.email)
def set_payment_mean(fexml, invoice):
payment_mean = invoice.invoice_payment_mean
fexml.set_element('./cac:PaymentMeans/cbc:ID', payment_mean.id)
fexml.set_element(
'./cac:PaymentMeans/cbc:PaymentMeansCode',
payment_mean.code)
fexml.set_element(
'./cac:PaymentMeans/cbc:PaymentDueDate',
payment_mean.due_at.strftime('%Y-%m-%d'))
fexml.set_element(
'./cac:PaymentMeans/cbc:PaymentID',
payment_mean.payment_id)
def set_element_amount_for(fexml, xml, xpath, amount):
if not isinstance(amount, Amount):
raise TypeError("amount not is Amount")
xml.set_element(xpath, amount, currencyID=amount.currency.code)
def set_element_amount(fexml, xpath, amount):
if not isinstance(amount, Amount):
raise TypeError("amount not is Amount")
fexml.set_element(xpath, amount, currencyID=amount.currency.code)
def set_legal_monetary(fexml, invoice):
fexml.set_element_amount(
'./cac:LegalMonetaryTotal/cbc:LineExtensionAmount',
invoice.invoice_legal_monetary_total.line_extension_amount)
fexml.set_element_amount(
'./cac:LegalMonetaryTotal/cbc:TaxExclusiveAmount',
invoice.invoice_legal_monetary_total.tax_exclusive_amount)
fexml.set_element_amount(
'./cac:LegalMonetaryTotal/cbc:TaxInclusiveAmount',
invoice.invoice_legal_monetary_total.tax_inclusive_amount)
fexml.set_element_amount(
'./cac:LegalMonetaryTotal/cbc:ChargeTotalAmount',
invoice.invoice_legal_monetary_total.charge_total_amount)
fexml.set_element_amount(
'./cac:LegalMonetaryTotal/cbc:PayableAmount',
invoice.invoice_legal_monetary_total.payable_amount)
def _set_invoice_document_reference(fexml, reference):
fexml._do_set_billing_reference(
reference, 'cac:InvoiceDocumentReference')
def _set_credit_note_document_reference(fexml, reference):
fexml._do_set_billing_reference(
reference, 'cac:CreditNoteDocumentReference')
def _set_debit_note_document_reference(fexml, reference):
fexml._do_set_billing_reference(
reference, 'cac:DebitNoteDocumentReference')
def _do_set_billing_reference(fexml, reference, tag_document):
fexml.set_element('./cac:BillingReference/%s/cbc:ID' % (tag_document),
reference.ident)
fexml.set_element(
'./cac:BillingReference/cac:InvoiceDocumentReference/cbc:UUID',
reference.uuid,
schemeName='CUFE-SHA384')
fexml.set_element(
'./cac:BillingReference/cac:InvoiceDocumentReference/'
'cbc:IssueDate',
reference.date.strftime("%Y-%m-%d"))
def set_billing_reference(fexml, invoice):
reference = invoice.invoice_billing_reference
if reference is None:
return
if isinstance(reference, DebitNoteDocumentReference):
return fexml._set_debit_note_document_reference(reference)
if isinstance(reference, CreditNoteDocumentReference):
return fexml._set_credit_note_document_reference(reference)
if isinstance(reference, InvoiceDocumentReference):
return fexml._set_invoice_document_reference(reference)
def set_invoice_totals(fexml, invoice):
tax_amount_for = defaultdict(lambda: defaultdict(lambda: Amount(0.0)))
withholding_amount_for = defaultdict(
lambda: defaultdict(lambda: Amount(0.0)))
percent_for = defaultdict(lambda: None)
# requeridos para CUFE
# tax_amount_for['01']['tax_amount'] = Amount(0.0)
# tax_amount_for['01']['taxable_amount'] = Amount(0.0)
# DIAN 1.7.-2020: FAS07 => Se debe construir estrategia para su manejo
# tax_amount_for['04']['tax_amount'] += 0.0
# tax_amount_for['04']['taxable_amount'] += 0.0
# tax_amount_for['03']['tax_amount'] += 0.0
# tax_amount_for['03']['taxable_amount'] += 0.0
total_tax_amount = Amount(0.0)
total_withholding_amount = Amount(0.0)
for invoice_line in invoice.invoice_lines:
for subtotal in invoice_line.tax.subtotals:
if subtotal.scheme is not None:
scheme = subtotal.scheme.code
tax_amount_for[scheme][
'tax_amount'] += subtotal.tax_amount
tax_amount_for[scheme][
'taxable_amount'] += invoice_line.taxable_amount
tax_amount_for[scheme]['name'] = subtotal.scheme.name
# MACHETE ojo InvoiceLine.tax pasar a Invoice
percent_for[subtotal.scheme.code] = subtotal.percent
total_tax_amount += subtotal.tax_amount
for subtotal_withholding in invoice_line.withholding.subtotals:
if subtotal_withholding.scheme is not None:
scheme = subtotal_withholding.scheme.code
withholding_amount_for[scheme][
'tax_amount'] += subtotal_withholding.tax_amount
withholding_amount_for[scheme][
'taxable_amount'] += (
invoice_line.withholding_taxable_amount)
# MACHETE ojo InvoiceLine.tax pasar a Invoice
percent_for[scheme] = subtotal_withholding.percent
total_withholding_amount += subtotal_withholding.tax_amount
if total_tax_amount != Amount(0.0):
fexml.placeholder_for('./cac:TaxTotal')
fexml.set_element_amount('./cac:TaxTotal/cbc:TaxAmount',
total_tax_amount)
for index, item in enumerate(tax_amount_for.items()):
cod_impuesto, amount_of = item
next_append = index > 0
# DIAN 1.7.-2020: FAS01
line = fexml.fragment('./cac:TaxTotal', append=next_append)
# DIAN 1.7.-2020: FAU06
tax_amount = amount_of['tax_amount']
fexml.set_element_amount_for(line,
'/cac:TaxTotal/cbc:TaxAmount',
tax_amount)
# DIAN 1.7.-2020: FAS05
fexml.set_element_amount_for(
line,
'/cac:TaxTotal/cac:TaxSubtotal/cbc:TaxableAmount',
amount_of['taxable_amount'])
# DIAN 1.7.-2020: FAU06
fexml.set_element_amount_for(
line,
'/cac:TaxTotal/cac:TaxSubtotal/cbc:TaxAmount',
amount_of['tax_amount'])
# DIAN 1.7.-2020: FAS07
if percent_for[cod_impuesto]:
line.set_element('/cac:TaxTotal/cac:TaxSubtotal/cbc:Percent',
percent_for[cod_impuesto])
if percent_for[cod_impuesto]:
line.set_element(
'/cac:TaxTotal/cac:TaxSubtotal/cac:TaxCategory/'
'cbc:Percent',
percent_for[cod_impuesto])
line.set_element(
'/cac:TaxTotal/cac:TaxSubtotal/cac:TaxCategory/cac:TaxScheme'
'/cbc:ID',
cod_impuesto)
line.set_element(
'/cac:TaxTotal/cac:TaxSubtotal/cac:TaxCategory/cac:TaxScheme'
'/cbc:Name',
amount_of['name'])
for index, item in enumerate(withholding_amount_for.items()):
cod_impuesto, amount_of = item
next_append = index > 0
# DIAN 1.7.-2020: FAS01
line = fexml.fragment(
'./cac:WithholdingTaxTotal',
append=next_append)
# DIAN 1.7.-2020: FAU06
tax_amount = amount_of['tax_amount']
fexml.set_element_amount_for(
line, '/cac:WithholdingTaxTotal/cbc:TaxAmount', tax_amount)
# DIAN 1.7.-2020: FAS05
fexml.set_element_amount_for(
line,
'/cac:WithholdingTaxTotal/cac:TaxSubtotal/cbc:TaxableAmount',
amount_of['taxable_amount'])
# DIAN 1.7.-2020: FAU06
fexml.set_element_amount_for(
line,
'/cac:WithholdingTaxTotal/cac:TaxSubtotal/cbc:TaxAmount',
amount_of['tax_amount'])
# DIAN 1.7.-2020: FAS07
if percent_for[cod_impuesto]:
line.set_element(
'/cac:WithholdingTaxTotal/cac:TaxSubtotal/cbc:Percent',
percent_for[cod_impuesto])
if percent_for[cod_impuesto]:
line.set_element(
'/cac:WithholdingTaxTotal/cac:TaxSubtotal/cac:TaxCategory'
'/cbc:Percent',
percent_for[cod_impuesto])
line.set_element(
'/cac:WithholdingTaxTotal/cac:TaxSubtotal/cac:TaxCategory/cac'
':TaxScheme/cbc:ID',
cod_impuesto)
line.set_element(
'/cac:WithholdingTaxTotal/cac:TaxSubtotal/cac:TaxCategory/cac'
':TaxScheme/cbc:Name',
'ReteRenta')
# abstract method
def tag_document(fexml):
return 'Invoice'
# abstract method
def tag_document_concilied(fexml):
return 'Invoiced'
def set_invoice_line_tax(fexml, line, invoice_line):
fexml.set_element_amount_for(line,
'./cac:TaxTotal/cbc:TaxAmount',
invoice_line.tax_amount)
# DIAN 1.7.-2020: FAX05
fexml.set_element_amount_for(
line,
'./cac:TaxTotal/cac:TaxSubtotal/cbc:TaxableAmount',
invoice_line.taxable_amount)
for subtotal in invoice_line.tax.subtotals:
line.set_element(
'./cac:TaxTotal/cac:TaxSubtotal/cbc:TaxAmount',
subtotal.tax_amount,
currencyID='COP')
if subtotal.percent is not None:
line.set_element(
'./cac:TaxTotal/cac:TaxSubtotal/cac:TaxCategory/cbc'
':Percent',
'%0.2f' %
round(
subtotal.percent,
2))
if subtotal.scheme is not None:
# DIAN 1.7.-2020: FAX15
line.set_element(
'./cac:TaxTotal/cac:TaxSubtotal/cac:TaxCategory/cac'
':TaxScheme/cbc:ID',
subtotal.scheme.code)
line.set_element(
'./cac:TaxTotal/cac:TaxSubtotal/cac:TaxCategory/cac'
':TaxScheme/cbc:Name',
subtotal.scheme.name)
def set_invoice_line_withholding(fexml, line, invoice_line):
fexml.set_element_amount_for(line,
'./cac:WithholdingTaxTotal/cbc:TaxAmount',
invoice_line.withholding_amount)
# DIAN 1.7.-2020: FAX05
fexml.set_element_amount_for(
line,
'./cac:WithholdingTaxTotal/cac:TaxSubtotal/cbc:TaxableAmount',
invoice_line.withholding_taxable_amount)
for subtotal in invoice_line.withholding.subtotals:
line.set_element(
'./cac:WithholdingTaxTotal/cac:TaxSubtotal/cbc:TaxAmount',
subtotal.tax_amount,
currencyID='COP')
if subtotal.percent is not None:
line.set_element(
'./cac:WithholdingTaxTotal/cac:TaxSubtotal/cac'
':TaxCategory/cbc:Percent',
'%0.2f' %
round(
subtotal.percent,
2))
if subtotal.scheme is not None:
# DIAN 1.7.-2020: FAX15
line.set_element(
'./cac:WithholdingTaxTotal/cac:TaxSubtotal/cac'
':TaxCategory/cac:TaxScheme/cbc:ID',
subtotal.scheme.code)
line.set_element(
'./cac:WithholdingTaxTotal/cac:TaxSubtotal/cac'
':TaxCategory/cac:TaxScheme/cbc:Name',
subtotal.scheme.name)
def set_invoice_lines(fexml, invoice):
next_append = False
for index, invoice_line in enumerate(invoice.invoice_lines):
line = fexml.fragment(
'./cac:%sLine' %
(fexml.tag_document()),
append=next_append)
next_append = True
line.set_element('./cbc:ID', index + 1)
line.set_element(
'./cbc:%sQuantity' %
(fexml.tag_document_concilied()),
invoice_line.quantity,
unitCode='NAR')
fexml.set_element_amount_for(line,
'./cbc:LineExtensionAmount',
invoice_line.total_amount)
if not isinstance(invoice_line.tax, TaxTotalOmit):
fexml.set_invoice_line_tax(line, invoice_line)
if not isinstance(
invoice_line.withholding,
WithholdingTaxTotalOmit):
fexml.set_invoice_line_withholding(line, invoice_line)
line.set_element(
'./cac:Item/cbc:Description',
invoice_line.item.description)
line.set_element(
'./cac:Item/cac:StandardItemIdentification/cbc:ID',
invoice_line.item.id,
schemeID=invoice_line.item.scheme_id,
schemeName=invoice_line.item.scheme_name,
schemeAgencyID=invoice_line.item.scheme_agency_id)
line.set_element(
'./cac:Price/cbc:PriceAmount',
invoice_line.price.amount,
currencyID=invoice_line.price.amount.currency.code)
# DIAN 1.7.-2020: FBB04
line.set_element('./cac:Price/cbc:BaseQuantity',
invoice_line.price.quantity,
unitCode=invoice_line.quantity.code)
for idx, charge in enumerate(invoice_line.allowance_charge):
next_append_charge = idx > 0
fexml.append_allowance_charge(
line, index + 1, charge, append=next_append_charge)
def set_allowance_charge(fexml, invoice):
for idx, charge in enumerate(invoice.invoice_allowance_charge):
next_append = idx > 0
fexml.append_allowance_charge(
fexml, idx + 1, charge, append=next_append)
def append_allowance_charge(fexml, parent, idx, charge, append=False):
line = parent.fragment('./cac:AllowanceCharge', append=append)
# DIAN 1.7.-2020: FAQ02
line.set_element('./cbc:ID', idx)
# DIAN 1.7.-2020: FAQ03
line.set_element('./cbc:ChargeIndicator', str(
charge.charge_indicator).lower())
if charge.reason:
line.set_element(
'./cbc:AllowanceChargeReasonCode', charge.reason.code)
line.set_element(
'./cbc:allowanceChargeReason', charge.reason.reason)
line.set_element(
'./cbc:MultiplierFactorNumeric', str(
round(charge.multiplier_factor_numeric, 2)))
fexml.set_element_amount_for(
line, './cbc:Amount', charge.amount)
fexml.set_element_amount_for(
line, './cbc:BaseAmount', charge.base_amount)
def attach_invoice(fexml, invoice):
"""adiciona etiquetas a FEXML y retorna FEXML
en caso de fallar validacion retorna None"""
fexml.placeholder_for('./ext:UBLExtensions')
fexml.set_element('./cbc:UBLVersionID', 'UBL 2.1')
fexml.set_element(
'./cbc:CustomizationID',
invoice.invoice_operation_type)
fexml.placeholder_for('./cbc:ProfileID')
fexml.placeholder_for('./cbc:ProfileExecutionID')
fexml.set_element('./cbc:ID', invoice.invoice_ident)
fexml.placeholder_for('./cbc:UUID')
fexml.set_element(
'./cbc:IssueDate',
invoice.invoice_issue.strftime('%Y-%m-%d'))
# DIAN 1.7.-2020: FAD10
fexml.set_element(
'./cbc:IssueTime',
invoice.invoice_issue.strftime('%H:%M:%S-05:00'))
fexml.set_element(
'./cbc:%sTypeCode' %
(fexml.tag_document()),
invoice.invoice_type_code,
listAgencyID='195',
listAgencyName=(
'No matching global declaration available for the '
'validation root'),
listURI='http://www.dian.gov.co')
fexml.set_element('./cbc:DocumentCurrencyCode', 'COP')
fexml.set_element('./cbc:LineCountNumeric', len(invoice.invoice_lines))
if fexml.tag_document() == 'Invoice':
fexml.set_element('./cac:%sPeriod/cbc:StartDate' % (
fexml.tag_document()),
invoice.invoice_period_start.strftime('%Y-%m-%d'))
fexml.set_element('./cac:%sPeriod/cbc:EndDate' % (
fexml.tag_document()),
invoice.invoice_period_end.strftime('%Y-%m-%d'))
fexml.set_billing_reference(invoice)
fexml.customize(invoice)
fexml.set_supplier(invoice)
fexml.set_customer(invoice)
fexml.set_payment_mean(invoice)
fexml.set_invoice_totals(invoice)
fexml.set_legal_monetary(invoice)
fexml.set_invoice_lines(invoice)
fexml.set_allowance_charge(invoice)
return fexml
def customize(fexml, invoice):
"""adiciona etiquetas a FEXML y retorna FEXML
en caso de fallar validacion retorna None"""