facho: Amount nueva clase para gestion moneda.
facho/fe/form_xml(DIANInvoiceXML): se extrae de form.py para resolver ciclo en dependencias. facho/fe/form.py(Amount): clase para gestion de moneda. test/: se actualizan para hacer uso de Amount.
This commit is contained in:
parent
54bed07e91
commit
a892fe664c
|
@ -259,6 +259,7 @@ def generate_invoice(private_key, passphrase, scriptname, generate=False, ssl=Tr
|
|||
spec.loader.exec_module(module)
|
||||
|
||||
import facho.fe.form as form
|
||||
from facho.fe.form_xml import DIANInvoiceXML
|
||||
from facho import fe
|
||||
|
||||
invoice = module.invoice()
|
||||
|
@ -269,7 +270,7 @@ def generate_invoice(private_key, passphrase, scriptname, generate=False, ssl=Tr
|
|||
print("ERROR:", error)
|
||||
|
||||
if generate:
|
||||
xml = form.DIANInvoiceXML(invoice)
|
||||
xml = DIANInvoiceXML(invoice)
|
||||
|
||||
extensions = module.extensions(invoice)
|
||||
for extension in extensions:
|
||||
|
|
|
@ -12,6 +12,7 @@ import warnings
|
|||
import hashlib
|
||||
from contextlib import contextmanager
|
||||
from .data.dian import codelist
|
||||
from . import form
|
||||
|
||||
SCHEME_AGENCY_ATTRS = {
|
||||
'schemeAgencyName': 'CO, DIAN (Dirección de Impuestos y Aduanas Nacionales)',
|
||||
|
@ -112,17 +113,14 @@ class DianXMLExtensionCUFE(FachoXMLExtension):
|
|||
ValorBruto = invoice.invoice_legal_monetary_total.line_extension_amount
|
||||
ValorTotalPagar = invoice.invoice_legal_monetary_total.payable_amount
|
||||
ValorImpuestoPara = {}
|
||||
ValorImpuesto1 = 0.0
|
||||
CodImpuesto1 = 1
|
||||
ValorImpuesto2 = 0.0
|
||||
CodImpuesto2 = 4
|
||||
ValorImpuesto3 = 0.0
|
||||
CodImpuesto3 = 3
|
||||
for invoice_line in invoice.invoice_lines:
|
||||
for subtotal in invoice_line.tax.subtotals:
|
||||
# TODO cual es la naturaleza de tax_scheme_ident?
|
||||
codigo_impuesto = int(subtotal.tax_scheme_ident)
|
||||
ValorImpuestoPara.setdefault(codigo_impuesto, 0.0)
|
||||
ValorImpuestoPara.setdefault(codigo_impuesto, form.Amount(0.0))
|
||||
ValorImpuestoPara[codigo_impuesto] += subtotal.tax_amount
|
||||
|
||||
NitOFE = invoice.invoice_supplier.ident
|
||||
|
@ -134,14 +132,14 @@ class DianXMLExtensionCUFE(FachoXMLExtension):
|
|||
'%s' % NumFac,
|
||||
'%s' % FecFac,
|
||||
'%s' % HoraFac,
|
||||
'%.02f' % ValorBruto,
|
||||
'%.02f' % round(ValorBruto, 2),
|
||||
'%02d' % CodImpuesto1,
|
||||
'%.02f' % ValorImpuestoPara.get(CodImpuesto1, 0.0),
|
||||
'%.02f' % round(ValorImpuestoPara.get(CodImpuesto1, 0.0), 2),
|
||||
'%02d' % CodImpuesto2,
|
||||
'%.02f' % ValorImpuestoPara.get(CodImpuesto2, 0.0),
|
||||
'%.02f' % round(ValorImpuestoPara.get(CodImpuesto2, 0.0), 2),
|
||||
'%02d' % CodImpuesto3,
|
||||
'%.02f' % ValorImpuestoPara.get(CodImpuesto3, 0.0),
|
||||
'%.02f' % ValorTotalPagar,
|
||||
'%.02f' % round(ValorImpuestoPara.get(CodImpuesto3, 0.0), 2),
|
||||
'%.02f' % round(ValorTotalPagar, 2),
|
||||
'%s' % NitOFE,
|
||||
'%s' % NumAdq,
|
||||
'%s' % ClTec,
|
||||
|
|
511
facho/fe/form.py
511
facho/fe/form.py
|
@ -7,10 +7,89 @@ import copy
|
|||
from dataclasses import dataclass
|
||||
from datetime import datetime
|
||||
from collections import defaultdict
|
||||
import decimal
|
||||
from decimal import Decimal
|
||||
|
||||
|
||||
from .data.dian import codelist
|
||||
from . import fe
|
||||
|
||||
DECIMAL_PRECISION = 6
|
||||
|
||||
class AmountCurrencyError(TypeError):
|
||||
pass
|
||||
|
||||
@dataclass
|
||||
class Currency:
|
||||
code: str
|
||||
|
||||
def __eq__(self, other):
|
||||
return self.code == other.code
|
||||
|
||||
def __str__(self):
|
||||
return self.code
|
||||
|
||||
class Collection:
|
||||
|
||||
def __init__(self, array):
|
||||
self.array = array
|
||||
|
||||
def filter(self, filterer):
|
||||
new_array = filter(filterer, self.array)
|
||||
return self.__class__(new_array)
|
||||
|
||||
def map(self, mapper):
|
||||
new_array = map(mapper, self.array)
|
||||
return self.__class__(new_array)
|
||||
|
||||
def sum(self):
|
||||
return sum(self.array)
|
||||
|
||||
class AmountCollection(Collection):
|
||||
|
||||
def sum(self):
|
||||
total = Amount(0)
|
||||
for v in self.array:
|
||||
total += v
|
||||
return total
|
||||
|
||||
class Amount:
|
||||
def __init__(self, amount: int or float or Amount, currency: Currency = Currency('COP')):
|
||||
|
||||
if isinstance(amount, Amount):
|
||||
self.amount = amount.amount
|
||||
self.currency = amount.currency
|
||||
else:
|
||||
self.amount = Decimal(amount, decimal.Context(prec=DECIMAL_PRECISION, rounding=decimal.ROUND_HALF_DOWN ))
|
||||
self.currency = currency
|
||||
|
||||
def __round__(self, prec):
|
||||
return round(self.amount, prec)
|
||||
|
||||
def __str__(self):
|
||||
return '%.06f' % self.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)
|
||||
|
||||
def __add__(self, other):
|
||||
if not self.is_same_currency(other):
|
||||
raise AmountCurrencyError()
|
||||
return Amount(self.amount + other.amount, self.currency)
|
||||
|
||||
def __sub__(self, other):
|
||||
if not self.is_same_currency(other):
|
||||
raise AmountCurrencyError()
|
||||
return Amount(self.amount - other.amount, self.currency)
|
||||
|
||||
def __mul__(self, other):
|
||||
if not self.is_same_currency(other):
|
||||
raise AmountCurrencyError()
|
||||
return Amount(self.amount * other.amount, self.currency)
|
||||
|
||||
def is_same_currency(self, other):
|
||||
return self.currency == other.currency
|
||||
|
||||
@dataclass
|
||||
class Item:
|
||||
|
@ -97,19 +176,19 @@ class TaxSubTotal:
|
|||
tax_scheme_ident: str = '01'
|
||||
tax_scheme_name: str = 'IVA'
|
||||
|
||||
tax_amount: float = 0.0
|
||||
taxable_amount: float = 0.0
|
||||
tax_amount: Amount = Amount(0.0)
|
||||
taxable_amount: Amount = Amount(0.0)
|
||||
|
||||
def calculate(self, invline):
|
||||
self.tax_amount = invline.total_amount * (self.percent / 100)
|
||||
self.tax_amount = invline.total_amount * Amount(self.percent / 100)
|
||||
self.taxable_amount = invline.total_amount
|
||||
|
||||
|
||||
@dataclass
|
||||
class TaxTotal:
|
||||
subtotals: list
|
||||
tax_amount: float = 0.0
|
||||
taxable_amount: float = 0.0
|
||||
tax_amount: Amount = Amount(0.0)
|
||||
taxable_amount: Amount = Amount(0.0)
|
||||
|
||||
def calculate(self, invline):
|
||||
for subtax in self.subtotals:
|
||||
|
@ -120,7 +199,7 @@ class TaxTotal:
|
|||
|
||||
@dataclass
|
||||
class Price:
|
||||
amount: float
|
||||
amount: Amount
|
||||
type_code: str
|
||||
type: str
|
||||
|
||||
|
@ -140,7 +219,7 @@ class PaymentMean:
|
|||
@dataclass
|
||||
class PrePaidPayment:
|
||||
#DIAN 1.7.-2020: FBD03
|
||||
paid_amount: float = 0.0
|
||||
paid_amount: Amount = Amount(0.0)
|
||||
|
||||
|
||||
@dataclass
|
||||
|
@ -158,7 +237,7 @@ class InvoiceLine:
|
|||
|
||||
@property
|
||||
def total_amount(self):
|
||||
return self.quantity * self.price.amount
|
||||
return Amount(self.quantity) * self.price.amount
|
||||
|
||||
@property
|
||||
def total_tax_inclusive_amount(self):
|
||||
|
@ -182,19 +261,19 @@ class InvoiceLine:
|
|||
|
||||
@dataclass
|
||||
class LegalMonetaryTotal:
|
||||
line_extension_amount: float = 0.0
|
||||
tax_exclusive_amount: float = 0.0
|
||||
tax_inclusive_amount: float = 0.0
|
||||
charge_total_amount: float = 0.0
|
||||
allowance_total_amount: float = 0.0
|
||||
payable_amount: float = 0.0
|
||||
prepaid_amount: float = 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)
|
||||
|
||||
@dataclass
|
||||
class AllowanceCharge:
|
||||
#DIAN 1.7.-2020: FAQ03
|
||||
charge_indicator: bool = True
|
||||
amount: float = 0.0
|
||||
amount: Amount = Amount(0.0)
|
||||
|
||||
def isCharge(self):
|
||||
return self.charge_indicator == True
|
||||
|
@ -217,7 +296,7 @@ class Invoice:
|
|||
self.invoice_issue = None
|
||||
self.invoice_ident = None
|
||||
self.invoice_operation_type = None
|
||||
self.invoice_legal_monetary_total = LegalMonetaryTotal(0, 0, 0, 0, 0)
|
||||
self.invoice_legal_monetary_total = LegalMonetaryTotal()
|
||||
self.invoice_customer = None
|
||||
self.invoice_supplier = None
|
||||
self.invoice_payment_mean = None
|
||||
|
@ -274,18 +353,21 @@ class Invoice:
|
|||
self.invoice_legal_monetary_total.tax_inclusive_amount += invline.total_tax_inclusive_amount
|
||||
|
||||
#DIAN 1.7.-2020: FAU08
|
||||
allownaces = filter(lambda charge: charge.isDiscount(), self.invoice_allowance_charge)
|
||||
amounts_allowance = map(lambda charge: charge.amount, allownaces)
|
||||
self.invoice_legal_monetary_total.allowance_total_amount = sum(amounts_allowance)
|
||||
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
|
||||
allowance_charges = filter(lambda charge: charge.isCharge(), self.invoice_allowance_charge)
|
||||
amounts_allowance_charge = map(lambda charge: charge.amount, allowance_charges)
|
||||
self.invoice_legal_monetary_total.charge_total_amount = sum(amounts_allowance_charge)
|
||||
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
|
||||
amounts_prepaid = map(lambda paid: paid.paid_amount, self.invoice_prepaid_payment)
|
||||
self.invoice_legal_monetary_total.prepaid_amount = sum(amounts_prepaid)
|
||||
self.invoice_legal_monetary_total.prepaid_amount = AmountCollection(self.invoice_prepaid_payment)\
|
||||
.map(lambda paid: paid.paid_amount)\
|
||||
.sum()
|
||||
|
||||
#DIAN 1.7.-2020: FAU14
|
||||
self.invoice_legal_monetary_total.payable_amount = \
|
||||
|
@ -294,6 +376,7 @@ class Invoice:
|
|||
+ self.invoice_legal_monetary_total.charge_total_amount \
|
||||
- self.invoice_legal_monetary_total.prepaid_amount
|
||||
|
||||
|
||||
def calculate(self):
|
||||
for invline in self.invoice_lines:
|
||||
invline.calculate()
|
||||
|
@ -371,379 +454,3 @@ class DianResolucion0001Validator:
|
|||
|
||||
def valid(self):
|
||||
return not self.errors
|
||||
|
||||
|
||||
class DIANInvoiceXML(fe.FeXML):
|
||||
"""
|
||||
DianInvoiceXML mapea objeto form.Invoice a XML segun
|
||||
lo indicado para la facturacion electronica.
|
||||
"""
|
||||
|
||||
def __init__(self, invoice):
|
||||
super().__init__('Invoice', 'http://www.dian.gov.co/contratos/facturaelectronica/v1')
|
||||
self.placeholder_for('/fe:Invoice/ext:UBLExtensions/ext:UBLExtension/ext:ExtensionContent')
|
||||
|
||||
# ZE02 se requiere existencia para firmar
|
||||
ublextension = self.fragment('/fe:Invoice/ext:UBLExtensions/ext:UBLExtension', append=True)
|
||||
extcontent = ublextension.find_or_create_element('/ext:UBLExtension/ext:ExtensionContent')
|
||||
self.attach_invoice(invoice)
|
||||
|
||||
def set_supplier(fexml, invoice):
|
||||
fexml.placeholder_for('/fe:Invoice/cac:AccountingSupplierParty')
|
||||
#DIAN 1.7.-2020: FAJ02
|
||||
fexml.set_element('/fe:Invoice/cac:AccountingSupplierParty/cbc:AdditionalAccountID',
|
||||
invoice.invoice_supplier.organization_code)
|
||||
#DIAN 1.7.-2020: FAJ06
|
||||
fexml.set_element('/fe:Invoice/cac:AccountingSupplierParty/cac:Party/cac:PartyName/cbc:Name',
|
||||
invoice.invoice_supplier.name)
|
||||
#DIAN 1.7.-2020: FAJ07
|
||||
fexml.placeholder_for('/fe:Invoice/cac:AccountingSupplierParty/cac:Party/cac:PhysicalLocation/cac:Address')
|
||||
#DIAN 1.7.-2020: FAJ08
|
||||
fexml.set_element('/fe:Invoice/cac:AccountingSupplierParty/cac:Party/cac:PhysicalLocation/cac:Address/cbc:ID',
|
||||
invoice.invoice_supplier.address.city.code)
|
||||
#DIAN 1.7.-2020: FAJ09
|
||||
fexml.set_element('/fe:Invoice/cac:AccountingSupplierParty/cac:Party/cac:PhysicalLocation/cac:Address/cbc:CityName',
|
||||
invoice.invoice_supplier.address.city.name)
|
||||
#DIAN 1.7.-2020: FAJ11
|
||||
fexml.set_element('/fe:Invoice/cac:AccountingSupplierParty/cac:Party/cac:PhysicalLocation/cac:Address/cbc:CountrySubentity',
|
||||
invoice.invoice_supplier.address.countrysubentity.name)
|
||||
#DIAN 1.7.-2020: FAJ12
|
||||
fexml.set_element('/fe:Invoice/cac:AccountingSupplierParty/cac:Party/cac:PhysicalLocation/cac:Address/cbc:CountrySubentityCode',
|
||||
invoice.invoice_supplier.address.countrysubentity.code)
|
||||
#DIAN 1.7.-2020: FAJ14
|
||||
fexml.set_element('/fe:Invoice/cac:AccountingSupplierParty/cac:Party/cac:PhysicalLocation/cac:Address/cac:AddressLine/cbc:Line',
|
||||
invoice.invoice_supplier.address.street)
|
||||
#DIAN 1.7.-2020: FAJ16
|
||||
fexml.set_element('/fe:Invoice/cac:AccountingSupplierParty/cac:Party/cac:PhysicalLocation/cac:Address/cac:Country/cbc:IdentificationCode',
|
||||
invoice.invoice_supplier.address.country.code)
|
||||
|
||||
supplier_address_id_attrs = {'languageID' : 'es'}
|
||||
#DIAN 1.7.-2020: FAJ17
|
||||
fexml.set_element('/fe:Invoice/cac:AccountingSupplierParty/cac:Party/cac:PhysicalLocation/cac:Address/cac:Country/cbc:Name',
|
||||
invoice.invoice_supplier.address.country.name,
|
||||
#DIAN 1.7.-2020: FAJ18
|
||||
**supplier_address_id_attrs)
|
||||
|
||||
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
|
||||
fexml.placeholder_for('/fe:Invoice/cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme')
|
||||
#DIAN 1.7.-2020: FAJ20
|
||||
fexml.set_element('/fe:Invoice/cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme/cbc:RegistrationName',
|
||||
invoice.invoice_supplier.legal_name)
|
||||
#DIAN 1.7.-2020: FAJ21
|
||||
fexml.set_element('/fe:Invoice/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('/fe:Invoice/cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme/cbc:TaxLevelCode',
|
||||
#DIAN 1.7.-2020: FAJ26
|
||||
invoice.invoice_supplier.responsability_code,
|
||||
#DIAN 1.7.-2020: FAJ27
|
||||
listName=invoice.invoice_supplier.responsability_regime_code)
|
||||
#DIAN 1.7.-2020: FAJ28
|
||||
fexml.placeholder_for('/fe:Invoice/cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme/cac:RegistrationAddress')
|
||||
#DIAN 1.7.-2020: FAJ29
|
||||
fexml.set_element('/fe:Invoice/cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme/cac:RegistrationAddress/cbc:ID',
|
||||
invoice.invoice_supplier.address.city.code)
|
||||
#DIAN 1.7.-2020: FAJ30
|
||||
fexml.set_element('/fe:Invoice/cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme/cac:RegistrationAddress/cbc:CityName', invoice.invoice_supplier.address.city.name)
|
||||
#DIAN 1.7.-2020: FAJ31
|
||||
fexml.set_element('/fe:Invoice/cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme/cac:RegistrationAddress/cbc:CountrySubentity',
|
||||
invoice.invoice_supplier.address.countrysubentity.name)
|
||||
#DIAN 1.7.-2020: FAJ32
|
||||
fexml.set_element('/fe:Invoice/cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme/cac:RegistrationAddress/cbc:CountrySubentityCode',
|
||||
invoice.invoice_supplier.address.countrysubentity.code)
|
||||
#DIAN 1.7.-2020: FAJ33,FAJ34
|
||||
fexml.set_element('/fe:Invoice/cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme/cac:RegistrationAddress/cac:AddressLine/cbc:Line',
|
||||
invoice.invoice_supplier.address.street)
|
||||
#DIAN 1.7.-2020: FAJ35,FAJ36
|
||||
fexml.set_element('/fe:Invoice/cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme/cac:RegistrationAddress/cac:Country/cbc:IdentificationCode',
|
||||
invoice.invoice_supplier.address.country.code)
|
||||
supplier_address_id_attrs = {'languageID' : 'es'}
|
||||
#DIAN 1.7.-2020: FAJ37,FAJ38
|
||||
fexml.set_element('/fe:Invoice/cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme/cac:RegistrationAddress/cac:Country/cbc:Name',
|
||||
invoice.invoice_supplier.address.country.name,
|
||||
**supplier_address_id_attrs)
|
||||
#DIAN 1.7.-2020: FAJ39
|
||||
fexml.placeholder_for('/fe:Invoice/cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme/cac:TaxScheme')
|
||||
|
||||
#DIAN 1.7.-2020: FAJ42
|
||||
fexml.placeholder_for('/fe:Invoice/cac:AccountingSupplierParty/cac:Party/cac:PartyLegalEntity')
|
||||
#DIAN 1.7.-2020: FAJ43
|
||||
fexml.set_element('/fe:Invoice/cac:AccountingSupplierParty/cac:Party/cac:PartyLegalEntity/cbc:RegistrationName',
|
||||
invoice.invoice_supplier.legal_name)
|
||||
#DIAN 1.7.-2020: FAJ44,FAJ45,FAJ46,FAJ47,FAJ48
|
||||
fexml.set_element('/fe:Invoice/cac:AccountingSupplierParty/cac:Party/cac:PartyLegalEntity/cbc:CompanyID',
|
||||
invoice.invoice_supplier.ident,
|
||||
**supplier_company_id_attrs)
|
||||
#DIAN 1.7.-2020: FAJ49
|
||||
fexml.placeholder_for('/fe:Invoice/cac:AccountingSupplierParty/cac:Party/cac:PartyLegalEntity/cac:CorporateRegistrationScheme')
|
||||
#DIAN 1.7.-2020: FAJ50
|
||||
fexml.set_element('/fe:Invoice/cac:AccountingSupplierParty/cac:Party/cac:PartyLegalEntity/cac:CorporateRegistrationScheme/cbc:ID',
|
||||
'SETP')
|
||||
fexml.placeholder_for('/fe:Invoice/cac:AccountingSupplierParty/cac:Party/cac:Contact')
|
||||
#DIAN 1.7.-2020: FAJ71
|
||||
fexml.set_element('/fe:Invoice/cac:AccountingSupplierParty/cac:Party/cac:Contact/cbc:ElectronicMail',
|
||||
invoice.invoice_supplier.email)
|
||||
|
||||
|
||||
def set_customer(fexml, invoice):
|
||||
fexml.placeholder_for('/fe:Invoice/cac:AccountingCustomerParty')
|
||||
fexml.set_element('/fe:Invoice/cac:AccountingCustomerParty/cbc:AdditionalAccountID',
|
||||
invoice.invoice_customer.organization_code)
|
||||
fexml.set_element('/fe:Invoice/cac:AccountingCustomerParty/cac:Party/cac:PartyIdentification/cbc:ID',
|
||||
invoice.invoice_customer.ident)
|
||||
fexml.set_element('/fe:Invoice/cac:AccountingCustomerParty/cac:Party/cac:PartyName/cbc:Name',
|
||||
invoice.invoice_customer.name)
|
||||
|
||||
fexml.placeholder_for('/fe:Invoice/cac:AccountingCustomerParty/cac:Party/cac:PhysicalLocation')
|
||||
customer_company_id_attrs = fe.SCHEME_AGENCY_ATTRS.copy()
|
||||
#DIAN 1.7.-2020: FAK25
|
||||
customer_company_id_attrs.update({'schemeID': invoice.invoice_customer.ident.dv,
|
||||
'schemeName': invoice.invoice_customer.ident.type_fiscal})
|
||||
#DIAN 1.7.-2020: FAK07
|
||||
fexml.placeholder_for('/fe:Invoice/cac:AccountingCustomerParty/cac:Party/cac:PhysicalLocation/cac:Address')
|
||||
#DIAN 1.7.-2020: FAK08
|
||||
fexml.set_element('/fe:Invoice/cac:AccountingCustomerParty/cac:Party/cac:PhysicalLocation/cac:Address/cbc:ID',
|
||||
invoice.invoice_customer.address.city.code)
|
||||
#DIAN 1.7.-2020: FAK09
|
||||
fexml.set_element('/fe:Invoice/cac:AccountingCustomerParty/cac:Party/cac:PhysicalLocation/cac:Address/cbc:CityName', invoice.invoice_customer.address.city.name)
|
||||
#DIAN 1.7.-2020: FAK11
|
||||
fexml.set_element('/fe:Invoice/cac:AccountingCustomerParty/cac:Party/cac:PhysicalLocation/cac:Address/cbc:CountrySubentity',
|
||||
invoice.invoice_customer.address.countrysubentity.name)
|
||||
#DIAN 1.7.-2020: FAK12
|
||||
fexml.set_element('/fe:Invoice/cac:AccountingCustomerParty/cac:Party/cac:PhysicalLocation/cac:Address/cbc:CountrySubentityCode',
|
||||
invoice.invoice_customer.address.countrysubentity.code)
|
||||
fexml.set_element('/fe:Invoice/cac:AccountingCustomerParty/cac:Party/cac:PhysicalLocation/cac:Address/cac:AddressLine/cbc:Line',
|
||||
invoice.invoice_customer.address.street)
|
||||
#DIAN 1.7.-2020: FAK16
|
||||
fexml.set_element('/fe:Invoice/cac:AccountingCustomerParty/cac:Party/cac:PhysicalLocation/cac:Address/cac:Country/cbc:IdentificationCode',
|
||||
invoice.invoice_customer.address.country.code)
|
||||
customer_address_id_attrs = {'languageID' : 'es'}
|
||||
#DIAN 1.7.-2020: FAK17
|
||||
fexml.set_element('/fe:Invoice/cac:AccountingCustomerParty/cac:Party/cac:PhysicalLocation/cac:Address/cac:Country/cbc:Name',
|
||||
invoice.invoice_customer.address.country.name,
|
||||
#DIAN 1.7.-2020: FAK18
|
||||
**customer_address_id_attrs)
|
||||
#DIAN 1.7.-2020: FAK17,FAK19
|
||||
fexml.placeholder_for('/fe:Invoice/cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme')
|
||||
#DIAN 1.7.-2020: FAK17,FAK20
|
||||
fexml.set_element('/fe:Invoice/cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cbc:RegistrationName',
|
||||
invoice.invoice_customer.legal_name)
|
||||
#DIAN 1.7.-2020: FAK21
|
||||
fexml.set_element('/fe:Invoice/cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cbc:CompanyID',
|
||||
invoice.invoice_customer.ident,
|
||||
**customer_company_id_attrs)
|
||||
fexml.set_element('/fe:Invoice/cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cbc:TaxLevelCode',
|
||||
#DIAN 1.7.-2020: FAK26
|
||||
invoice.invoice_customer.responsability_code,
|
||||
#DIAN 1.7.-2020: FAK27
|
||||
listName=invoice.invoice_customer.responsability_regime_code)
|
||||
#DIAN 1.7.-2020: FAK28
|
||||
fexml.placeholder_for('/fe:Invoice/cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cac:RegistrationAddress')
|
||||
#DIAN 1.7.-2020: FAK29
|
||||
fexml.set_element('/fe:Invoice/cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cac:RegistrationAddress/cbc:ID',
|
||||
invoice.invoice_customer.address.city.code)
|
||||
#DIAN 1.7.-2020: FAK30
|
||||
fexml.set_element('/fe:Invoice/cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cac:RegistrationAddress/cbc:CityName',
|
||||
invoice.invoice_customer.address.city.name)
|
||||
#DIAN 1.7.-2020: FAK31
|
||||
fexml.set_element('/fe:Invoice/cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cac:RegistrationAddress/cbc:CountrySubentity',
|
||||
invoice.invoice_customer.address.countrysubentity.name)
|
||||
#DIAN 1.7.-2020: FAK32
|
||||
fexml.set_element('/fe:Invoice/cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cac:RegistrationAddress/cbc:CountrySubentityCode',
|
||||
invoice.invoice_customer.address.countrysubentity.code)
|
||||
#DIAN 1.7.-2020: FAK33
|
||||
fexml.placeholder_for('/fe:Invoice/cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cac:RegistrationAddress/cac:AddressLine')
|
||||
#DIAN 1.7.-2020: FAK34
|
||||
fexml.set_element('/fe:Invoice/cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cac:RegistrationAddress/cac:AddressLine/cbc:Line',
|
||||
invoice.invoice_customer.address.street)
|
||||
#DIAN 1.7.-2020: FAK35
|
||||
fexml.placeholder_for('/fe:Invoice/cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cac:RegistrationAddress/cac:Country')
|
||||
#DIAN 1.7.-2020: FAK36
|
||||
fexml.set_element('/fe:Invoice/cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cac:RegistrationAddress/cac:Country/cbc:IdentificationCode',
|
||||
invoice.invoice_customer.address.country.code)
|
||||
#DIAN 1.7.-2020: FAK37
|
||||
fexml.set_element('/fe:Invoice/cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cac:RegistrationAddress/cac:Country/cbc:Name', invoice.invoice_customer.address.country.name)
|
||||
#DIAN 1.7.-2020: FAK38
|
||||
fexml.set_element('/fe:Invoice/cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cac:RegistrationAddress/cac:Country/cbc:IdentificationCode',
|
||||
invoice.invoice_customer.address.country.code,
|
||||
**customer_address_id_attrs)
|
||||
#DIAN 1.7.-2020: FAK39
|
||||
fexml.placeholder_for('/fe:Invoice/cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cac:TaxScheme')
|
||||
#DIAN 1.7.-2020: FAK40 Machete Construir Validación
|
||||
fexml.set_element('/fe:Invoice/cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cac:TaxScheme/cbc:ID',
|
||||
'ZY')
|
||||
#DIAN 1.7.-2020: FAK41 Machete Construir Validación
|
||||
fexml.set_element('/fe:Invoice/cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cac:TaxScheme/cbc:Name',
|
||||
'No causa')
|
||||
#DIAN 1.7.-2020: FAK42
|
||||
fexml.placeholder_for('/fe:Invoice/cac:AccountingCustomerParty/cac:Party/cac:PartyLegalEntity')
|
||||
#DIAN 1.7.-2020: FAK43
|
||||
fexml.set_element('/fe:Invoice/cac:AccountingCustomerParty/cac:Party/cac:PartyLegalEntity/cbc:RegistrationName',
|
||||
invoice.invoice_customer.legal_name)
|
||||
#DIAN 1.7.-2020: FAK44,FAK45,FAK46,FAK47,FAK48
|
||||
fexml.set_element('/fe:Invoice/cac:AccountingCustomerParty/cac:Party/cac:PartyLegalEntity/cbc:CompanyID',
|
||||
invoice.invoice_customer.ident,
|
||||
**customer_company_id_attrs)
|
||||
#DIAN 1.7.-2020: FAK51
|
||||
fexml.placeholder_for('/fe:Invoice/cac:AccountingCustomerParty/cac:Party/cac:PartyLegalEntity')
|
||||
#DIAN 1.7.-2020: FAK55
|
||||
fexml.set_element('/fe:Invoice/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('/fe:Invoice/cac:PaymentMeans/cbc:ID', payment_mean.id)
|
||||
fexml.set_element('/fe:Invoice/cac:PaymentMeans/cbc:PaymentMeansCode', payment_mean.code)
|
||||
fexml.set_element('/fe:Invoice/cac:PaymentMeans/cbc:PaymentDueDate', payment_mean.due_at.strftime('%Y-%m-%d'))
|
||||
fexml.set_element('/fe:Invoice/cac:PaymentMeans/cbc:PaymentID', payment_mean.payment_id)
|
||||
|
||||
def set_legal_monetary(fexml, invoice):
|
||||
fexml.set_element('/fe:Invoice/cac:LegalMonetaryTotal/cbc:LineExtensionAmount',
|
||||
#MACHETE redondeo en valor
|
||||
'%.02f' % (invoice.invoice_legal_monetary_total.line_extension_amount),
|
||||
currencyID='COP')
|
||||
fexml.set_element('/fe:Invoice/cac:LegalMonetaryTotal/cbc:TaxExclusiveAmount',
|
||||
#MACHETE redondeo en valor
|
||||
'%.02f' % (invoice.invoice_legal_monetary_total.tax_exclusive_amount),
|
||||
currencyID='COP')
|
||||
fexml.set_element('/fe:Invoice/cac:LegalMonetaryTotal/cbc:TaxInclusiveAmount',
|
||||
#MACHETE ...
|
||||
'%.02f' % (invoice.invoice_legal_monetary_total.tax_inclusive_amount),
|
||||
currencyID='COP')
|
||||
fexml.set_element('/fe:Invoice/cac:LegalMonetaryTotal/cbc:ChargeTotalAmount',
|
||||
#MACHETE ...
|
||||
'%.02f' % (invoice.invoice_legal_monetary_total.charge_total_amount),
|
||||
currencyID='COP')
|
||||
fexml.set_element('/fe:Invoice/cac:LegalMonetaryTotal/cbc:PayableAmount',
|
||||
#MACHETE ...
|
||||
'%.02f' % (invoice.invoice_legal_monetary_total.payable_amount),
|
||||
currencyID='COP')
|
||||
|
||||
def set_invoice_totals(fexml, invoice):
|
||||
tax_amount_for = defaultdict(lambda: defaultdict(lambda: 0.0))
|
||||
percent_for = defaultdict(lambda: None)
|
||||
|
||||
#requeridos para CUFE
|
||||
tax_amount_for['01']['tax_amount'] = 0.0
|
||||
tax_amount_for['01']['taxable_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 = 0.0
|
||||
|
||||
for invoice_line in invoice.invoice_lines:
|
||||
for subtotal in invoice_line.tax.subtotals:
|
||||
tax_amount_for[subtotal.tax_scheme_ident]['tax_amount'] += subtotal.tax_amount
|
||||
tax_amount_for[subtotal.tax_scheme_ident]['taxable_amount'] += subtotal.taxable_amount
|
||||
total_tax_amount += subtotal.tax_amount
|
||||
# MACHETE ojo InvoiceLine.tax pasar a Invoice
|
||||
percent_for[subtotal.tax_scheme_ident] = subtotal.percent
|
||||
|
||||
fexml.placeholder_for('/fe:Invoice/cac:TaxTotal')
|
||||
fexml.set_element('/fe:Invoice/cac:TaxTotal/cbc:TaxAmount', total_tax_amount,
|
||||
currencyID='COP')
|
||||
|
||||
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('/fe:Invoice/cac:TaxTotal', append=next_append)
|
||||
|
||||
#DIAN 1.7.-2020: FAU06
|
||||
tax_amount = amount_of['tax_amount']
|
||||
line.set_element('/cac:TaxTotal/cbc:TaxAmount',
|
||||
# MACHETE
|
||||
'%.02f' % (tax_amount), currencyID='COP')
|
||||
|
||||
#DIAN 1.7.-2020: FAS05
|
||||
line.set_element('/cac:TaxTotal/cac:TaxSubtotal/cbc:TaxableAmount',
|
||||
# MACHETE
|
||||
'%.02f' % (amount_of['taxable_amount']), currencyID='COP')
|
||||
|
||||
#DIAN 1.7.-2020: FAU06
|
||||
line.set_element('/cac:TaxTotal/cac:TaxSubtotal/cbc:TaxAmount',
|
||||
# MACHETE
|
||||
'%.02f' % (amount_of['tax_amount']), currencyID='COP')
|
||||
|
||||
#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)
|
||||
|
||||
|
||||
def set_invoice_lines(fexml, invoice):
|
||||
next_append = False
|
||||
for index, invoice_line in enumerate(invoice.invoice_lines):
|
||||
line = fexml.fragment('/fe:Invoice/cac:InvoiceLine', append=next_append)
|
||||
next_append = True
|
||||
|
||||
line.set_element('/cac:InvoiceLine/cbc:ID', index + 1)
|
||||
line.set_element('/cac:InvoiceLine/cbc:InvoicedQuantity', invoice_line.quantity, unitCode = 'NAR')
|
||||
line.set_element('/cac:InvoiceLine/cbc:LineExtensionAmount', invoice_line.total_amount, currencyID="COP")
|
||||
line.set_element('/cac:InvoiceLine/cac:TaxTotal/cbc:TaxAmount', invoice_line.tax_amount, currencyID='COP')
|
||||
|
||||
#condition_price = line.fragment('/cac:InvoiceLine/cac:PricingReference/cac:AlternativeConditionPrice')
|
||||
#condition_price.set_element('/cac:AlternativeConditionPrice/cbc:PriceAmount', invoice_line.price.amount, currencyID='COP')
|
||||
#condition_price.set_element('/cac:AlternativeConditionPrice/cbc:PriceTypeCode', invoice_line.price.type_code)
|
||||
#condition_price.set_element('/cac:AlternativeConditionPrice/cbc:PriceType', invoice_line.price.type)
|
||||
|
||||
for subtotal in invoice_line.tax.subtotals:
|
||||
line.set_element('/cac:InvoiceLine/cac:TaxTotal/cac:TaxSubtotal/cbc:TaxableAmount', subtotal.taxable_amount, currencyID='COP')
|
||||
line.set_element('/cac:InvoiceLine/cac:TaxTotal/cac:TaxSubtotal/cbc:TaxAmount', subtotal.tax_amount, currencyID='COP')
|
||||
line.set_element('/cac:InvoiceLine/cac:TaxTotal/cac:TaxSubtotal/cac:TaxCategory/cbc:Percent', subtotal.percent)
|
||||
line.set_element('/cac:InvoiceLine/cac:TaxTotal/cac:TaxSubtotal/cac:TaxCategory/cac:TaxScheme/cbc:ID', subtotal.tax_scheme_ident)
|
||||
line.set_element('/cac:InvoiceLine/cac:TaxTotal/cac:TaxSubtotal/cac:TaxCategory/cac:TaxScheme/cbc:Name', subtotal.tax_scheme_name)
|
||||
line.set_element('/cac:InvoiceLine/cac:Item/cbc:Description', invoice_line.item.description)
|
||||
# TODO
|
||||
line.set_element('/cac:InvoiceLine/cac:Item/cac:StandardItemIdentification/cbc:ID', invoice_line.item.id)
|
||||
line.set_element('/cac:InvoiceLine/cac:Price/cbc:PriceAmount', invoice_line.price.amount, currencyID="COP")
|
||||
#DIAN 1.7.-2020: FBB04
|
||||
line.set_element('/cac:InvoiceLine/cac:Price/cbc:BaseQuantity', invoice_line.price.amount)
|
||||
|
||||
def attach_invoice(fexml, invoice):
|
||||
"""adiciona etiquetas a FEXML y retorna FEXML
|
||||
en caso de fallar validacion retorna None"""
|
||||
|
||||
fexml.placeholder_for('/fe:Invoice/ext:UBLExtensions')
|
||||
fexml.set_element('/fe:Invoice/cbc:UBLVersionID', 'UBL 2.1')
|
||||
fexml.set_element('/fe:Invoice/cbc:CustomizationID', invoice.invoice_operation_type)
|
||||
fexml.placeholder_for('/fe:Invoice/cbc:ProfileID')
|
||||
fexml.placeholder_for('/fe:Invoice/cbc:ProfileExecutionID')
|
||||
fexml.set_element('/fe:Invoice/cbc:ID', invoice.invoice_ident)
|
||||
fexml.placeholder_for('/fe:Invoice/cbc:UUID')
|
||||
fexml.set_element('/fe:Invoice/cbc:DocumentCurrencyCode', 'COP')
|
||||
fexml.set_element('/fe:Invoice/cbc:IssueDate', invoice.invoice_issue.strftime('%Y-%m-%d'))
|
||||
#DIAN 1.7.-2020: FAD10
|
||||
fexml.set_element('/fe:Invoice/cbc:IssueTime', invoice.invoice_issue.strftime('%H:%M:%S-05:00'))
|
||||
fexml.set_element('/fe:Invoice/cbc:InvoiceTypeCode', codelist.TipoDocumento.by_name('Factura de Venta Nacional')['code'],
|
||||
listAgencyID='195',
|
||||
listAgencyName='No matching global declaration available for the validation root',
|
||||
listURI='http://www.dian.gov.co')
|
||||
fexml.set_element('/fe:Invoice/cbc:LineCountNumeric', len(invoice.invoice_lines))
|
||||
fexml.set_element('/fe:Invoice/cac:InvoicePeriod/cbc:StartDate', invoice.invoice_period_start.strftime('%Y-%m-%d'))
|
||||
fexml.set_element('/fe:Invoice/cac:InvoicePeriod/cbc:EndDate', invoice.invoice_period_end.strftime('%Y-%m-%d'))
|
||||
|
||||
fexml.set_supplier(invoice)
|
||||
fexml.set_customer(invoice)
|
||||
fexml.set_legal_monetary(invoice)
|
||||
fexml.set_invoice_totals(invoice)
|
||||
fexml.set_invoice_lines(invoice)
|
||||
fexml.set_payment_mean(invoice)
|
||||
|
||||
return fexml
|
||||
|
|
|
@ -0,0 +1,391 @@
|
|||
from . import fe
|
||||
from .form import *
|
||||
|
||||
class DIANInvoiceXML(fe.FeXML):
|
||||
"""
|
||||
DianInvoiceXML mapea objeto form.Invoice a XML segun
|
||||
lo indicado para la facturacion electronica.
|
||||
"""
|
||||
|
||||
def __init__(self, invoice):
|
||||
super().__init__('Invoice', 'http://www.dian.gov.co/contratos/facturaelectronica/v1')
|
||||
self.placeholder_for('/fe:Invoice/ext:UBLExtensions/ext:UBLExtension/ext:ExtensionContent')
|
||||
|
||||
# ZE02 se requiere existencia para firmar
|
||||
ublextension = self.fragment('/fe:Invoice/ext:UBLExtensions/ext:UBLExtension', append=True)
|
||||
extcontent = ublextension.find_or_create_element('/ext:UBLExtension/ext:ExtensionContent')
|
||||
self.attach_invoice(invoice)
|
||||
|
||||
def set_supplier(fexml, invoice):
|
||||
fexml.placeholder_for('/fe:Invoice/cac:AccountingSupplierParty')
|
||||
#DIAN 1.7.-2020: FAJ02
|
||||
fexml.set_element('/fe:Invoice/cac:AccountingSupplierParty/cbc:AdditionalAccountID',
|
||||
invoice.invoice_supplier.organization_code)
|
||||
#DIAN 1.7.-2020: FAJ06
|
||||
fexml.set_element('/fe:Invoice/cac:AccountingSupplierParty/cac:Party/cac:PartyName/cbc:Name',
|
||||
invoice.invoice_supplier.name)
|
||||
#DIAN 1.7.-2020: FAJ07
|
||||
fexml.placeholder_for('/fe:Invoice/cac:AccountingSupplierParty/cac:Party/cac:PhysicalLocation/cac:Address')
|
||||
#DIAN 1.7.-2020: FAJ08
|
||||
fexml.set_element('/fe:Invoice/cac:AccountingSupplierParty/cac:Party/cac:PhysicalLocation/cac:Address/cbc:ID',
|
||||
invoice.invoice_supplier.address.city.code)
|
||||
#DIAN 1.7.-2020: FAJ09
|
||||
fexml.set_element('/fe:Invoice/cac:AccountingSupplierParty/cac:Party/cac:PhysicalLocation/cac:Address/cbc:CityName',
|
||||
invoice.invoice_supplier.address.city.name)
|
||||
#DIAN 1.7.-2020: FAJ11
|
||||
fexml.set_element('/fe:Invoice/cac:AccountingSupplierParty/cac:Party/cac:PhysicalLocation/cac:Address/cbc:CountrySubentity',
|
||||
invoice.invoice_supplier.address.countrysubentity.name)
|
||||
#DIAN 1.7.-2020: FAJ12
|
||||
fexml.set_element('/fe:Invoice/cac:AccountingSupplierParty/cac:Party/cac:PhysicalLocation/cac:Address/cbc:CountrySubentityCode',
|
||||
invoice.invoice_supplier.address.countrysubentity.code)
|
||||
#DIAN 1.7.-2020: FAJ14
|
||||
fexml.set_element('/fe:Invoice/cac:AccountingSupplierParty/cac:Party/cac:PhysicalLocation/cac:Address/cac:AddressLine/cbc:Line',
|
||||
invoice.invoice_supplier.address.street)
|
||||
#DIAN 1.7.-2020: FAJ16
|
||||
fexml.set_element('/fe:Invoice/cac:AccountingSupplierParty/cac:Party/cac:PhysicalLocation/cac:Address/cac:Country/cbc:IdentificationCode',
|
||||
invoice.invoice_supplier.address.country.code)
|
||||
|
||||
supplier_address_id_attrs = {'languageID' : 'es'}
|
||||
#DIAN 1.7.-2020: FAJ17
|
||||
fexml.set_element('/fe:Invoice/cac:AccountingSupplierParty/cac:Party/cac:PhysicalLocation/cac:Address/cac:Country/cbc:Name',
|
||||
invoice.invoice_supplier.address.country.name,
|
||||
#DIAN 1.7.-2020: FAJ18
|
||||
**supplier_address_id_attrs)
|
||||
|
||||
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
|
||||
fexml.placeholder_for('/fe:Invoice/cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme')
|
||||
#DIAN 1.7.-2020: FAJ20
|
||||
fexml.set_element('/fe:Invoice/cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme/cbc:RegistrationName',
|
||||
invoice.invoice_supplier.legal_name)
|
||||
#DIAN 1.7.-2020: FAJ21
|
||||
fexml.set_element('/fe:Invoice/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('/fe:Invoice/cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme/cbc:TaxLevelCode',
|
||||
#DIAN 1.7.-2020: FAJ26
|
||||
invoice.invoice_supplier.responsability_code,
|
||||
#DIAN 1.7.-2020: FAJ27
|
||||
listName=invoice.invoice_supplier.responsability_regime_code)
|
||||
#DIAN 1.7.-2020: FAJ28
|
||||
fexml.placeholder_for('/fe:Invoice/cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme/cac:RegistrationAddress')
|
||||
#DIAN 1.7.-2020: FAJ29
|
||||
fexml.set_element('/fe:Invoice/cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme/cac:RegistrationAddress/cbc:ID',
|
||||
invoice.invoice_supplier.address.city.code)
|
||||
#DIAN 1.7.-2020: FAJ30
|
||||
fexml.set_element('/fe:Invoice/cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme/cac:RegistrationAddress/cbc:CityName', invoice.invoice_supplier.address.city.name)
|
||||
#DIAN 1.7.-2020: FAJ31
|
||||
fexml.set_element('/fe:Invoice/cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme/cac:RegistrationAddress/cbc:CountrySubentity',
|
||||
invoice.invoice_supplier.address.countrysubentity.name)
|
||||
#DIAN 1.7.-2020: FAJ32
|
||||
fexml.set_element('/fe:Invoice/cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme/cac:RegistrationAddress/cbc:CountrySubentityCode',
|
||||
invoice.invoice_supplier.address.countrysubentity.code)
|
||||
#DIAN 1.7.-2020: FAJ33,FAJ34
|
||||
fexml.set_element('/fe:Invoice/cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme/cac:RegistrationAddress/cac:AddressLine/cbc:Line',
|
||||
invoice.invoice_supplier.address.street)
|
||||
#DIAN 1.7.-2020: FAJ35,FAJ36
|
||||
fexml.set_element('/fe:Invoice/cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme/cac:RegistrationAddress/cac:Country/cbc:IdentificationCode',
|
||||
invoice.invoice_supplier.address.country.code)
|
||||
supplier_address_id_attrs = {'languageID' : 'es'}
|
||||
#DIAN 1.7.-2020: FAJ37,FAJ38
|
||||
fexml.set_element('/fe:Invoice/cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme/cac:RegistrationAddress/cac:Country/cbc:Name',
|
||||
invoice.invoice_supplier.address.country.name,
|
||||
**supplier_address_id_attrs)
|
||||
#DIAN 1.7.-2020: FAJ39
|
||||
fexml.placeholder_for('/fe:Invoice/cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme/cac:TaxScheme')
|
||||
|
||||
#DIAN 1.7.-2020: FAJ42
|
||||
fexml.placeholder_for('/fe:Invoice/cac:AccountingSupplierParty/cac:Party/cac:PartyLegalEntity')
|
||||
#DIAN 1.7.-2020: FAJ43
|
||||
fexml.set_element('/fe:Invoice/cac:AccountingSupplierParty/cac:Party/cac:PartyLegalEntity/cbc:RegistrationName',
|
||||
invoice.invoice_supplier.legal_name)
|
||||
#DIAN 1.7.-2020: FAJ44,FAJ45,FAJ46,FAJ47,FAJ48
|
||||
fexml.set_element('/fe:Invoice/cac:AccountingSupplierParty/cac:Party/cac:PartyLegalEntity/cbc:CompanyID',
|
||||
invoice.invoice_supplier.ident,
|
||||
**supplier_company_id_attrs)
|
||||
#DIAN 1.7.-2020: FAJ49
|
||||
fexml.placeholder_for('/fe:Invoice/cac:AccountingSupplierParty/cac:Party/cac:PartyLegalEntity/cac:CorporateRegistrationScheme')
|
||||
#DIAN 1.7.-2020: FAJ50
|
||||
fexml.set_element('/fe:Invoice/cac:AccountingSupplierParty/cac:Party/cac:PartyLegalEntity/cac:CorporateRegistrationScheme/cbc:ID',
|
||||
'SETP')
|
||||
fexml.placeholder_for('/fe:Invoice/cac:AccountingSupplierParty/cac:Party/cac:Contact')
|
||||
#DIAN 1.7.-2020: FAJ71
|
||||
fexml.set_element('/fe:Invoice/cac:AccountingSupplierParty/cac:Party/cac:Contact/cbc:ElectronicMail',
|
||||
invoice.invoice_supplier.email)
|
||||
|
||||
|
||||
def set_customer(fexml, invoice):
|
||||
fexml.placeholder_for('/fe:Invoice/cac:AccountingCustomerParty')
|
||||
fexml.set_element('/fe:Invoice/cac:AccountingCustomerParty/cbc:AdditionalAccountID',
|
||||
invoice.invoice_customer.organization_code)
|
||||
fexml.set_element('/fe:Invoice/cac:AccountingCustomerParty/cac:Party/cac:PartyIdentification/cbc:ID',
|
||||
invoice.invoice_customer.ident)
|
||||
fexml.set_element('/fe:Invoice/cac:AccountingCustomerParty/cac:Party/cac:PartyName/cbc:Name',
|
||||
invoice.invoice_customer.name)
|
||||
|
||||
fexml.placeholder_for('/fe:Invoice/cac:AccountingCustomerParty/cac:Party/cac:PhysicalLocation')
|
||||
customer_company_id_attrs = fe.SCHEME_AGENCY_ATTRS.copy()
|
||||
#DIAN 1.7.-2020: FAK25
|
||||
customer_company_id_attrs.update({'schemeID': invoice.invoice_customer.ident.dv,
|
||||
'schemeName': invoice.invoice_customer.ident.type_fiscal})
|
||||
#DIAN 1.7.-2020: FAK07
|
||||
fexml.placeholder_for('/fe:Invoice/cac:AccountingCustomerParty/cac:Party/cac:PhysicalLocation/cac:Address')
|
||||
#DIAN 1.7.-2020: FAK08
|
||||
fexml.set_element('/fe:Invoice/cac:AccountingCustomerParty/cac:Party/cac:PhysicalLocation/cac:Address/cbc:ID',
|
||||
invoice.invoice_customer.address.city.code)
|
||||
#DIAN 1.7.-2020: FAK09
|
||||
fexml.set_element('/fe:Invoice/cac:AccountingCustomerParty/cac:Party/cac:PhysicalLocation/cac:Address/cbc:CityName', invoice.invoice_customer.address.city.name)
|
||||
#DIAN 1.7.-2020: FAK11
|
||||
fexml.set_element('/fe:Invoice/cac:AccountingCustomerParty/cac:Party/cac:PhysicalLocation/cac:Address/cbc:CountrySubentity',
|
||||
invoice.invoice_customer.address.countrysubentity.name)
|
||||
#DIAN 1.7.-2020: FAK12
|
||||
fexml.set_element('/fe:Invoice/cac:AccountingCustomerParty/cac:Party/cac:PhysicalLocation/cac:Address/cbc:CountrySubentityCode',
|
||||
invoice.invoice_customer.address.countrysubentity.code)
|
||||
fexml.set_element('/fe:Invoice/cac:AccountingCustomerParty/cac:Party/cac:PhysicalLocation/cac:Address/cac:AddressLine/cbc:Line',
|
||||
invoice.invoice_customer.address.street)
|
||||
#DIAN 1.7.-2020: FAK16
|
||||
fexml.set_element('/fe:Invoice/cac:AccountingCustomerParty/cac:Party/cac:PhysicalLocation/cac:Address/cac:Country/cbc:IdentificationCode',
|
||||
invoice.invoice_customer.address.country.code)
|
||||
customer_address_id_attrs = {'languageID' : 'es'}
|
||||
#DIAN 1.7.-2020: FAK17
|
||||
fexml.set_element('/fe:Invoice/cac:AccountingCustomerParty/cac:Party/cac:PhysicalLocation/cac:Address/cac:Country/cbc:Name',
|
||||
invoice.invoice_customer.address.country.name,
|
||||
#DIAN 1.7.-2020: FAK18
|
||||
**customer_address_id_attrs)
|
||||
#DIAN 1.7.-2020: FAK17,FAK19
|
||||
fexml.placeholder_for('/fe:Invoice/cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme')
|
||||
#DIAN 1.7.-2020: FAK17,FAK20
|
||||
fexml.set_element('/fe:Invoice/cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cbc:RegistrationName',
|
||||
invoice.invoice_customer.legal_name)
|
||||
#DIAN 1.7.-2020: FAK21
|
||||
fexml.set_element('/fe:Invoice/cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cbc:CompanyID',
|
||||
invoice.invoice_customer.ident,
|
||||
**customer_company_id_attrs)
|
||||
fexml.set_element('/fe:Invoice/cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cbc:TaxLevelCode',
|
||||
#DIAN 1.7.-2020: FAK26
|
||||
invoice.invoice_customer.responsability_code,
|
||||
#DIAN 1.7.-2020: FAK27
|
||||
listName=invoice.invoice_customer.responsability_regime_code)
|
||||
#DIAN 1.7.-2020: FAK28
|
||||
fexml.placeholder_for('/fe:Invoice/cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cac:RegistrationAddress')
|
||||
#DIAN 1.7.-2020: FAK29
|
||||
fexml.set_element('/fe:Invoice/cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cac:RegistrationAddress/cbc:ID',
|
||||
invoice.invoice_customer.address.city.code)
|
||||
#DIAN 1.7.-2020: FAK30
|
||||
fexml.set_element('/fe:Invoice/cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cac:RegistrationAddress/cbc:CityName',
|
||||
invoice.invoice_customer.address.city.name)
|
||||
#DIAN 1.7.-2020: FAK31
|
||||
fexml.set_element('/fe:Invoice/cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cac:RegistrationAddress/cbc:CountrySubentity',
|
||||
invoice.invoice_customer.address.countrysubentity.name)
|
||||
#DIAN 1.7.-2020: FAK32
|
||||
fexml.set_element('/fe:Invoice/cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cac:RegistrationAddress/cbc:CountrySubentityCode',
|
||||
invoice.invoice_customer.address.countrysubentity.code)
|
||||
#DIAN 1.7.-2020: FAK33
|
||||
fexml.placeholder_for('/fe:Invoice/cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cac:RegistrationAddress/cac:AddressLine')
|
||||
#DIAN 1.7.-2020: FAK34
|
||||
fexml.set_element('/fe:Invoice/cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cac:RegistrationAddress/cac:AddressLine/cbc:Line',
|
||||
invoice.invoice_customer.address.street)
|
||||
#DIAN 1.7.-2020: FAK35
|
||||
fexml.placeholder_for('/fe:Invoice/cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cac:RegistrationAddress/cac:Country')
|
||||
#DIAN 1.7.-2020: FAK36
|
||||
fexml.set_element('/fe:Invoice/cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cac:RegistrationAddress/cac:Country/cbc:IdentificationCode',
|
||||
invoice.invoice_customer.address.country.code)
|
||||
#DIAN 1.7.-2020: FAK37
|
||||
fexml.set_element('/fe:Invoice/cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cac:RegistrationAddress/cac:Country/cbc:Name', invoice.invoice_customer.address.country.name)
|
||||
#DIAN 1.7.-2020: FAK38
|
||||
fexml.set_element('/fe:Invoice/cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cac:RegistrationAddress/cac:Country/cbc:IdentificationCode',
|
||||
invoice.invoice_customer.address.country.code,
|
||||
**customer_address_id_attrs)
|
||||
#DIAN 1.7.-2020: FAK39
|
||||
fexml.placeholder_for('/fe:Invoice/cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cac:TaxScheme')
|
||||
#DIAN 1.7.-2020: FAK40 Machete Construir Validación
|
||||
fexml.set_element('/fe:Invoice/cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cac:TaxScheme/cbc:ID',
|
||||
'ZY')
|
||||
#DIAN 1.7.-2020: FAK41 Machete Construir Validación
|
||||
fexml.set_element('/fe:Invoice/cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cac:TaxScheme/cbc:Name',
|
||||
'No causa')
|
||||
#DIAN 1.7.-2020: FAK42
|
||||
fexml.placeholder_for('/fe:Invoice/cac:AccountingCustomerParty/cac:Party/cac:PartyLegalEntity')
|
||||
#DIAN 1.7.-2020: FAK43
|
||||
fexml.set_element('/fe:Invoice/cac:AccountingCustomerParty/cac:Party/cac:PartyLegalEntity/cbc:RegistrationName',
|
||||
invoice.invoice_customer.legal_name)
|
||||
#DIAN 1.7.-2020: FAK44,FAK45,FAK46,FAK47,FAK48
|
||||
fexml.set_element('/fe:Invoice/cac:AccountingCustomerParty/cac:Party/cac:PartyLegalEntity/cbc:CompanyID',
|
||||
invoice.invoice_customer.ident,
|
||||
**customer_company_id_attrs)
|
||||
#DIAN 1.7.-2020: FAK51
|
||||
fexml.placeholder_for('/fe:Invoice/cac:AccountingCustomerParty/cac:Party/cac:PartyLegalEntity')
|
||||
#DIAN 1.7.-2020: FAK55
|
||||
fexml.set_element('/fe:Invoice/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('/fe:Invoice/cac:PaymentMeans/cbc:ID', payment_mean.id)
|
||||
fexml.set_element('/fe:Invoice/cac:PaymentMeans/cbc:PaymentMeansCode', payment_mean.code)
|
||||
fexml.set_element('/fe:Invoice/cac:PaymentMeans/cbc:PaymentDueDate', payment_mean.due_at.strftime('%Y-%m-%d'))
|
||||
fexml.set_element('/fe:Invoice/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('/fe:Invoice/cac:LegalMonetaryTotal/cbc:LineExtensionAmount',
|
||||
invoice.invoice_legal_monetary_total.line_extension_amount)
|
||||
|
||||
fexml.set_element_amount('/fe:Invoice/cac:LegalMonetaryTotal/cbc:TaxExclusiveAmount',
|
||||
invoice.invoice_legal_monetary_total.tax_exclusive_amount)
|
||||
|
||||
fexml.set_element_amount('/fe:Invoice/cac:LegalMonetaryTotal/cbc:TaxInclusiveAmount',
|
||||
invoice.invoice_legal_monetary_total.tax_inclusive_amount)
|
||||
|
||||
fexml.set_element_amount('/fe:Invoice/cac:LegalMonetaryTotal/cbc:ChargeTotalAmount',
|
||||
invoice.invoice_legal_monetary_total.charge_total_amount)
|
||||
|
||||
fexml.set_element_amount('/fe:Invoice/cac:LegalMonetaryTotal/cbc:PayableAmount',
|
||||
invoice.invoice_legal_monetary_total.payable_amount)
|
||||
|
||||
|
||||
def set_invoice_totals(fexml, invoice):
|
||||
tax_amount_for = defaultdict(lambda: defaultdict(lambda: 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)
|
||||
|
||||
for invoice_line in invoice.invoice_lines:
|
||||
for subtotal in invoice_line.tax.subtotals:
|
||||
tax_amount_for[subtotal.tax_scheme_ident]['tax_amount'] += subtotal.tax_amount
|
||||
tax_amount_for[subtotal.tax_scheme_ident]['taxable_amount'] += subtotal.taxable_amount
|
||||
total_tax_amount += subtotal.tax_amount
|
||||
# MACHETE ojo InvoiceLine.tax pasar a Invoice
|
||||
percent_for[subtotal.tax_scheme_ident] = subtotal.percent
|
||||
|
||||
fexml.placeholder_for('/fe:Invoice/cac:TaxTotal')
|
||||
fexml.set_element_amount('/fe:Invoice/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('/fe:Invoice/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)
|
||||
|
||||
|
||||
def set_invoice_lines(fexml, invoice):
|
||||
next_append = False
|
||||
for index, invoice_line in enumerate(invoice.invoice_lines):
|
||||
line = fexml.fragment('/fe:Invoice/cac:InvoiceLine', append=next_append)
|
||||
next_append = True
|
||||
|
||||
line.set_element('/cac:InvoiceLine/cbc:ID', index + 1)
|
||||
line.set_element('/cac:InvoiceLine/cbc:InvoicedQuantity', invoice_line.quantity, unitCode = 'NAR')
|
||||
fexml.set_element_amount_for(line,
|
||||
'/cac:InvoiceLine/cbc:LineExtensionAmount',
|
||||
invoice_line.total_amount)
|
||||
fexml.set_element_amount_for(line,
|
||||
'/cac:InvoiceLine/cac:TaxTotal/cbc:TaxAmount',
|
||||
invoice_line.tax_amount)
|
||||
|
||||
#condition_price = line.fragment('/cac:InvoiceLine/cac:PricingReference/cac:AlternativeConditionPrice')
|
||||
#condition_price.set_element('/cac:AlternativeConditionPrice/cbc:PriceAmount', invoice_line.price.amount, currencyID='COP')
|
||||
#condition_price.set_element('/cac:AlternativeConditionPrice/cbc:PriceTypeCode', invoice_line.price.type_code)
|
||||
#condition_price.set_element('/cac:AlternativeConditionPrice/cbc:PriceType', invoice_line.price.type)
|
||||
|
||||
for subtotal in invoice_line.tax.subtotals:
|
||||
fexml.set_element_amount_for(line,
|
||||
'/cac:InvoiceLine/cac:TaxTotal/cac:TaxSubtotal/cbc:TaxableAmount',
|
||||
subtotal.taxable_amount)
|
||||
line.set_element('/cac:InvoiceLine/cac:TaxTotal/cac:TaxSubtotal/cbc:TaxAmount', subtotal.tax_amount, currencyID='COP')
|
||||
line.set_element('/cac:InvoiceLine/cac:TaxTotal/cac:TaxSubtotal/cac:TaxCategory/cbc:Percent', subtotal.percent)
|
||||
line.set_element('/cac:InvoiceLine/cac:TaxTotal/cac:TaxSubtotal/cac:TaxCategory/cac:TaxScheme/cbc:ID', subtotal.tax_scheme_ident)
|
||||
line.set_element('/cac:InvoiceLine/cac:TaxTotal/cac:TaxSubtotal/cac:TaxCategory/cac:TaxScheme/cbc:Name', subtotal.tax_scheme_name)
|
||||
line.set_element('/cac:InvoiceLine/cac:Item/cbc:Description', invoice_line.item.description)
|
||||
# TODO
|
||||
line.set_element('/cac:InvoiceLine/cac:Item/cac:StandardItemIdentification/cbc:ID', invoice_line.item.id)
|
||||
line.set_element('/cac:InvoiceLine/cac:Price/cbc:PriceAmount', invoice_line.price.amount, currencyID="COP")
|
||||
#DIAN 1.7.-2020: FBB04
|
||||
line.set_element('/cac:InvoiceLine/cac:Price/cbc:BaseQuantity', invoice_line.price.amount)
|
||||
|
||||
def attach_invoice(fexml, invoice):
|
||||
"""adiciona etiquetas a FEXML y retorna FEXML
|
||||
en caso de fallar validacion retorna None"""
|
||||
|
||||
fexml.placeholder_for('/fe:Invoice/ext:UBLExtensions')
|
||||
fexml.set_element('/fe:Invoice/cbc:UBLVersionID', 'UBL 2.1')
|
||||
fexml.set_element('/fe:Invoice/cbc:CustomizationID', invoice.invoice_operation_type)
|
||||
fexml.placeholder_for('/fe:Invoice/cbc:ProfileID')
|
||||
fexml.placeholder_for('/fe:Invoice/cbc:ProfileExecutionID')
|
||||
fexml.set_element('/fe:Invoice/cbc:ID', invoice.invoice_ident)
|
||||
fexml.placeholder_for('/fe:Invoice/cbc:UUID')
|
||||
fexml.set_element('/fe:Invoice/cbc:DocumentCurrencyCode', 'COP')
|
||||
fexml.set_element('/fe:Invoice/cbc:IssueDate', invoice.invoice_issue.strftime('%Y-%m-%d'))
|
||||
#DIAN 1.7.-2020: FAD10
|
||||
fexml.set_element('/fe:Invoice/cbc:IssueTime', invoice.invoice_issue.strftime('%H:%M:%S-05:00'))
|
||||
fexml.set_element('/fe:Invoice/cbc:InvoiceTypeCode', codelist.TipoDocumento.by_name('Factura de Venta Nacional')['code'],
|
||||
listAgencyID='195',
|
||||
listAgencyName='No matching global declaration available for the validation root',
|
||||
listURI='http://www.dian.gov.co')
|
||||
fexml.set_element('/fe:Invoice/cbc:LineCountNumeric', len(invoice.invoice_lines))
|
||||
fexml.set_element('/fe:Invoice/cac:InvoicePeriod/cbc:StartDate', invoice.invoice_period_start.strftime('%Y-%m-%d'))
|
||||
fexml.set_element('/fe:Invoice/cac:InvoicePeriod/cbc:EndDate', invoice.invoice_period_end.strftime('%Y-%m-%d'))
|
||||
|
||||
fexml.set_supplier(invoice)
|
||||
fexml.set_customer(invoice)
|
||||
fexml.set_legal_monetary(invoice)
|
||||
fexml.set_invoice_totals(invoice)
|
||||
fexml.set_invoice_lines(invoice)
|
||||
fexml.set_payment_mean(invoice)
|
||||
|
||||
return fexml
|
|
@ -0,0 +1,18 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
# This file is part of facho. The COPYRIGHT file at the top level of
|
||||
# this repository contains the full copyright notices and license terms.
|
||||
|
||||
"""Tests for `facho` package."""
|
||||
|
||||
import pytest
|
||||
|
||||
import facho.fe.form as form
|
||||
|
||||
|
||||
def test_amount_equals():
|
||||
price1 = form.Amount(110.0)
|
||||
price2 = form.Amount(100 + 10.0)
|
||||
assert price1 == price2
|
||||
assert price1 == form.Amount(100) + form.Amount(10)
|
||||
assert price1 == form.Amount(10) * form.Amount(10) + form.Amount(10)
|
|
@ -12,7 +12,7 @@ import zipfile
|
|||
|
||||
import facho.fe.form as form
|
||||
from facho import fe
|
||||
|
||||
from facho.fe.form_xml import DIANInvoiceXML
|
||||
|
||||
@pytest.fixture
|
||||
def simple_invoice_without_lines():
|
||||
|
@ -80,10 +80,10 @@ def simple_invoice():
|
|||
quantity = 1,
|
||||
description = 'producto facho',
|
||||
item = form.StandardItem('test', 9999),
|
||||
price = form.Price(100.0, '01', ''),
|
||||
price = form.Price(form.Amount(100.0), '01', ''),
|
||||
tax = form.TaxTotal(
|
||||
tax_amount = 0.0,
|
||||
taxable_amount = 0.0,
|
||||
tax_amount = form.Amount(0.0),
|
||||
taxable_amount = form.Amount(0.0),
|
||||
subtotals = [
|
||||
form.TaxSubTotal(
|
||||
percent = 19.0,
|
||||
|
@ -99,7 +99,7 @@ def test_invoicesimple_build(simple_invoice):
|
|||
|
||||
invoice_validator.validate(simple_invoice)
|
||||
assert invoice_validator.errors == []
|
||||
xml = form.DIANInvoiceXML(simple_invoice)
|
||||
xml = DIANInvoiceXML(simple_invoice)
|
||||
|
||||
supplier_name = xml.get_element_text('/fe:Invoice/cac:AccountingSupplierParty/cac:Party/cac:PartyName/cbc:Name')
|
||||
assert supplier_name == simple_invoice.invoice_supplier.name
|
||||
|
@ -111,7 +111,7 @@ def test_invoicesimple_build(simple_invoice):
|
|||
def test_invoicesimple_build_with_cufe(simple_invoice):
|
||||
invoice_validator = form.DianResolucion0001Validator()
|
||||
assert invoice_validator.validate(simple_invoice) == True
|
||||
xml = form.DIANInvoiceXML(simple_invoice)
|
||||
xml = DIANInvoiceXML(simple_invoice)
|
||||
cufe_extension = fe.DianXMLExtensionCUFE(simple_invoice)
|
||||
xml.add_extension(cufe_extension)
|
||||
cufe = xml.get_element_text('/fe:Invoice/cbc:UUID')
|
||||
|
@ -121,7 +121,7 @@ def test_invoicesimple_build_with_cufe(simple_invoice):
|
|||
def test_invoicesimple_xml_signed(monkeypatch, simple_invoice):
|
||||
invoice_validator = form.DianResolucion0001Validator()
|
||||
assert invoice_validator.validate(simple_invoice) == True
|
||||
xml = form.DIANInvoiceXML(simple_invoice)
|
||||
xml = DIANInvoiceXML(simple_invoice)
|
||||
|
||||
signer = fe.DianXMLExtensionSigner('./tests/example.p12')
|
||||
|
||||
|
@ -135,7 +135,7 @@ def test_invoicesimple_xml_signed(monkeypatch, simple_invoice):
|
|||
assert elem.text is not None
|
||||
|
||||
def test_invoicesimple_zip(simple_invoice):
|
||||
xml_invoice = form.DIANInvoiceXML(simple_invoice)
|
||||
xml_invoice = DIANInvoiceXML(simple_invoice)
|
||||
|
||||
zipdata = io.BytesIO()
|
||||
with fe.DianZIP(zipdata) as dianzip:
|
||||
|
@ -147,24 +147,24 @@ def test_invoicesimple_zip(simple_invoice):
|
|||
|
||||
|
||||
def test_bug_cbcid_empty_on_invoice_line(simple_invoice):
|
||||
xml_invoice = form.DIANInvoiceXML(simple_invoice)
|
||||
xml_invoice = DIANInvoiceXML(simple_invoice)
|
||||
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 = form.DIANInvoiceXML(simple_invoice)
|
||||
xml_invoice = DIANInvoiceXML(simple_invoice)
|
||||
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 = form.DIANInvoiceXML(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)
|
||||
assert id_ == 2
|
||||
|
||||
def test_invoice_invoice_type_code(simple_invoice):
|
||||
xml_invoice = form.DIANInvoiceXML(simple_invoice)
|
||||
xml_invoice = DIANInvoiceXML(simple_invoice)
|
||||
id_ = xml_invoice.get_element_text('/fe:Invoice/cbc:InvoiceTypeCode', format_=int)
|
||||
assert id_ == 1
|
||||
|
||||
|
@ -178,7 +178,7 @@ def test_invoice_totals(simple_invoice_without_lines):
|
|||
quantity = 1,
|
||||
description = 'producto',
|
||||
item = form.StandardItem('test', 9999),
|
||||
price = form.Price(1_500_000, '', ''),
|
||||
price = form.Price(form.Amount(1_500_000), '', ''),
|
||||
tax = form.TaxTotal(
|
||||
subtotals = [
|
||||
form.TaxSubTotal(
|
||||
|
@ -188,8 +188,8 @@ def test_invoice_totals(simple_invoice_without_lines):
|
|||
))
|
||||
simple_invoice.calculate()
|
||||
assert 1 == len(simple_invoice.invoice_lines)
|
||||
assert 1_500_000 == simple_invoice.invoice_legal_monetary_total.line_extension_amount
|
||||
assert 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
|
||||
|
@ -201,7 +201,7 @@ def test_invoice_cufe(simple_invoice_without_lines):
|
|||
quantity = 1,
|
||||
description = 'producto',
|
||||
item = form.StandardItem('test', 111),
|
||||
price = form.Price(1_500_000, '', ''),
|
||||
price = form.Price(form.Amount(1_500_000), '', ''),
|
||||
tax = form.TaxTotal(
|
||||
subtotals = [
|
||||
form.TaxSubTotal(
|
||||
|
@ -211,7 +211,7 @@ def test_invoice_cufe(simple_invoice_without_lines):
|
|||
))
|
||||
|
||||
simple_invoice.calculate()
|
||||
xml_invoice = form.DIANInvoiceXML(simple_invoice)
|
||||
xml_invoice = DIANInvoiceXML(simple_invoice)
|
||||
|
||||
cufe_extension = fe.DianXMLExtensionCUFE(
|
||||
simple_invoice,
|
||||
|
|
|
@ -20,7 +20,7 @@ def test_invoice_legalmonetary():
|
|||
description = 'producto facho',
|
||||
item = form.StandardItem('test', 9999),
|
||||
price = form.Price(
|
||||
amount = 100.0,
|
||||
amount = form.Amount(100.0),
|
||||
type_code = '01',
|
||||
type = 'x'
|
||||
),
|
||||
|
@ -33,10 +33,10 @@ def test_invoice_legalmonetary():
|
|||
)
|
||||
))
|
||||
inv.calculate()
|
||||
assert inv.invoice_legal_monetary_total.line_extension_amount == 100.0
|
||||
assert inv.invoice_legal_monetary_total.tax_exclusive_amount == 100.0
|
||||
assert inv.invoice_legal_monetary_total.tax_inclusive_amount == 119.0
|
||||
assert inv.invoice_legal_monetary_total.charge_total_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_FAU10():
|
||||
|
@ -46,7 +46,7 @@ def test_FAU10():
|
|||
description = 'producto facho',
|
||||
item = form.StandardItem('test', 9999),
|
||||
price = form.Price(
|
||||
amount = 100.0,
|
||||
amount = form.Amount(100.0),
|
||||
type_code = '01',
|
||||
type = 'x'
|
||||
),
|
||||
|
@ -58,13 +58,13 @@ def test_FAU10():
|
|||
]
|
||||
)
|
||||
))
|
||||
inv.add_allownace_charge(form.AllowanceCharge(amount=19.0))
|
||||
inv.add_allownace_charge(form.AllowanceCharge(amount=form.Amount(19.0)))
|
||||
|
||||
inv.calculate()
|
||||
assert inv.invoice_legal_monetary_total.line_extension_amount == 100.0
|
||||
assert inv.invoice_legal_monetary_total.tax_exclusive_amount == 100.0
|
||||
assert inv.invoice_legal_monetary_total.tax_inclusive_amount == 119.0
|
||||
assert inv.invoice_legal_monetary_total.charge_total_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():
|
||||
|
@ -74,7 +74,7 @@ def test_FAU14():
|
|||
description = 'producto facho',
|
||||
item = form.StandardItem('test', 9999),
|
||||
price = form.Price(
|
||||
amount = 100.0,
|
||||
amount = form.Amount(100.0),
|
||||
type_code = '01',
|
||||
type = 'x'
|
||||
),
|
||||
|
@ -86,8 +86,10 @@ def test_FAU14():
|
|||
]
|
||||
)
|
||||
))
|
||||
inv.add_allownace_charge(form.AllowanceCharge(amount=19.0))
|
||||
inv.add_prepaid_payment(form.PrePaidPayment(paid_amount = 50.0))
|
||||
inv.add_allownace_charge(form.AllowanceCharge(amount=form.Amount(19.0)))
|
||||
inv.add_prepaid_payment(form.PrePaidPayment(paid_amount = form.Amount(50.0)))
|
||||
inv.calculate()
|
||||
|
||||
assert inv.invoice_legal_monetary_total.payable_amount == 119.0 + 19.0 - 50.0
|
||||
wants = form.Amount(119.0 + 19.0 - 50.0)
|
||||
|
||||
assert inv.invoice_legal_monetary_total.payable_amount == wants, "got %s want %s" % (inv.invoice_legal_monetary_total.payable_amount, wants)
|
||||
|
|
Loading…
Reference in New Issue