import { computeVariantPrice, DEFAULT_PRODUCT_PRICE_EXTRA_PERCENT, } from './variant-price'; /** * D4 — unit-test the reference price formula against a HAND calculation. * * The expected numbers below were derived by walking * henza_server/app/Models/ProductVariant.php:183-190 step by step with a * calculator, NOT by running the implementation under test. Each case lists * its intermediate steps so a reviewer can check the arithmetic without * trusting the code. * * Reference formula: * price = gold + gold * ((pay + extra) / 100) * price = price + price * 0.07 * temp = price - gold * temp = temp + temp * 0.09 * price = gold + temp * price = price * grams * return floor(price / 1000) * 1000 */ describe('computeVariantPrice — reference parity', () => { describe('without the customer making-charge offset', () => { it('matches the hand calculation for 1g at 15% wage', () => { // gold = 30,000,000 pay = 15 extra = 7 grams = 1 // 1) 30,000,000 + 30,000,000 * (22/100) = 36,600,000 // 2) 36,600,000 * 1.07 = 39,162,000 // 3) temp = 39,162,000 - 30,000,000 = 9,162,000 // 4) temp * 1.09 = 9,986,580 // 5) 30,000,000 + 9,986,580 = 39,986,580 // 6) * 1 gram = 39,986,580 // 7) floor(/1000)*1000 = 39,986,000 expect( computeVariantPrice({ goldPricePerGram: 30_000_000, wagePercent: 15, grams: 1, }), ).toBe(39_986_000); }); it('applies VAT to the non-metal portion only, not the whole price', () => { // This is what the three SUPERSEDED formulas got wrong. If 9% were // applied to the whole price the answer would be // 39,162,000 * 1.09 = 42,686,580 -> 42,686,000 // The live formula gives 39,986,000. Guards against porting the wrong one. const live = computeVariantPrice({ goldPricePerGram: 30_000_000, wagePercent: 15, grams: 1, }); const ifVatOnWholePrice = Math.floor((30_000_000 * 1.22 * 1.07 * 1.09) / 1000) * 1000; expect(live).toBe(39_986_000); expect(ifVatOnWholePrice).toBe(42_686_000); expect(live).not.toBe(ifVatOnWholePrice); }); it('handles a zero making charge', () => { // 1) 30,000,000 * 1.07 (extra 7 only) = 32,100,000 // 2) * 1.07 = 34,347,000 // 3) temp = 4,347,000 4) * 1.09 = 4,738,230 // 5) 34,738,230 6) * 1g 7) floor -> 34,738,000 expect( computeVariantPrice({ goldPricePerGram: 30_000_000, wagePercent: 0, grams: 1, }), ).toBe(34_738_000); }); it('multiplies by weight before the final floor', () => { // gold = 28,500,000 pay = 20 extra = 7 grams = 2.345 // 1) 28,500,000 * 1.27 = 36,195,000 // 2) * 1.07 = 38,728,650 // 3) temp = 10,228,650 4) * 1.09 = 11,149,228.5 // 5) 39,649,228.5 6) * 2.345 = 92,977,440.8325 // 7) floor(/1000)*1000 = 92,977,000 expect( computeVariantPrice({ goldPricePerGram: 28_500_000, wagePercent: 20, grams: 2.345, }), ).toBe(92_977_000); }); it('rounds DOWN to the nearest 1000, never up', () => { const price = computeVariantPrice({ goldPricePerGram: 28_500_000, wagePercent: 20, grams: 2.345, }); // The unfloored value is 92,977,440.83 — rounding half-up would give // 92,977,000 too, so also assert the property directly. expect(price % 1000).toBe(0); expect(price).toBeLessThanOrEqual(92_977_440.8325); }); it('defaults extraPercent to 7 when the config row is missing', () => { expect(DEFAULT_PRODUCT_PRICE_EXTRA_PERCENT).toBe(7); expect( computeVariantPrice({ goldPricePerGram: 30_000_000, wagePercent: 15, grams: 1 }), ).toBe( computeVariantPrice({ goldPricePerGram: 30_000_000, wagePercent: 15, grams: 1, extraPercent: 7, }), ); }); }); describe('with the customer making-charge offset (customers.percent)', () => { it('adds the offset to the wage percent — hand-checked at offset 3', () => { // gold = 30,000,000 pay = 15 offset = 3 -> effective wage 18 // 1) 30,000,000 + 30,000,000 * (25/100) = 37,500,000 // 2) * 1.07 = 40,125,000 // 3) temp = 10,125,000 4) * 1.09 = 11,036,250 // 5) 41,036,250 6) * 1g 7) floor = 41,036,000 expect( computeVariantPrice({ goldPricePerGram: 30_000_000, wagePercent: 15, grams: 1, makingChargeOffsetPercent: 3, }), ).toBe(41_036_000); }); it('an offset of 0 is identical to passing no offset at all', () => { // This is the behaviour-neutrality guarantee behind the migration's // DEFAULT 0: every existing customer keeps today's price. const withoutField = computeVariantPrice({ goldPricePerGram: 30_000_000, wagePercent: 15, grams: 1, }); const withZero = computeVariantPrice({ goldPricePerGram: 30_000_000, wagePercent: 15, grams: 1, makingChargeOffsetPercent: 0, }); expect(withZero).toBe(withoutField); expect(withZero).toBe(39_986_000); }); it('a non-zero offset raises the price — the default must stay 0', () => { const atZero = computeVariantPrice({ goldPricePerGram: 30_000_000, wagePercent: 15, grams: 1, makingChargeOffsetPercent: 0, }); const atThree = computeVariantPrice({ goldPricePerGram: 30_000_000, wagePercent: 15, grams: 1, makingChargeOffsetPercent: 3, }); expect(atThree).toBeGreaterThan(atZero); // Backfilling 3 instead of 0 would have moved this price by 1,050,000. expect(atThree - atZero).toBe(1_050_000); }); it('matches the hand calculation for a multi-gram line with an offset', () => { // gold = 35,000,000 pay = 12.5 offset = 3 extra = 7 grams = 5.25 // 1) 35,000,000 * 1.225 = 42,875,000 // 2) * 1.07 = 45,876,250 // 3) temp = 10,876,250 4) * 1.09 = 11,855,112.5 // 5) 46,855,112.5 6) * 5.25 = 245,989,340.625 // 7) floor(/1000)*1000 = 245,989,000 expect( computeVariantPrice({ goldPricePerGram: 35_000_000, wagePercent: 12.5, grams: 5.25, makingChargeOffsetPercent: 3, }), ).toBe(245_989_000); }); it('is equivalent to folding the offset into wagePercent, as the app does', () => { // henza_pwa sends percent_pay = state.percent + variant.percentage_pay // (src/Mixins/product/index.ts:69-75), so the two must agree exactly. const asAppSendsIt = computeVariantPrice({ goldPricePerGram: 31_250_000, wagePercent: 18 + 4, grams: 3.1, }); const asServerWillDeriveIt = computeVariantPrice({ goldPricePerGram: 31_250_000, wagePercent: 18, grams: 3.1, makingChargeOffsetPercent: 4, }); expect(asServerWillDeriveIt).toBe(asAppSendsIt); }); }); });