se retira validador y se sustituye por asserciones de precondicion
FossilOrigin-Name: 1c360b4b29bb4fa1909ca1daae5b18cb01a56212cd84d71e7562cf700a5d7006
This commit is contained in:
parent
b31e467bc0
commit
2aa0107579
@ -259,15 +259,6 @@ def generate_invoice(private_key, passphrase, scriptname, generate=False, ssl=Tr
|
||||
invoice = module.invoice()
|
||||
invoice.calculate()
|
||||
|
||||
try:
|
||||
validator = module.validator()
|
||||
except AttributeError:
|
||||
validator = form.DianResolucion0001Validator()
|
||||
|
||||
if not validator.validate(invoice):
|
||||
for error in validator.errors:
|
||||
print("ERROR:", error)
|
||||
|
||||
if generate:
|
||||
xml = invoice_xml(invoice)
|
||||
|
||||
|
@ -93,3 +93,4 @@ TipoOperacionF = CodeList(path_for_codelist('TipoOperacionF-2.1.gc'), 'code', 'n
|
||||
.update(CodeList(path_for_codelist('TipoOperacionF-2.1.custom.gc'), 'code', 'name'))
|
||||
Municipio = CodeList(path_for_codelist('Municipio-2.1.gc'), 'code', 'name')
|
||||
Departamento = CodeList(path_for_codelist('Departamentos-2.1.gc'), 'code', 'name')
|
||||
Paises = CodeList(path_for_codelist('Paises-2.1.gc'), 'code', 'name')
|
||||
|
137
facho/fe/form.py
137
facho/fe/form.py
@ -9,7 +9,7 @@ from datetime import datetime
|
||||
from collections import defaultdict
|
||||
import decimal
|
||||
from decimal import Decimal
|
||||
|
||||
import typing
|
||||
|
||||
from .data.dian import codelist
|
||||
|
||||
@ -105,25 +105,40 @@ class StandardItem(Item):
|
||||
@dataclass
|
||||
class Country:
|
||||
code: str
|
||||
name: str
|
||||
name: str = ''
|
||||
|
||||
def __post_init__(self):
|
||||
if self.code not in codelist.Paises:
|
||||
raise ValueError("code [%s] not found" % (self.code))
|
||||
self.name = codelist.Paises[self.code]['name']
|
||||
|
||||
@dataclass
|
||||
class CountrySubentity:
|
||||
code: str
|
||||
name: str
|
||||
name: str = ''
|
||||
|
||||
def __post_init__(self):
|
||||
if self.code not in codelist.Departamento:
|
||||
raise ValueError("code [%s] not found" % (self.code))
|
||||
self.name = codelist.Departamento[self.code]['name']
|
||||
|
||||
@dataclass
|
||||
class City:
|
||||
code: str
|
||||
name: str
|
||||
name: str = ''
|
||||
|
||||
def __post_init__(self):
|
||||
if self.code not in codelist.Municipio:
|
||||
raise ValueError("code [%s] not found" % (self.code))
|
||||
self.name = codelist.Municipio[self.code]['name']
|
||||
|
||||
@dataclass
|
||||
class Address:
|
||||
name: str
|
||||
street: str = ''
|
||||
city: City = City('', '')
|
||||
country: Country = Country('CO', 'Colombia')
|
||||
countrysubentity: CountrySubentity = CountrySubentity('', '')
|
||||
city: City = City('05001')
|
||||
country: Country = Country('CO')
|
||||
countrysubentity: CountrySubentity = CountrySubentity('05')
|
||||
|
||||
@dataclass
|
||||
class PartyIdentification:
|
||||
@ -159,14 +174,19 @@ class TaxScheme:
|
||||
name: str = ''
|
||||
|
||||
|
||||
def __post_init__(self):
|
||||
if self.code not in codelist.TipoImpuesto:
|
||||
raise ValueError("code not found")
|
||||
self.name = codelist.TipoImpuesto[self.code]['name']
|
||||
|
||||
@dataclass
|
||||
class Party:
|
||||
name: str
|
||||
ident: str
|
||||
responsability_code: str
|
||||
responsability_code: typing.List[Responsability]
|
||||
responsability_regime_code: str
|
||||
organization_code: str
|
||||
tax_scheme: TaxScheme = TaxScheme('')
|
||||
tax_scheme: TaxScheme = TaxScheme('01')
|
||||
|
||||
phone: str = ''
|
||||
address: Address = Address('')
|
||||
@ -175,6 +195,13 @@ class Party:
|
||||
legal_company_ident: str = ''
|
||||
legal_address: str = ''
|
||||
|
||||
def __post_init__(self):
|
||||
if self.organization_code not in codelist.TipoOrganizacion:
|
||||
raise ValueError("organization_code not found")
|
||||
|
||||
for code in self.responsability_code:
|
||||
if code not in codelist.TipoResponsabilidad:
|
||||
raise ValueError("responsability_code %s not found" % (code))
|
||||
|
||||
@dataclass
|
||||
class TaxSubTotal:
|
||||
@ -209,6 +236,10 @@ class Price:
|
||||
type_code: str
|
||||
type: str
|
||||
|
||||
def __post_init__(self):
|
||||
if self.type_code not in codelist.CodigoPrecioReferencia:
|
||||
raise ValueError("type_code [%s] not found" % (self.type_code))
|
||||
|
||||
|
||||
@dataclass
|
||||
class PaymentMean:
|
||||
@ -216,6 +247,9 @@ class PaymentMean:
|
||||
CREDIT = '02'
|
||||
|
||||
def __init__(self, id: str, code: str, due_at: datetime, payment_id: str):
|
||||
if code not in codelist.MediosPago:
|
||||
raise ValueError("code not found")
|
||||
|
||||
self.id = id
|
||||
self.code = code
|
||||
self.due_at = due_at
|
||||
@ -325,6 +359,9 @@ class Invoice:
|
||||
self.invoice_period_end = enddate
|
||||
|
||||
def set_issue(self, dtime: datetime):
|
||||
if dtime.tzname() not in ['UTC-05:00', '-05', None]:
|
||||
raise ValueError("dtime must be UTC-05:00")
|
||||
|
||||
self.invoice_issue = dtime
|
||||
|
||||
def set_ident(self, ident: str):
|
||||
@ -340,6 +377,9 @@ class Invoice:
|
||||
self.invoice_payment_mean = payment_mean
|
||||
|
||||
def set_operation_type(self, operation):
|
||||
if operation not in codelist.TipoOperacionF:
|
||||
raise ValueError("operation not found")
|
||||
|
||||
self.invoice_operation_type = operation
|
||||
|
||||
def add_allownace_charge(self, charge: AllowanceCharge):
|
||||
@ -399,82 +439,3 @@ class Invoice:
|
||||
for invline in self.invoice_lines:
|
||||
invline.calculate()
|
||||
self._calculate_legal_monetary_total()
|
||||
|
||||
|
||||
class DianResolucion0001Validator:
|
||||
|
||||
def __init__(self):
|
||||
self.errors = []
|
||||
|
||||
def _validate_party(self, model, party):
|
||||
for code in party.responsability_code:
|
||||
if code not in codelist.TipoResponsabilidad:
|
||||
self.errors.append((model,
|
||||
'responsability_code',
|
||||
'not found %s' % (code)))
|
||||
|
||||
try:
|
||||
codelist.TipoOrganizacion[party.organization_code]
|
||||
except KeyError:
|
||||
self.errors.append((model, 'organization_code' ,
|
||||
'not found %s' % (party.organization_code)))
|
||||
try:
|
||||
if isinstance(party.tax_scheme, (str, str)):
|
||||
codelist.TipoImpuesto[party.tax_scheme.code]
|
||||
except KeyError:
|
||||
self.errors.append((model , 'tax_scheme' ,
|
||||
'not found %s' % (party.tax_scheme)))
|
||||
try:
|
||||
codelist.Departamento[party.address.countrysubentity.code]
|
||||
except KeyError:
|
||||
self.errors.append((model, 'countrysubentity_code',
|
||||
'not found %s' % (party.address.countrysubentity.code)))
|
||||
try:
|
||||
codelist.Municipio[party.address.city.code]
|
||||
except KeyError:
|
||||
self.errors.append((model, 'city_code',
|
||||
'not found %s' % (party.address.city.code)))
|
||||
|
||||
def _validate_invoice(self, invoice):
|
||||
try:
|
||||
codelist.TipoOperacionF[invoice.invoice_operation_type]
|
||||
except KeyError:
|
||||
self.errors.append(('invoice', 'operation_type',
|
||||
'not found %s' % (invoice.invoice_operation_type)))
|
||||
|
||||
# MACHETE se espera en zona horario colombia
|
||||
if invoice.invoice_issue.tzname() not in ['UTC-05:00', '-05', None]:
|
||||
self.errors.append(('invoice', 'invoice_issue',
|
||||
'expected timezone UTC-05:00 or -05 or empty got %s' % (invoice.invoice_issue.tzname())))
|
||||
|
||||
def validate(self, invoice):
|
||||
invoice.accept(self)
|
||||
self._validate_invoice(invoice)
|
||||
|
||||
return not self.errors
|
||||
|
||||
def visit_payment_mean(self, mean):
|
||||
try:
|
||||
codelist.MediosPago[mean.code]
|
||||
except KeyError:
|
||||
self.errors.append(('payment_mean', 'code',
|
||||
'not found %s' % (mean.code)))
|
||||
|
||||
def visit_customer(self, customer):
|
||||
self._validate_party('customer', customer)
|
||||
|
||||
def visit_supplier(self, supplier):
|
||||
self._validate_party('supplier', supplier)
|
||||
|
||||
def visit_payment(self, payment):
|
||||
pass
|
||||
|
||||
def visit_invoice_line(self, line):
|
||||
try:
|
||||
codelist.CodigoPrecioReferencia[line.price.type_code]
|
||||
except KeyError:
|
||||
self.errors.append(('invoice_line', 'line.price',
|
||||
'not found %s' % (line.price.type_code)))
|
||||
|
||||
def valid(self):
|
||||
return not self.errors
|
||||
|
@ -19,3 +19,6 @@ def test_tipoorganizacion():
|
||||
def test_tipodocumento():
|
||||
assert codelist.TipoDocumento.short_name == 'TipoDocumento'
|
||||
assert codelist.TipoDocumento.by_name('Factura de Venta Nacional')['code'] == '01'
|
||||
|
||||
def test_departamento():
|
||||
assert codelist.Departamento['05']['name'] == 'Antioquia'
|
||||
|
@ -95,10 +95,6 @@ def simple_invoice():
|
||||
|
||||
|
||||
def test_invoicesimple_build(simple_invoice):
|
||||
invoice_validator = form.DianResolucion0001Validator()
|
||||
|
||||
invoice_validator.validate(simple_invoice)
|
||||
assert invoice_validator.errors == []
|
||||
xml = DIANInvoiceXML(simple_invoice)
|
||||
|
||||
supplier_name = xml.get_element_text('/fe:Invoice/cac:AccountingSupplierParty/cac:Party/cac:PartyName/cbc:Name')
|
||||
@ -109,8 +105,6 @@ 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 = DIANInvoiceXML(simple_invoice)
|
||||
cufe_extension = fe.DianXMLExtensionCUFE(simple_invoice)
|
||||
xml.add_extension(cufe_extension)
|
||||
@ -119,8 +113,6 @@ 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 = DIANInvoiceXML(simple_invoice)
|
||||
|
||||
signer = fe.DianXMLExtensionSigner('./tests/example.p12')
|
||||
@ -178,7 +170,7 @@ def test_invoice_totals(simple_invoice_without_lines):
|
||||
quantity = 1,
|
||||
description = 'producto',
|
||||
item = form.StandardItem('test', 9999),
|
||||
price = form.Price(form.Amount(1_500_000), '', ''),
|
||||
price = form.Price(form.Amount(1_500_000), '01', ''),
|
||||
tax = form.TaxTotal(
|
||||
subtotals = [
|
||||
form.TaxSubTotal(
|
||||
@ -201,7 +193,7 @@ def test_invoice_cufe(simple_invoice_without_lines):
|
||||
quantity = 1,
|
||||
description = 'producto',
|
||||
item = form.StandardItem('test', 111),
|
||||
price = form.Price(form.Amount(1_500_000), '', ''),
|
||||
price = form.Price(form.Amount(1_500_000), '01', ''),
|
||||
tax = form.TaxTotal(
|
||||
subtotals = [
|
||||
form.TaxSubTotal(
|
||||
@ -254,8 +246,3 @@ def test_invoice_cufe(simple_invoice_without_lines):
|
||||
cufe = xml_invoice.get_element_text('/fe:Invoice/cbc:UUID')
|
||||
# RESOLUCION 004: pagina 689
|
||||
assert cufe == '8bb918b19ba22a694f1da11c643b5e9de39adf60311cf179179e9b33381030bcd4c3c3f156c506ed5908f9276f5bd9b4'
|
||||
|
||||
|
||||
def test_invoice_payment_mean(monkeypatch, simple_invoice):
|
||||
invoice_validator = form.DianResolucion0001Validator()
|
||||
assert invoice_validator.validate(simple_invoice) == True
|
||||
|
Loading…
Reference in New Issue
Block a user