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.

FossilOrigin-Name: 714687a7a825715d272392d361de5e42d7c25d6078ec68a81df653f8843c37a3
This commit is contained in:
bit4bit@riseup.net 2020-10-22 02:09:47 +00:00
parent ae91c2e68d
commit 8c7302d8a2
7 changed files with 561 additions and 444 deletions

View File

@ -259,6 +259,7 @@ def generate_invoice(private_key, passphrase, scriptname, generate=False, ssl=Tr
spec.loader.exec_module(module) spec.loader.exec_module(module)
import facho.fe.form as form import facho.fe.form as form
from facho.fe.form_xml import DIANInvoiceXML
from facho import fe from facho import fe
invoice = module.invoice() invoice = module.invoice()
@ -269,7 +270,7 @@ def generate_invoice(private_key, passphrase, scriptname, generate=False, ssl=Tr
print("ERROR:", error) print("ERROR:", error)
if generate: if generate:
xml = form.DIANInvoiceXML(invoice) xml = DIANInvoiceXML(invoice)
extensions = module.extensions(invoice) extensions = module.extensions(invoice)
for extension in extensions: for extension in extensions:

View File

@ -12,6 +12,7 @@ import warnings
import hashlib import hashlib
from contextlib import contextmanager from contextlib import contextmanager
from .data.dian import codelist from .data.dian import codelist
from . import form
SCHEME_AGENCY_ATTRS = { SCHEME_AGENCY_ATTRS = {
'schemeAgencyName': 'CO, DIAN (Dirección de Impuestos y Aduanas Nacionales)', '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 ValorBruto = invoice.invoice_legal_monetary_total.line_extension_amount
ValorTotalPagar = invoice.invoice_legal_monetary_total.payable_amount ValorTotalPagar = invoice.invoice_legal_monetary_total.payable_amount
ValorImpuestoPara = {} ValorImpuestoPara = {}
ValorImpuesto1 = 0.0
CodImpuesto1 = 1 CodImpuesto1 = 1
ValorImpuesto2 = 0.0
CodImpuesto2 = 4 CodImpuesto2 = 4
ValorImpuesto3 = 0.0
CodImpuesto3 = 3 CodImpuesto3 = 3
for invoice_line in invoice.invoice_lines: for invoice_line in invoice.invoice_lines:
for subtotal in invoice_line.tax.subtotals: for subtotal in invoice_line.tax.subtotals:
# TODO cual es la naturaleza de tax_scheme_ident? # TODO cual es la naturaleza de tax_scheme_ident?
codigo_impuesto = int(subtotal.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 ValorImpuestoPara[codigo_impuesto] += subtotal.tax_amount
NitOFE = invoice.invoice_supplier.ident NitOFE = invoice.invoice_supplier.ident
@ -134,14 +132,14 @@ class DianXMLExtensionCUFE(FachoXMLExtension):
'%s' % NumFac, '%s' % NumFac,
'%s' % FecFac, '%s' % FecFac,
'%s' % HoraFac, '%s' % HoraFac,
'%.02f' % ValorBruto, '%.02f' % round(ValorBruto, 2),
'%02d' % CodImpuesto1, '%02d' % CodImpuesto1,
'%.02f' % ValorImpuestoPara.get(CodImpuesto1, 0.0), '%.02f' % round(ValorImpuestoPara.get(CodImpuesto1, 0.0), 2),
'%02d' % CodImpuesto2, '%02d' % CodImpuesto2,
'%.02f' % ValorImpuestoPara.get(CodImpuesto2, 0.0), '%.02f' % round(ValorImpuestoPara.get(CodImpuesto2, 0.0), 2),
'%02d' % CodImpuesto3, '%02d' % CodImpuesto3,
'%.02f' % ValorImpuestoPara.get(CodImpuesto3, 0.0), '%.02f' % round(ValorImpuestoPara.get(CodImpuesto3, 0.0), 2),
'%.02f' % ValorTotalPagar, '%.02f' % round(ValorTotalPagar, 2),
'%s' % NitOFE, '%s' % NitOFE,
'%s' % NumAdq, '%s' % NumAdq,
'%s' % ClTec, '%s' % ClTec,

View File

@ -7,10 +7,89 @@ import copy
from dataclasses import dataclass from dataclasses import dataclass
from datetime import datetime from datetime import datetime
from collections import defaultdict from collections import defaultdict
import decimal
from decimal import Decimal
from .data.dian import codelist 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 @dataclass
class Item: class Item:
@ -97,19 +176,19 @@ class TaxSubTotal:
tax_scheme_ident: str = '01' tax_scheme_ident: str = '01'
tax_scheme_name: str = 'IVA' tax_scheme_name: str = 'IVA'
tax_amount: float = 0.0 tax_amount: Amount = Amount(0.0)
taxable_amount: float = 0.0 taxable_amount: Amount = Amount(0.0)
def calculate(self, invline): 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 self.taxable_amount = invline.total_amount
@dataclass @dataclass
class TaxTotal: class TaxTotal:
subtotals: list subtotals: list
tax_amount: float = 0.0 tax_amount: Amount = Amount(0.0)
taxable_amount: float = 0.0 taxable_amount: Amount = Amount(0.0)
def calculate(self, invline): def calculate(self, invline):
for subtax in self.subtotals: for subtax in self.subtotals:
@ -120,7 +199,7 @@ class TaxTotal:
@dataclass @dataclass
class Price: class Price:
amount: float amount: Amount
type_code: str type_code: str
type: str type: str
@ -140,7 +219,7 @@ class PaymentMean:
@dataclass @dataclass
class PrePaidPayment: class PrePaidPayment:
#DIAN 1.7.-2020: FBD03 #DIAN 1.7.-2020: FBD03
paid_amount: float = 0.0 paid_amount: Amount = Amount(0.0)
@dataclass @dataclass
@ -158,7 +237,7 @@ class InvoiceLine:
@property @property
def total_amount(self): def total_amount(self):
return self.quantity * self.price.amount return Amount(self.quantity) * self.price.amount
@property @property
def total_tax_inclusive_amount(self): def total_tax_inclusive_amount(self):
@ -182,19 +261,19 @@ class InvoiceLine:
@dataclass @dataclass
class LegalMonetaryTotal: class LegalMonetaryTotal:
line_extension_amount: float = 0.0 line_extension_amount: Amount = Amount(0.0)
tax_exclusive_amount: float = 0.0 tax_exclusive_amount: Amount = Amount(0.0)
tax_inclusive_amount: float = 0.0 tax_inclusive_amount: Amount = Amount(0.0)
charge_total_amount: float = 0.0 charge_total_amount: Amount = Amount(0.0)
allowance_total_amount: float = 0.0 allowance_total_amount: Amount = Amount(0.0)
payable_amount: float = 0.0 payable_amount: Amount = Amount(0.0)
prepaid_amount: float = 0.0 prepaid_amount: Amount = Amount(0.0)
@dataclass @dataclass
class AllowanceCharge: class AllowanceCharge:
#DIAN 1.7.-2020: FAQ03 #DIAN 1.7.-2020: FAQ03
charge_indicator: bool = True charge_indicator: bool = True
amount: float = 0.0 amount: Amount = Amount(0.0)
def isCharge(self): def isCharge(self):
return self.charge_indicator == True return self.charge_indicator == True
@ -217,7 +296,7 @@ class Invoice:
self.invoice_issue = None self.invoice_issue = None
self.invoice_ident = None self.invoice_ident = None
self.invoice_operation_type = 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_customer = None
self.invoice_supplier = None self.invoice_supplier = None
self.invoice_payment_mean = 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 self.invoice_legal_monetary_total.tax_inclusive_amount += invline.total_tax_inclusive_amount
#DIAN 1.7.-2020: FAU08 #DIAN 1.7.-2020: FAU08
allownaces = filter(lambda charge: charge.isDiscount(), self.invoice_allowance_charge) self.invoice_legal_monetary_total.allowance_total_amount = AmountCollection(self.invoice_allowance_charge)\
amounts_allowance = map(lambda charge: charge.amount, allownaces) .filter(lambda charge: charge.isDiscount())\
self.invoice_legal_monetary_total.allowance_total_amount = sum(amounts_allowance) .map(lambda charge: charge.amount)\
.sum()
#DIAN 1.7.-2020: FAU10 #DIAN 1.7.-2020: FAU10
allowance_charges = filter(lambda charge: charge.isCharge(), self.invoice_allowance_charge) self.invoice_legal_monetary_total.charge_total_amount = AmountCollection(self.invoice_allowance_charge)\
amounts_allowance_charge = map(lambda charge: charge.amount, allowance_charges) .filter(lambda charge: charge.isCharge())\
self.invoice_legal_monetary_total.charge_total_amount = sum(amounts_allowance_charge) .map(lambda charge: charge.amount)\
.sum()
#DIAN 1.7.-2020: FAU12 #DIAN 1.7.-2020: FAU12
amounts_prepaid = map(lambda paid: paid.paid_amount, self.invoice_prepaid_payment) self.invoice_legal_monetary_total.prepaid_amount = AmountCollection(self.invoice_prepaid_payment)\
self.invoice_legal_monetary_total.prepaid_amount = sum(amounts_prepaid) .map(lambda paid: paid.paid_amount)\
.sum()
#DIAN 1.7.-2020: FAU14 #DIAN 1.7.-2020: FAU14
self.invoice_legal_monetary_total.payable_amount = \ 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.charge_total_amount \
- self.invoice_legal_monetary_total.prepaid_amount - self.invoice_legal_monetary_total.prepaid_amount
def calculate(self): def calculate(self):
for invline in self.invoice_lines: for invline in self.invoice_lines:
invline.calculate() invline.calculate()
@ -371,379 +454,3 @@ class DianResolucion0001Validator:
def valid(self): def valid(self):
return not self.errors 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

391
facho/fe/form_xml.py Normal file
View File

@ -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

18
tests/test_amount.py Normal file
View File

@ -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)

View File

@ -12,7 +12,7 @@ import zipfile
import facho.fe.form as form import facho.fe.form as form
from facho import fe from facho import fe
from facho.fe.form_xml import DIANInvoiceXML
@pytest.fixture @pytest.fixture
def simple_invoice_without_lines(): def simple_invoice_without_lines():
@ -80,10 +80,10 @@ def simple_invoice():
quantity = 1, quantity = 1,
description = 'producto facho', description = 'producto facho',
item = form.StandardItem('test', 9999), item = form.StandardItem('test', 9999),
price = form.Price(100.0, '01', ''), price = form.Price(form.Amount(100.0), '01', ''),
tax = form.TaxTotal( tax = form.TaxTotal(
tax_amount = 0.0, tax_amount = form.Amount(0.0),
taxable_amount = 0.0, taxable_amount = form.Amount(0.0),
subtotals = [ subtotals = [
form.TaxSubTotal( form.TaxSubTotal(
percent = 19.0, percent = 19.0,
@ -99,7 +99,7 @@ def test_invoicesimple_build(simple_invoice):
invoice_validator.validate(simple_invoice) invoice_validator.validate(simple_invoice)
assert invoice_validator.errors == [] 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') supplier_name = xml.get_element_text('/fe:Invoice/cac:AccountingSupplierParty/cac:Party/cac:PartyName/cbc:Name')
assert supplier_name == simple_invoice.invoice_supplier.name assert supplier_name == simple_invoice.invoice_supplier.name
@ -111,7 +111,7 @@ def test_invoicesimple_build(simple_invoice):
def test_invoicesimple_build_with_cufe(simple_invoice): def test_invoicesimple_build_with_cufe(simple_invoice):
invoice_validator = form.DianResolucion0001Validator() invoice_validator = form.DianResolucion0001Validator()
assert invoice_validator.validate(simple_invoice) == True assert invoice_validator.validate(simple_invoice) == True
xml = form.DIANInvoiceXML(simple_invoice) xml = DIANInvoiceXML(simple_invoice)
cufe_extension = fe.DianXMLExtensionCUFE(simple_invoice) cufe_extension = fe.DianXMLExtensionCUFE(simple_invoice)
xml.add_extension(cufe_extension) xml.add_extension(cufe_extension)
cufe = xml.get_element_text('/fe:Invoice/cbc:UUID') 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): def test_invoicesimple_xml_signed(monkeypatch, simple_invoice):
invoice_validator = form.DianResolucion0001Validator() invoice_validator = form.DianResolucion0001Validator()
assert invoice_validator.validate(simple_invoice) == True assert invoice_validator.validate(simple_invoice) == True
xml = form.DIANInvoiceXML(simple_invoice) xml = DIANInvoiceXML(simple_invoice)
signer = fe.DianXMLExtensionSigner('./tests/example.p12') signer = fe.DianXMLExtensionSigner('./tests/example.p12')
@ -135,7 +135,7 @@ def test_invoicesimple_xml_signed(monkeypatch, simple_invoice):
assert elem.text is not None assert elem.text is not None
def test_invoicesimple_zip(simple_invoice): def test_invoicesimple_zip(simple_invoice):
xml_invoice = form.DIANInvoiceXML(simple_invoice) xml_invoice = DIANInvoiceXML(simple_invoice)
zipdata = io.BytesIO() zipdata = io.BytesIO()
with fe.DianZIP(zipdata) as dianzip: 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): 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) cbc_id = xml_invoice.get_element_text('/fe:Invoice/cac:InvoiceLine[1]/cbc:ID', format_=int)
assert cbc_id == 1 assert cbc_id == 1
def test_invoice_line_count_numeric(simple_invoice): def test_invoice_line_count_numeric(simple_invoice):
xml_invoice = form.DIANInvoiceXML(simple_invoice) xml_invoice = DIANInvoiceXML(simple_invoice)
count = xml_invoice.get_element_text('/fe:Invoice/cbc:LineCountNumeric', format_=int) count = xml_invoice.get_element_text('/fe:Invoice/cbc:LineCountNumeric', format_=int)
assert count == len(simple_invoice.invoice_lines) assert count == len(simple_invoice.invoice_lines)
def test_invoice_profileexecutionid(simple_invoice): def test_invoice_profileexecutionid(simple_invoice):
xml_invoice = form.DIANInvoiceXML(simple_invoice) xml_invoice = DIANInvoiceXML(simple_invoice)
cufe_extension = fe.DianXMLExtensionCUFE(simple_invoice) cufe_extension = fe.DianXMLExtensionCUFE(simple_invoice)
xml_invoice.add_extension(cufe_extension) xml_invoice.add_extension(cufe_extension)
id_ = xml_invoice.get_element_text('/fe:Invoice/cbc:ProfileExecutionID', format_=int) id_ = xml_invoice.get_element_text('/fe:Invoice/cbc:ProfileExecutionID', format_=int)
assert id_ == 2 assert id_ == 2
def test_invoice_invoice_type_code(simple_invoice): def test_invoice_invoice_type_code(simple_invoice):
xml_invoice = form.DIANInvoiceXML(simple_invoice) xml_invoice = DIANInvoiceXML(simple_invoice)
id_ = xml_invoice.get_element_text('/fe:Invoice/cbc:InvoiceTypeCode', format_=int) id_ = xml_invoice.get_element_text('/fe:Invoice/cbc:InvoiceTypeCode', format_=int)
assert id_ == 1 assert id_ == 1
@ -178,7 +178,7 @@ def test_invoice_totals(simple_invoice_without_lines):
quantity = 1, quantity = 1,
description = 'producto', description = 'producto',
item = form.StandardItem('test', 9999), item = form.StandardItem('test', 9999),
price = form.Price(1_500_000, '', ''), price = form.Price(form.Amount(1_500_000), '', ''),
tax = form.TaxTotal( tax = form.TaxTotal(
subtotals = [ subtotals = [
form.TaxSubTotal( form.TaxSubTotal(
@ -188,8 +188,8 @@ def test_invoice_totals(simple_invoice_without_lines):
)) ))
simple_invoice.calculate() simple_invoice.calculate()
assert 1 == len(simple_invoice.invoice_lines) assert 1 == len(simple_invoice.invoice_lines)
assert 1_500_000 == simple_invoice.invoice_legal_monetary_total.line_extension_amount assert form.Amount(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_785_000) == simple_invoice.invoice_legal_monetary_total.payable_amount
def test_invoice_cufe(simple_invoice_without_lines): def test_invoice_cufe(simple_invoice_without_lines):
simple_invoice = simple_invoice_without_lines simple_invoice = simple_invoice_without_lines
@ -201,7 +201,7 @@ def test_invoice_cufe(simple_invoice_without_lines):
quantity = 1, quantity = 1,
description = 'producto', description = 'producto',
item = form.StandardItem('test', 111), item = form.StandardItem('test', 111),
price = form.Price(1_500_000, '', ''), price = form.Price(form.Amount(1_500_000), '', ''),
tax = form.TaxTotal( tax = form.TaxTotal(
subtotals = [ subtotals = [
form.TaxSubTotal( form.TaxSubTotal(
@ -211,7 +211,7 @@ def test_invoice_cufe(simple_invoice_without_lines):
)) ))
simple_invoice.calculate() simple_invoice.calculate()
xml_invoice = form.DIANInvoiceXML(simple_invoice) xml_invoice = DIANInvoiceXML(simple_invoice)
cufe_extension = fe.DianXMLExtensionCUFE( cufe_extension = fe.DianXMLExtensionCUFE(
simple_invoice, simple_invoice,

View File

@ -20,7 +20,7 @@ def test_invoice_legalmonetary():
description = 'producto facho', description = 'producto facho',
item = form.StandardItem('test', 9999), item = form.StandardItem('test', 9999),
price = form.Price( price = form.Price(
amount = 100.0, amount = form.Amount(100.0),
type_code = '01', type_code = '01',
type = 'x' type = 'x'
), ),
@ -33,10 +33,10 @@ def test_invoice_legalmonetary():
) )
)) ))
inv.calculate() inv.calculate()
assert inv.invoice_legal_monetary_total.line_extension_amount == 100.0 assert inv.invoice_legal_monetary_total.line_extension_amount == form.Amount(100.0)
assert inv.invoice_legal_monetary_total.tax_exclusive_amount == 100.0 assert inv.invoice_legal_monetary_total.tax_exclusive_amount == form.Amount(100.0)
assert inv.invoice_legal_monetary_total.tax_inclusive_amount == 119.0 assert inv.invoice_legal_monetary_total.tax_inclusive_amount == form.Amount(119.0)
assert inv.invoice_legal_monetary_total.charge_total_amount == 0.0 assert inv.invoice_legal_monetary_total.charge_total_amount == form.Amount(0.0)
def test_FAU10(): def test_FAU10():
@ -46,7 +46,7 @@ def test_FAU10():
description = 'producto facho', description = 'producto facho',
item = form.StandardItem('test', 9999), item = form.StandardItem('test', 9999),
price = form.Price( price = form.Price(
amount = 100.0, amount = form.Amount(100.0),
type_code = '01', type_code = '01',
type = 'x' 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() inv.calculate()
assert inv.invoice_legal_monetary_total.line_extension_amount == 100.0 assert inv.invoice_legal_monetary_total.line_extension_amount == form.Amount(100.0)
assert inv.invoice_legal_monetary_total.tax_exclusive_amount == 100.0 assert inv.invoice_legal_monetary_total.tax_exclusive_amount == form.Amount(100.0)
assert inv.invoice_legal_monetary_total.tax_inclusive_amount == 119.0 assert inv.invoice_legal_monetary_total.tax_inclusive_amount == form.Amount(119.0)
assert inv.invoice_legal_monetary_total.charge_total_amount == 19.0 assert inv.invoice_legal_monetary_total.charge_total_amount == form.Amount(19.0)
def test_FAU14(): def test_FAU14():
@ -74,7 +74,7 @@ def test_FAU14():
description = 'producto facho', description = 'producto facho',
item = form.StandardItem('test', 9999), item = form.StandardItem('test', 9999),
price = form.Price( price = form.Price(
amount = 100.0, amount = form.Amount(100.0),
type_code = '01', type_code = '01',
type = 'x' type = 'x'
), ),
@ -86,8 +86,10 @@ def test_FAU14():
] ]
) )
)) ))
inv.add_allownace_charge(form.AllowanceCharge(amount=19.0)) inv.add_allownace_charge(form.AllowanceCharge(amount=form.Amount(19.0)))
inv.add_prepaid_payment(form.PrePaidPayment(paid_amount = 50.0)) inv.add_prepaid_payment(form.PrePaidPayment(paid_amount = form.Amount(50.0)))
inv.calculate() 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)