diff --git a/__init__.py b/__init__.py index ba49a66aed..d0e99ecf81 100644 --- a/__init__.py +++ b/__init__.py @@ -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') diff --git a/currency.py b/currency.py index 6e6cd5e8b8..618bd8e045 100644 --- a/currency.py +++ b/currency.py @@ -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() diff --git a/tests/test_currency.py b/tests/test_currency.py index bf8a0d384c..e2b3f2b2cf 100644 --- a/tests/test_currency.py +++ b/tests/test_currency.py @@ -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()