From 507ddbe5583c27f76ea03b8e3fc2a6213034575b Mon Sep 17 00:00:00 2001 From: bit4bit Date: Sun, 27 Jun 2021 02:16:16 +0000 Subject: [PATCH] se crea legal monetary total FossilOrigin-Name: 8ae3dfadfe9b90b8cc5ad59d2a73f4c8a987c5aac498573e56754a2d32e9e2ae --- facho/fe/form/__init__.py | 2 +- facho/fe/model/__init__.py | 77 +++++++++++++++++++++++++++++++--- facho/model/__init__.py | 8 ++-- facho/model/fields/one2many.py | 3 +- tests/test_model_invoice.py | 16 ++++++- 5 files changed, 94 insertions(+), 12 deletions(-) diff --git a/facho/fe/form/__init__.py b/facho/fe/form/__init__.py index 0aa6318..b12a86a 100644 --- a/facho/fe/form/__init__.py +++ b/facho/fe/form/__init__.py @@ -99,7 +99,7 @@ class Amount: return self.fromNumber(val) if isinstance(val, Amount): return val - raise TypeError("cant cast to amount") + raise TypeError("cant cast %s to amount" % (type(val))) def __add__(self, rother): other = self._cast(rother) diff --git a/facho/fe/model/__init__.py b/facho/fe/model/__init__.py index 74aaf01..e1d895c 100644 --- a/facho/fe/model/__init__.py +++ b/facho/fe/model/__init__.py @@ -1,6 +1,8 @@ import facho.model as model import facho.model.fields as fields +import facho.fe.form as form from datetime import date, datetime +from copy import copy class Name(model.Model): __name__ = 'Name' @@ -48,24 +50,44 @@ class AccountingSupplierParty(model.Model): party = fields.Many2One(Party) -class InvoicedQuantity(model.Model): - __name__ = 'InvoiceQuantity' +class Quantity(model.Model): + __name__ = 'Quantity' code = fields.Attribute('unitCode', default='NAR') + value = fields.Virtual(default=0, update_internal=True) + + def __default_set__(self, value): + self.value = value + return value + + def __mul__(self, other): + return form.Amount(self.value) * other.value class Amount(model.Model): __name__ = 'Amount' currency = fields.Attribute('currencyID', default='COP') - + value = fields.Virtual(default=form.Amount(0), update_internal=True) + + def __default_set__(self, value): + self.value = value + return value + class Price(model.Model): __name__ = 'Price' amount = fields.Many2One(Amount, name='PriceAmount') - + value = fields.Virtual(default=form.Amount(0)) + def __default_set__(self, value): self.amount = value + self.value = value + return value + + def __mul__(self, other): + return self.value * other.value + class Percent(model.Model): __name__ = 'Percent' @@ -102,13 +124,52 @@ class TaxTotal(model.Model): tax_amount = fields.Many2One(Amount, name='TaxAmount') subtotals = fields.One2Many(TaxSubTotal) + + +class AllowanceCharge(model.Model): + __name__ = 'AllowanceCharge' + + amount = fields.Many2One(Amount) + is_discount = fields.Virtual(default=False) + def isCharge(self): + return self.is_discount == False + + def isDiscount(self): + return self.is_discount == True + class InvoiceLine(model.Model): __name__ = 'InvoiceLine' - quantity = fields.Many2One(InvoicedQuantity) + quantity = fields.Many2One(Quantity, name='InvoicedQuantity') taxtotal = fields.Many2One(TaxTotal) price = fields.Many2One(Price) + amount = fields.Many2One(Amount, name='LineExtensionAmount') + allowance_charge = fields.One2Many(AllowanceCharge) + + @fields.on_change(['price', 'quantity']) + def update_amount(self, name, value): + charge = form.AmountCollection(self.allowance_charge)\ + .filter(lambda charge: charge.isCharge())\ + .map(lambda charge: charge.amount)\ + .sum() + + discount = form.AmountCollection(self.allowance_charge)\ + .filter(lambda charge: charge.isDiscount())\ + .map(lambda charge: charge.amount)\ + .sum() + + total = self.quantity * self.price + self.amount = total + charge - discount + +class LegalMonetaryTotal(model.Model): + __name__ = 'LegalMonetaryTotal' + + line_extension_amount = fields.Many2One(Amount, name='LineExtensionAmount', default=form.Amount(0)) + tax_exclusive_amount = fields.Many2One(Amount, name='TaxExclusiveAmount') + tax_inclusive_amount = fields.Many2One(Amount, name='TaxInclusiveAmount') + charge_total_amount = fields.Many2One(Amount, name='ChargeTotalAmount') + payable_amount = fields.Many2One(Amount, name='PayableAmount') class Invoice(model.Model): __name__ = 'Invoice' @@ -123,7 +184,13 @@ class Invoice(model.Model): supplier = fields.Many2One(AccountingSupplierParty) customer = fields.Many2One(AccountingCustomerParty) lines = fields.One2Many(InvoiceLine) + legal_monetary_total = fields.Many2One(LegalMonetaryTotal) + @fields.on_change(['lines']) + def update_legal_monetary_total(self, name, value): + for line in self.lines: + self.legal_monetary_total.line_extension_amount.value += line.amount.value + def set_issue(self, name, value): if not isinstance(value, datetime): raise ValueError('expected type datetime') diff --git a/facho/model/__init__.py b/facho/model/__init__.py index 57f7489..0c2f73c 100644 --- a/facho/model/__init__.py +++ b/facho/model/__init__.py @@ -19,7 +19,7 @@ class ModelBase(object, metaclass=ModelMeta): obj = super().__new__(cls, *args, **kwargs) obj._xml_attributes = {} obj._fields = {} - obj._text = "" + obj._value = None obj._namespace_prefix = None obj._on_change_fields = defaultdict(list) obj._order_fields = [] @@ -72,7 +72,7 @@ class ModelBase(object, metaclass=ModelMeta): def _set_content(self, value): default = self.__default_set__(value) if default is not None: - self._text = str(default) + self._value = default def _hook_before_xml(self): self.__before_xml__() @@ -116,7 +116,9 @@ class ModelBase(object, metaclass=ModelMeta): content += value.to_xml() elif isinstance(value, str): content += value - content += self._text + + if self._value is not None: + content += str(self._value) if content == "": return "<%s%s%s/>" % (ns, tag, attributes) diff --git a/facho/model/fields/one2many.py b/facho/model/fields/one2many.py index 112a92f..aeb6547 100644 --- a/facho/model/fields/one2many.py +++ b/facho/model/fields/one2many.py @@ -17,9 +17,10 @@ class _RelationProxy(): def __setattr__(self, attr, value): # TODO(bit4bit) hacemos proxy al sistema de notificacion de cambios # algo burdo, se usa __dict__ para saltarnos el __getattr__ y generar un fallo por recursion + response = setattr(self._obj, attr, value) for fun in self.__dict__['_inst']._on_change_fields[self.__dict__['_attribute']]: fun(self.__dict__['_inst'], self.__dict__['_attribute'], value) - return setattr(self._obj, attr, value) + return response class _Relation(): def __init__(self, creator, inst, attribute): diff --git a/tests/test_model_invoice.py b/tests/test_model_invoice.py index a28df1c..6472fd0 100644 --- a/tests/test_model_invoice.py +++ b/tests/test_model_invoice.py @@ -20,9 +20,21 @@ def test_simple_invoice(): invoice.customer.party.id = '800199436' line = invoice.lines.create() - line.quantity = form.Quantity(1, '94') + line.quantity = 1 line.price = form.Amount(5_000) subtotal = line.taxtotal.subtotals.create() subtotal.percent = 19.0 + assert '3232000001292019-01-16T10:53:10-05:0010:5310-05:00700085371800199436119.001IVA5000.05000.05000.035000.0' == invoice.to_xml() - assert '3232000001292019-01-16T10:53:10-05:0010:5310-05:007000853718001994361.019.001IVA5000.0' == invoice.to_xml() +def _test_simple_invoice_cufe(): + invoice = model.Invoice() + 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' + invoice.customer.party.id = '800199436' + + line = invoice.lines.create() + line.quantity = form.Quantity(1, '94') + line.price = form.Amount(1_500_000) + subtotal = line.taxtotal.subtotals.create() + subtotal.percent = 19.0