first commit
This commit is contained in:
6
modules/currency/tests/__init__.py
Normal file
6
modules/currency/tests/__init__.py
Normal file
@@ -0,0 +1,6 @@
|
||||
# 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 .test_module import add_currency_rate, create_currency
|
||||
|
||||
__all__ = ['create_currency', 'add_currency_rate']
|
||||
BIN
modules/currency/tests/__pycache__/__init__.cpython-311.pyc
Normal file
BIN
modules/currency/tests/__pycache__/__init__.cpython-311.pyc
Normal file
Binary file not shown.
BIN
modules/currency/tests/__pycache__/test_module.cpython-311.pyc
Normal file
BIN
modules/currency/tests/__pycache__/test_module.cpython-311.pyc
Normal file
Binary file not shown.
BIN
modules/currency/tests/__pycache__/test_scenario.cpython-311.pyc
Normal file
BIN
modules/currency/tests/__pycache__/test_scenario.cpython-311.pyc
Normal file
Binary file not shown.
BIN
modules/currency/tests/__pycache__/tools.cpython-311.pyc
Normal file
BIN
modules/currency/tests/__pycache__/tools.cpython-311.pyc
Normal file
Binary file not shown.
23
modules/currency/tests/scenario_currency_compute.rst
Normal file
23
modules/currency/tests/scenario_currency_compute.rst
Normal file
@@ -0,0 +1,23 @@
|
||||
=========================
|
||||
Currency Compute Scenario
|
||||
=========================
|
||||
|
||||
Imports::
|
||||
|
||||
>>> from decimal import Decimal
|
||||
|
||||
>>> from proteus import Model
|
||||
>>> from trytond.modules.currency.tests.tools import get_currency
|
||||
>>> from trytond.tests.tools import activate_modules
|
||||
|
||||
Activate modules::
|
||||
|
||||
>>> config = activate_modules('currency')
|
||||
|
||||
Call compute::
|
||||
|
||||
>>> Currency = Model.get('currency.currency')
|
||||
>>> usd = get_currency(code='USD')
|
||||
>>> eur = get_currency(code='EUR')
|
||||
>>> Currency.compute(usd.id, Decimal('10.00'), eur.id, {})
|
||||
Decimal('20.00')
|
||||
21
modules/currency/tests/scenario_currency_import.rst
Normal file
21
modules/currency/tests/scenario_currency_import.rst
Normal file
@@ -0,0 +1,21 @@
|
||||
===============
|
||||
Currency Import
|
||||
===============
|
||||
|
||||
Imports::
|
||||
|
||||
>>> from proteus import Model
|
||||
>>> from trytond.modules.currency.scripts import import_currencies
|
||||
>>> from trytond.tests.tools import activate_modules
|
||||
|
||||
Activate modules::
|
||||
|
||||
>>> config = activate_modules('currency')
|
||||
|
||||
Import currencies::
|
||||
|
||||
>>> Currency = Model.get('currency.currency')
|
||||
>>> eur = Currency(name="Euro", symbol="€", code='EUR')
|
||||
>>> eur.save()
|
||||
|
||||
>>> import_currencies.do_import()
|
||||
55
modules/currency/tests/scenario_currency_rate_update.rst
Normal file
55
modules/currency/tests/scenario_currency_rate_update.rst
Normal file
@@ -0,0 +1,55 @@
|
||||
====================
|
||||
Currency Rate Update
|
||||
====================
|
||||
|
||||
Imports::
|
||||
|
||||
>>> import datetime as dt
|
||||
|
||||
>>> from proteus import Model
|
||||
>>> from trytond.tests.tools import activate_modules
|
||||
|
||||
>>> today = dt.date.today()
|
||||
>>> previous_week = today - dt.timedelta(days=7)
|
||||
>>> before_previous_week = previous_week - dt.timedelta(days=1)
|
||||
|
||||
Activate modules::
|
||||
|
||||
>>> config = activate_modules('currency')
|
||||
|
||||
Import models::
|
||||
|
||||
>>> Currency = Model.get('currency.currency')
|
||||
>>> Cron = Model.get('currency.cron')
|
||||
|
||||
Create some currencies::
|
||||
|
||||
>>> eur = Currency(name="Euro", code='EUR', symbol="€")
|
||||
>>> eur.save()
|
||||
>>> usd = Currency(name="U.S. Dollar", code='USD', symbol="$")
|
||||
>>> usd.save()
|
||||
|
||||
Setup cron::
|
||||
|
||||
>>> cron = Cron()
|
||||
>>> cron.source = 'ecb'
|
||||
>>> cron.frequency = 'daily'
|
||||
>>> cron.day = None
|
||||
>>> cron.currency = eur
|
||||
>>> cron.currencies.append(Currency(usd.id))
|
||||
>>> cron.last_update = before_previous_week
|
||||
>>> cron.save()
|
||||
|
||||
Run update::
|
||||
|
||||
>>> cron.click('run')
|
||||
>>> cron.last_update >= previous_week
|
||||
True
|
||||
|
||||
>>> eur.reload()
|
||||
>>> rate = [r for r in eur.rates if r.date < today][0]
|
||||
>>> rate.rate
|
||||
Decimal('1.000000')
|
||||
>>> rate = [r for r in usd.rates if r.date < today][0]
|
||||
>>> bool(rate.rate)
|
||||
True
|
||||
403
modules/currency/tests/test_module.py
Normal file
403
modules/currency/tests/test_module.py
Normal file
@@ -0,0 +1,403 @@
|
||||
# This file is part of Tryton. The COPYRIGHT file at the top level of
|
||||
# this repository contains the full copyright notices and license terms.
|
||||
|
||||
import datetime
|
||||
from decimal import ROUND_HALF_DOWN, Decimal
|
||||
|
||||
from trytond import backend
|
||||
from trytond.modules.currency.ecb import (
|
||||
RatesNotAvailableError, UnsupportedCurrencyError, get_rates)
|
||||
from trytond.pool import Pool
|
||||
from trytond.tests.test_tryton import (
|
||||
ModuleTestCase, TestCase, with_transaction)
|
||||
from trytond.transaction import Transaction
|
||||
|
||||
|
||||
def create_currency(name):
|
||||
pool = Pool()
|
||||
Currency = pool.get('currency.currency')
|
||||
return Currency.create([{
|
||||
'name': name,
|
||||
'symbol': name,
|
||||
'code': name,
|
||||
}])[0]
|
||||
|
||||
|
||||
def add_currency_rate(currency, rate, date=datetime.date.min):
|
||||
pool = Pool()
|
||||
Rate = pool.get('currency.currency.rate')
|
||||
return Rate.create([{
|
||||
'currency': currency.id,
|
||||
'rate': rate,
|
||||
'date': date,
|
||||
}])[0]
|
||||
|
||||
|
||||
class CurrencyTestCase(ModuleTestCase):
|
||||
'Test Currency module'
|
||||
module = 'currency'
|
||||
|
||||
def get_currency(self, code):
|
||||
return self.currency.search([
|
||||
('code', '=', code),
|
||||
], limit=1)[0]
|
||||
|
||||
@with_transaction()
|
||||
def test_currencies(self):
|
||||
'Create currencies'
|
||||
cu1 = create_currency('cu1')
|
||||
cu2 = create_currency('cu2')
|
||||
self.assertTrue(cu1)
|
||||
self.assertTrue(cu2)
|
||||
|
||||
@with_transaction()
|
||||
def test_rate(self):
|
||||
'Create rates'
|
||||
cu1 = create_currency('cu1')
|
||||
cu2 = create_currency('cu2')
|
||||
|
||||
rate1 = add_currency_rate(cu1, Decimal("1.3"))
|
||||
rate2 = add_currency_rate(cu2, Decimal("1"))
|
||||
self.assertTrue(rate1)
|
||||
self.assertTrue(rate2)
|
||||
|
||||
self.assertEqual(cu1.rate, Decimal("1.3"))
|
||||
|
||||
@with_transaction()
|
||||
def test_rate_unicity(self):
|
||||
'Rate unicity'
|
||||
pool = Pool()
|
||||
Rate = pool.get('currency.currency.rate')
|
||||
Date = pool.get('ir.date')
|
||||
today = Date.today()
|
||||
|
||||
cu = create_currency('cu')
|
||||
|
||||
Rate.create([{
|
||||
'rate': Decimal("1.3"),
|
||||
'currency': cu.id,
|
||||
'date': today,
|
||||
}])
|
||||
|
||||
self.assertRaises(Exception, Rate.create, {
|
||||
'rate': Decimal("1.3"),
|
||||
'currency': cu.id,
|
||||
'date': today,
|
||||
})
|
||||
|
||||
@with_transaction()
|
||||
def test_round(self):
|
||||
"Test simple round"
|
||||
cu = create_currency('cu')
|
||||
cu.rounding = Decimal('0.001')
|
||||
cu.digits = 3
|
||||
cu.save()
|
||||
|
||||
rounded = cu.round(Decimal('1.2345678'))
|
||||
|
||||
self.assertEqual(rounded, Decimal('1.235'))
|
||||
|
||||
@with_transaction()
|
||||
def test_round_non_unity(self):
|
||||
"Test round with non unity"
|
||||
cu = create_currency('cu')
|
||||
cu.rounding = Decimal('0.02')
|
||||
cu.digits = 2
|
||||
cu.save()
|
||||
|
||||
rounded = cu.round(Decimal('1.2345'))
|
||||
|
||||
self.assertEqual(rounded, Decimal('1.24'))
|
||||
|
||||
@with_transaction()
|
||||
def test_round_big_number(self):
|
||||
"Test rounding big number"
|
||||
cu = create_currency('cu')
|
||||
|
||||
rounded = cu.round(Decimal('1E50'))
|
||||
|
||||
self.assertEqual(rounded, Decimal('1E50'))
|
||||
|
||||
@with_transaction()
|
||||
def test_round_opposite(self):
|
||||
"Test the opposite rounding"
|
||||
cu = create_currency('cu')
|
||||
cu.save()
|
||||
|
||||
rounded = cu.round(Decimal('1.235'))
|
||||
self.assertEqual(rounded, Decimal('1.24'))
|
||||
opposite_rounded = cu.round(Decimal('1.235'), opposite=True)
|
||||
self.assertEqual(opposite_rounded, Decimal('1.24'))
|
||||
|
||||
@with_transaction()
|
||||
def test_round_opposite_HALF_DOWN(self):
|
||||
"Test the oposite rounding of ROUND_HALF_DOWN"
|
||||
cu = create_currency('cu')
|
||||
cu.save()
|
||||
|
||||
rounded = cu.round(Decimal('1.235'), rounding=ROUND_HALF_DOWN)
|
||||
self.assertEqual(rounded, Decimal('1.23'))
|
||||
opposite_rounded = cu.round(
|
||||
Decimal('1.235'), rounding=ROUND_HALF_DOWN, opposite=True)
|
||||
self.assertEqual(opposite_rounded, Decimal('1.24'))
|
||||
|
||||
@with_transaction()
|
||||
def test_is_zero(self):
|
||||
"Test is zero"
|
||||
cu = create_currency('cu')
|
||||
cu.rounding = Decimal('0.001')
|
||||
cu.digits = 3
|
||||
cu.save()
|
||||
|
||||
for value, result in [
|
||||
(Decimal('0'), True),
|
||||
(Decimal('0.0002'), True),
|
||||
(Decimal('0.0009'), False),
|
||||
(Decimal('0.002'), False),
|
||||
]:
|
||||
with self.subTest(value=value):
|
||||
self.assertEqual(cu.is_zero(value), result)
|
||||
with self.subTest(value=-value):
|
||||
self.assertEqual(cu.is_zero(-value), result)
|
||||
|
||||
@with_transaction()
|
||||
def test_compute_simple(self):
|
||||
'Simple conversion'
|
||||
pool = Pool()
|
||||
Currency = pool.get('currency.currency')
|
||||
cu1 = create_currency('cu1')
|
||||
cu2 = create_currency('cu2')
|
||||
add_currency_rate(cu1, Decimal("1.3"))
|
||||
add_currency_rate(cu2, Decimal("1"))
|
||||
|
||||
amount = Decimal("10")
|
||||
expected = Decimal("13")
|
||||
converted_amount = Currency.compute(
|
||||
cu2, amount, cu1, True)
|
||||
self.assertEqual(converted_amount, expected)
|
||||
|
||||
@with_transaction()
|
||||
def test_compute_nonfinite(self):
|
||||
'Conversion with rounding on non-finite decimal representation'
|
||||
pool = Pool()
|
||||
Currency = pool.get('currency.currency')
|
||||
cu1 = create_currency('cu1')
|
||||
cu2 = create_currency('cu2')
|
||||
add_currency_rate(cu1, Decimal("1.3"))
|
||||
add_currency_rate(cu2, Decimal("1"))
|
||||
|
||||
amount = Decimal("10")
|
||||
expected = Decimal("7.69")
|
||||
converted_amount = Currency.compute(
|
||||
cu1, amount, cu2, True)
|
||||
self.assertEqual(converted_amount, expected)
|
||||
|
||||
@with_transaction()
|
||||
def test_compute_nonfinite_worounding(self):
|
||||
'Same without rounding'
|
||||
pool = Pool()
|
||||
Currency = pool.get('currency.currency')
|
||||
cu1 = create_currency('cu1')
|
||||
cu2 = create_currency('cu2')
|
||||
add_currency_rate(cu1, Decimal("1.3"))
|
||||
add_currency_rate(cu2, Decimal("1"))
|
||||
|
||||
amount = Decimal("10")
|
||||
expected = Decimal('7.692307692307692307692307692')
|
||||
converted_amount = Currency.compute(
|
||||
cu1, amount, cu2, False)
|
||||
self.assertEqual(converted_amount, expected)
|
||||
|
||||
@with_transaction()
|
||||
def test_compute_same(self):
|
||||
'Conversion to the same currency'
|
||||
pool = Pool()
|
||||
Currency = pool.get('currency.currency')
|
||||
cu1 = create_currency('cu1')
|
||||
add_currency_rate(cu1, Decimal("1.3"))
|
||||
|
||||
amount = Decimal("10")
|
||||
converted_amount = Currency.compute(
|
||||
cu1, amount, cu1, True)
|
||||
self.assertEqual(converted_amount, amount)
|
||||
|
||||
@with_transaction()
|
||||
def test_compute_zeroamount(self):
|
||||
'Conversion with zero amount'
|
||||
pool = Pool()
|
||||
Currency = pool.get('currency.currency')
|
||||
cu1 = create_currency('cu1')
|
||||
cu2 = create_currency('cu2')
|
||||
add_currency_rate(cu1, Decimal("1.3"))
|
||||
add_currency_rate(cu2, Decimal("1"))
|
||||
|
||||
expected = Decimal("0")
|
||||
converted_amount = Currency.compute(
|
||||
cu1, Decimal("0"), cu2, True)
|
||||
self.assertEqual(converted_amount, expected)
|
||||
|
||||
@with_transaction()
|
||||
def test_compute_zerorate(self):
|
||||
'Conversion with zero rate'
|
||||
pool = Pool()
|
||||
Currency = pool.get('currency.currency')
|
||||
cu1 = create_currency('cu1')
|
||||
cu2 = create_currency('cu2')
|
||||
|
||||
add_currency_rate(cu2, Decimal('1'))
|
||||
|
||||
amount = Decimal("10")
|
||||
self.assertRaises(Exception, Currency.compute,
|
||||
cu1, amount, cu2, True)
|
||||
self.assertRaises(Exception, Currency.compute,
|
||||
cu2, amount, cu1, True)
|
||||
|
||||
@with_transaction()
|
||||
def test_compute_missingrate(self):
|
||||
'Conversion with missing rate'
|
||||
pool = Pool()
|
||||
Currency = pool.get('currency.currency')
|
||||
cu1 = create_currency('cu1')
|
||||
cu3 = create_currency('cu3')
|
||||
add_currency_rate(cu1, Decimal("1.3"))
|
||||
|
||||
amount = Decimal("10")
|
||||
self.assertRaises(Exception, Currency.compute,
|
||||
cu3, amount, cu1, True)
|
||||
self.assertRaises(Exception, Currency.compute,
|
||||
cu1, amount, cu3, True)
|
||||
|
||||
@with_transaction()
|
||||
def test_compute_bothmissingrate(self):
|
||||
'Conversion with both missing rate'
|
||||
pool = Pool()
|
||||
Currency = pool.get('currency.currency')
|
||||
cu3 = create_currency('cu3')
|
||||
cu4 = create_currency('cu4')
|
||||
|
||||
amount = Decimal("10")
|
||||
self.assertRaises(Exception, Currency.compute,
|
||||
cu3, amount, cu4, True)
|
||||
|
||||
@with_transaction()
|
||||
def test_delete_cascade(self):
|
||||
'Test deletion of currency deletes rates'
|
||||
pool = Pool()
|
||||
Currency = pool.get('currency.currency')
|
||||
Rate = pool.get('currency.currency.rate')
|
||||
currencies = [create_currency('cu%s' % i) for i in range(3)]
|
||||
[add_currency_rate(c, Decimal('1')) for c in currencies]
|
||||
Currency.delete(currencies)
|
||||
|
||||
rates = Rate.search([(
|
||||
'currency', 'in', list(map(int, currencies)),
|
||||
)], 0, None, None)
|
||||
self.assertFalse(rates)
|
||||
|
||||
@with_transaction()
|
||||
def test_currency_rate_sql(self):
|
||||
"Test currency rate SQL"
|
||||
pool = Pool()
|
||||
Currency = pool.get('currency.currency')
|
||||
Rate = pool.get('currency.currency.rate')
|
||||
transaction = Transaction()
|
||||
cursor = transaction.connection.cursor()
|
||||
date = datetime.date
|
||||
|
||||
cu1 = create_currency('cu1')
|
||||
for date_, rate in [
|
||||
(date(2017, 1, 1), Decimal(1)),
|
||||
(date(2017, 2, 1), Decimal(2)),
|
||||
(date(2017, 3, 1), Decimal(3))]:
|
||||
add_currency_rate(cu1, rate, date_)
|
||||
cu2 = create_currency('cu2')
|
||||
for date_, rate in [
|
||||
(date(2017, 2, 1), Decimal(2)),
|
||||
(date(2017, 4, 1), Decimal(4))]:
|
||||
add_currency_rate(cu2, rate, date_)
|
||||
|
||||
query = Currency.currency_rate_sql()
|
||||
if backend.name == 'sqlite':
|
||||
query.columns[-1].output_name += (
|
||||
' [%s]' % Rate.date.sql_type().base)
|
||||
cursor.execute(*query)
|
||||
data = set(cursor)
|
||||
result = {
|
||||
(cu1.id, Decimal(1), date(2017, 1, 1), date(2017, 2, 1)),
|
||||
(cu1.id, Decimal(2), date(2017, 2, 1), date(2017, 3, 1)),
|
||||
(cu1.id, Decimal(3), date(2017, 3, 1), None),
|
||||
(cu2.id, Decimal(2), date(2017, 2, 1), date(2017, 4, 1)),
|
||||
(cu2.id, Decimal(4), date(2017, 4, 1), None),
|
||||
}
|
||||
|
||||
self.assertSetEqual(data, result)
|
||||
|
||||
|
||||
class ECBtestCase(TestCase):
|
||||
|
||||
def test_rate_EUR_week_ago(self):
|
||||
"Fetch EUR rates a week ago"
|
||||
week_ago = datetime.date.today() - datetime.timedelta(days=7)
|
||||
|
||||
rates = get_rates('EUR', week_ago)
|
||||
|
||||
self.assertNotIn('EUR', rates)
|
||||
self.assertIn('USD', rates)
|
||||
|
||||
def test_rate_USD_week_ago(self):
|
||||
"Fetch USD rates a week ago"
|
||||
week_ago = datetime.date.today() - datetime.timedelta(days=7)
|
||||
|
||||
rates = get_rates('USD', week_ago)
|
||||
|
||||
self.assertIn('EUR', rates)
|
||||
self.assertNotIn('USD', rates)
|
||||
|
||||
def test_rate_EUR_on_weekend(self):
|
||||
"Fetch EUR rates on weekend"
|
||||
|
||||
rates_fr = get_rates('EUR', datetime.date(2022, 9, 30))
|
||||
rates_sa = get_rates('EUR', datetime.date(2022, 10, 2))
|
||||
rates_su = get_rates('EUR', datetime.date(2022, 10, 2))
|
||||
|
||||
self.assertEqual(rates_sa, rates_fr)
|
||||
self.assertEqual(rates_su, rates_fr)
|
||||
|
||||
def test_rate_USD_on_weekend(self):
|
||||
"Fetch USD rates on weekend"
|
||||
|
||||
rates_fr = get_rates('USD', datetime.date(2022, 9, 30))
|
||||
rates_sa = get_rates('USD', datetime.date(2022, 10, 2))
|
||||
rates_su = get_rates('USD', datetime.date(2022, 10, 2))
|
||||
|
||||
self.assertEqual(rates_sa, rates_fr)
|
||||
self.assertEqual(rates_su, rates_fr)
|
||||
|
||||
def test_rate_EUR_start_date(self):
|
||||
"Fetch EUR rates at start date"
|
||||
|
||||
rates = get_rates('EUR', datetime.date(1999, 1, 4))
|
||||
|
||||
self.assertEqual(rates['USD'], Decimal('1.1789'))
|
||||
|
||||
def test_rate_USD_start_date(self):
|
||||
"Fetch USD rates at start date"
|
||||
|
||||
rates = get_rates('USD', datetime.date(1999, 1, 4))
|
||||
|
||||
self.assertEqual(rates['EUR'], Decimal('0.8482'))
|
||||
|
||||
def test_rate_in_future(self):
|
||||
"Fetch rate in future raise an error"
|
||||
future = datetime.date.today() + datetime.timedelta(days=2)
|
||||
|
||||
with self.assertRaises(RatesNotAvailableError):
|
||||
get_rates('USD', future)
|
||||
|
||||
def test_rate_unsupported_currency(self):
|
||||
"Fetch rate for unsupported currency"
|
||||
with self.assertRaises(UnsupportedCurrencyError):
|
||||
get_rates('XXX', datetime.date(2022, 10, 3))
|
||||
|
||||
|
||||
del ModuleTestCase
|
||||
10
modules/currency/tests/test_scenario.py
Normal file
10
modules/currency/tests/test_scenario.py
Normal file
@@ -0,0 +1,10 @@
|
||||
# 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.tests.test_tryton import TEST_NETWORK, load_doc_tests
|
||||
|
||||
|
||||
def load_tests(*args, **kwargs):
|
||||
if not TEST_NETWORK:
|
||||
kwargs.setdefault('skips', set()).add('scenario_currency_import.rst')
|
||||
return load_doc_tests(__name__, __file__, *args, **kwargs)
|
||||
42
modules/currency/tests/tools.py
Normal file
42
modules/currency/tests/tools.py
Normal file
@@ -0,0 +1,42 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# This file is part of Tryton. The COPYRIGHT file at the top level of
|
||||
# this repository contains the full copyright notices and license terms.
|
||||
import datetime
|
||||
from decimal import Decimal
|
||||
|
||||
from proteus import Model
|
||||
|
||||
__all__ = ['get_currency']
|
||||
|
||||
_names = {
|
||||
'USD': 'U.S. Dollar',
|
||||
'EUR': 'Euro',
|
||||
}
|
||||
_symbols = {
|
||||
'USD': '$',
|
||||
'EUR': '€',
|
||||
}
|
||||
_rates = {
|
||||
'USD': Decimal('1.0'),
|
||||
'EUR': Decimal('2.0'),
|
||||
}
|
||||
|
||||
|
||||
def get_currency(code='USD', config=None):
|
||||
"Get currency with code"
|
||||
Currency = Model.get('currency.currency', config=config)
|
||||
CurrencyRate = Model.get('currency.currency.rate', config=config)
|
||||
|
||||
currencies = Currency.find([('code', '=', code)])
|
||||
if not currencies:
|
||||
currency = Currency(name=_names.get(code, code),
|
||||
symbol=_symbols.get(code), code=code,
|
||||
rounding=Decimal('0.01'))
|
||||
currency.save()
|
||||
rate = _rates.get(code)
|
||||
if rate is not None:
|
||||
CurrencyRate(date=datetime.date.min, rate=rate,
|
||||
currency=currency).save()
|
||||
else:
|
||||
currency, = currencies
|
||||
return currency
|
||||
Reference in New Issue
Block a user