Functional Weave
Code in Rust

hospitality.recipe-cost@1.0.1

README.md

2,007 bytes · view raw

# hospitality.recipe-cost

Costs a recipe the way a kitchen costing sheet does: what each ingredient costs
in the recipe, the total, and the cost of one portion.

For each ingredient:

1. **Convert** the quantity the recipe needs into the unit the pack is sold in,
   with `units.convert` (250 g of a 1.5 kg bag is 0.25 kg). Count items use the
   unit `each`, which only converts to `each` (3 eggs from a tray of 12).
2. **Gross up for yield.** `quantity` is the usable amount the recipe needs
   after trimming and peeling. `yieldBasisPoints` is the usable share of what
   is bought: 400 g of peeled carrots at an 8000 (80%) yield means buying 500 g.
   A common mistake multiplies by the yield instead, costing 320 g.
3. **Price it** as that share of the pack price, in one exact division, rounded
   half-up to the minor unit.

The total is the sum of the rounded line costs, so the sheet adds up, and the
portion cost is the total divided by `portions`, rounded half-up.

## Precision

Quantities and pack sizes are decimal text (`"0.75"`), never floats. The
converted quantity is carried to 9 decimal places of the pack unit, a
microgram of a kilogram pack, and everything after that is exact integer
arithmetic. Pack sizes may have at most 6 decimal places and 15 digits. An
ingredient whose cost cannot be held exactly (beyond 2^53 minor units) is an
error rather than a rounded guess.

A line under half a penny (a pinch of salt) costs 0. Kitchens that want those
counted usually add a "sundries" line as a fixed amount.

## Errors

- Units must be ones `units.convert` knows (`g`, `kg`, `mL`, `L`, `oz`, `lb`,
  `floz_imp`...) or `each`, and a quantity and its pack must be the same
  dimension: oil costed in grams but bought by the litre needs a density,
  which this does not guess.
- `yieldBasisPoints` is 1 to 10000, `portions` at least 1, pack prices not
  negative, and every pack price in `currency`.

1.0.1 fixes Python accepting a trailing newline in quantity and packSize; adds tests.