# `Money`

An amount of money: a whole number of a currency's smallest units, and the currency.

Use it for every amount of money. Never hold money in a `Decimal`: this type can't lose a penny to rounding, and it can't add pounds to euros. For an amount with whether tax is included, use [`Price`](/docs/types/type-price.md). To fix the currency, build your own type on it.

## Shape

One value, with no fields to fill in. As text it reads `GBP 1234.50`.

## Example

```jadpo
money.from_minor(1050, Currency.gbp)
```

`text.parse(raw, as: Money)` reads one from text that comes from outside. Text that isn't one at all fails `malformed`, such as `10.50`, `GBP 10.505` or `GBP 10.5`.

These operations give one: `money.allocate`, `money.convert`, `money.from_minor`, `money.of`, `money.split` and `money.sum`.

## Good to know

- `money.from_minor(1050, Currency.gbp)` is £10.50, since it takes pence. `money.of` takes a decimal amount instead.
- A percentage scales it: `price * 120%` adds 20%. To share it out, use `money.split`, which loses nothing: £10.00 three ways is £3.34, £3.33 and £3.33. `price / 3` is refused.
- `<` compares two amounts in one currency. With two different currencies the compiler refuses it, or, if a currency is only known while running, it fails with `currency_mismatch`.
- A narrower type is often better: `type Sterling = Money { currency: Currency.gbp min: 0 }`.
- `==` tells you whether two are the same, and `<` and `list.sort_by` compare and sort them.

## In JSON

An object of its parts, in a request, a response or `json.write`:

```json
{"amount":-500,"currency":"GBP"}
{"amount":5,"currency":"GBP"}
{"amount":1050,"currency":"GBP"}
```

## Details

<details>
<summary>Text form, storage, how it is shown, and operations</summary>

`Money` is in the group Money and banking, and has been in Jadpo since 0.1.0.

### Text form

`text.from` writes one standard text form, the same form `text.parse` reads back: `GBP -5.00`, `GBP 0.05` and `GBP 10.50`.

### How it is stored

It is stored in 2 columns, each named for the field, `_` and its suffix. This is `GBP 10.50`:

| Column | Type | Holds |
|---|---|---|
| `<field>_amount` | `bigint` | `1050` |
| `<field>_currency` | `char(3)` | `'GBP'` |

### How it is shown to people

`text.format(value, locale:)` shows it to people in their locale. A call may also give `precision:`, `display:` or `sign:`.

With `locale: Locale.en_gb`:

| Value | Shown |
|---|---|
| `GBP -5.00` | `-£5.00` |
| `GBP 0.05` | `£0.05` |
| `GBP 10.50` | `£10.50` |
| `GBP 1234.50` | `£1,234.50` |

### Operations

- `money.allocate(total: Money, ratios: List<Int>) -> List<Money>`: `total` shared in proportion to `ratios`, whole numbers never below 0, at least one of them; a share of ratio 0 is nothing.
- `money.convert(value: Money, rate: ExchangeRate) -> Money`: `value` in `rate`'s `to` currency, from its `from` one: the major units times `rate.rate`, rounded half to even to a minor unit, so £10.50 at a `GBP/EUR` rate of 1.17 is €12.28. It fails with `currency_mismatch` when `value` isn't in `rate.from`, unless that is known: an inverted rate is then a compile error. The rate is one the app holds; nothing here fetches one.
- `money.currency(value: Money) -> Currency`: The currency `value` is in.
- `money.from_minor(amount: Int, currency: Currency) -> Money`: `amount` minor units of `currency`: `money.from_minor(1050, Currency.gbp)` is £10.50.
- `money.minor(value: Money) -> Int`: `value`'s minor units: 1050 for £10.50.
- `money.of(amount: Decimal, currency: Currency) -> Money`: `amount` major units of `currency`, a `Decimal`: `money.of(10.50, Currency.gbp)` is £10.50. It fails with `too_many_places` for more places than the currency has (10.505 in pounds), unless the currency is written out and the amount proved to have no more.
- `money.split(total: Money, parts: Int) -> List<Money>`: `total` in `parts` near-equal shares, `parts` at least 1: £10.00 in 3 is £3.34, £3.33 and £3.33.
- `money.sum(items: List<Money>, currency: Currency) -> Money`: The items added up in `currency`, the zero an empty list gives. It fails with `currency_mismatch` for an item in another currency, unless the items' type fixes `currency`.

It also takes `text.parse`, `text.from`, `text.format`, `json.read`, `json.write` and `==`. [How every standard type behaves](/docs/every-standard-type-meets-one-contract.md) says how they work.

</details>
