Functional Weave
Code in Python

retail.price-rounding@1.0.0

README.md

1,364 bytes · view raw

# retail.price-rounding

Snaps a price to the nearest allowed price point. The points are every
`k x step - ending` for whole `k`, so one rule covers the common policies:

| policy | step | ending | points |
|---|---|---|---|
| nearest 5p | 5 | 0 | 1.20, 1.25, 1.30 |
| nearest 10p | 10 | 0 | 1.20, 1.30 |
| charm .99 | 100 | 1 | 0.99, 1.99, 2.99 |
| charm .95 | 100 | 5 | 0.95, 1.95, 2.95 |
| .49 / .99 | 50 | 1 | 0.49, 0.99, 1.49 |
| 9.99, 19.99 | 1000 | 1 | 9.99, 19.99, 29.99 |

`up` gives the smallest point at or above the price, `down` the largest at or
below, `nearest` whichever is closer. A tie (2.49 between 1.99 and 2.99) goes
to the higher price, which is the usual retail choice and matches
`math.round-div`'s half-up. A price already on a point is returned unchanged.

## Edge cases

- Points are never negative. `nearest` for a price below the first point (0.00
  under charm .99) returns the first point, 0.99; `down` has no answer there
  and is an error.
- The price must be 0 or more. Discounts and refunds are not rounded to price
  points.
- Currency does not matter to the arithmetic, so the same rule works for yen
  (step 100, ending 20 gives ¥980, ¥1,080) as for pence.

This rounds a price someone is about to print on a shelf. It is not for tax or
invoice arithmetic, which should use `math.round-div` or `money.apply-rate`
directly.