From 2aa0107579f8c48d0001538fc1b349fef7a2100c Mon Sep 17 00:00:00 2001 From: "bit4bit@riseup.net" Date: Wed, 28 Oct 2020 23:56:17 +0000 Subject: [PATCH] se retira validador y se sustituye por asserciones de precondicion FossilOrigin-Name: 1c360b4b29bb4fa1909ca1daae5b18cb01a56212cd84d71e7562cf700a5d7006 --- facho/cli.py | 9 -- facho/fe/data/dian/codelist/__init__.py | 1 + facho/fe/form.py | 139 +++++++++--------------- tests/test_data.py | 3 + tests/test_fe_form.py | 17 +-- 5 files changed, 56 insertions(+), 113 deletions(-) diff --git a/facho/cli.py b/facho/cli.py index ea5c2b5..a305ea5 100644 --- a/facho/cli.py +++ b/facho/cli.py @@ -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) diff --git a/facho/fe/data/dian/codelist/__init__.py b/facho/fe/data/dian/codelist/__init__.py index 12d55f8..85596db 100644 --- a/facho/fe/data/dian/codelist/__init__.py +++ b/facho/fe/data/dian/codelist/__init__.py @@ -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') diff --git a/facho/fe/form.py b/facho/fe/form.py index ca72cc8..c8eecbc 100644 --- a/facho/fe/form.py +++ b/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: @@ -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 diff --git a/tests/test_data.py b/tests/test_data.py index 6374393..88381b0 100644 --- a/tests/test_data.py +++ b/tests/test_data.py @@ -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' diff --git a/tests/test_fe_form.py b/tests/test_fe_form.py index 05a8325..843f154 100644 --- a/tests/test_fe_form.py +++ b/tests/test_fe_form.py @@ -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