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 = module.invoice()
invoice.calculate() 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: if generate:
xml = invoice_xml(invoice) 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')) .update(CodeList(path_for_codelist('TipoOperacionF-2.1.custom.gc'), 'code', 'name'))
Municipio = CodeList(path_for_codelist('Municipio-2.1.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') 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 from collections import defaultdict
import decimal import decimal
from decimal import Decimal from decimal import Decimal
import typing
from .data.dian import codelist from .data.dian import codelist
@ -105,25 +105,40 @@ class StandardItem(Item):
@dataclass @dataclass
class Country: class Country:
code: str 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 @dataclass
class CountrySubentity: class CountrySubentity:
code: str 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 @dataclass
class City: class City:
code: str 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 @dataclass
class Address: class Address:
name: str name: str
street: str = '' street: str = ''
city: City = City('', '') city: City = City('05001')
country: Country = Country('CO', 'Colombia') country: Country = Country('CO')
countrysubentity: CountrySubentity = CountrySubentity('', '') countrysubentity: CountrySubentity = CountrySubentity('05')
@dataclass @dataclass
class PartyIdentification: class PartyIdentification:
@ -158,15 +173,20 @@ class TaxScheme:
code: str code: str
name: 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 @dataclass
class Party: class Party:
name: str name: str
ident: str ident: str
responsability_code: str responsability_code: typing.List[Responsability]
responsability_regime_code: str responsability_regime_code: str
organization_code: str organization_code: str
tax_scheme: TaxScheme = TaxScheme('') tax_scheme: TaxScheme = TaxScheme('01')
phone: str = '' phone: str = ''
address: Address = Address('') address: Address = Address('')
@ -175,7 +195,14 @@ class Party:
legal_company_ident: str = '' legal_company_ident: str = ''
legal_address: 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 @dataclass
class TaxSubTotal: class TaxSubTotal:
percent: float percent: float
@ -209,13 +236,20 @@ class Price:
type_code: str type_code: str
type: 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 @dataclass
class PaymentMean: class PaymentMean:
DEBIT = '01' DEBIT = '01'
CREDIT = '02' CREDIT = '02'
def __init__(self, id: str, code: str, due_at: datetime, payment_id: str): 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.id = id
self.code = code self.code = code
self.due_at = due_at self.due_at = due_at
@ -325,6 +359,9 @@ class Invoice:
self.invoice_period_end = enddate self.invoice_period_end = enddate
def set_issue(self, dtime: datetime): 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 self.invoice_issue = dtime
def set_ident(self, ident: str): def set_ident(self, ident: str):
@ -340,6 +377,9 @@ class Invoice:
self.invoice_payment_mean = payment_mean self.invoice_payment_mean = payment_mean
def set_operation_type(self, operation): def set_operation_type(self, operation):
if operation not in codelist.TipoOperacionF:
raise ValueError("operation not found")
self.invoice_operation_type = operation self.invoice_operation_type = operation
def add_allownace_charge(self, charge: AllowanceCharge): def add_allownace_charge(self, charge: AllowanceCharge):
@ -399,82 +439,3 @@ class Invoice:
for invline in self.invoice_lines: for invline in self.invoice_lines:
invline.calculate() invline.calculate()
self._calculate_legal_monetary_total() 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(): def test_tipodocumento():
assert codelist.TipoDocumento.short_name == 'TipoDocumento' assert codelist.TipoDocumento.short_name == 'TipoDocumento'
assert codelist.TipoDocumento.by_name('Factura de Venta Nacional')['code'] == '01' 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): def test_invoicesimple_build(simple_invoice):
invoice_validator = form.DianResolucion0001Validator()
invoice_validator.validate(simple_invoice)
assert invoice_validator.errors == []
xml = 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')
@ -109,8 +105,6 @@ 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()
assert invoice_validator.validate(simple_invoice) == True
xml = 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)
@ -119,8 +113,6 @@ 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()
assert invoice_validator.validate(simple_invoice) == True
xml = DIANInvoiceXML(simple_invoice) xml = DIANInvoiceXML(simple_invoice)
signer = fe.DianXMLExtensionSigner('./tests/example.p12') signer = fe.DianXMLExtensionSigner('./tests/example.p12')
@ -178,7 +170,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(form.Amount(1_500_000), '', ''), price = form.Price(form.Amount(1_500_000), '01', ''),
tax = form.TaxTotal( tax = form.TaxTotal(
subtotals = [ subtotals = [
form.TaxSubTotal( form.TaxSubTotal(
@ -201,7 +193,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(form.Amount(1_500_000), '', ''), price = form.Price(form.Amount(1_500_000), '01', ''),
tax = form.TaxTotal( tax = form.TaxTotal(
subtotals = [ subtotals = [
form.TaxSubTotal( form.TaxSubTotal(
@ -254,8 +246,3 @@ def test_invoice_cufe(simple_invoice_without_lines):
cufe = xml_invoice.get_element_text('/fe:Invoice/cbc:UUID') cufe = xml_invoice.get_element_text('/fe:Invoice/cbc:UUID')
# RESOLUCION 004: pagina 689 # RESOLUCION 004: pagina 689
assert cufe == '8bb918b19ba22a694f1da11c643b5e9de39adf60311cf179179e9b33381030bcd4c3c3f156c506ed5908f9276f5bd9b4' assert cufe == '8bb918b19ba22a694f1da11c643b5e9de39adf60311cf179179e9b33381030bcd4c3c3f156c506ed5908f9276f5bd9b4'
def test_invoice_payment_mean(monkeypatch, simple_invoice):
invoice_validator = form.DianResolucion0001Validator()
assert invoice_validator.validate(simple_invoice) == True