Converting OpenFeed's monetary values to Decimal.
Why this exists
OpenFeed is not consistent about how it encodes money on the wire, and the inconsistency runs along domain lines:
| Field | JSON type |
|---|---|
BankingTransaction.amount | string — "-52.00" (ISO 20022) |
BankingBalance.currentBalance | string |
EnergyInvoice.invoiceAmount | number — 193.8 |
EnergyBillingTransaction.amount | number |
So Decimal.new/1 works on a banking amount and raises on an energy one,
while Decimal.from_float/1 is the reverse. Getting this wrong produces a
crash on whichever domain you tested second.
to_decimal/1 takes either, plus nil for absent optional fields.
Never use floats for money
Energy amounts arrive as JSON numbers, which Jason decodes to floats. Convert
once at the boundary with this function and keep Decimal from then on —
don't add floats together and convert at the end.
Examples
iex> OpenFeed.Amount.to_decimal("-52.00")
Decimal.new("-52.00")
iex> OpenFeed.Amount.to_decimal(193.8)
Decimal.new("193.8")
iex> OpenFeed.Amount.to_decimal(200)
Decimal.new(200)
iex> OpenFeed.Amount.to_decimal(nil)
nil
Summary
Functions
Convert an OpenFeed monetary value to a Decimal.
Like to_decimal/1 but raises ArgumentError on an unparseable value.
Functions
Convert an OpenFeed monetary value to a Decimal.
Accepts an ISO 20022 amount string, a JSON number (integer or float), an
existing Decimal, or nil. Returns nil for nil and for anything
unparseable, so a malformed optional field does not take down a sync.
Examples
iex> OpenFeed.Amount.to_decimal("1234.56")
Decimal.new("1234.56")
iex> OpenFeed.Amount.to_decimal("not money")
nilFloats convert via their shortest representation, not their binary expansion:
iex> OpenFeed.Amount.to_decimal(0.1)
Decimal.new("0.1")
Like to_decimal/1 but raises ArgumentError on an unparseable value.
Use this for fields the spec marks required, where silently getting nil
would hide a real problem.
Examples
iex> OpenFeed.Amount.to_decimal!("-52.00")
Decimal.new("-52.00")
iex> OpenFeed.Amount.to_decimal!("nope")
** (ArgumentError) could not read "nope" as an OpenFeed monetary amount