@tallyui/pos 3.3.0 → 3.4.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.
- package/dist/index.d.ts +6 -1
- package/dist/index.js +24 -6
- package/package.json +6 -6
- package/src/order/order-builder.ts +12 -2
- package/src/register/session-store.ts +3 -1
- package/src/sale/use-sale.ts +3 -0
- package/src/tax/tax-provider.tsx +4 -2
- package/src/tax/types.ts +2 -0
package/dist/index.d.ts
CHANGED
|
@@ -125,6 +125,8 @@ type TaxRateMap = {
|
|
|
125
125
|
[taxClass: string]: number;
|
|
126
126
|
};
|
|
127
127
|
interface TaxContext {
|
|
128
|
+
shippingTaxClass?: string;
|
|
129
|
+
taxClassSlugs?: readonly string[];
|
|
128
130
|
getTaxRates?(taxClass?: string, options?: {
|
|
129
131
|
shipping?: boolean;
|
|
130
132
|
}): readonly WooRate[];
|
|
@@ -148,7 +150,7 @@ interface TaxProviderProps extends Pick<StoreSettings, 'taxRoundAtSubtotal' | 's
|
|
|
148
150
|
rateCodes?: Record<string, string>;
|
|
149
151
|
children: ReactNode;
|
|
150
152
|
}
|
|
151
|
-
declare function TaxProvider({ ratesPpm, pricesIncludeTax, rounding, rateCodes, taxRates, children }: TaxProviderProps): react_jsx_runtime.JSX.Element;
|
|
153
|
+
declare function TaxProvider({ ratesPpm, pricesIncludeTax, rounding, rateCodes, taxRates, shippingTaxClass, taxClassSlugs, children }: TaxProviderProps): react_jsx_runtime.JSX.Element;
|
|
152
154
|
declare function useTax(): TaxContext;
|
|
153
155
|
|
|
154
156
|
type StoreSettingsResolution = {
|
|
@@ -1957,6 +1959,9 @@ declare function useSale(settings: Pick<StoreSettings, 'currency'>, opts: {
|
|
|
1957
1959
|
stage: SaleStage;
|
|
1958
1960
|
error: string | null;
|
|
1959
1961
|
idle: boolean;
|
|
1962
|
+
/** The store's capabilities as passed in: a component offers fees, shipping and custom lines
|
|
1963
|
+
* only when `orderCreate >= 5`, and their tax status and class only as `lineTax` allows. */
|
|
1964
|
+
capabilities: ServerCapabilities | undefined;
|
|
1960
1965
|
/** A completion is pending (see `complete()`): the sale is locked, and the UI offers Retry. */
|
|
1961
1966
|
saving: boolean;
|
|
1962
1967
|
/** The failed save's order is confirmed stored (see `isStored`): the UI also offers Continue (`continueSale()`). */
|
package/dist/index.js
CHANGED
|
@@ -327,7 +327,7 @@ import { woocommerceTax as woocommerceTax2 } from "@tallyui/core";
|
|
|
327
327
|
import { jsx as jsx2 } from "react/jsx-runtime";
|
|
328
328
|
var taxLogger = createLogger("tax");
|
|
329
329
|
var TaxCtx = createContext2(null);
|
|
330
|
-
function TaxProvider({ ratesPpm, pricesIncludeTax, rounding, rateCodes, taxRates, children }) {
|
|
330
|
+
function TaxProvider({ ratesPpm, pricesIncludeTax, rounding, rateCodes, taxRates, shippingTaxClass, taxClassSlugs, children }) {
|
|
331
331
|
const value = useMemo2(
|
|
332
332
|
() => {
|
|
333
333
|
if (Object.values(ratesPpm).some((rate) => !Number.isSafeInteger(rate) || rate < 0)) {
|
|
@@ -350,10 +350,12 @@ function TaxProvider({ ratesPpm, pricesIncludeTax, rounding, rateCodes, taxRates
|
|
|
350
350
|
return rateCodes && Object.hasOwn(rateCodes, key) ? rateCodes[key] : void 0;
|
|
351
351
|
},
|
|
352
352
|
pricesIncludeTax,
|
|
353
|
-
...rounding ? { rounding } : {}
|
|
353
|
+
...rounding ? { rounding } : {},
|
|
354
|
+
...shippingTaxClass !== void 0 ? { shippingTaxClass } : {},
|
|
355
|
+
...taxClassSlugs !== void 0 ? { taxClassSlugs } : {}
|
|
354
356
|
};
|
|
355
357
|
},
|
|
356
|
-
[ratesPpm, pricesIncludeTax, rounding, rateCodes, taxRates]
|
|
358
|
+
[ratesPpm, pricesIncludeTax, rounding, rateCodes, taxRates, shippingTaxClass, taxClassSlugs]
|
|
357
359
|
);
|
|
358
360
|
return /* @__PURE__ */ jsx2(TaxCtx.Provider, { value, children });
|
|
359
361
|
}
|
|
@@ -554,7 +556,7 @@ function createRepository(collection) {
|
|
|
554
556
|
|
|
555
557
|
// src/order/order-builder.ts
|
|
556
558
|
import { BehaviorSubject } from "rxjs";
|
|
557
|
-
import { resolvePrice } from "@tallyui/core";
|
|
559
|
+
import { resolvePrice, woocommerceTax as woocommerceTax3 } from "@tallyui/core";
|
|
558
560
|
|
|
559
561
|
// src/order/allocate-order-discount.ts
|
|
560
562
|
function allocateOrderDiscount(lineAmountsMinor, totalMinor) {
|
|
@@ -682,6 +684,7 @@ function createOrderBuilder(options) {
|
|
|
682
684
|
});
|
|
683
685
|
const shares = allocateOrderDiscount(lineAmounts, recalcedOrderDiscounts.reduce((sum, d) => sum + d.amountMinor, 0));
|
|
684
686
|
const lines = lineItems.map((li, index) => recalculateLine(li, shares[index]));
|
|
687
|
+
if (woo) shipping = shipping.map((charge) => ({ ...charge, ...recalculateCharge(charge.id, charge, true) }));
|
|
685
688
|
const wooTotals = woo ? woocommerceTotals(lines, fees, shipping, currency, woo.roundAtSubtotal, taxContext.pricesIncludeTax) : void 0;
|
|
686
689
|
const { subtotalMinor } = wooTotals ?? sumLines(lines, taxContext.rounding);
|
|
687
690
|
const charges = [...fees, ...shipping];
|
|
@@ -799,7 +802,18 @@ function createOrderBuilder(options) {
|
|
|
799
802
|
if (!input.name.trim()) throw new RangeError("Name must not be empty");
|
|
800
803
|
const taxStatus = input.taxStatus ?? "taxable";
|
|
801
804
|
if (woo) {
|
|
802
|
-
const
|
|
805
|
+
const taxClass = shipping2 ? taxContext.shippingTaxClass === void 0 || taxContext.shippingTaxClass === "inherit" ? woocommerceTax3.resolveInheritedShippingTaxClass(lineItems.map((line3) => ({
|
|
806
|
+
tax_class: line3.taxClass,
|
|
807
|
+
taxable: line3.taxStatus !== "none"
|
|
808
|
+
})), taxContext.taxClassSlugs ?? []) : taxContext.shippingTaxClass : input.taxClass;
|
|
809
|
+
const untaxed = shipping2 ? taxClass === woocommerceTax3.NO_SHIPPING_TAX : taxStatus === "none";
|
|
810
|
+
const line2 = wooLine(
|
|
811
|
+
input.amountMinor,
|
|
812
|
+
taxClass === woocommerceTax3.NO_SHIPPING_TAX ? void 0 : taxClass,
|
|
813
|
+
taxContext.pricesIncludeTax,
|
|
814
|
+
untaxed,
|
|
815
|
+
shipping2
|
|
816
|
+
);
|
|
803
817
|
return {
|
|
804
818
|
id: id2,
|
|
805
819
|
name: input.name,
|
|
@@ -2882,7 +2896,8 @@ async function writeClosure({
|
|
|
2882
2896
|
const rounding = recorded.granularity === "custom" ? DEFAULT_TAX_ROUNDING : recorded;
|
|
2883
2897
|
roundings.add(rounding.granularity === "woocommerce" ? `woocommerce ${rounding.roundAtSubtotal}` : `${rounding.granularity} ${rounding.mode}`);
|
|
2884
2898
|
const lines = order.lines.map((line) => ({ ...line, taxInclusive: line.taxInclusive ?? order.pricesIncludeTax }));
|
|
2885
|
-
|
|
2899
|
+
const taxedLines = [...lines, ...[...order.fees ?? [], ...order.shipping ?? []].map((charge) => ({ ...charge, taxInclusive: order.pricesIncludeTax }))];
|
|
2900
|
+
for (const { ratePpm, netMinor, amountMinor } of taxLinesByRate(taxedLines, order.taxMinor, void 0, order.taxRounding)) {
|
|
2886
2901
|
const existing2 = taxRates.get(String(ratePpm));
|
|
2887
2902
|
taxRates.set(String(ratePpm), {
|
|
2888
2903
|
ratePpm,
|
|
@@ -4079,6 +4094,9 @@ function useSale(settings, opts) {
|
|
|
4079
4094
|
stage,
|
|
4080
4095
|
error,
|
|
4081
4096
|
idle: idle3,
|
|
4097
|
+
/** The store's capabilities as passed in: a component offers fees, shipping and custom lines
|
|
4098
|
+
* only when `orderCreate >= 5`, and their tax status and class only as `lineTax` allows. */
|
|
4099
|
+
capabilities: opts.capabilities,
|
|
4082
4100
|
/** A completion is pending (see `complete()`): the sale is locked, and the UI offers Retry. */
|
|
4083
4101
|
saving,
|
|
4084
4102
|
/** The failed save's order is confirmed stored (see `isStored`): the UI also offers Continue (`continueSale()`). */
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tallyui/pos",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.4.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Shared POS business logic for Tally UI — tax, orders, payments, discounts, receipts",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -27,20 +27,20 @@
|
|
|
27
27
|
"directory": "packages/pos"
|
|
28
28
|
},
|
|
29
29
|
"peerDependencies": {
|
|
30
|
-
"@tallyui/core": "^3.
|
|
30
|
+
"@tallyui/core": "^3.4.0",
|
|
31
31
|
"react": ">=18",
|
|
32
32
|
"rxdb": "~17.5.0",
|
|
33
33
|
"rxjs": ">=7"
|
|
34
34
|
},
|
|
35
35
|
"devDependencies": {
|
|
36
|
-
"@tallyui/core": "3.
|
|
36
|
+
"@tallyui/core": "3.4.0",
|
|
37
37
|
"@types/react": ">=18",
|
|
38
38
|
"react": "^19.2.4",
|
|
39
39
|
"rxdb": "17.5.0",
|
|
40
40
|
"rxjs": "7.8.2",
|
|
41
|
-
"@tallyui/connector-medusa": "3.
|
|
42
|
-
"@tallyui/connector-woocommerce": "3.
|
|
43
|
-
"@tallyui/database": "3.
|
|
41
|
+
"@tallyui/connector-medusa": "3.4.0",
|
|
42
|
+
"@tallyui/connector-woocommerce": "3.4.0",
|
|
43
|
+
"@tallyui/database": "3.4.0"
|
|
44
44
|
},
|
|
45
45
|
"scripts": {
|
|
46
46
|
"build": "tsup",
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { BehaviorSubject, type Observable } from 'rxjs';
|
|
2
|
-
import { resolvePrice, type ProductTraits, type TaxRounding } from '@tallyui/core';
|
|
2
|
+
import { resolvePrice, woocommerceTax, type ProductTraits, type TaxRounding } from '@tallyui/core';
|
|
3
3
|
import type { TaxContext } from '../tax/types';
|
|
4
4
|
import { taxMicros, roundMicrosToMinor, roundedTaxByRate } from '../tax/exact';
|
|
5
5
|
import { taxLogger } from '../tax/tax-provider';
|
|
@@ -188,6 +188,7 @@ export function createOrderBuilder(options: OrderBuilderOptions): OrderBuilder {
|
|
|
188
188
|
const shares = allocateOrderDiscount(lineAmounts, recalcedOrderDiscounts.reduce((sum, d) => sum + d.amountMinor, 0));
|
|
189
189
|
const lines = lineItems.map((li, index) => recalculateLine(li, shares[index]));
|
|
190
190
|
|
|
191
|
+
if (woo) shipping = shipping.map((charge) => ({ ...charge, ...recalculateCharge(charge.id, charge, true) }));
|
|
191
192
|
const wooTotals = woo ? woocommerceTotals(lines, fees, shipping, currency, woo.roundAtSubtotal, taxContext.pricesIncludeTax) : undefined;
|
|
192
193
|
const { subtotalMinor } = wooTotals ?? sumLines(lines, taxContext.rounding);
|
|
193
194
|
const charges = [...fees, ...shipping];
|
|
@@ -310,7 +311,16 @@ export function createOrderBuilder(options: OrderBuilderOptions): OrderBuilder {
|
|
|
310
311
|
if (!input.name.trim()) throw new RangeError('Name must not be empty');
|
|
311
312
|
const taxStatus = input.taxStatus ?? 'taxable';
|
|
312
313
|
if (woo) {
|
|
313
|
-
const
|
|
314
|
+
const taxClass = shipping
|
|
315
|
+
? taxContext.shippingTaxClass === undefined || taxContext.shippingTaxClass === 'inherit'
|
|
316
|
+
? woocommerceTax.resolveInheritedShippingTaxClass(lineItems.map((line) => ({
|
|
317
|
+
tax_class: line.taxClass, taxable: line.taxStatus !== 'none',
|
|
318
|
+
})), taxContext.taxClassSlugs ?? [])
|
|
319
|
+
: taxContext.shippingTaxClass
|
|
320
|
+
: input.taxClass;
|
|
321
|
+
const untaxed = shipping ? taxClass === woocommerceTax.NO_SHIPPING_TAX : taxStatus === 'none';
|
|
322
|
+
const line = wooLine(input.amountMinor, taxClass === woocommerceTax.NO_SHIPPING_TAX ? undefined : taxClass,
|
|
323
|
+
taxContext.pricesIncludeTax, untaxed, shipping);
|
|
314
324
|
return { id, name: input.name, amountMinor: input.amountMinor, taxClass: input.taxClass, taxStatus,
|
|
315
325
|
...line };
|
|
316
326
|
}
|
|
@@ -484,7 +484,9 @@ export async function writeClosure({
|
|
|
484
484
|
const rounding = recorded.granularity === 'custom' ? DEFAULT_TAX_ROUNDING : recorded;
|
|
485
485
|
roundings.add(rounding.granularity === 'woocommerce' ? `woocommerce ${rounding.roundAtSubtotal}` : `${rounding.granularity} ${rounding.mode}`);
|
|
486
486
|
const lines = order.lines.map((line) => ({ ...line, taxInclusive: line.taxInclusive ?? order.pricesIncludeTax }));
|
|
487
|
-
|
|
487
|
+
const taxedLines = [...lines, ...[...(order.fees ?? []), ...(order.shipping ?? [])]
|
|
488
|
+
.map((charge) => ({ ...charge, taxInclusive: order.pricesIncludeTax }))];
|
|
489
|
+
for (const { ratePpm, netMinor, amountMinor } of taxLinesByRate(taxedLines, order.taxMinor, undefined, order.taxRounding)) {
|
|
488
490
|
const existing = taxRates.get(String(ratePpm));
|
|
489
491
|
taxRates.set(String(ratePpm), {
|
|
490
492
|
ratePpm,
|
package/src/sale/use-sale.ts
CHANGED
|
@@ -249,6 +249,9 @@ export function useSale(settings: Pick<StoreSettings, 'currency'>, opts: {
|
|
|
249
249
|
|
|
250
250
|
const result = {
|
|
251
251
|
order, stage, error, idle,
|
|
252
|
+
/** The store's capabilities as passed in: a component offers fees, shipping and custom lines
|
|
253
|
+
* only when `orderCreate >= 5`, and their tax status and class only as `lineTax` allows. */
|
|
254
|
+
capabilities: opts.capabilities,
|
|
252
255
|
/** A completion is pending (see `complete()`): the sale is locked, and the UI offers Retry. */
|
|
253
256
|
saving,
|
|
254
257
|
/** The failed save's order is confirmed stored (see `isStored`): the UI also offers Continue (`continueSale()`). */
|
package/src/tax/tax-provider.tsx
CHANGED
|
@@ -19,7 +19,7 @@ export interface TaxProviderProps extends Pick<StoreSettings, 'taxRoundAtSubtota
|
|
|
19
19
|
children: ReactNode;
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
-
export function TaxProvider({ ratesPpm, pricesIncludeTax, rounding, rateCodes, taxRates, children }: TaxProviderProps) {
|
|
22
|
+
export function TaxProvider({ ratesPpm, pricesIncludeTax, rounding, rateCodes, taxRates, shippingTaxClass, taxClassSlugs, children }: TaxProviderProps) {
|
|
23
23
|
const value = useMemo<TaxContext>(
|
|
24
24
|
() => {
|
|
25
25
|
if (Object.values(ratesPpm).some((rate) => !Number.isSafeInteger(rate) || rate < 0)) {
|
|
@@ -45,9 +45,11 @@ export function TaxProvider({ ratesPpm, pricesIncludeTax, rounding, rateCodes, t
|
|
|
45
45
|
},
|
|
46
46
|
pricesIncludeTax,
|
|
47
47
|
...(rounding ? { rounding } : {}),
|
|
48
|
+
...(shippingTaxClass !== undefined ? { shippingTaxClass } : {}),
|
|
49
|
+
...(taxClassSlugs !== undefined ? { taxClassSlugs } : {}),
|
|
48
50
|
};
|
|
49
51
|
},
|
|
50
|
-
[ratesPpm, pricesIncludeTax, rounding, rateCodes, taxRates],
|
|
52
|
+
[ratesPpm, pricesIncludeTax, rounding, rateCodes, taxRates, shippingTaxClass, taxClassSlugs],
|
|
51
53
|
);
|
|
52
54
|
|
|
53
55
|
return <TaxCtx.Provider value={value}>{children}</TaxCtx.Provider>;
|
package/src/tax/types.ts
CHANGED
|
@@ -9,6 +9,8 @@ export type TaxRateMap = {
|
|
|
9
9
|
};
|
|
10
10
|
|
|
11
11
|
export interface TaxContext {
|
|
12
|
+
shippingTaxClass?: string;
|
|
13
|
+
taxClassSlugs?: readonly string[];
|
|
12
14
|
getTaxRates?(taxClass?: string, options?: { shipping?: boolean }): readonly WooRate[];
|
|
13
15
|
/** Tax rate for a tax class as integer parts per million (19% = 190000); the default class when omitted or unknown. */
|
|
14
16
|
getTaxRatePpm(taxClass?: string): number;
|