@stll/money 0.2.3 → 0.3.1

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.
package/dist/index.d.ts CHANGED
@@ -110,6 +110,12 @@ export type ProrateHourlyCentsInput = {
110
110
  hourlyRateCents: CentsAmount;
111
111
  };
112
112
  export declare const prorateHourlyCents: ({ billedMinutes, hourlyRateCents }: ProrateHourlyCentsInput) => CentsAmount;
113
+ type TimeEntryAmountInput = {
114
+ billedMinutes: number;
115
+ rateAtEntry: CentsAmount;
116
+ noCharge: boolean;
117
+ };
118
+ export declare const timeEntryAmount: (entry: TimeEntryAmountInput) => CentsAmount;
113
119
  export type ApplyMarkupCentsInput = {
114
120
  amountCents: CentsAmount;
115
121
  markupPercent: number;
package/dist/index.js CHANGED
@@ -171,18 +171,34 @@ const toMajorUnits = ({ amountCents, currency }) => amountCents / 10 ** currency
171
171
  * RangeError would take the whole board down with it.
172
172
  */
173
173
  const formatMoneyCents = ({ amountCents, currency, locale, fractionDigits }) => {
174
- const major = toMajorUnits({
175
- amountCents,
176
- currency
174
+ const minorDigits = currencyMinorUnitDigits(currency);
175
+ const digits = fractionDigits ?? minorDigits;
176
+ if (!Number.isInteger(digits) || digits < 0 || digits > 100) return panic("Money fraction digits must be an integer from 0 to 100");
177
+ const magnitude = BigInt(Math.abs(cents(amountCents)));
178
+ const negative = amountCents < 0 || Object.is(amountCents, -0);
179
+ const displayUnits = digits >= minorDigits ? magnitude * 10n ** BigInt(digits - minorDigits) : (magnitude + 10n ** BigInt(minorDigits - digits) / 2n) / 10n ** BigInt(minorDigits - digits);
180
+ const scale = 10n ** BigInt(digits);
181
+ const whole = displayUnits / scale;
182
+ const fraction = (displayUnits % scale).toString().padStart(digits, "0");
183
+ const formatted = Result.try(() => {
184
+ const formatter = new Intl.NumberFormat(locale, {
185
+ style: "currency",
186
+ currency,
187
+ minimumFractionDigits: digits,
188
+ maximumFractionDigits: digits
189
+ });
190
+ const digitFormatter = new Intl.NumberFormat(locale, { useGrouping: false });
191
+ const localizedFraction = fraction.split("").map((digit) => digitFormatter.format(Number(digit))).join("");
192
+ const negativeSubunit = negative && whole === 0n;
193
+ let template = whole;
194
+ if (negative) template = negativeSubunit ? -1n : -whole;
195
+ return formatter.formatToParts(template).map((part) => {
196
+ if (part.type === "fraction") return localizedFraction;
197
+ if (negativeSubunit && part.type === "integer") return digitFormatter.format(0);
198
+ return part.value;
199
+ }).join("");
177
200
  });
178
- const digits = fractionDigits ?? currencyMinorUnitDigits(currency);
179
- const formatted = Result.try(() => new Intl.NumberFormat(locale, {
180
- style: "currency",
181
- currency,
182
- minimumFractionDigits: digits,
183
- maximumFractionDigits: digits
184
- }).format(major));
185
- return formatted.isErr() ? `${major.toFixed(digits)} ${currency}` : formatted.value;
201
+ return formatted.isErr() ? `${negative ? "-" : ""}${whole}${digits === 0 ? "" : `.${fraction}`} ${currency}` : formatted.value;
186
202
  };
187
203
  //#endregion
188
204
  //#region src/index.ts
@@ -212,6 +228,10 @@ const prorateHourlyCents = ({ billedMinutes, hourlyRateCents }) => {
212
228
  const product = BigInt(billedMinutes) * BigInt(hourlyRateCents);
213
229
  return cents(Number((product + 30n) / 60n));
214
230
  };
231
+ const timeEntryAmount = (entry) => entry.noCharge ? cents(0) : prorateHourlyCents({
232
+ billedMinutes: entry.billedMinutes,
233
+ hourlyRateCents: entry.rateAtEntry
234
+ });
215
235
  const applyMarkupCents = ({ amountCents, markupPercent }) => {
216
236
  assertNonNegativeInteger("amountCents", amountCents);
217
237
  assertNonNegativeInteger("markupPercent", markupPercent);
@@ -277,4 +297,4 @@ var MoneyTotals = class {
277
297
  }
278
298
  };
279
299
  //#endregion
280
- export { MoneyTotals, addCents, applyMarkupCents, cents, currencyCents, currencyMinorUnitDigits, formatMoneyCents, prorateHourlyCents, toMajorUnits, toMinorUnits, tryToMinorUnits, unsafeCents };
300
+ export { MoneyTotals, addCents, applyMarkupCents, cents, currencyCents, currencyMinorUnitDigits, formatMoneyCents, prorateHourlyCents, timeEntryAmount, toMajorUnits, toMinorUnits, tryToMinorUnits, unsafeCents };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stll/money",
3
- "version": "0.2.3",
3
+ "version": "0.3.1",
4
4
  "description": "Branded minor-unit monetary amounts and currency-safe billing arithmetic.",
5
5
  "keywords": [
6
6
  "billing",