se adiciona extensions para la dian
FossilOrigin-Name: f5521ddbfb903915de88a26ba5197b67efa1ebfd66337061ee9e3653c59dd217
This commit is contained in:
parent
088fa9e6e0
commit
64b312a432
@ -2,54 +2,14 @@ import facho.model as model
|
||||
import facho.model.fields as fields
|
||||
import facho.fe.form as form
|
||||
from facho import fe
|
||||
from .common import *
|
||||
from . import dian
|
||||
|
||||
from datetime import date, datetime
|
||||
from collections import defaultdict
|
||||
from copy import copy
|
||||
import hashlib
|
||||
|
||||
class Name(model.Model):
|
||||
__name__ = 'Name'
|
||||
|
||||
class Date(model.Model):
|
||||
__name__ = 'Date'
|
||||
|
||||
def __default_set__(self, value):
|
||||
if isinstance(value, str):
|
||||
return value
|
||||
if isinstance(value, date):
|
||||
return value.isoformat()
|
||||
|
||||
def __str__(self):
|
||||
return str(self._value)
|
||||
|
||||
class Time(model.Model):
|
||||
__name__ = 'Time'
|
||||
|
||||
def __default_set__(self, value):
|
||||
if isinstance(value, str):
|
||||
return value
|
||||
if isinstance(value, date):
|
||||
return value.strftime('%H:%M:%S-05:00')
|
||||
|
||||
def __str__(self):
|
||||
return str(self._value)
|
||||
|
||||
class InvoicePeriod(model.Model):
|
||||
__name__ = 'InvoicePeriod'
|
||||
|
||||
start_date = fields.Many2One(Date, name='StartDate', namespace='cbc')
|
||||
|
||||
end_date = fields.Many2One(Date, name='EndDate', namespace='cbc')
|
||||
|
||||
class ID(model.Model):
|
||||
__name__ = 'ID'
|
||||
|
||||
def __default_get__(self, name, value):
|
||||
return self._value
|
||||
|
||||
def __str__(self):
|
||||
return str(self._value)
|
||||
|
||||
class PartyTaxScheme(model.Model):
|
||||
__name__ = 'PartyTaxScheme'
|
||||
@ -139,10 +99,10 @@ class TaxCategory(model.Model):
|
||||
class TaxSubTotal(model.Model):
|
||||
__name__ = 'TaxSubTotal'
|
||||
|
||||
taxable_amount = fields.Many2One(Amount, name='TaxableAmount', default=0.00)
|
||||
tax_amount = fields.Many2One(Amount, name='TaxAmount', default=0.00)
|
||||
tax_percent = fields.Many2One(Percent)
|
||||
tax_category = fields.Many2One(TaxCategory)
|
||||
taxable_amount = fields.Many2One(Amount, name='TaxableAmount', namespace='cbc', default=0.00)
|
||||
tax_amount = fields.Many2One(Amount, name='TaxAmount', namespace='cbc', default=0.00)
|
||||
tax_percent = fields.Many2One(Percent, namespace='cbc')
|
||||
tax_category = fields.Many2One(TaxCategory, namespace='cac')
|
||||
|
||||
percent = fields.Virtual(setter='set_category', getter='get_category')
|
||||
scheme = fields.Virtual(setter='set_category', getter='get_category')
|
||||
@ -266,6 +226,28 @@ class LegalMonetaryTotal(model.Model):
|
||||
def update_payable_amount(self, name, value):
|
||||
self.payable_amount = self.tax_inclusive_amount + self.charge_total_amount
|
||||
|
||||
|
||||
class DIANExtension(model.Model):
|
||||
__name__ = 'UBLExtension'
|
||||
|
||||
_content = fields.Many2One(Element, name='ExtensionContent', namespace='ext')
|
||||
|
||||
dian = fields.Many2One(dian.DianExtensions, name='DianExtensions', namespace='sts')
|
||||
|
||||
def __default_get__(self, name, value):
|
||||
return self.dian
|
||||
|
||||
class UBLExtension(model.Model):
|
||||
__name__ = 'UBLExtension'
|
||||
|
||||
content = fields.Many2One(Element, name='ExtensionContent', namespace='ext', default='')
|
||||
|
||||
class UBLExtensions(model.Model):
|
||||
__name__ = 'UBLExtensions'
|
||||
|
||||
dian = fields.Many2One(DIANExtension, namespace='ext', create=True)
|
||||
extension = fields.Many2One(UBLExtension, namespace='ext', create=True)
|
||||
|
||||
class Invoice(model.Model):
|
||||
__name__ = 'Invoice'
|
||||
__namespace__ = {
|
||||
@ -277,18 +259,25 @@ class Invoice(model.Model):
|
||||
'xades': 'http://uri.etsi.org/01903/v1.3.2#',
|
||||
'ds': 'http://www.w3.org/2000/09/xmldsig#'
|
||||
}
|
||||
|
||||
|
||||
_ubl_extensions = fields.Many2One(UBLExtensions, namespace='ext')
|
||||
dian = fields.Virtual(getter='get_dian_extension')
|
||||
|
||||
profile_id = fields.Many2One(Element, name='ProfileID', namespace='cbc', default='DIAN 2.1')
|
||||
profile_execute_id = fields.Many2One(Element, name='ProfileExecuteID', namespace='cbc', default='2')
|
||||
|
||||
id = fields.Many2One(ID, namespace='cbc')
|
||||
issue = fields.Virtual(setter='set_issue')
|
||||
issue_date = fields.Many2One(Date, name='IssueDate', namespace='cbc')
|
||||
issue_time = fields.Many2One(Time, name='IssueTime', namespace='cbc')
|
||||
|
||||
period = fields.Many2One(InvoicePeriod, namespace='cac')
|
||||
period = fields.Many2One(Period, name='InvoicePeriod', namespace='cac')
|
||||
|
||||
supplier = fields.Many2One(AccountingSupplierParty, namespace='cac')
|
||||
customer = fields.Many2One(AccountingCustomerParty, namespace='cac')
|
||||
lines = fields.One2Many(InvoiceLine, namespace='cac')
|
||||
legal_monetary_total = fields.Many2One(LegalMonetaryTotal, namespace='cac')
|
||||
lines = fields.One2Many(InvoiceLine, namespace='cac')
|
||||
|
||||
taxtotal_01 = fields.Many2One(TaxTotal)
|
||||
taxtotal_04 = fields.Many2One(TaxTotal)
|
||||
@ -359,3 +348,6 @@ class Invoice(model.Model):
|
||||
raise ValueError('expected type datetime')
|
||||
self.issue_date = value.date()
|
||||
self.issue_time = value
|
||||
|
||||
def get_dian_extension(self, name, _value):
|
||||
return self._ubl_extensions.dian
|
||||
|
55
facho/fe/model/common.py
Normal file
55
facho/fe/model/common.py
Normal file
@ -0,0 +1,55 @@
|
||||
import facho.model as model
|
||||
import facho.model.fields as fields
|
||||
|
||||
from datetime import date, datetime
|
||||
|
||||
__all__ = ['Element', 'Name', 'Date', 'Time', 'Period', 'ID']
|
||||
|
||||
class Element(model.Model):
|
||||
"""
|
||||
Lo usuamos para elementos que solo manejan contenido
|
||||
"""
|
||||
__name__ = 'Element'
|
||||
|
||||
class Name(model.Model):
|
||||
__name__ = 'Name'
|
||||
|
||||
class Date(model.Model):
|
||||
__name__ = 'Date'
|
||||
|
||||
def __default_set__(self, value):
|
||||
if isinstance(value, str):
|
||||
return value
|
||||
if isinstance(value, date):
|
||||
return value.isoformat()
|
||||
|
||||
def __str__(self):
|
||||
return str(self._value)
|
||||
|
||||
class Time(model.Model):
|
||||
__name__ = 'Time'
|
||||
|
||||
def __default_set__(self, value):
|
||||
if isinstance(value, str):
|
||||
return value
|
||||
if isinstance(value, date):
|
||||
return value.strftime('%H:%M:%S-05:00')
|
||||
|
||||
def __str__(self):
|
||||
return str(self._value)
|
||||
|
||||
class Period(model.Model):
|
||||
__name__ = 'Period'
|
||||
|
||||
start_date = fields.Many2One(Date, name='StartDate', namespace='cbc')
|
||||
|
||||
end_date = fields.Many2One(Date, name='EndDate', namespace='cbc')
|
||||
|
||||
class ID(model.Model):
|
||||
__name__ = 'ID'
|
||||
|
||||
def __default_get__(self, name, value):
|
||||
return self._value
|
||||
|
||||
def __str__(self):
|
||||
return str(self._value)
|
37
facho/fe/model/dian.py
Normal file
37
facho/fe/model/dian.py
Normal file
@ -0,0 +1,37 @@
|
||||
import facho.model as model
|
||||
import facho.model.fields as fields
|
||||
from .common import *
|
||||
|
||||
class SoftwareProvider(model.Model):
|
||||
__name__ = 'SoftwareProvider'
|
||||
|
||||
provider_id = fields.Many2One(Element, name='ProviderID', namespace='sts')
|
||||
software_id = fields.Many2One(Element, name='SoftwareID', namespace='sts')
|
||||
|
||||
class InvoiceSource(model.Model):
|
||||
__name__ = 'InvoiceSource'
|
||||
|
||||
identification_code = fields.Many2One(Element, name='IdentificationCode', namespace='sts', default='CO')
|
||||
|
||||
class AuthorizedInvoices(model.Model):
|
||||
__name__ = 'AuthorizedInvoices'
|
||||
|
||||
prefix = fields.Many2One(Element, name='Prefix', namespace='sts')
|
||||
from_range = fields.Many2One(Element, name='From', namespace='sts')
|
||||
to_range = fields.Many2One(Element, name='To', namespace='sts')
|
||||
|
||||
class InvoiceControl(model.Model):
|
||||
__name__ = 'InvoiceControl'
|
||||
|
||||
authorization = fields.Many2One(Element, name='InvoiceAuthorization', namespace='sts')
|
||||
period = fields.Many2One(Period, name='AuthorizationPeriod', namespace='sts')
|
||||
invoices = fields.Many2One(AuthorizedInvoices, namespace='sts')
|
||||
|
||||
class DianExtensions(model.Model):
|
||||
__name__ = 'DianExtensions'
|
||||
|
||||
software_security_code = fields.Many2One(Element, name='SoftwareSecurityCode', namespace='sts')
|
||||
software_provider = fields.Many2One(SoftwareProvider, namespace='sts')
|
||||
source = fields.Many2One(InvoiceSource, namespace='sts')
|
||||
control = fields.Many2One(InvoiceControl, namespace='sts')
|
||||
|
@ -45,7 +45,9 @@ class ModelBase(object, metaclass=ModelMeta):
|
||||
if isinstance(v, fields.Attribute) or isinstance(v, fields.Many2One) or isinstance(v, fields.Function) or isinstance(v, fields.Amount):
|
||||
if hasattr(v, 'default') and v.default is not None:
|
||||
setattr(obj, key, v.default)
|
||||
|
||||
if hasattr(v, 'create') and v.create == True:
|
||||
setattr(obj, key, '')
|
||||
|
||||
# register callbacks for changes
|
||||
(fun, on_change_fields) = on_change_fields_for_function()
|
||||
for field in on_change_fields:
|
||||
|
31
facho/model/fields/amount.py
Normal file
31
facho/model/fields/amount.py
Normal file
@ -0,0 +1,31 @@
|
||||
from .field import Field
|
||||
from collections import defaultdict
|
||||
import facho.fe.form as form
|
||||
|
||||
class Amount(Field):
|
||||
def __init__(self, name=None, default=None, precision=6):
|
||||
self.field_name = name
|
||||
self.values = {}
|
||||
self.default = default
|
||||
self.precision = precision
|
||||
|
||||
def __get__(self, model, cls):
|
||||
if model is None:
|
||||
return self
|
||||
assert self.name is not None
|
||||
|
||||
self.__init_value(model)
|
||||
model._set_field(self.name, self)
|
||||
return self.values[model]
|
||||
|
||||
def __set__(self, model, value):
|
||||
assert self.name is not None
|
||||
self.__init_value(model)
|
||||
model._set_field(self.name, self)
|
||||
self.values[model] = form.Amount(value, precision=self.precision)
|
||||
|
||||
self._changed_field(model, self.name, value)
|
||||
|
||||
def __init_value(self, model):
|
||||
if model not in self.values:
|
||||
self.values[model] = form.Amount(self.default or 0)
|
@ -2,7 +2,7 @@ from .field import Field
|
||||
from collections import defaultdict
|
||||
|
||||
class Many2One(Field):
|
||||
def __init__(self, model, name=None, setter=None, namespace=None, default=None, virtual=False):
|
||||
def __init__(self, model, name=None, setter=None, namespace=None, default=None, virtual=False, create=False):
|
||||
self.model = model
|
||||
self.setter = setter
|
||||
self.namespace = namespace
|
||||
@ -10,7 +10,8 @@ class Many2One(Field):
|
||||
self.default = default
|
||||
self.virtual = virtual
|
||||
self.relations = defaultdict(dict)
|
||||
|
||||
self.create = create
|
||||
|
||||
def __get__(self, inst, cls):
|
||||
if inst is None:
|
||||
return self
|
||||
|
45
facho/model/fields/virtual.py
Normal file
45
facho/model/fields/virtual.py
Normal file
@ -0,0 +1,45 @@
|
||||
from .field import Field
|
||||
|
||||
# Un campo virtual
|
||||
# no participa del renderizado
|
||||
# pero puede interactura con este
|
||||
class Virtual(Field):
|
||||
def __init__(self,
|
||||
setter=None,
|
||||
getter='bob',
|
||||
default=None,
|
||||
update_internal=False):
|
||||
self.default = default
|
||||
self.setter = setter
|
||||
self.getter = getter
|
||||
self.values = {}
|
||||
self.update_internal = update_internal
|
||||
self.virtual = True
|
||||
|
||||
def __get__(self, inst, cls):
|
||||
if inst is None:
|
||||
return self
|
||||
assert self.name is not None
|
||||
|
||||
value = self.default
|
||||
try:
|
||||
value = self.values[inst]
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
try:
|
||||
self.values[inst] = getattr(inst, self.getter)(self.name, value)
|
||||
except AttributeError:
|
||||
self.values[inst] = value
|
||||
|
||||
return self.values[inst]
|
||||
|
||||
def __set__(self, inst, value):
|
||||
if self.update_internal:
|
||||
inst._value = value
|
||||
|
||||
if self.setter is None:
|
||||
self.values[inst] = value
|
||||
else:
|
||||
self.values[inst] = self._call(inst, self.setter, self.name, value)
|
||||
self._changed_field(inst, self.name, value)
|
@ -80,6 +80,35 @@ def test_many2one_with_custom_setter():
|
||||
party.location = 99
|
||||
assert '<Party><PhysicalLocation ID="99"/></Party>' == party.to_xml()
|
||||
|
||||
def test_many2one_always_create():
|
||||
class Name(facho.model.Model):
|
||||
__name__ = 'Name'
|
||||
|
||||
class Person(facho.model.Model):
|
||||
__name__ = 'Person'
|
||||
|
||||
name = fields.Many2One(Name, default='facho')
|
||||
|
||||
person = Person()
|
||||
assert '<Person><Name>facho</Name></Person>' == person.to_xml()
|
||||
|
||||
def test_many2one_nested_always_create():
|
||||
class Name(facho.model.Model):
|
||||
__name__ = 'Name'
|
||||
|
||||
class Contact(facho.model.Model):
|
||||
__name__ = 'Contact'
|
||||
|
||||
name = fields.Many2One(Name, default='facho')
|
||||
|
||||
class Person(facho.model.Model):
|
||||
__name__ = 'Person'
|
||||
|
||||
contact = fields.Many2One(Contact, create=True)
|
||||
|
||||
person = Person()
|
||||
assert '<Person><Contact><Name>facho</Name></Contact></Person>' == person.to_xml()
|
||||
|
||||
def test_many2one_auto_create():
|
||||
class TaxAmount(facho.model.Model):
|
||||
__name__ = 'TaxAmount'
|
||||
|
@ -13,8 +13,15 @@ import facho.fe.model as model
|
||||
import facho.fe.form as form
|
||||
from facho import fe
|
||||
|
||||
def _test_simple_invoice():
|
||||
def test_simple_invoice():
|
||||
invoice = model.Invoice()
|
||||
invoice.dian.software_security_code = '12345'
|
||||
invoice.dian.software_provider.provider_id = 'provider-id'
|
||||
invoice.dian.software_provider.software_id = 'facho'
|
||||
invoice.dian.control.prefix = 'SETP'
|
||||
invoice.dian.control.from_range = '1000'
|
||||
invoice.dian.control.to_range = '1000'
|
||||
|
||||
invoice.id = '323200000129'
|
||||
invoice.issue = datetime.strptime('2019-01-16 10:53:10-05:00', '%Y-%m-%d %H:%M:%S%z')
|
||||
invoice.supplier.party.id = '700085371'
|
||||
|
Loading…
Reference in New Issue
Block a user