Специальный класс-обёртка для семантически-корректной работы с денежными суммами.
Хранит информацию о валюте и сумме с точностью, соответствующей минимальной единицей валюты. Предоставляет методы для операций с денежными суммами с "правильной" точностью и без проблем, связанных с хранением дробных значений в переменных с плавающей запятой.
Экземплярами класса Money представлены runtime-значения полей heap-таблиц, объявленных через Heap. Money.
Класс Money может быть использован для удобной работы с денежными суммами вне контекста хипа.
import { Money } from '@app/heap'
const moneyVal = new Money(amount, currency)
constructor(amount: number, currency: Currency)
constructor(moneyLike: { amount: number, currency: Currency })
constructor(money: Money)
constructor(serialized: [amount: number, currency: Currency])
Конструктор класса. Принимает на вход как другой экземпляр Money
, так и другие форматы.
amount: number
сумма в указанной валюте.
currency: Currency
стрёхбуквенный международный код валюты (ISO 4217).
Сумма может быть передана с любой точностью, но всегда округляется до количества знаков после запятой, указанного в стандарте ISO 4217 для выбранной валюты (используется метаинформация о валюте из встроенной в JS подсистемы Intl. Округление производится по правилу "half away from zero".
amount: number
Количество (сумма) валюты, хранящееся в данном экземпляре. Хранится в виде дробного числа с точностью,
соответствующей требованиям валюты по стандарту ISO 4217 (для большинства валют - это 2 знака).
currency: Currency
Строка с трёхбуквенным международным кодом валюты (ISO 4217).
add(summand: Money): Money
Операция сложения. Прибавляет переданную денежную сумму к текущей и возвращает результат в виде нового
экземпляра Money.
- При попытке сложить две разные валюты будет брошено исключение.
- Операция производится с целочисленными значениями и не подвержена проблемам, связанным с операциями над числами с плавающей запятой.
- Результат округляется в соответствии с правилами для валюты, описанными выше.
Money
substract(subtracted: Money): Money
Операция вычитания. Вычитает переданную денежную сумму из текущей и возвращает результат в виде нового
экземпляра Money.
- При попытке вычесть валюту, отличную от уменьшаемого, будет брошено исключение.
- Операция производится с целочисленными значениями и не подвержена проблемам, связанным с операциями над числами с плавающей запятой.
- Результат округляется в соответствии с правилами для валюты, описанными выше.
Money
multiply(multiplier: number): Money
Операция умножения. Умножает текущую сумму на заданный множитель и возвращает результат в виде нового
экземпляра Money с исходной валютой.
- Операция производится с целочисленными значениями и не подвержена проблемам, связанным с операциями над числами с плавающей запятой.
- Результат округляется в соответствии с правилами для валюты, описанными выше.
number
divide(divider: number): Money
Операция деления. Делит текущую сумму на заданный делитель и возвращает результат в виде нового экземпляра
Money с исходной валютой.
- Операция производится с целочисленными значениями и не подвержена проблемам, связанным с операциями над числами с плавающей запятой.
- Результат округляется в соответствии с правилами для валюты, описанными выше.
number
format(ctx: app. Ctx, options?: MoneyFormatOptions): string
Возвращает строку с денежной суммой, отформатированной с помощью
[Number.toLocaleString()](
Правила локализации (по правилам какой страны/языка форматировать валюту) берутся из переданного контекста.
ctx: app. Ctx
Контекст запроса, из которого метод "узнаёт" - какой код локали (в формате en-US
) использовать
в конструкторе Intl.NumberFormat
.
options: {
style,
currencyDisplay,
signDisplay,
currencySign,
useGrouping,
minimumIntegerDigits,
minimumFractionDigits,
maximumFractionDigits,
minimumSignificantDigits,
maximumSignificantDigits }
Параметры форматирования. Поддерживаются почти все параметры Intl.NumberFormat.
Параметр currency
не поддерживается, поскольку всегда подставляется автоматически из валюты,
хранящейся в экземпляре Money.
style: 'currency' | 'decimal'
(по умолчанию 'currency'
)
Выводить в виде обычного числа или числа с валютой.
currencyDisplay: 'symbol' | 'code' | 'name' | 'narrowSymbol'
(по умолчанию 'symbol'
)
Как выводить знак валюты, если style=currency
:
'symbol'
- локализованный символ валюты ($, €, ₽)'code'
- трёхбуквенный международный код (USD, EUR, RUB)'name'
- локализованное название валюты (dollar, euro, рубль)'narrowSymbol'
- сокращённый символ валюты - имеет смысл для некоторых валют, которые могут иметь
один и тот же символ - например в локали en-AU
австралийский и американский доллары будут выводиться
по-разному ($ и USD) для symbol
и одинаково ($ и $) для narrowSymbol
. Также в некоторых локалях
по умолчанию используется международный код для иностранных валют, narrowSymbol
форсирует использование
короткого символа.signDisplay: 'always' | 'auto' | 'exceptZero' | 'never'
(по умолчанию 'auto'
)
Когда выводить знак числового значения.
'always'
- всегда (для нуля +
)'auto'
- только для положительных значений'exceptZero'
- для всего кроме нуля'never'
- не показывать знакcurrencySign: 'standard' | 'accounting'
(по умолчанию 'standard'
)
Каким образом форматировать отрицательные значения.
'standard'
- использовать знак минус (-
)'accounting'
- во многих локалях в этом режиме отрицательные значения выводятся в круглых скобках
без знака минус (например в en-US (₽1,234.00)
)useGrouping: 'auto' | true | false
(по умолчанию 'auto'
)
Использовать ли разделитель тысячных разрядов.
'auto'
- в соответствие с настройками локалиtrue
- всегдаfalse
- никогдаminimumIntegerDigits: number
(по умолчанию 1
)
Минимальное количество выводимых цифр (от 1 до 21) в целочисленной части выводимой суммы. Если количество
значимых знаков меньше этого числа, то оно "добивается" нулями слева до нужного количества.
Свойство игнорируется, если явно заданы minimumSignificantDigits или maximumSignificantDigits.
minimumFractionDigits: number
(по умолчанию - количество дробных знаков для валюты из ISO 4217)
Минимальное количество выводимых цифр (от 0 до 20) в дробной части выводимой суммы. Если количество
значимых знаков меньше этого числа, то оно "добивается" нулями справа до нужного количества.
Свойство игнорируется, если явно заданы minimumSignificantDigits или maximumSignificantDigits.
maximumFractionDigits: number
(по умолчанию max(minimumFractionDigits, количество дробных знаков для валюты из ISO 4217)
)
Максимальное количество выводимых цифр (от 0 до 20) в дробной части выводимой суммы. Если количество
значимых знаков больше этого числа, то оно округляется по правилу half-away-from-zero.
Не может быть меньше, чем minimumFractionDigits.
Свойство игнорируется, если явно заданы minimumSignificantDigits или maximumSignificantDigits.
minimumSignificantDigits: number
(по умолчанию 1
)
Минимальное количество выводимых цифр (от 1 до 21) в общем в целочисленной и в дробной части выводимой
суммы. Если общее количество значимых знаков меньше этого числа, то оно "добивается" нулями в дробной
части справа до нужного количества.
Использование этого свойства отлючает действие minimumIntegerDigits, minimumFractionDigits и maximumFractionDigits.
maximumSignificantDigits: number
(по умолчанию 21
)
Максимальное количество выводимых цифр (от 1 до 21) в общем в целочисленной и в дробной части выводимой суммы.
Если общее количество значимых знаков больше этого числа, то оно округляется по правилу half-away-from-zero,
таким образом, чтобы количество значимых цифр начиная от старшего разряда до последней ненулевой цифры
не превышало заданного значения. Округление действует не только для дробной части, но и для целочисленной:
например сумма 1251 с maximumSignificantDigits=2 превратится в 1300.
Не может быть меньше, чем minimumSignificantDigits.
Использование этого свойства отлючает действие minimumIntegerDigits, minimumFractionDigits и maximumFractionDigits.
const Balances = Heap.Table('balances', {
amount: Heap.Money(),
})
async function addBonusPlus10Percent(
ctx: app.Ctx, balanceId: string, bonus: Money,
): Promise<typeof Balances.T> {
const balance = await Balances.getById(ctx, balanceId)
return Balances.update({
id: balance.id,
amount: balance.amount.add(bonus).multiply(1.1),
})
}
const balanceWithBonus = await addBonusPlus10Percent(
ctx, req.params.id, new Money(req.params.bonus, 'USD'),
)
ctx.console.log('new balance', balanceWithBonus.amount.format(ctx, {
currencyDisplay: 'narrowSymbol',
minimumFractionDigits: 0,
maximumFractionDigits: 0,
})