se adiciona extensions para la dian
FossilOrigin-Name: f5521ddbfb903915de88a26ba5197b67efa1ebfd66337061ee9e3653c59dd217
This commit is contained in:
		| @@ -2,54 +2,14 @@ import facho.model as model | |||||||
| import facho.model.fields as fields | import facho.model.fields as fields | ||||||
| import facho.fe.form as form | import facho.fe.form as form | ||||||
| from facho import fe | from facho import fe | ||||||
|  | from .common import * | ||||||
|  | from . import dian | ||||||
|  |  | ||||||
| from datetime import date, datetime | from datetime import date, datetime | ||||||
| from collections import defaultdict | from collections import defaultdict | ||||||
| from copy import copy | from copy import copy | ||||||
| import hashlib | 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): | class PartyTaxScheme(model.Model): | ||||||
|     __name__ = 'PartyTaxScheme' |     __name__ = 'PartyTaxScheme' | ||||||
| @@ -139,10 +99,10 @@ class TaxCategory(model.Model): | |||||||
| class TaxSubTotal(model.Model): | class TaxSubTotal(model.Model): | ||||||
|     __name__ = 'TaxSubTotal' |     __name__ = 'TaxSubTotal' | ||||||
|  |  | ||||||
|     taxable_amount = fields.Many2One(Amount, name='TaxableAmount', default=0.00) |     taxable_amount = fields.Many2One(Amount, name='TaxableAmount', namespace='cbc', default=0.00) | ||||||
|     tax_amount = fields.Many2One(Amount, name='TaxAmount', default=0.00) |     tax_amount = fields.Many2One(Amount, name='TaxAmount', namespace='cbc', default=0.00) | ||||||
|     tax_percent = fields.Many2One(Percent) |     tax_percent = fields.Many2One(Percent, namespace='cbc') | ||||||
|     tax_category = fields.Many2One(TaxCategory) |     tax_category = fields.Many2One(TaxCategory, namespace='cac') | ||||||
|  |  | ||||||
|     percent = fields.Virtual(setter='set_category', getter='get_category') |     percent = fields.Virtual(setter='set_category', getter='get_category') | ||||||
|     scheme = 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): |     def update_payable_amount(self, name, value): | ||||||
|         self.payable_amount = self.tax_inclusive_amount + self.charge_total_amount |         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): | class Invoice(model.Model): | ||||||
|     __name__ = 'Invoice' |     __name__ = 'Invoice' | ||||||
|     __namespace__ = { |     __namespace__ = { | ||||||
| @@ -278,17 +260,24 @@ class Invoice(model.Model): | |||||||
|         'ds': 'http://www.w3.org/2000/09/xmldsig#' |         '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') |     id = fields.Many2One(ID, namespace='cbc') | ||||||
|     issue = fields.Virtual(setter='set_issue') |     issue = fields.Virtual(setter='set_issue') | ||||||
|     issue_date = fields.Many2One(Date, name='IssueDate', namespace='cbc') |     issue_date = fields.Many2One(Date, name='IssueDate', namespace='cbc') | ||||||
|     issue_time = fields.Many2One(Time, name='IssueTime', 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') |     supplier = fields.Many2One(AccountingSupplierParty, namespace='cac') | ||||||
|     customer = fields.Many2One(AccountingCustomerParty, namespace='cac') |     customer = fields.Many2One(AccountingCustomerParty, namespace='cac') | ||||||
|     lines = fields.One2Many(InvoiceLine, namespace='cac') |  | ||||||
|     legal_monetary_total = fields.Many2One(LegalMonetaryTotal, namespace='cac') |     legal_monetary_total = fields.Many2One(LegalMonetaryTotal, namespace='cac') | ||||||
|  |     lines = fields.One2Many(InvoiceLine, namespace='cac') | ||||||
|      |      | ||||||
|     taxtotal_01 = fields.Many2One(TaxTotal) |     taxtotal_01 = fields.Many2One(TaxTotal) | ||||||
|     taxtotal_04 = fields.Many2One(TaxTotal) |     taxtotal_04 = fields.Many2One(TaxTotal) | ||||||
| @@ -359,3 +348,6 @@ class Invoice(model.Model): | |||||||
|             raise ValueError('expected type datetime') |             raise ValueError('expected type datetime') | ||||||
|         self.issue_date = value.date() |         self.issue_date = value.date() | ||||||
|         self.issue_time = value |         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,6 +45,8 @@ 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 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: |                 if hasattr(v, 'default') and v.default is not None: | ||||||
|                     setattr(obj, key, v.default) |                     setattr(obj, key, v.default) | ||||||
|  |                 if hasattr(v, 'create') and v.create == True: | ||||||
|  |                     setattr(obj, key, '') | ||||||
|  |  | ||||||
|                 # register callbacks for changes |                 # register callbacks for changes | ||||||
|                 (fun, on_change_fields) = on_change_fields_for_function() |                 (fun, on_change_fields) = on_change_fields_for_function() | ||||||
|   | |||||||
							
								
								
									
										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 | from collections import defaultdict | ||||||
|  |  | ||||||
| class Many2One(Field): | 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.model = model | ||||||
|         self.setter = setter |         self.setter = setter | ||||||
|         self.namespace = namespace |         self.namespace = namespace | ||||||
| @@ -10,6 +10,7 @@ class Many2One(Field): | |||||||
|         self.default = default |         self.default = default | ||||||
|         self.virtual = virtual |         self.virtual = virtual | ||||||
|         self.relations = defaultdict(dict) |         self.relations = defaultdict(dict) | ||||||
|  |         self.create = create | ||||||
|  |  | ||||||
|     def __get__(self, inst, cls): |     def __get__(self, inst, cls): | ||||||
|         if inst is None: |         if inst is None: | ||||||
|   | |||||||
							
								
								
									
										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 |     party.location = 99 | ||||||
|     assert '<Party><PhysicalLocation ID="99"/></Party>' == party.to_xml() |     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(): | def test_many2one_auto_create(): | ||||||
|     class TaxAmount(facho.model.Model): |     class TaxAmount(facho.model.Model): | ||||||
|         __name__ = 'TaxAmount' |         __name__ = 'TaxAmount' | ||||||
|   | |||||||
| @@ -13,8 +13,15 @@ import facho.fe.model as model | |||||||
| import facho.fe.form as form | import facho.fe.form as form | ||||||
| from facho import fe | from facho import fe | ||||||
|  |  | ||||||
| def _test_simple_invoice(): | def test_simple_invoice(): | ||||||
|     invoice = model.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.id = '323200000129' | ||||||
|     invoice.issue = datetime.strptime('2019-01-16 10:53:10-05:00', '%Y-%m-%d %H:%M:%S%z') |     invoice.issue = datetime.strptime('2019-01-16 10:53:10-05:00', '%Y-%m-%d %H:%M:%S%z') | ||||||
|     invoice.supplier.party.id = '700085371' |     invoice.supplier.party.id = '700085371' | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user