Active Record

This commit is contained in:
Cédric Krier committed 2012-09-24 22:17:53 +02:00
1 parent 367e090c07
commit 3f285bfc5d
3 files changed
+208 -186

No files matched your search

+8
View File
@@ -1,4 +1,12 @@
#This file is part of Tryton. The COPYRIGHT file at the top level of
#this repository contains the full copyright notices and license terms.
from trytond.pool import Pool
from .currency import *
def register():
Pool.register(
Currency,
Rate,
module='currency', type_='model')
+110 -94
View File
@@ -6,12 +6,14 @@ from trytond.model import ModelView, ModelSQL, fields
from trytond.tools import safe_eval, datetime_strftime
from trytond.transaction import Transaction
from trytond.pool import Pool
from trytond.rpc import RPC
__all__ = ['Currency', 'Rate']
class Currency(ModelSQL, ModelView):
'Currency'
_name = 'currency.currency'
_description = __doc__
__name__ = 'currency.currency'
name = fields.Char('Name', required=True, translate=True)
symbol = fields.Char('Symbol', size=10, required=True)
code = fields.Char('Code', size=3, required=True)
@@ -36,95 +38,112 @@ class Currency(ModelSQL, ModelView):
p_sep_by_space = fields.Boolean('Positive Separate by Space')
n_sep_by_space = fields.Boolean('Negative Separate by Space')
def __init__(self):
super(Currency, self).__init__()
self._order.insert(0, ('code', 'ASC'))
self._constraints += [
@classmethod
def __setup__(cls):
super(Currency, cls).__setup__()
cls._order.insert(0, ('code', 'ASC'))
cls._constraints += [
('check_mon_grouping', 'invalid_mon_grouping'),
]
self._error_messages.update({
'no_rate': 'No rate found for the currency: %s at the date: %s',
'invalid_grouping': 'Invalid Grouping!',
})
self._rpc.update({
'compute': False,
})
]
cls._error_messages.update({
'no_rate': 'No rate found for the currency: ' \
'%s at the date: %s',
'invalid_grouping': 'Invalid Grouping!',
})
cls.__rpc__.update({
'compute': RPC(),
})
def default_active(self):
@staticmethod
def default_active():
return True
def default_rounding(self):
@staticmethod
def default_rounding():
return Decimal('0.01')
def default_digits(self):
@staticmethod
def default_digits():
return 2
def default_mon_grouping(self):
@staticmethod
def default_mon_grouping():
return '[]'
def default_mon_thousands_sep(self):
@staticmethod
def default_mon_thousands_sep():
return ','
def default_mon_decimal_point(self):
@staticmethod
def default_mon_decimal_point():
return '.'
def default_p_sign_posn(self):
@staticmethod
def default_p_sign_posn():
return 1
def default_n_sign_posn(self):
@staticmethod
def default_n_sign_posn():
return 1
def default_negative_sign(self):
@staticmethod
def default_negative_sign():
return '-'
def default_positive_sign(self):
@staticmethod
def default_positive_sign():
return ''
def default_p_cs_precedes(self):
@staticmethod
def default_p_cs_precedes():
return True
def default_n_cs_precedes(self):
@staticmethod
def default_n_cs_precedes():
return True
def default_p_sep_by_space(self):
@staticmethod
def default_p_sep_by_space():
return False
def default_n_sep_by_space(self):
@staticmethod
def default_n_sep_by_space():
return False
def check_mon_grouping(self, ids):
def check_mon_grouping(self):
'''
Check if mon_grouping is list of numbers
'''
for currency in self.browse(ids):
try:
grouping = safe_eval(currency.mon_grouping)
for i in grouping:
if not isinstance(i, int):
return False
except Exception:
return False
try:
grouping = safe_eval(self.mon_grouping)
for i in grouping:
if not isinstance(i, int):
return False
except Exception:
return False
return True
def check_xml_record(self, ids, values):
@classmethod
def check_xml_record(cls, records, values):
return True
def search_rec_name(self, name, clause):
ids = []
@classmethod
def search_rec_name(cls, name, clause):
currencies = None
field = None
for field in ('code', 'numeric_code'):
ids = self.search([(field,) + clause[1:]], limit=1)
if ids:
currencies = cls.search([(field,) + clause[1:]], limit=1)
if currencies:
break
if ids:
if currencies:
return [(field,) + clause[1:]]
return [(self._rec_name,) + clause[1:]]
return [(cls._rec_name,) + clause[1:]]
def on_change_with_rate(self, vals):
def on_change_with_rate(self):
now = datetime.date.today()
closer = datetime.date.min
res = Decimal('0.0')
for rate in vals.get('rates', []):
for rate in self.rates or []:
if 'date' not in rate or 'rate' not in rate:
continue
if rate['date'] <= now and rate['date'] > closer:
@@ -132,26 +151,27 @@ class Currency(ModelSQL, ModelView):
closer = rate['date']
return res
def get_rate(self, ids, name):
@staticmethod
def get_rate(currencies, name):
'''
Return the rate at the date from the context or the current date
'''
rate_obj = Pool().get('currency.currency.rate')
date_obj = Pool().get('ir.date')
Rate = Pool().get('currency.currency.rate')
Date = Pool().get('ir.date')
res = {}
date = Transaction().context.get('date', date_obj.today())
for currency_id in ids:
rate_ids = rate_obj.search([
('currency', '=', currency_id),
('date', '<=', date),
], limit=1, order=[('date', 'DESC')])
if rate_ids:
res[currency_id] = rate_ids[0]
date = Transaction().context.get('date', Date.today())
for currency in currencies:
rates = Rate.search([
('currency', '=', currency.id),
('date', '<=', date),
], limit=1, order=[('date', 'DESC')])
if rates:
res[currency.id] = rates[0].id
else:
res[currency_id] = 0.0
res[currency.id] = 0.0
rate_ids = [x for x in res.values() if x]
rates = rate_obj.browse(rate_ids)
rates = Rate.browse(rate_ids)
id2rate = {}
for rate in rates:
id2rate[rate.id] = rate
@@ -160,7 +180,7 @@ class Currency(ModelSQL, ModelView):
res[currency_id] = id2rate[res[currency_id]].rate
return res
def round(self, currency, amount, rounding=ROUND_HALF_EVEN):
def round(self, amount, rounding=ROUND_HALF_EVEN):
'''
Round the amount depending of the currency
@@ -171,80 +191,76 @@ class Currency(ModelSQL, ModelView):
:param rounding: the rounding option
:return: a Decimal
'''
return (amount / currency.rounding).quantize(Decimal('1.'),
rounding=rounding) * currency.rounding
return (amount / self.rounding).quantize(Decimal('1.'),
rounding=rounding) * self.rounding
def is_zero(self, currency, amount):
def is_zero(self, amount):
'Return True if the amount can be considered as zero for the currency'
return abs(self.round(currency, amount)) < currency.rounding
return abs(self.round(amount)) < self.rounding
def compute(self, from_currency, amount, to_currency, round=True):
@classmethod
def compute(cls, from_currency, amount, to_currency, round=True):
'''
Take a currency and an amount
Return the amount to the new currency
Use the rate of the date of the context or the current date if ids are
given
'''
date_obj = Pool().get('ir.date')
lang_obj = Pool().get('ir.lang')
Date = Pool().get('ir.date')
Lang = Pool().get('ir.lang')
from_currency = cls(from_currency.id)
to_currency = cls(to_currency.id)
if isinstance(from_currency, (int, long)):
from_currency = self.browse(from_currency)
if isinstance(to_currency, (int, long)):
to_currency = self.browse(to_currency)
if to_currency == from_currency:
if round:
return self.round(to_currency, amount)
return to_currency.round(amount)
else:
return amount
if (not from_currency.rate) or (not to_currency.rate):
date = Transaction().context.get('date', date_obj.today())
date = Transaction().context.get('date', Date.today())
if not from_currency.rate:
name = from_currency.name
else:
name = to_currency.name
lang_id, = lang_obj.search([
lang, = Lang.search([
('code', '=', Transaction().language),
])
lang = lang_obj.browse(lang_id)
self.raise_user_error('no_rate', (name,
datetime_strftime(date, str(lang.date))))
cls.raise_user_error('no_rate', (name,
datetime_strftime(date, str(lang.date))))
if round:
return self.round(to_currency,
amount * to_currency.rate / from_currency.rate)
return to_currency.round(
amount * to_currency.rate / from_currency.rate)
else:
return amount * to_currency.rate / from_currency.rate
Currency()
class Rate(ModelSQL, ModelView):
'Rate'
_name = 'currency.currency.rate'
_description = __doc__
__name__ = 'currency.currency.rate'
_rec_name = 'date'
date = fields.Date('Date', required=True, select=True)
rate = fields.Numeric('Rate', digits=(12, 6), required=1)
currency = fields.Many2One('currency.currency', 'Currency',
ondelete='CASCADE',)
def __init__(self):
super(Rate, self).__init__()
self._sql_constraints = [
@classmethod
def __setup__(cls):
super(Rate, cls).__setup__()
cls._sql_constraints = [
('date_currency_uniq', 'UNIQUE(date, currency)',
'A currency can only have one rate by date!'),
('check_currency_rate', 'CHECK(rate >= 0)',
'The currency rate must greater than or equal to 0'),
]
self._order.insert(0, ('date', 'DESC'))
]
cls._order.insert(0, ('date', 'DESC'))
def default_date(self):
date_obj = Pool().get('ir.date')
return date_obj.today()
@staticmethod
def default_date():
Date = Pool().get('ir.date')
return Date.today()
def check_xml_record(self, ids, values):
@classmethod
def check_xml_record(self, records, values):
return True
Rate()
+90 -92
View File
@@ -51,19 +51,19 @@ class CurrencyTestCase(unittest.TestCase):
with Transaction().start(DB_NAME, USER,
context=CONTEXT) as transaction:
cu1_id = self.currency.create({
'name': 'cu1',
'symbol': 'cu1',
'code': 'cu1'
})
self.assert_(cu1_id)
cu1 = self.currency.create({
'name': 'cu1',
'symbol': 'cu1',
'code': 'cu1'
})
self.assert_(cu1)
cu2_id = self.currency.create({
'name': 'cu2',
'symbol': 'cu2',
'code': 'cu2'
})
self.assert_(cu2_id)
cu2 = self.currency.create({
'name': 'cu2',
'symbol': 'cu2',
'code': 'cu2'
})
self.assert_(cu2)
transaction.cursor.commit()
@@ -72,18 +72,18 @@ class CurrencyTestCase(unittest.TestCase):
Check grouping
'''
with Transaction().start(DB_NAME, USER, context=CONTEXT):
cu1_id = self.get_currency('cu1')
cu1 = self.get_currency('cu1')
self.assertRaises(Exception, self.currency.write, cu1_id,
self.assertRaises(Exception, self.currency.write, [cu1],
{'mon_grouping': ''})
self.assertRaises(Exception, self.currency.write, cu1_id,
self.assertRaises(Exception, self.currency.write, [cu1],
{'mon_grouping': '[a]'})
self.assertRaises(Exception, self.currency.write, cu1_id,
self.assertRaises(Exception, self.currency.write, [cu1],
{'mon_grouping': '[1,"a"]'})
self.assertRaises(Exception, self.currency.write, cu1_id,
self.assertRaises(Exception, self.currency.write, [cu1],
{'mon_grouping': '[1,"1"]'})
def test0030rate(self):
@@ -92,24 +92,22 @@ class CurrencyTestCase(unittest.TestCase):
'''
with Transaction().start(DB_NAME, USER,
context=CONTEXT) as transaction:
cu1_id = self.get_currency('cu1')
cu2_id = self.get_currency('cu2')
cu1 = self.get_currency('cu1')
cu2 = self.get_currency('cu2')
rate1 = Decimal("1.3")
rate1_id = self.rate.create({
'rate': rate1,
'currency': cu1_id,
})
self.assert_(rate1_id)
rate1 = self.rate.create({
'rate': Decimal("1.3"),
'currency': cu1.id,
})
self.assert_(rate1)
rate2_id = self.rate.create({
'rate': Decimal("1"),
'currency': cu2_id,
})
self.assert_(rate2_id)
rate2 = self.rate.create({
'rate': Decimal("1"),
'currency': cu2.id,
})
self.assert_(rate2)
self.assertEqual(rate1, self.currency.read(cu1_id,
['rate'])['rate'])
self.assertEqual(cu1.rate, Decimal("1.3"))
transaction.cursor.commit()
@@ -121,21 +119,21 @@ class CurrencyTestCase(unittest.TestCase):
context=CONTEXT) as transaction:
today = self.date.today()
cu_id = self.currency.create({
'name': 'cu',
'symbol': 'cu',
'code': 'cu'
})
cu = self.currency.create({
'name': 'cu',
'symbol': 'cu',
'code': 'cu'
})
self.rate.create({
'rate': Decimal("1.3"),
'currency': cu_id,
'date': today,
})
'rate': Decimal("1.3"),
'currency': cu.id,
'date': today,
})
self.assertRaises(Exception, self.rate.create, {
'rate': Decimal("1.3"),
'currency': cu_id,
'currency': cu.id,
'date': today,
})
@@ -147,13 +145,13 @@ class CurrencyTestCase(unittest.TestCase):
'''
with Transaction().start(DB_NAME, USER,
context=CONTEXT) as transaction:
cu1_id = self.get_currency('cu1')
cu2_id = self.get_currency('cu2')
cu1 = self.get_currency('cu1')
cu2 = self.get_currency('cu2')
amount = Decimal("10")
expected = Decimal("13")
converted_amount = self.currency.compute(
cu2_id, amount, cu1_id, True)
cu2, amount, cu1, True)
self.assertEqual(converted_amount, expected)
transaction.cursor.commit()
@@ -163,53 +161,53 @@ class CurrencyTestCase(unittest.TestCase):
Conversion with rounding on non-finite decimal representation
'''
with Transaction().start(DB_NAME, USER, context=CONTEXT):
cu1_id = self.get_currency('cu1')
cu2_id = self.get_currency('cu2')
cu1 = self.get_currency('cu1')
cu2 = self.get_currency('cu2')
amount = Decimal("10")
expected = Decimal("7.69")
converted_amount = self.currency.compute(
cu1_id, amount, cu2_id, True)
self.assert_(converted_amount == expected)
cu1, amount, cu2, True)
self.assertEqual(converted_amount, expected)
def test0070compute_nonfinite_worounding(self):
'''
Same without rounding
'''
with Transaction().start(DB_NAME, USER, context=CONTEXT):
cu1_id = self.get_currency('cu1')
cu2_id = self.get_currency('cu2')
cu1 = self.get_currency('cu1')
cu2 = self.get_currency('cu2')
amount = Decimal("10")
expected = Decimal('7.692307692307692307692307692')
converted_amount = self.currency.compute(
cu1_id, amount, cu2_id, False)
self.assert_(converted_amount == expected)
cu1, amount, cu2, False)
self.assertEqual(converted_amount, expected)
def test0080compute_same(self):
'''
Conversion to the same currency
'''
with Transaction().start(DB_NAME, USER, context=CONTEXT):
cu1_id = self.get_currency('cu1')
cu1 = self.get_currency('cu1')
amount = Decimal("10")
converted_amount = self.currency.compute(
cu1_id, amount, cu1_id, True)
self.assert_(converted_amount == amount)
cu1, amount, cu1, True)
self.assertEqual(converted_amount, amount)
def test0090compute_zeroamount(self):
'''
Conversion with zero amount
'''
with Transaction().start(DB_NAME, USER, context=CONTEXT):
cu1_id = self.get_currency('cu1')
cu2_id = self.get_currency('cu2')
cu1 = self.get_currency('cu1')
cu2 = self.get_currency('cu2')
expected = Decimal("0")
converted_amount = self.currency.compute(
cu1_id, Decimal("0"), cu2_id, True)
self.assert_(converted_amount == expected)
cu1, Decimal("0"), cu2, True)
self.assertEqual(converted_amount, expected)
def test0100compute_zerorate(self):
'''
@@ -217,20 +215,20 @@ class CurrencyTestCase(unittest.TestCase):
'''
with Transaction().start(DB_NAME, USER,
context=CONTEXT) as transaction:
cu1_id = self.get_currency('cu1')
cu2_id = self.get_currency('cu2')
cu1 = self.get_currency('cu1')
cu2 = self.get_currency('cu2')
rate_ids = self.rate.search([
('currency', '=', cu1_id),
rates = self.rate.search([
('currency', '=', cu1.id),
], 0, 1, None)
self.rate.write(rate_ids, {
self.rate.write(rates, {
'rate': Decimal("0"),
})
amount = Decimal("10")
self.assertRaises(Exception, self.currency.compute,
cu1_id, amount, cu2_id, True)
cu1, amount, cu2, True)
self.assertRaises(Exception, self.currency.compute,
cu2_id, amount, cu1_id, True)
cu2, amount, cu1, True)
transaction.cursor.rollback()
@@ -240,18 +238,18 @@ class CurrencyTestCase(unittest.TestCase):
'''
with Transaction().start(DB_NAME, USER,
context=CONTEXT) as transaction:
cu1_id = self.get_currency('cu1')
cu3_id = self.currency.create({
'name': 'cu3',
'symbol': 'cu3',
'code': 'cu3'
})
cu1 = self.get_currency('cu1')
cu3 = self.currency.create({
'name': 'cu3',
'symbol': 'cu3',
'code': 'cu3'
})
amount = Decimal("10")
self.assertRaises(Exception, self.currency.compute,
cu3_id, amount, cu1_id, True)
cu3, amount, cu1, True)
self.assertRaises(Exception, self.currency.compute,
cu1_id, amount, cu3_id, True)
cu1, amount, cu3, True)
transaction.cursor.rollback()
@@ -261,20 +259,20 @@ class CurrencyTestCase(unittest.TestCase):
'''
with Transaction().start(DB_NAME, USER,
context=CONTEXT) as transaction:
cu3_id = self.currency.create({
'name': 'cu3',
'symbol': 'cu3',
'code': 'cu3'
})
cu4_id = self.currency.create({
'name': 'cu4',
'symbol': 'cu4',
'code': 'cu4'
})
cu3 = self.currency.create({
'name': 'cu3',
'symbol': 'cu3',
'code': 'cu3'
})
cu4 = self.currency.create({
'name': 'cu4',
'symbol': 'cu4',
'code': 'cu4'
})
amount = Decimal("10")
self.assertRaises(Exception, self.currency.compute,
cu3_id, amount, cu4_id, True)
cu3, amount, cu4, True)
transaction.cursor.rollback()
@@ -285,13 +283,13 @@ class CurrencyTestCase(unittest.TestCase):
with Transaction().start(DB_NAME, USER,
context=CONTEXT) as transaction:
codes = ['cu%s' % (i + 1) for i in range(2)]
currency_ids = [self.get_currency(i) for i in codes]
self.currency.delete(currency_ids)
currencies = [self.get_currency(i) for i in codes]
self.currency.delete(currencies)
rate_ids = self.rate.search([(
'currency', 'in', currency_ids,
)], 0, None, None)
self.assertFalse(rate_ids)
rates = self.rate.search([(
'currency', 'in', map(int, currencies),
)], 0, None, None)
self.assertFalse(rates)
transaction.cursor.rollback()