diff --git a/CHANGELOG b/CHANGELOG index 49970d4e53..2bdea310be 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,4 @@ +* Add SQL query for currency by date range * Remove monetary formatting from currency Version 4.6.0 - 2017-10-30 diff --git a/currency.py b/currency.py index 06788dce85..35b75c0343 100644 --- a/currency.py +++ b/currency.py @@ -4,6 +4,9 @@ import datetime from decimal import Decimal, ROUND_HALF_EVEN +from sql import Window +from sql.functions import NthValue + from trytond import backend from trytond.model import ModelView, ModelSQL, fields, Unique, Check from trytond.tools import datetime_strftime @@ -175,6 +178,40 @@ class Currency(ModelSQL, ModelView): else: return amount * to_currency.rate / from_currency.rate + @classmethod + def currency_rate_sql(cls): + "Return a SQL query with currency, rate, start_date and end_date" + pool = Pool() + Rate = pool.get('currency.currency.rate') + transaction = Transaction() + database = transaction.database + + rate = Rate.__table__() + if database.has_window_functions(): + window = Window( + [rate.currency], + order_by=[rate.date.asc], + frame='ROWS', start=0, end=1) + # Use NthValue instead of LastValue to get NULL for the last row + end_date = NthValue(rate.date, 2, window=window) + else: + next_rate = Rate.__table__() + end_date = next_rate.select( + next_rate.date, + where=(next_rate.currency == rate.currency) + & (next_rate.date > rate.date), + order_by=[next_rate.date.asc], + limit=1) + + query = (rate + .select( + rate.currency.as_('currency'), + rate.rate.as_('rate'), + rate.date.as_('start_date'), + end_date.as_('end_date'), + )) + return query + class Rate(ModelSQL, ModelView): 'Rate' diff --git a/tests/test_currency.py b/tests/test_currency.py index e53ee4e27f..a880970cee 100644 --- a/tests/test_currency.py +++ b/tests/test_currency.py @@ -1,8 +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. +import datetime import unittest from decimal import Decimal import trytond.tests.test_tryton +from trytond.transaction import Transaction from trytond.tests.test_tryton import ModuleTestCase, with_transaction from trytond.pool import Pool @@ -17,12 +19,15 @@ def create_currency(name): }])[0] -def add_currency_rate(currency, rate): +def add_currency_rate(currency, rate, date=None): pool = Pool() Rate = pool.get('currency.currency.rate') + if date is None: + date = Rate.default_date() return Rate.create([{ 'currency': currency.id, 'rate': rate, + 'date': date, }])[0] @@ -213,6 +218,40 @@ class CurrencyTestCase(ModuleTestCase): )], 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') + 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() + cursor.execute(*query) + data = set(cursor.fetchall()) + 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) + def suite(): suite = trytond.tests.test_tryton.suite()