Points I explain:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 | # ====================================================================== # Q: Implement B2B pricing: customer price > volume tier discount > promo, then # VAT and rounding. # # Points I explain: # - Decimal everywhere and explicit ROUND_HALF_UP (Python's default is # ROUND_HALF_EVEN, which does not agree with the ERP/accounting). Unit price # with 4 decimals, amounts with 2. # - The order of precedence and whether discounts stack is a business decision, # not mine. I write it down explicitly and confirm it in writing. # - Pure function: no database access. The caller loads the PriceContext (one # query for all lines). # - The rule is stored on the order line: when the customer asks "why did I pay # this?", there is an answer. # - VAT per line or on the total gives a one-cent difference: I follow whatever # the ERP does, so the invoices agree. # ====================================================================== from dataclasses import dataclass from decimal import Decimal, ROUND_HALF_UP CENT = Decimal('0.01') @dataclass(frozen=True) class PriceContext: list_price: Decimal # list price, excluding VAT customer_price: Decimal | None # special price from the customer's price list volume_tiers: tuple[tuple[int, Decimal], ...] # ((50, 0.05), (100, 0.08)) promo_pct: Decimal | None # active promo, if any vat_rate: Decimal # e.g. Decimal('0.24') @dataclass(frozen=True) class LinePrice: unit_net: Decimal net: Decimal vat: Decimal gross: Decimal rule: str # which rule was applied (audit) def price_line(ctx: PriceContext, qty: int) -> LinePrice: if qty <= 0: raise ValueError('qty must be positive') if ctx.customer_price is not None: # 1. agreed price: does not stack unit, rule = ctx.customer_price, 'customer_price' else: unit, rule = ctx.list_price, 'list' tier = max((pct for min_q, pct in ctx.volume_tiers if qty >= min_q), default=Decimal('0')) if tier: # 2. volume tier discount unit, rule = unit * (1 - tier), f'volume_{tier}' if ctx.promo_pct and ctx.promo_pct > tier: # 3. promo only if it is better unit, rule = ctx.list_price * (1 - ctx.promo_pct), 'promo' unit = unit.quantize(Decimal('0.0001'), ROUND_HALF_UP) net = (unit * qty).quantize(CENT, ROUND_HALF_UP) vat = (net * ctx.vat_rate).quantize(CENT, ROUND_HALF_UP) # VAT per line return LinePrice(unit, net, vat, net + vat, rule) |