@amboras-dev/money 0.0.0-stage → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (78) hide show
  1. package/README.md +401 -2
  2. package/dist/ai.cjs +185 -0
  3. package/dist/ai.d.cts +97 -0
  4. package/dist/ai.d.ts +97 -0
  5. package/dist/ai.js +175 -0
  6. package/dist/allocate-CZRg4TOP.cjs +222 -0
  7. package/dist/allocate-X7_jAtOj.js +199 -0
  8. package/dist/amount-BV0O-aN8.cjs +241 -0
  9. package/dist/amount-BoYBMgK9.js +200 -0
  10. package/dist/arithmetic-BhNwisjb.js +132 -0
  11. package/dist/arithmetic-DjT4Kzgt.cjs +233 -0
  12. package/dist/billing.cjs +184 -0
  13. package/dist/billing.d.cts +81 -0
  14. package/dist/billing.d.ts +81 -0
  15. package/dist/billing.js +175 -0
  16. package/dist/commerce.cjs +536 -0
  17. package/dist/commerce.d.cts +293 -0
  18. package/dist/commerce.d.ts +293 -0
  19. package/dist/commerce.js +516 -0
  20. package/dist/format-BdPnijvn.js +99 -0
  21. package/dist/format-DLhno7eX.cjs +128 -0
  22. package/dist/fx.cjs +407 -0
  23. package/dist/fx.d.cts +157 -0
  24. package/dist/fx.d.ts +157 -0
  25. package/dist/fx.js +374 -0
  26. package/dist/index.cjs +352 -0
  27. package/dist/index.d.cts +300 -0
  28. package/dist/index.d.ts +300 -0
  29. package/dist/index.js +276 -0
  30. package/dist/input.cjs +163 -0
  31. package/dist/input.d.cts +203 -0
  32. package/dist/input.d.ts +203 -0
  33. package/dist/input.js +153 -0
  34. package/dist/iso-minor-CqkWDxHS.js +48 -0
  35. package/dist/iso-minor-wSwJN57T.cjs +65 -0
  36. package/dist/medusa-4K8N1pUc.d.cts +87 -0
  37. package/dist/medusa-DBXJXXvF.d.ts +87 -0
  38. package/dist/medusa-DqJY1qpj.js +116 -0
  39. package/dist/medusa-lRY9xlsf.cjs +175 -0
  40. package/dist/money-B-3aFjNJ.d.cts +248 -0
  41. package/dist/money-B-3aFjNJ.d.ts +248 -0
  42. package/dist/money-B3wH_Z4S.cjs +3990 -0
  43. package/dist/money-WhnGiI00.js +3583 -0
  44. package/dist/number-text-BWu67jUq.cjs +145 -0
  45. package/dist/number-text-DE-q4jO5.js +134 -0
  46. package/dist/own-D7544D3k.js +4 -0
  47. package/dist/own-oP9DXFWv.cjs +9 -0
  48. package/dist/percent-C0InC2t2.cjs +43 -0
  49. package/dist/percent-D1VB9xXG.js +20 -0
  50. package/dist/rate-CVeMii4a.cjs +217 -0
  51. package/dist/rate-DBPiYgT1.d.ts +63 -0
  52. package/dist/rate-DicStBue.js +152 -0
  53. package/dist/rate-tqK8ijG0.d.cts +63 -0
  54. package/dist/reporting-CG-ai2sM.d.cts +31 -0
  55. package/dist/reporting-tpGJbqYp.d.ts +31 -0
  56. package/dist/retail-1mu888J1.js +69 -0
  57. package/dist/retail-C_BPLvMj.d.cts +31 -0
  58. package/dist/retail-Cml8YSKg.cjs +86 -0
  59. package/dist/retail-DRUTTQoJ.d.ts +31 -0
  60. package/dist/rounding-DHBtNCHG.cjs +79 -0
  61. package/dist/rounding-g9rvye0k.js +50 -0
  62. package/dist/sql.cjs +290 -0
  63. package/dist/sql.d.cts +147 -0
  64. package/dist/sql.d.ts +147 -0
  65. package/dist/sql.js +271 -0
  66. package/dist/testing.cjs +61995 -0
  67. package/dist/testing.d.cts +125 -0
  68. package/dist/testing.d.ts +125 -0
  69. package/dist/testing.js +61986 -0
  70. package/dist/wire-Bhuut8cW.cjs +2097 -0
  71. package/dist/wire-CaUN9Vf_.d.ts +309 -0
  72. package/dist/wire-sW0yzqth.js +1888 -0
  73. package/dist/wire-xjYXv4go.d.cts +309 -0
  74. package/dist/wire.cjs +37 -0
  75. package/dist/wire.d.cts +2 -0
  76. package/dist/wire.d.ts +2 -0
  77. package/dist/wire.js +2 -0
  78. package/package.json +158 -3
@@ -0,0 +1,152 @@
1
+ import { $ as describeValue, B as decimalOf, J as compareRatio, Q as MoneyError, Z as ratioToDecimal, dt as roundToScale, lt as parseDecimal, pt as toDecimalText, ut as pow10 } from "./money-WhnGiI00.js";
2
+ import { t as hasOwnKey } from "./own-D7544D3k.js";
3
+ //#region src/fx/rate.ts
4
+ /** Denominator of every locked rate (`store_currency_rate.rate_denominator` default): 7 decimals. */
5
+ const RATE_REF = 10000000n;
6
+ /** Inclusive bounds of a locked rate's value: `lockedRateNumerator` (n >= 1 at RATE_REF, value <= 1e9). */
7
+ const RATE_BOUNDS = {
8
+ min: "0.0000001",
9
+ max: "1000000000"
10
+ };
11
+ const MIN_RATE = {
12
+ units: 1n,
13
+ scale: 7
14
+ };
15
+ const MAX_RATE = {
16
+ units: 1000000000n,
17
+ scale: 0
18
+ };
19
+ const invalid = (message) => new MoneyError("rate_invalid", message);
20
+ /** A positive integer part (safe integer number, bigint or digit string), or null. */
21
+ function partOf(value) {
22
+ let part = null;
23
+ if (typeof value === "bigint") part = value;
24
+ else if (typeof value === "number" && Number.isSafeInteger(value)) part = BigInt(value);
25
+ else if (typeof value === "string") part = parseDecimal(value, "integer")?.units ?? null;
26
+ return part !== null && part >= 1n ? part : null;
27
+ }
28
+ const withinBounds = (n, d) => compareRatio(n, d, MIN_RATE.units, pow10(MIN_RATE.scale)) >= 0 && compareRatio(n, d, MAX_RATE.units, 1n) <= 0;
29
+ /**
30
+ * A Rate from its parts: integers >= 1 (safe integer number, bigint or digit string) whose
31
+ * value lies within `RATE_BOUNDS`. Anything else throws `rate_invalid`. Not reduced: a
32
+ * RATE_REF denominator is written back as given.
33
+ */
34
+ function rate(numerator, denominator) {
35
+ const n = partOf(numerator);
36
+ const d = partOf(denominator);
37
+ if (n === null || d === null) throw invalid(`Rate parts must be whole numbers >= 1, got ${describeValue(numerator)}/${describeValue(denominator)}`);
38
+ if (!withinBounds(n, d)) throw invalid(`Rate ${n}/${d} is outside ${RATE_BOUNDS.min}..${RATE_BOUNDS.max}`);
39
+ return {
40
+ numerator: n,
41
+ denominator: d
42
+ };
43
+ }
44
+ /** A `store_currency_rate` row's rate, or null for missing, non-integer, non-positive or out-of-bounds parts. Never throws. */
45
+ function rateFromRow(row) {
46
+ if (typeof row !== "object" || row === null) return null;
47
+ const n = partOf(row.rate_numerator);
48
+ const d = partOf(row.rate_denominator);
49
+ return n !== null && d !== null && withinBounds(n, d) ? {
50
+ numerator: n,
51
+ denominator: d
52
+ } : null;
53
+ }
54
+ /** The exact decimal of a rate input; anything that is not a finite decimal throws `rate_invalid`. */
55
+ function rateDecimal(value) {
56
+ try {
57
+ return decimalOf(value);
58
+ } catch {
59
+ throw invalid(`Rate must be a finite decimal, got ${describeValue(value)}`);
60
+ }
61
+ }
62
+ /** `floor(log10(v))` of a positive decimal, from its digits (no float log). */
63
+ function exponentOf(v) {
64
+ return v.units.toString().length - 1 - v.scale;
65
+ }
66
+ /**
67
+ * A major-unit rate given as a number or decimal text (`83.42`, `'0.000029175'`) as a Rate,
68
+ * rounded half-up once. Numbers are taken by their shortest text, so `83.42` is exactly
69
+ * 834200000 / 1e7. See `RateFromDecimalOptions` for the two encodings.
70
+ */
71
+ function rateFromDecimal(value, options = {}) {
72
+ const v = rateDecimal(value);
73
+ if (options.encoding === "precise") {
74
+ if (v.units <= 0n) throw invalid(`Rate must be greater than 0, got ${describeValue(value)}`);
75
+ const exponent = Math.max(7, 9 - exponentOf(v));
76
+ return {
77
+ numerator: roundToScale(v, exponent, "half-up").units,
78
+ denominator: pow10(exponent)
79
+ };
80
+ }
81
+ if (compareRatio(v.units, pow10(v.scale), MAX_RATE.units, 1n) > 0) throw invalid(`Rate must not be greater than ${RATE_BOUNDS.max}, got ${describeValue(value)}`);
82
+ const numerator = roundToScale(v, 7, "half-up").units;
83
+ if (numerator < 1n) throw invalid(`Rate must be greater than 0, got ${describeValue(value)}`);
84
+ return {
85
+ numerator,
86
+ denominator: RATE_REF
87
+ };
88
+ }
89
+ /** Throws `rate_invalid` unless `r` has positive integer parts. */
90
+ function checkRate(r) {
91
+ if (typeof r !== "object" || r === null || typeof r.numerator !== "bigint" || typeof r.denominator !== "bigint" || r.numerator < 1n || r.denominator < 1n) throw invalid(`Not a rate: ${describeValue(r)}`);
92
+ return r;
93
+ }
94
+ function checkDecimals(decimals) {
95
+ if (!Number.isSafeInteger(decimals) || decimals < 0 || decimals > 30) throw new MoneyError("amount_invalid", `decimals must be an integer from 0 to 30, got ${describeValue(decimals)}`);
96
+ return decimals;
97
+ }
98
+ /** `r` as text rounded half-up to `decimals` fraction digits (default 12: persisted `fx_rate`, `draft_market_rate`). */
99
+ function rateToDecimal(r, options = {}) {
100
+ const { numerator, denominator } = checkRate(r);
101
+ const decimals = checkDecimals(options.decimals ?? 12);
102
+ return toDecimalText(ratioToDecimal(numerator, denominator, decimals));
103
+ }
104
+ /** `r` as a number: rounded half-up to 7 decimals by default (Medusa `toMajorRate`), or `'exact'` = `Number(n) / Number(d)`. */
105
+ function rateToNumber(r, options = {}) {
106
+ const { numerator, denominator } = checkRate(r);
107
+ if (options.decimals === "exact") return Number(numerator) / Number(denominator);
108
+ return Number(rateToDecimal(r, { decimals: options.decimals ?? 7 }));
109
+ }
110
+ /** 1 quote = d/n base. */
111
+ function invertRate(r) {
112
+ const { numerator, denominator } = checkRate(r);
113
+ return {
114
+ numerator: denominator,
115
+ denominator: numerator
116
+ };
117
+ }
118
+ const ONE = {
119
+ numerator: 1n,
120
+ denominator: 1n
121
+ };
122
+ function lookup(table, code) {
123
+ const lower = code.toLowerCase();
124
+ const found = hasOwnKey(table, lower) ? table[lower] : hasOwnKey(table, code.toUpperCase()) ? table[code.toUpperCase()] : void 0;
125
+ return found === void 0 ? void 0 : checkRate(found);
126
+ }
127
+ /**
128
+ * 1 `from` = ? `to`, from a table of rates that each say what 1 `base` is worth (keys any
129
+ * case; `base` itself counts as 1/1). Same code (any case) gives 1/1; a missing side gives
130
+ * null. Exact on bigint (`{ b.n x a.d, b.d x a.n }`), never overflows.
131
+ */
132
+ function crossRate(table, from, to, base) {
133
+ const f = from.toLowerCase();
134
+ const t = to.toLowerCase();
135
+ const b = base.toLowerCase();
136
+ if (f === t) return ONE;
137
+ const a = f === b ? ONE : lookup(table, f);
138
+ const c = t === b ? ONE : lookup(table, t);
139
+ if (!a || !c) return null;
140
+ return {
141
+ numerator: c.numerator * a.denominator,
142
+ denominator: c.denominator * a.numerator
143
+ };
144
+ }
145
+ /** Same value (cross-multiplied), whatever the parts: 2/4 equals 1/2. */
146
+ function rateEquals(a, b) {
147
+ const x = checkRate(a);
148
+ const y = checkRate(b);
149
+ return compareRatio(x.numerator, x.denominator, y.numerator, y.denominator) === 0;
150
+ }
151
+ //#endregion
152
+ export { invertRate as a, rateFromDecimal as c, rateToNumber as d, crossRate as i, rateFromRow as l, RATE_REF as n, rate as o, checkRate as r, rateEquals as s, RATE_BOUNDS as t, rateToDecimal as u };
@@ -0,0 +1,63 @@
1
+ import { R as DecimalText } from "./money-B-3aFjNJ.cjs";
2
+ //#region src/fx/rate.d.ts
3
+ /** 1 base unit = numerator / denominator quote units, both in major units. Positive integers. */
4
+ interface Rate {
5
+ readonly numerator: bigint;
6
+ readonly denominator: bigint;
7
+ }
8
+ /** A `store_currency_rate` row as the database driver returns it (bigint columns usually as text). */
9
+ interface RateRow {
10
+ readonly rate_numerator: unknown;
11
+ readonly rate_denominator: unknown;
12
+ readonly currency_code?: unknown;
13
+ }
14
+ /** Denominator of every locked rate (`store_currency_rate.rate_denominator` default): 7 decimals. */
15
+ declare const RATE_REF = 10000000n;
16
+ /** Inclusive bounds of a locked rate's value: `lockedRateNumerator` (n >= 1 at RATE_REF, value <= 1e9). */
17
+ declare const RATE_BOUNDS: {
18
+ readonly min: "0.0000001";
19
+ readonly max: "1000000000";
20
+ };
21
+ /**
22
+ * A Rate from its parts: integers >= 1 (safe integer number, bigint or digit string) whose
23
+ * value lies within `RATE_BOUNDS`. Anything else throws `rate_invalid`. Not reduced: a
24
+ * RATE_REF denominator is written back as given.
25
+ */
26
+ declare function rate(numerator: number | bigint | string, denominator: number | bigint | string): Rate;
27
+ /** A `store_currency_rate` row's rate, or null for missing, non-integer, non-positive or out-of-bounds parts. Never throws. */
28
+ declare function rateFromRow(row: RateRow | null | undefined): Rate | null;
29
+ interface RateFromDecimalOptions {
30
+ /**
31
+ * `'locked'` (default): `n = round_half_up(value x 1e7)`, `d = RATE_REF` (orch
32
+ * `lockedRateNumerator`); `n < 1` or `value > 1e9` throws `rate_invalid`.
33
+ * `'precise'`: about ten significant digits, `d = 10^max(7, 9 - floor(log10(value)))`
34
+ * (orch `precisePriceRate`, for one-off conversions); `value <= 0` throws `rate_invalid`.
35
+ */
36
+ encoding?: 'locked' | 'precise';
37
+ }
38
+ /**
39
+ * A major-unit rate given as a number or decimal text (`83.42`, `'0.000029175'`) as a Rate,
40
+ * rounded half-up once. Numbers are taken by their shortest text, so `83.42` is exactly
41
+ * 834200000 / 1e7. See `RateFromDecimalOptions` for the two encodings.
42
+ */
43
+ declare function rateFromDecimal(value: number | string, options?: RateFromDecimalOptions): Rate;
44
+ /** `r` as text rounded half-up to `decimals` fraction digits (default 12: persisted `fx_rate`, `draft_market_rate`). */
45
+ declare function rateToDecimal(r: Rate, options?: {
46
+ decimals?: number;
47
+ }): DecimalText;
48
+ /** `r` as a number: rounded half-up to 7 decimals by default (Medusa `toMajorRate`), or `'exact'` = `Number(n) / Number(d)`. */
49
+ declare function rateToNumber(r: Rate, options?: {
50
+ decimals?: number | 'exact';
51
+ }): number;
52
+ /** 1 quote = d/n base. */
53
+ declare function invertRate(r: Rate): Rate;
54
+ /**
55
+ * 1 `from` = ? `to`, from a table of rates that each say what 1 `base` is worth (keys any
56
+ * case; `base` itself counts as 1/1). Same code (any case) gives 1/1; a missing side gives
57
+ * null. Exact on bigint (`{ b.n x a.d, b.d x a.n }`), never overflows.
58
+ */
59
+ declare function crossRate(table: Readonly<Record<string, Rate>>, from: string, to: string, base: string): Rate | null;
60
+ /** Same value (cross-multiplied), whatever the parts: 2/4 equals 1/2. */
61
+ declare function rateEquals(a: Rate, b: Rate): boolean;
62
+ //#endregion
63
+ export { RateRow as a, rate as c, rateFromRow as d, rateToDecimal as f, RateFromDecimalOptions as i, rateEquals as l, RATE_REF as n, crossRate as o, rateToNumber as p, Rate as r, invertRate as s, RATE_BOUNDS as t, rateFromDecimal as u };
@@ -0,0 +1,31 @@
1
+ import { R as DecimalText, g as CurrencyCode, i as Money } from "./money-B-3aFjNJ.cjs";
2
+ import { a as RateRow } from "./rate-tqK8ijG0.cjs";
3
+ //#region src/fx/reporting.d.ts
4
+ /** `factors[lower code]` takes a stored amount in that currency to a stored amount in `base`. */
5
+ interface FactorTable {
6
+ readonly base: CurrencyCode;
7
+ readonly factors: Readonly<Record<string, DecimalText>>;
8
+ }
9
+ /**
10
+ * The reporting factors of a store whose locked rates say `1 default = n/d <code>`:
11
+ * `factor(code) = storageScale(default) / storageScale(code) x d / n`, rounded half-up to 20
12
+ * fraction digits; the default currency is `'1'`. Rows for the default currency, unknown
13
+ * codes or invalid rates (see `rateFromRow`) are skipped, so those currencies have no factor.
14
+ * Port of Medusa `storeTotalFactors`, exact (it measured the factor at a 1e9 reference).
15
+ */
16
+ declare function storeFactors(defaultCurrency: string, rows: readonly RateRow[]): FactorTable;
17
+ interface ReportingTotal {
18
+ readonly total: Money;
19
+ /** False when some item's currency has no factor (it is excluded from `total`). */
20
+ readonly complete: boolean;
21
+ /** The excluded currencies, sorted, each once. */
22
+ readonly missing: CurrencyCode[];
23
+ }
24
+ /**
25
+ * The sum of `items` in `table.base`: each converted with its currency's factor and rounded
26
+ * half-up to a storage unit (per item, as customer-stats rounds per order), then summed.
27
+ * Items whose currency has no factor are excluded and reported in `missing`.
28
+ */
29
+ declare function toReporting(items: readonly Money[], table: FactorTable): ReportingTotal;
30
+ //#endregion
31
+ export { toReporting as i, ReportingTotal as n, storeFactors as r, FactorTable as t };
@@ -0,0 +1,31 @@
1
+ import { R as DecimalText, g as CurrencyCode, i as Money } from "./money-B-3aFjNJ.js";
2
+ import { a as RateRow } from "./rate-DBPiYgT1.js";
3
+ //#region src/fx/reporting.d.ts
4
+ /** `factors[lower code]` takes a stored amount in that currency to a stored amount in `base`. */
5
+ interface FactorTable {
6
+ readonly base: CurrencyCode;
7
+ readonly factors: Readonly<Record<string, DecimalText>>;
8
+ }
9
+ /**
10
+ * The reporting factors of a store whose locked rates say `1 default = n/d <code>`:
11
+ * `factor(code) = storageScale(default) / storageScale(code) x d / n`, rounded half-up to 20
12
+ * fraction digits; the default currency is `'1'`. Rows for the default currency, unknown
13
+ * codes or invalid rates (see `rateFromRow`) are skipped, so those currencies have no factor.
14
+ * Port of Medusa `storeTotalFactors`, exact (it measured the factor at a 1e9 reference).
15
+ */
16
+ declare function storeFactors(defaultCurrency: string, rows: readonly RateRow[]): FactorTable;
17
+ interface ReportingTotal {
18
+ readonly total: Money;
19
+ /** False when some item's currency has no factor (it is excluded from `total`). */
20
+ readonly complete: boolean;
21
+ /** The excluded currencies, sorted, each once. */
22
+ readonly missing: CurrencyCode[];
23
+ }
24
+ /**
25
+ * The sum of `items` in `table.base`: each converted with its currency's factor and rounded
26
+ * half-up to a storage unit (per item, as customer-stats rounds per order), then summed.
27
+ * Items whose currency has no factor are excluded and reported in `missing`.
28
+ */
29
+ declare function toReporting(items: readonly Money[], table: FactorTable): ReportingTotal;
30
+ //#endregion
31
+ export { toReporting as i, ReportingTotal as n, storeFactors as r, FactorTable as t };
@@ -0,0 +1,69 @@
1
+ import { $ as describeValue, E as resolveCurrency, Q as MoneyError, rt as divRound, s as storedDecimal, t as boundedMoney } from "./money-WhnGiI00.js";
2
+ import { a as unitSize, t as mulDivToUnit } from "./rounding-g9rvye0k.js";
3
+ import { r as percentOfUnits } from "./percent-D1VB9xXG.js";
4
+ //#region src/fx/retail.ts
5
+ const RETAIL_ROUNDINGS = [
6
+ "none",
7
+ "nearest",
8
+ "whole",
9
+ ".99",
10
+ ".95",
11
+ "up-to-99"
12
+ ];
13
+ /** `round(0.95 x scale)`: the market charm (95, or 950 for three-decimal currencies). */
14
+ /** A price ending (`.95`, `.99`) in storage units: `hundredths` of one major unit of `scale` storage units. */
15
+ const endingOf = (scale, hundredths) => percentOfUnits(scale, {
16
+ units: hundredths,
17
+ scale: 0
18
+ }, "half-up");
19
+ const charmOf = (scale) => endingOf(scale, 95n);
20
+ function checkMode(mode) {
21
+ if (!RETAIL_ROUNDINGS.includes(mode)) throw new MoneyError("amount_invalid", `Unknown retail rounding: ${describeValue(mode)}`);
22
+ return mode;
23
+ }
24
+ /** Internal: the stored amount `p / q` (`q > 0`) rounded by `mode`, as integer storage units. */
25
+ function retailUnits(p, q, info, mode) {
26
+ const scale = BigInt(info.storageScale);
27
+ checkMode(mode);
28
+ if (mode === "up-to-99") return divRound(divRound(p, q, "ceil"), scale, "floor") * scale + scale - 1n;
29
+ const zeroDecimal = info.storageDigits === 0;
30
+ if (mode === "none" && zeroDecimal) {
31
+ const size = unitSize(info, "currency");
32
+ return divRound(2n * p + q * size, 2n * q * size, "floor") * size;
33
+ }
34
+ if (mode === "none" || p <= 0n) {
35
+ if (q === 1n) return p;
36
+ return mulDivToUnit(p, 1n, q, info, "currency");
37
+ }
38
+ if (mode === "nearest") {
39
+ if (zeroDecimal) return divRound(p, q * 100n * scale, "ceil") * 100n * scale;
40
+ const charm = charmOf(scale);
41
+ const whole = divRound(p, q * scale, "floor") * scale;
42
+ return p <= (whole + charm) * q ? whole + charm : whole + scale + charm;
43
+ }
44
+ if (mode === "whole" || zeroDecimal) return divRound(p, q * scale, "ceil") * scale;
45
+ const ending = endingOf(scale, mode === ".99" ? 99n : 95n);
46
+ const candidate = divRound(p, q * scale, "floor") * scale + ending;
47
+ return candidate * q < p ? candidate + scale : candidate;
48
+ }
49
+ /** `m` rounded to a retail price by `mode` (see `RetailRounding`). */
50
+ function roundRetail(m, mode) {
51
+ const { info } = storedDecimal(m);
52
+ return boundedMoney(retailUnits(BigInt(m.amount), 1n, info, mode), info);
53
+ }
54
+ /**
55
+ * The market rounding parameters of `currency` for the SQL builders' unnest arrays:
56
+ * `unit` = storage units per currency unit, `majorUnit` = storageScale, `charm` = the
57
+ * `.95` charm in storage units, null for a zero-decimal currency.
58
+ */
59
+ function priceRoundingParams(currency) {
60
+ const info = resolveCurrency(currency, { allowHistoric: true });
61
+ const scale = BigInt(info.storageScale);
62
+ return {
63
+ unit: Number(unitSize(info, "currency")),
64
+ majorUnit: info.storageScale,
65
+ charm: info.storageDigits === 0 ? null : Number(charmOf(scale))
66
+ };
67
+ }
68
+ //#endregion
69
+ export { retailUnits as n, roundRetail as r, priceRoundingParams as t };
@@ -0,0 +1,31 @@
1
+ import { i as Money } from "./money-B-3aFjNJ.cjs";
2
+ //#region src/fx/retail.d.ts
3
+ /**
4
+ * - `'none'`: Medusa `applyRounding` 'none': a zero-decimal currency is rounded to a whole
5
+ * unit (half toward +infinity, as `Math.round`: JPY 150050 gives 150100, since there is no
6
+ * ¥0.50); any other currency is unchanged (an exact fractional price is rounded half-up to
7
+ * the currency unit).
8
+ * - `'nearest'`: the market rule (Medusa `applyRounding`): up to the next `.95` of a major unit
9
+ * (`.950` for three-decimal), or up to the next 100 major units for a zero-decimal currency.
10
+ * - `'whole'`, `'.99'`, `'.95'`: the AliExpress rule: up to a whole unit, or the smallest price
11
+ * ending in .99 / .95 at or above it (zero-decimal currencies: up to a whole unit).
12
+ * - `'up-to-99'`: the smallest amount at or above it whose last storage digits are all 9
13
+ * (domain retail `roundUpTo99`: 1234 cents gives 1299).
14
+ * A non-positive amount is never raised and a whole one is left as is (except by `'up-to-99'`
15
+ * and by `'none'` on a zero-decimal currency).
16
+ */
17
+ type RetailRounding = 'none' | 'nearest' | 'whole' | '.99' | '.95' | 'up-to-99';
18
+ /** `m` rounded to a retail price by `mode` (see `RetailRounding`). */
19
+ declare function roundRetail(m: Money, mode: RetailRounding): Money;
20
+ /**
21
+ * The market rounding parameters of `currency` for the SQL builders' unnest arrays:
22
+ * `unit` = storage units per currency unit, `majorUnit` = storageScale, `charm` = the
23
+ * `.95` charm in storage units, null for a zero-decimal currency.
24
+ */
25
+ declare function priceRoundingParams(currency: string): {
26
+ unit: 1 | 100;
27
+ majorUnit: 100 | 1000;
28
+ charm: number | null;
29
+ };
30
+ //#endregion
31
+ export { priceRoundingParams as n, roundRetail as r, RetailRounding as t };
@@ -0,0 +1,86 @@
1
+ const require_money = require("./money-B3wH_Z4S.cjs");
2
+ const require_rounding = require("./rounding-DHBtNCHG.cjs");
3
+ const require_percent = require("./percent-C0InC2t2.cjs");
4
+ //#region src/fx/retail.ts
5
+ const RETAIL_ROUNDINGS = [
6
+ "none",
7
+ "nearest",
8
+ "whole",
9
+ ".99",
10
+ ".95",
11
+ "up-to-99"
12
+ ];
13
+ /** `round(0.95 x scale)`: the market charm (95, or 950 for three-decimal currencies). */
14
+ /** A price ending (`.95`, `.99`) in storage units: `hundredths` of one major unit of `scale` storage units. */
15
+ const endingOf = (scale, hundredths) => require_percent.percentOfUnits(scale, {
16
+ units: hundredths,
17
+ scale: 0
18
+ }, "half-up");
19
+ const charmOf = (scale) => endingOf(scale, 95n);
20
+ function checkMode(mode) {
21
+ if (!RETAIL_ROUNDINGS.includes(mode)) throw new require_money.MoneyError("amount_invalid", `Unknown retail rounding: ${require_money.describeValue(mode)}`);
22
+ return mode;
23
+ }
24
+ /** Internal: the stored amount `p / q` (`q > 0`) rounded by `mode`, as integer storage units. */
25
+ function retailUnits(p, q, info, mode) {
26
+ const scale = BigInt(info.storageScale);
27
+ checkMode(mode);
28
+ if (mode === "up-to-99") return require_money.divRound(require_money.divRound(p, q, "ceil"), scale, "floor") * scale + scale - 1n;
29
+ const zeroDecimal = info.storageDigits === 0;
30
+ if (mode === "none" && zeroDecimal) {
31
+ const size = require_rounding.unitSize(info, "currency");
32
+ return require_money.divRound(2n * p + q * size, 2n * q * size, "floor") * size;
33
+ }
34
+ if (mode === "none" || p <= 0n) {
35
+ if (q === 1n) return p;
36
+ return require_rounding.mulDivToUnit(p, 1n, q, info, "currency");
37
+ }
38
+ if (mode === "nearest") {
39
+ if (zeroDecimal) return require_money.divRound(p, q * 100n * scale, "ceil") * 100n * scale;
40
+ const charm = charmOf(scale);
41
+ const whole = require_money.divRound(p, q * scale, "floor") * scale;
42
+ return p <= (whole + charm) * q ? whole + charm : whole + scale + charm;
43
+ }
44
+ if (mode === "whole" || zeroDecimal) return require_money.divRound(p, q * scale, "ceil") * scale;
45
+ const ending = endingOf(scale, mode === ".99" ? 99n : 95n);
46
+ const candidate = require_money.divRound(p, q * scale, "floor") * scale + ending;
47
+ return candidate * q < p ? candidate + scale : candidate;
48
+ }
49
+ /** `m` rounded to a retail price by `mode` (see `RetailRounding`). */
50
+ function roundRetail(m, mode) {
51
+ const { info } = require_money.storedDecimal(m);
52
+ return require_money.boundedMoney(retailUnits(BigInt(m.amount), 1n, info, mode), info);
53
+ }
54
+ /**
55
+ * The market rounding parameters of `currency` for the SQL builders' unnest arrays:
56
+ * `unit` = storage units per currency unit, `majorUnit` = storageScale, `charm` = the
57
+ * `.95` charm in storage units, null for a zero-decimal currency.
58
+ */
59
+ function priceRoundingParams(currency) {
60
+ const info = require_money.resolveCurrency(currency, { allowHistoric: true });
61
+ const scale = BigInt(info.storageScale);
62
+ return {
63
+ unit: Number(require_rounding.unitSize(info, "currency")),
64
+ majorUnit: info.storageScale,
65
+ charm: info.storageDigits === 0 ? null : Number(charmOf(scale))
66
+ };
67
+ }
68
+ //#endregion
69
+ Object.defineProperty(exports, "priceRoundingParams", {
70
+ enumerable: true,
71
+ get: function() {
72
+ return priceRoundingParams;
73
+ }
74
+ });
75
+ Object.defineProperty(exports, "retailUnits", {
76
+ enumerable: true,
77
+ get: function() {
78
+ return retailUnits;
79
+ }
80
+ });
81
+ Object.defineProperty(exports, "roundRetail", {
82
+ enumerable: true,
83
+ get: function() {
84
+ return roundRetail;
85
+ }
86
+ });
@@ -0,0 +1,31 @@
1
+ import { i as Money } from "./money-B-3aFjNJ.js";
2
+ //#region src/fx/retail.d.ts
3
+ /**
4
+ * - `'none'`: Medusa `applyRounding` 'none': a zero-decimal currency is rounded to a whole
5
+ * unit (half toward +infinity, as `Math.round`: JPY 150050 gives 150100, since there is no
6
+ * ¥0.50); any other currency is unchanged (an exact fractional price is rounded half-up to
7
+ * the currency unit).
8
+ * - `'nearest'`: the market rule (Medusa `applyRounding`): up to the next `.95` of a major unit
9
+ * (`.950` for three-decimal), or up to the next 100 major units for a zero-decimal currency.
10
+ * - `'whole'`, `'.99'`, `'.95'`: the AliExpress rule: up to a whole unit, or the smallest price
11
+ * ending in .99 / .95 at or above it (zero-decimal currencies: up to a whole unit).
12
+ * - `'up-to-99'`: the smallest amount at or above it whose last storage digits are all 9
13
+ * (domain retail `roundUpTo99`: 1234 cents gives 1299).
14
+ * A non-positive amount is never raised and a whole one is left as is (except by `'up-to-99'`
15
+ * and by `'none'` on a zero-decimal currency).
16
+ */
17
+ type RetailRounding = 'none' | 'nearest' | 'whole' | '.99' | '.95' | 'up-to-99';
18
+ /** `m` rounded to a retail price by `mode` (see `RetailRounding`). */
19
+ declare function roundRetail(m: Money, mode: RetailRounding): Money;
20
+ /**
21
+ * The market rounding parameters of `currency` for the SQL builders' unnest arrays:
22
+ * `unit` = storage units per currency unit, `majorUnit` = storageScale, `charm` = the
23
+ * `.95` charm in storage units, null for a zero-decimal currency.
24
+ */
25
+ declare function priceRoundingParams(currency: string): {
26
+ unit: 1 | 100;
27
+ majorUnit: 100 | 1000;
28
+ charm: number | null;
29
+ };
30
+ //#endregion
31
+ export { priceRoundingParams as n, roundRetail as r, RetailRounding as t };
@@ -0,0 +1,79 @@
1
+ const require_money = require("./money-B3wH_Z4S.cjs");
2
+ //#region src/rounding.ts
3
+ /** Internal: the size of `unit` in storage units. */
4
+ function unitSize(info, unit) {
5
+ if (unit === "storage") return 1n;
6
+ const scale = BigInt(info.storageScale);
7
+ return unit === "major" ? scale : scale / require_money.pow10(info.storageDigits);
8
+ }
9
+ /** Internal: `a x b / c` storage units rounded once by `mode` (default half-up) to a whole `unit`, in storage units. */
10
+ function mulDivToUnit(a, b, c, info, unit, mode = "half-up") {
11
+ const size = unitSize(info, unit);
12
+ return require_money.mulDiv(a, b, c * size, mode) * size;
13
+ }
14
+ /** Internal: a fractional stored amount rounded to a whole unit (default a storage unit, half-up) as Money. */
15
+ function roundStoredDecimal(amount, info, options = {}) {
16
+ const units = mulDivToUnit(amount.units, 1n, require_money.pow10(amount.scale), info, options.unit ?? "storage", options.rounding);
17
+ return require_money.boundedMoney(units, info);
18
+ }
19
+ /**
20
+ * A stored amount (possibly fractional: a percentage of a price, a pro-rated fee) rounded to
21
+ * a whole `unit` with `rounding`, as Money. Replaces `roundToStorageUnit`,
22
+ * `roundToDisplayUnit` (`unit: 'currency'`), `Math.floor(round2(x))` (`rounding: 'floor'`).
23
+ * A Money input must be in `currency`; a number must be finite; text is plain decimal.
24
+ */
25
+ function roundToUnit(value, currency, options = {}) {
26
+ const info = require_money.resolveCurrency(currency, { allowHistoric: true });
27
+ let amount;
28
+ if (typeof value === "object" && value !== null) {
29
+ const { info: own } = require_money.storedDecimal(value);
30
+ if (own.code !== info.code) throw new require_money.MoneyError("currency_mismatch", `Cannot round a ${own.code} amount as ${info.code}`);
31
+ amount = {
32
+ units: BigInt(value.amount),
33
+ scale: 0
34
+ };
35
+ } else amount = require_money.decimalOf(value);
36
+ return roundStoredDecimal(amount, info, options);
37
+ }
38
+ /**
39
+ * A major-unit number rounded to the currency's display digits (default) or storage digits,
40
+ * half-up by default, as a number. For report outputs that already are majors; new code
41
+ * keeps Money and converts once.
42
+ */
43
+ function roundMajor(major, currency, options = {}) {
44
+ const info = require_money.resolveCurrency(currency, { allowHistoric: true });
45
+ const value = require_money.decimalOf(major);
46
+ const digits = options.digits === "storage" ? info.storageDigits : info.displayDigits;
47
+ return Number(require_money.formatDecimal(require_money.roundToScale(value, digits, options.rounding ?? "half-up")));
48
+ }
49
+ //#endregion
50
+ Object.defineProperty(exports, "mulDivToUnit", {
51
+ enumerable: true,
52
+ get: function() {
53
+ return mulDivToUnit;
54
+ }
55
+ });
56
+ Object.defineProperty(exports, "roundMajor", {
57
+ enumerable: true,
58
+ get: function() {
59
+ return roundMajor;
60
+ }
61
+ });
62
+ Object.defineProperty(exports, "roundStoredDecimal", {
63
+ enumerable: true,
64
+ get: function() {
65
+ return roundStoredDecimal;
66
+ }
67
+ });
68
+ Object.defineProperty(exports, "roundToUnit", {
69
+ enumerable: true,
70
+ get: function() {
71
+ return roundToUnit;
72
+ }
73
+ });
74
+ Object.defineProperty(exports, "unitSize", {
75
+ enumerable: true,
76
+ get: function() {
77
+ return unitSize;
78
+ }
79
+ });
@@ -0,0 +1,50 @@
1
+ import { B as decimalOf, E as resolveCurrency, Q as MoneyError, X as mulDiv, dt as roundToScale, it as formatDecimal, s as storedDecimal, t as boundedMoney, ut as pow10 } from "./money-WhnGiI00.js";
2
+ //#region src/rounding.ts
3
+ /** Internal: the size of `unit` in storage units. */
4
+ function unitSize(info, unit) {
5
+ if (unit === "storage") return 1n;
6
+ const scale = BigInt(info.storageScale);
7
+ return unit === "major" ? scale : scale / pow10(info.storageDigits);
8
+ }
9
+ /** Internal: `a x b / c` storage units rounded once by `mode` (default half-up) to a whole `unit`, in storage units. */
10
+ function mulDivToUnit(a, b, c, info, unit, mode = "half-up") {
11
+ const size = unitSize(info, unit);
12
+ return mulDiv(a, b, c * size, mode) * size;
13
+ }
14
+ /** Internal: a fractional stored amount rounded to a whole unit (default a storage unit, half-up) as Money. */
15
+ function roundStoredDecimal(amount, info, options = {}) {
16
+ const units = mulDivToUnit(amount.units, 1n, pow10(amount.scale), info, options.unit ?? "storage", options.rounding);
17
+ return boundedMoney(units, info);
18
+ }
19
+ /**
20
+ * A stored amount (possibly fractional: a percentage of a price, a pro-rated fee) rounded to
21
+ * a whole `unit` with `rounding`, as Money. Replaces `roundToStorageUnit`,
22
+ * `roundToDisplayUnit` (`unit: 'currency'`), `Math.floor(round2(x))` (`rounding: 'floor'`).
23
+ * A Money input must be in `currency`; a number must be finite; text is plain decimal.
24
+ */
25
+ function roundToUnit(value, currency, options = {}) {
26
+ const info = resolveCurrency(currency, { allowHistoric: true });
27
+ let amount;
28
+ if (typeof value === "object" && value !== null) {
29
+ const { info: own } = storedDecimal(value);
30
+ if (own.code !== info.code) throw new MoneyError("currency_mismatch", `Cannot round a ${own.code} amount as ${info.code}`);
31
+ amount = {
32
+ units: BigInt(value.amount),
33
+ scale: 0
34
+ };
35
+ } else amount = decimalOf(value);
36
+ return roundStoredDecimal(amount, info, options);
37
+ }
38
+ /**
39
+ * A major-unit number rounded to the currency's display digits (default) or storage digits,
40
+ * half-up by default, as a number. For report outputs that already are majors; new code
41
+ * keeps Money and converts once.
42
+ */
43
+ function roundMajor(major, currency, options = {}) {
44
+ const info = resolveCurrency(currency, { allowHistoric: true });
45
+ const value = decimalOf(major);
46
+ const digits = options.digits === "storage" ? info.storageDigits : info.displayDigits;
47
+ return Number(formatDecimal(roundToScale(value, digits, options.rounding ?? "half-up")));
48
+ }
49
+ //#endregion
50
+ export { unitSize as a, roundToUnit as i, roundMajor as n, roundStoredDecimal as r, mulDivToUnit as t };