Работа с денежными суммами
Для работы с деньгами heap-таблицы поддерживают специальный тип поля Money , который представляет собой семантически корректное представление денежных сумм. Обладает следующими свойствами:
-
Хранит сумму вместе с валютой.
-
Значения представлены в виде экземпляров специального класса Money , который предоставляет методы для корректных с точки зрения округления и валюты операций над деньгами.
-
Методы фильтрации и сортировки таблицы поддерживают специальный синтаксис и семантику для денежного типа
Фильтрация
Фильтрация по денежному типу поддерживает несколько вариантов:
-
По деньгам в целом - сумма с учётом валюты. Наиболее "безопасный" вариант.
-
По сумме без учёта валюты. Можно выстрелить себе в ногу, но в умелых руках может быть полезно.
-
Отдельно по валюте.
Безопасная фильтрация по деньгам с учётом валюты
Если у в таблице объявлено поле money: Heap.Money() , то вы можете "сравнивать" его в фильтрации:
-
С объектом класса
Moneyimport { Money } from '@app/heap' await table.findAll(ctx, { where: { money: new Money(10, 'RUB')} } -
С парой сумма+валюта вида
[10, 'RUB']await table.findAll(ctx, { where: { money: { $lte: [1000, 'RUB'] } } } -
С нулём. Сравнение с любыми другими значениями суммы без валюты небезопасно.
await table.findAll(ctx, { where: { money: { $gt: 0 } } }
В этом режиме (когда мы при фильтрации "обращаемся" непосредственно к полю), поиск происходит в рамках фиксированной валюты из правой части фильтра, за исключением сравнения с нулём в третьем варианте.
В этом режиме не поддерживается IN-фильтрация, когда в правой части указан массив со списком значений. Если необходимо сравнить со списком значений, можно использовать сравнение с псевдо-полем amount (см. ниже).
Фильтрация отдельно по сумме и валюте (псевдо-поля amount и currency )
Для случаев, когда разработчик знает, что делает, поля типа Heap.Money поддерживают фильтрацию отдельно по сумме, отдельно по валюте, а также их комбинации. Для этого в фильтре можно использовать специальные дочерние псевдо-поля amount и currency. Для каждого из них поддерживается IN-фильтрация по списку значений, но для currency не поддерживаются стандартные операторы сравнения.
-
Только сумма:
await table.findBy(ctx, { money: { amount: { $gt: 100 } } }) await table.findAll(ctx, { where: { money: { amount: [10, 20, 30, 40] } } }) -
Только валюта:
await table.findBy(ctx, { money: { currency: 'USD' } }) await table.findAll(ctx, { where: { money: { currency: ['RUB', 'EUR'] } } }) -
Когда указаны оба поля, это сооветствует двум условиям, объединённым через
AND:await table.findBy(ctx, { money: { amount: [10, 20, 30, 40] currency: 'USD' } }) await table.findAll(ctx, { where: { money: { amount: { $lt: 1000 }, currency: ['USD', 'EUR'] } } })
Сортировка
Если поле с типом Heap.Money используется в свойстве order метода findAll, то действуют следующие принципы:
- Сначала записи сортируются по коду валюты в алфавитном порядке (в соответствии с выбранным направлением сортировки).
- Группа каждой валюты сортируется по числовому значению суммы отдельно в соответствии с выбранным направлением сортировки.
Сортировка по сумме без учёта валюты, а также по отдельно по валюте не имеет практического смысла и поэтому не поддерживается. Если необходимо получить строго упорядоченный по сумме список, следует отфильтровать его по одной валюте с помошью псевдо-поля currency.
await table.findAll(ctx, { where: { money: { currency: 'USD' } },
order: { money: 'desc' } })
Автоматическое приведение значений к единой валюте для корректной сортировки также не поддерживается, но легко выполнимо в обычном js-коде, если имеются курсы обмена валют.
