Functional Weave
Code in Rust

retail.refund-calculate@1.0.0

README.md

2,243 bytes · view raw

# retail.refund-calculate

Works out what to refund when a customer brings items back, so that the
refund matches what they actually paid for those items, including their share
of any discount on the whole basket, to the penny.

## Two steps

1. **Share the basket discount across every line of the sale**, returned or
   not, in proportion to each line's net, with `money.allocate`. A £5 coupon
   on a £35 basket takes £2.14 off the £15 line, £1.14 off the £8 line and
   £1.72 off the £12 line (the spare penny goes to the largest remainder), so
   each line's `paid` adds up to exactly what was taken at the till.
2. **Refund units of a line by cumulative share.** Returning `q` units when
   `r` have already come back refunds

   `round(paid x (r + q) / quantity) - round(paid x r / quantity)`

   rounding half up. Because each refund is the difference of two running
   totals, returning every unit, in any number of visits, refunds exactly
   `paid`. A line of three that cost £12.86 refunds £4.29, £4.28 and £4.29,
   never 3 x £4.29 = £12.87.

The line nets should already include line promotions (`retail.promotion-apply`
allocates each deal back to the lines in its groups for exactly this reason),
so a returned item from a 3 for 2 is refunded at its share of the deal, not at
full price.

The naive refund, unit price times quantity, ignores the coupon and refunds
£5.00 for an item that cost £4.29 after it.

## What it does not do

- It does not decide whether a refund is due; returns policies and consumer
  law (the 14-day cancellation period under the Consumer Contracts
  Regulations 2013, for instance) are the caller's.
- Delivery charges are not refunded here. Under the Consumer Contracts
  Regulations a trader cancelling a whole distance order refunds the standard
  delivery too; add it when the whole order comes back.
- It does not re-check promotions. If returning an item breaks a deal (one of a
  BOGOF pair), whether the kept item should now cost more is a policy
  decision; this refunds what the returned item was actually paid.

## Errors

Returning more units than remain, an unknown line, the same line twice in one
return, a basket discount bigger than the basket, or mixed currencies.