oc-facho/facho/fe/model/__init__.py
bit4bit 69a74c0714 generacion de cufe desde invoice
FossilOrigin-Name: d3494f20063452571b1e86d505f211e61fdf435aa43b870408136e3e9302bc17
2021-07-31 17:09:42 +00:00

357 lines
11 KiB
Python

import facho.model as model
import facho.model.fields as fields
import facho.fe.form as form
from facho import fe
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')
end_date = fields.Many2One(Date, name='EndDate')
class ID(model.Model):
__name__ = 'ID'
def __str__(self):
return str(self._value)
class Party(model.Model):
__name__ = 'Party'
id = fields.Many2One(ID)
class AccountingCustomerParty(model.Model):
__name__ = 'AccountingCustomerParty'
party = fields.Many2One(Party)
class AccountingSupplierParty(model.Model):
__name__ = 'AccountingSupplierParty'
party = fields.Many2One(Party)
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
def __add__(self, other):
return form.Amount(self.value) + other.value
class Amount(model.Model):
__name__ = 'Amount'
currency = fields.Attribute('currencyID', default='COP')
value = fields.Amount(name='amount', default=0.00, precision=2)
def __default_set__(self, value):
self.value = value
return value
def __default__get__(self, value):
return value
def __str__(self):
return str(self.value)
def __add__(self, other):
if isinstance(other, form.Amount):
return self.value + other
return self.value + other.value
class Price(model.Model):
__name__ = 'Price'
amount = fields.Many2One(Amount, name='PriceAmount')
value = fields.Amount(0.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'
class TaxScheme(model.Model):
__name__ = 'TaxScheme'
id = fields.Many2One(ID)
name= fields.Many2One(Name)
class TaxCategory(model.Model):
__name__ = 'TaxCategory'
percent = fields.Many2One(Percent)
tax_scheme = fields.Many2One(TaxScheme)
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)
percent = fields.Virtual(setter='set_category', getter='get_category')
scheme = fields.Virtual(setter='set_category', getter='get_category')
def set_category(self, name, value):
if name == 'percent':
self.tax_category.percent = value
# TODO(bit4bit) debe variar en conjunto?
self.tax_percent = value
elif name == 'scheme':
self.tax_category.tax_scheme.id = value
return value
def get_category(self, name, value):
if name == 'percent':
return value
elif name == 'scheme':
return self.tax_category.tax_scheme
class TaxTotal(model.Model):
__name__ = 'TaxTotal'
tax_amount = fields.Many2One(Amount, name='TaxAmount', default=0.00)
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 TaxScheme:
pass
class TaxIva(TaxScheme):
def __init__(self, percent):
self.scheme = '01'
self.percent = percent
def calculate(self, amount):
return form.Amount(amount) * form.Amount(self.percent / 100)
class InvoiceLine(model.Model):
__name__ = 'InvoiceLine'
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)
tax_amount = fields.Virtual(getter='get_tax_amount')
def __setup__(self):
self._taxs = defaultdict(list)
self._subtotals = {
'01': self.taxtotal.subtotals.create()
}
self._subtotals['01'].scheme = '01'
def get_tax_amount(self, name, value):
total = form.Amount(0)
for (scheme, subtotal) in self._subtotals.items():
total += subtotal.tax_amount.value
return total
def add_tax(self, tax):
if not isinstance(tax, TaxScheme):
raise ValueError('tax expected TaxScheme')
self._taxs[tax.scheme].append(tax)
@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
for (scheme, subtotal) in self._subtotals.items():
subtotal.tax_amount.value = 0
for (scheme, taxes) in self._taxs.items():
for tax in taxes:
self._subtotals[scheme].tax_amount += tax.calculate(self.amount.value)
class LegalMonetaryTotal(model.Model):
__name__ = 'LegalMonetaryTotal'
line_extension_amount = fields.Many2One(Amount, name='LineExtensionAmount', default=0)
tax_exclusive_amount = fields.Many2One(Amount, name='TaxExclusiveAmount', default=form.Amount(0))
tax_inclusive_amount = fields.Many2One(Amount, name='TaxInclusiveAmount', default=form.Amount(0))
charge_total_amount = fields.Many2One(Amount, name='ChargeTotalAmount', default=form.Amount(0))
payable_amount = fields.Many2One(Amount, name='PayableAmount', default=form.Amount(0))
@fields.on_change(['tax_inclusive_amount', 'charge_total'])
def update_payable_amount(self, name, value):
self.payable_amount = self.tax_inclusive_amount.value + self.charge_total_amount.value
class Technical(model.Model):
__name__ = 'Technical'
token = fields.Virtual(default='')
environment = fields.Virtual(default=fe.AMBIENTE_PRODUCCION)
class Invoice(model.Model):
__name__ = 'Invoice'
technical = fields.Many2One(Technical, virtual=True)
id = fields.Many2One(ID)
issue = fields.Virtual(setter='set_issue')
issue_date = fields.Many2One(Date, name='IssueDate')
issue_time = fields.Many2One(Time, name='IssueTime')
period = fields.Many2One(InvoicePeriod)
supplier = fields.Many2One(AccountingSupplierParty)
customer = fields.Many2One(AccountingCustomerParty)
lines = fields.One2Many(InvoiceLine)
legal_monetary_total = fields.Many2One(LegalMonetaryTotal)
taxtotal_01 = fields.Many2One(TaxTotal)
taxtotal_04 = fields.Many2One(TaxTotal)
taxtotal_03 = fields.Many2One(TaxTotal)
cufe = fields.Virtual(getter='calculate_cufe')
_subtotal_01 = fields.Virtual()
_subtotal_04 = fields.Virtual()
_subtotal_03 = fields.Virtual()
def __setup__(self):
# Se requieren minimo estos impuestos para
# validar el cufe
self._subtotal_01 = self.taxtotal_01.subtotals.create()
self._subtotal_01.scheme = '01'
self._subtotal_01.percent = 19.0
self._subtotal_04 = self.taxtotal_04.subtotals.create()
self._subtotal_04.scheme = '04'
self._subtotal_03 = self.taxtotal_03.subtotals.create()
self._subtotal_03.scheme = '03'
def calculate_cufe(self, name, value):
valor_bruto = self.legal_monetary_total.line_extension_amount.value
valor_total_pagar = self.legal_monetary_total.payable_amount.value
valor_impuesto_01 = form.Amount(0.0)
valor_impuesto_04 = form.Amount(0.0)
valor_impuesto_03 = form.Amount(0.0)
for line in self.lines:
for subtotal in line.taxtotal.subtotals:
scheme_id = subtotal.scheme
if str(subtotal.scheme.id) == '01':
valor_impuesto_01 += subtotal.tax_amount.value
elif subtotal.scheme.id == '04':
valor_impuesto_04 += subtotal.tax_amount.value
elif subtotal.scheme.id == '03':
valor_impuesto_03 += subtotal.tax_amount.value
pattern = [
'%s' % str(self.id),
'%s' % str(self.issue_date),
'%s' % str(self.issue_time),
valor_bruto.truncate_as_string(2),
'01', valor_impuesto_01.truncate_as_string(2),
'04', valor_impuesto_04.truncate_as_string(2),
'03', valor_impuesto_03.truncate_as_string(2),
valor_total_pagar.truncate_as_string(2),
str(self.supplier.party.id),
str(self.customer.party.id),
str(self.technical.token),
str(self.technical.environment)
]
cufe = "".join(pattern)
h = hashlib.sha384()
h.update(cufe.encode('utf-8'))
return h.hexdigest()
@fields.on_change(['lines'])
def update_legal_monetary_total(self, name, value):
self.legal_monetary_total.line_extension_amount.value = 0
self.legal_monetary_total.tax_inclusive_amount.value = 0
for line in self.lines:
self.legal_monetary_total.line_extension_amount.value += line.amount.value
self.legal_monetary_total.tax_inclusive_amount += line.amount.value + line.tax_amount
print("update legal monetary %s" % (str(line.amount.value)))
def set_issue(self, name, value):
if not isinstance(value, datetime):
raise ValueError('expected type datetime')
self.issue_date = value.date()
self.issue_time = value