se retira validador y se sustituye por asserciones de precondicion

FossilOrigin-Name: 1c360b4b29bb4fa1909ca1daae5b18cb01a56212cd84d71e7562cf700a5d7006
This commit is contained in:
bit4bit@riseup.net 2020-10-28 23:56:17 +00:00
parent b31e467bc0
commit 2aa0107579
5 changed files with 56 additions and 113 deletions

View File

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

View File

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

View File

@ -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:
@ -158,15 +173,20 @@ class TaxScheme:
code: str
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,7 +195,14 @@ 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:
percent: float
@ -209,13 +236,20 @@ 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:
DEBIT = '01'
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

View File

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

View File

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