@tallyui/connector-woocommerce 3.3.0 → 3.5.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 +10 -2
- package/dist/index.js +51 -25
- package/package.json +3 -3
- package/src/commands/transport.ts +46 -23
- package/src/store-settings.ts +2 -2
- package/src/traits/product.ts +4 -0
package/dist/index.d.ts
CHANGED
|
@@ -2,8 +2,8 @@ import { SyncContext, ServerCapabilities, StoreSettings, Customer, CustomerInput
|
|
|
2
2
|
export { ConnectorUnauthorizedError } from '@tallyui/core';
|
|
3
3
|
import { RxJsonSchema } from 'rxdb';
|
|
4
4
|
|
|
5
|
-
/** The highest order.create version the WooCommerce transport maps: discounts and
|
|
6
|
-
declare const WOO_ORDER_CREATE_VERSION =
|
|
5
|
+
/** The highest order.create version the WooCommerce transport maps: v4's net discounts, and v5's fees, shipping and custom lines (WCPOS push/orders; orders #146, #161). */
|
|
6
|
+
declare const WOO_ORDER_CREATE_VERSION = 5;
|
|
7
7
|
declare function wooStoreSettings(context: SyncContext): Promise<StoreSettings>;
|
|
8
8
|
declare function readWooCapabilities(context: SyncContext): Promise<ServerCapabilities | undefined>;
|
|
9
9
|
|
|
@@ -62,6 +62,14 @@ interface WooLocalOrder {
|
|
|
62
62
|
id: string;
|
|
63
63
|
netMicros?: string;
|
|
64
64
|
}>;
|
|
65
|
+
fees?: ReadonlyArray<{
|
|
66
|
+
id: string;
|
|
67
|
+
netMicros?: string;
|
|
68
|
+
}>;
|
|
69
|
+
shipping?: ReadonlyArray<{
|
|
70
|
+
id: string;
|
|
71
|
+
netMicros?: string;
|
|
72
|
+
}>;
|
|
65
73
|
}
|
|
66
74
|
declare function toWooOrderPayload(envelope: OrderCreateEnvelope, local?: WooLocalOrder, options?: {
|
|
67
75
|
paymentsList?: boolean;
|
package/dist/index.js
CHANGED
|
@@ -450,7 +450,8 @@ var wooProductTraits = {
|
|
|
450
450
|
getVariantCount: (doc) => doc.type === "variable" ? doc.variations?.length ?? 0 : 1,
|
|
451
451
|
getType: (doc) => doc.type ?? "simple",
|
|
452
452
|
getBarcode: (doc) => wooProductBarcode(doc),
|
|
453
|
-
getCategoryNames: (doc) => (doc.categories ?? []).map((cat) => cat.name).filter(Boolean)
|
|
453
|
+
getCategoryNames: (doc) => (doc.categories ?? []).map((cat) => cat.name).filter(Boolean),
|
|
454
|
+
getCategories: (doc) => (doc.categories ?? []).filter((cat) => cat.name).map((cat) => ({ id: cat.id != null ? String(cat.id) : cat.name, name: cat.name }))
|
|
454
455
|
};
|
|
455
456
|
|
|
456
457
|
// src/sync/products.ts
|
|
@@ -718,7 +719,7 @@ function createWooCustomers(options) {
|
|
|
718
719
|
|
|
719
720
|
// src/store-settings.ts
|
|
720
721
|
import { ConnectorUnauthorizedError as ConnectorUnauthorizedError2, StoreSettingsError, minorUnitDigits, woocommerceTax } from "@tallyui/core";
|
|
721
|
-
var WOO_ORDER_CREATE_VERSION =
|
|
722
|
+
var WOO_ORDER_CREATE_VERSION = 5;
|
|
722
723
|
async function wooStoreSettings(context) {
|
|
723
724
|
try {
|
|
724
725
|
const init = { method: "GET", headers: context.headers, signal: context.signal };
|
|
@@ -841,7 +842,7 @@ async function readWooCapabilities(context) {
|
|
|
841
842
|
}
|
|
842
843
|
|
|
843
844
|
// package.json
|
|
844
|
-
var version = "3.
|
|
845
|
+
var version = "3.5.0";
|
|
845
846
|
|
|
846
847
|
// src/commands/transport.ts
|
|
847
848
|
import { minorUnitDigits as minorUnitDigits2, moneyFromDecimalString, moneyFromMajor as moneyFromMajor2 } from "@tallyui/core";
|
|
@@ -857,9 +858,6 @@ function wooMinorFromDecimal(text, currency) {
|
|
|
857
858
|
}
|
|
858
859
|
function toWooOrderPayload(envelope, local, options) {
|
|
859
860
|
const p = envelope.payload;
|
|
860
|
-
if (envelope.version === 5 && (p.fees?.length || p.shipping?.length || p.lines.some((line) => line.custom))) {
|
|
861
|
-
return { error: { code: "invalid_payload", message: "Fees, shipping and custom lines are not supported by this connector yet." } };
|
|
862
|
-
}
|
|
863
861
|
if (p.payments.length === 0 || p.payments.length > 1 && !options?.paymentsList) {
|
|
864
862
|
return { error: { code: "invalid_payload", message: "WooCommerce orders take one payment." } };
|
|
865
863
|
}
|
|
@@ -870,35 +868,61 @@ function toWooOrderPayload(envelope, local, options) {
|
|
|
870
868
|
return { error: { code: "invalid_payload", message: "Payments do not add up to the order total." } };
|
|
871
869
|
}
|
|
872
870
|
for (const line of p.lines) {
|
|
873
|
-
if (!/^[1-9]\d*$/.test(line.variantId ?? "") || !Number.isSafeInteger(Number(line.variantId))) {
|
|
871
|
+
if (!line.custom && (!/^[1-9]\d*$/.test(line.variantId ?? "") || !Number.isSafeInteger(Number(line.variantId)))) {
|
|
874
872
|
return { error: { code: "invalid_payload", message: `Line ${line.clientLineId} has no WooCommerce product id.` } };
|
|
875
873
|
}
|
|
876
874
|
}
|
|
877
875
|
const digits = p.display?.exponent ?? minorUnitDigits2(p.currency);
|
|
878
876
|
const major = (minor) => (minor / 10 ** digits).toFixed(digits);
|
|
877
|
+
const inclusiveNet = (netMicros, id) => {
|
|
878
|
+
if (netMicros === void 0) {
|
|
879
|
+
return { error: { code: "unsupported_tax_mode", message: "Tax-inclusive prices need the till's WooCommerce tax figures; update the till." } };
|
|
880
|
+
}
|
|
881
|
+
const net = BigInt(netMicros), divisor = 10n ** BigInt(digits);
|
|
882
|
+
if (net < 0n) {
|
|
883
|
+
return { error: { code: "invalid_payload", message: `Line ${id} has a negative net.` } };
|
|
884
|
+
}
|
|
885
|
+
if (net % divisor !== 0n) {
|
|
886
|
+
return { error: { code: "invalid_payload", message: `Line ${id} has a net finer than 6 decimals.` } };
|
|
887
|
+
}
|
|
888
|
+
const micros = net / divisor;
|
|
889
|
+
return `${micros / 1000000n}.${(micros % 1000000n).toString().padStart(6, "0")}`;
|
|
890
|
+
};
|
|
879
891
|
const line_items = [];
|
|
880
892
|
for (const line of p.lines) {
|
|
881
|
-
let subtotal = major(line.unitPriceMinor * line.quantity);
|
|
882
893
|
let total = major(line.unitPriceMinor * line.quantity - (line.discountMinor ?? 0));
|
|
894
|
+
let subtotal = total;
|
|
883
895
|
if (line.taxInclusive ?? p.pricesIncludeTax) {
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
const netMicros = local?.lines.find((stored) => stored.id === line.clientLineId)?.netMicros;
|
|
888
|
-
if (netMicros === void 0) {
|
|
889
|
-
return { error: { code: "unsupported_tax_mode", message: "Tax-inclusive prices need the till's WooCommerce tax figures; update the till." } };
|
|
890
|
-
}
|
|
891
|
-
const net = BigInt(netMicros), divisor = 10n ** BigInt(digits);
|
|
892
|
-
if (net < 0n) {
|
|
893
|
-
return { error: { code: "invalid_payload", message: `Line ${line.clientLineId} has a negative net.` } };
|
|
894
|
-
}
|
|
895
|
-
if (net % divisor !== 0n) {
|
|
896
|
-
return { error: { code: "invalid_payload", message: `Line ${line.clientLineId} has a net finer than 6 decimals.` } };
|
|
897
|
-
}
|
|
898
|
-
const micros = net / divisor;
|
|
899
|
-
subtotal = total = `${micros / 1000000n}.${(micros % 1000000n).toString().padStart(6, "0")}`;
|
|
896
|
+
const net = inclusiveNet(local?.lines.find((stored) => stored.id === line.clientLineId)?.netMicros, line.clientLineId);
|
|
897
|
+
if (typeof net !== "string") return net;
|
|
898
|
+
subtotal = total = net;
|
|
900
899
|
}
|
|
901
|
-
|
|
900
|
+
const custom = line.custom;
|
|
901
|
+
const price = major(line.unitPriceMinor);
|
|
902
|
+
line_items.push(custom ? {
|
|
903
|
+
product_id: 0,
|
|
904
|
+
name: custom.name,
|
|
905
|
+
...custom.sku ? { sku: custom.sku } : {},
|
|
906
|
+
quantity: line.quantity,
|
|
907
|
+
subtotal,
|
|
908
|
+
total,
|
|
909
|
+
tax_class: custom.taxClass ?? "",
|
|
910
|
+
meta_data: [{
|
|
911
|
+
key: "_woocommerce_pos_data",
|
|
912
|
+
value: JSON.stringify({ price, regular_price: price, tax_status: custom.taxStatus })
|
|
913
|
+
}]
|
|
914
|
+
} : { product_id: Number(line.variantId), quantity: line.quantity, subtotal, total });
|
|
915
|
+
}
|
|
916
|
+
const fee_lines = [], shipping_lines = [];
|
|
917
|
+
for (const fee of p.fees ?? []) {
|
|
918
|
+
const total = p.pricesIncludeTax ? inclusiveNet(local?.fees?.find((stored) => stored.id === fee.clientFeeId)?.netMicros, fee.clientFeeId) : major(fee.amountMinor);
|
|
919
|
+
if (typeof total !== "string") return total;
|
|
920
|
+
fee_lines.push({ name: fee.name, total, tax_status: fee.taxStatus, tax_class: fee.taxClass ?? "" });
|
|
921
|
+
}
|
|
922
|
+
for (const shipping of p.shipping ?? []) {
|
|
923
|
+
const total = p.pricesIncludeTax ? inclusiveNet(local?.shipping?.find((stored) => stored.id === shipping.clientShippingId)?.netMicros, shipping.clientShippingId) : major(shipping.amountMinor);
|
|
924
|
+
if (typeof total !== "string") return total;
|
|
925
|
+
shipping_lines.push({ method_id: shipping.methodId ?? "pos", method_title: shipping.name, total });
|
|
902
926
|
}
|
|
903
927
|
const primary = p.payments.reduce((largest, payment) => payment.amountMinor > largest.amountMinor ? payment : largest);
|
|
904
928
|
const cash = primary.method === "cash";
|
|
@@ -920,6 +944,8 @@ function toWooOrderPayload(envelope, local, options) {
|
|
|
920
944
|
payment_method: cash ? "pos_cash" : "pos_card",
|
|
921
945
|
payment_method_title: cash ? "Cash" : "Card",
|
|
922
946
|
line_items,
|
|
947
|
+
...fee_lines.length ? { fee_lines } : {},
|
|
948
|
+
...shipping_lines.length ? { shipping_lines } : {},
|
|
923
949
|
meta_data,
|
|
924
950
|
...p.customer?.customerId && /^\d+$/.test(p.customer.customerId) ? { customer_id: Number(p.customer.customerId) } : {},
|
|
925
951
|
...p.customer?.email ? { billing: { email: p.customer.email } } : {}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tallyui/connector-woocommerce",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.5.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "WooCommerce connector for Tally UI",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -26,10 +26,10 @@
|
|
|
26
26
|
"directory": "connectors/woocommerce"
|
|
27
27
|
},
|
|
28
28
|
"peerDependencies": {
|
|
29
|
-
"@tallyui/core": "^3.
|
|
29
|
+
"@tallyui/core": "^3.5.0"
|
|
30
30
|
},
|
|
31
31
|
"devDependencies": {
|
|
32
|
-
"@tallyui/database": "3.
|
|
32
|
+
"@tallyui/database": "3.5.0",
|
|
33
33
|
"rxdb": "17.5.0"
|
|
34
34
|
},
|
|
35
35
|
"scripts": {
|
|
@@ -28,13 +28,14 @@ export function wooMinorFromDecimal(text: string, currency: string): number | un
|
|
|
28
28
|
return moneyFromDecimalString(end ? `${whole}.${fraction.slice(0, end)}` : whole, currency)?.amount;
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
-
export interface WooLocalOrder {
|
|
31
|
+
export interface WooLocalOrder {
|
|
32
|
+
lines: ReadonlyArray<{ id: string; netMicros?: string }>;
|
|
33
|
+
fees?: ReadonlyArray<{ id: string; netMicros?: string }>;
|
|
34
|
+
shipping?: ReadonlyArray<{ id: string; netMicros?: string }>;
|
|
35
|
+
}
|
|
32
36
|
|
|
33
37
|
export function toWooOrderPayload(envelope: OrderCreateEnvelope, local?: WooLocalOrder, options?: { paymentsList?: boolean }): { payload: Record<string, unknown> } | { error: { code: string; message: string } } {
|
|
34
38
|
const p = envelope.payload;
|
|
35
|
-
if (envelope.version === 5 && (p.fees?.length || p.shipping?.length || p.lines.some((line) => line.custom))) {
|
|
36
|
-
return { error: { code: 'invalid_payload', message: 'Fees, shipping and custom lines are not supported by this connector yet.' } };
|
|
37
|
-
}
|
|
38
39
|
if (p.payments.length === 0 || (p.payments.length > 1 && !options?.paymentsList)) {
|
|
39
40
|
return { error: { code: 'invalid_payload', message: 'WooCommerce orders take one payment.' } };
|
|
40
41
|
}
|
|
@@ -45,35 +46,55 @@ export function toWooOrderPayload(envelope: OrderCreateEnvelope, local?: WooLoca
|
|
|
45
46
|
return { error: { code: 'invalid_payload', message: 'Payments do not add up to the order total.' } };
|
|
46
47
|
}
|
|
47
48
|
for (const line of p.lines) {
|
|
48
|
-
if (!/^[1-9]\d*$/.test(line.variantId ?? '') || !Number.isSafeInteger(Number(line.variantId))) {
|
|
49
|
+
if (!line.custom && (!/^[1-9]\d*$/.test(line.variantId ?? '') || !Number.isSafeInteger(Number(line.variantId)))) {
|
|
49
50
|
return { error: { code: 'invalid_payload', message: `Line ${line.clientLineId} has no WooCommerce product id.` } };
|
|
50
51
|
}
|
|
51
52
|
}
|
|
52
53
|
const digits = p.display?.exponent ?? minorUnitDigits(p.currency);
|
|
53
54
|
const major = (minor: number) => (minor / 10 ** digits).toFixed(digits);
|
|
55
|
+
const inclusiveNet = (netMicros: string | undefined, id: string) => {
|
|
56
|
+
if (netMicros === undefined) {
|
|
57
|
+
return { error: { code: 'unsupported_tax_mode', message: "Tax-inclusive prices need the till's WooCommerce tax figures; update the till." } };
|
|
58
|
+
}
|
|
59
|
+
const net = BigInt(netMicros), divisor = 10n ** BigInt(digits);
|
|
60
|
+
if (net < 0n) {
|
|
61
|
+
return { error: { code: 'invalid_payload', message: `Line ${id} has a negative net.` } };
|
|
62
|
+
}
|
|
63
|
+
if (net % divisor !== 0n) {
|
|
64
|
+
return { error: { code: 'invalid_payload', message: `Line ${id} has a net finer than 6 decimals.` } };
|
|
65
|
+
}
|
|
66
|
+
const micros = net / divisor;
|
|
67
|
+
return `${micros / 1000000n}.${(micros % 1000000n).toString().padStart(6, '0')}`;
|
|
68
|
+
};
|
|
54
69
|
const line_items = [];
|
|
55
70
|
for (const line of p.lines) {
|
|
56
|
-
let subtotal = major(line.unitPriceMinor * line.quantity);
|
|
57
71
|
let total = major(line.unitPriceMinor * line.quantity - (line.discountMinor ?? 0));
|
|
72
|
+
let subtotal = total;
|
|
58
73
|
if (line.taxInclusive ?? p.pricesIncludeTax) {
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
const netMicros = local?.lines.find((stored) => stored.id === line.clientLineId)?.netMicros;
|
|
63
|
-
if (netMicros === undefined) {
|
|
64
|
-
return { error: { code: 'unsupported_tax_mode', message: "Tax-inclusive prices need the till's WooCommerce tax figures; update the till." } };
|
|
65
|
-
}
|
|
66
|
-
const net = BigInt(netMicros), divisor = 10n ** BigInt(digits);
|
|
67
|
-
if (net < 0n) {
|
|
68
|
-
return { error: { code: 'invalid_payload', message: `Line ${line.clientLineId} has a negative net.` } };
|
|
69
|
-
}
|
|
70
|
-
if (net % divisor !== 0n) {
|
|
71
|
-
return { error: { code: 'invalid_payload', message: `Line ${line.clientLineId} has a net finer than 6 decimals.` } };
|
|
72
|
-
}
|
|
73
|
-
const micros = net / divisor;
|
|
74
|
-
subtotal = total = `${micros / 1000000n}.${(micros % 1000000n).toString().padStart(6, '0')}`;
|
|
74
|
+
const net = inclusiveNet(local?.lines.find((stored) => stored.id === line.clientLineId)?.netMicros, line.clientLineId);
|
|
75
|
+
if (typeof net !== 'string') return net;
|
|
76
|
+
subtotal = total = net;
|
|
75
77
|
}
|
|
76
|
-
|
|
78
|
+
const custom = line.custom;
|
|
79
|
+
const price = major(line.unitPriceMinor);
|
|
80
|
+
line_items.push(custom ? {
|
|
81
|
+
product_id: 0, name: custom.name, ...(custom.sku ? { sku: custom.sku } : {}), quantity: line.quantity, subtotal, total,
|
|
82
|
+
tax_class: custom.taxClass ?? '', meta_data: [{ key: '_woocommerce_pos_data',
|
|
83
|
+
value: JSON.stringify({ price, regular_price: price, tax_status: custom.taxStatus }) }],
|
|
84
|
+
} : { product_id: Number(line.variantId), quantity: line.quantity, subtotal, total });
|
|
85
|
+
}
|
|
86
|
+
const fee_lines = [], shipping_lines = [];
|
|
87
|
+
for (const fee of p.fees ?? []) {
|
|
88
|
+
const total = p.pricesIncludeTax
|
|
89
|
+
? inclusiveNet(local?.fees?.find((stored) => stored.id === fee.clientFeeId)?.netMicros, fee.clientFeeId) : major(fee.amountMinor);
|
|
90
|
+
if (typeof total !== 'string') return total;
|
|
91
|
+
fee_lines.push({ name: fee.name, total, tax_status: fee.taxStatus, tax_class: fee.taxClass ?? '' });
|
|
92
|
+
}
|
|
93
|
+
for (const shipping of p.shipping ?? []) {
|
|
94
|
+
const total = p.pricesIncludeTax
|
|
95
|
+
? inclusiveNet(local?.shipping?.find((stored) => stored.id === shipping.clientShippingId)?.netMicros, shipping.clientShippingId) : major(shipping.amountMinor);
|
|
96
|
+
if (typeof total !== 'string') return total;
|
|
97
|
+
shipping_lines.push({ method_id: shipping.methodId ?? 'pos', method_title: shipping.name, total });
|
|
77
98
|
}
|
|
78
99
|
const primary = p.payments.reduce((largest, payment) => payment.amountMinor > largest.amountMinor ? payment : largest);
|
|
79
100
|
const cash = primary.method === 'cash';
|
|
@@ -91,6 +112,8 @@ export function toWooOrderPayload(envelope: OrderCreateEnvelope, local?: WooLoca
|
|
|
91
112
|
status: 'completed', set_paid: true, currency: p.currency,
|
|
92
113
|
payment_method: cash ? 'pos_cash' : 'pos_card', payment_method_title: cash ? 'Cash' : 'Card',
|
|
93
114
|
line_items,
|
|
115
|
+
...(fee_lines.length ? { fee_lines } : {}),
|
|
116
|
+
...(shipping_lines.length ? { shipping_lines } : {}),
|
|
94
117
|
meta_data,
|
|
95
118
|
...(p.customer?.customerId && /^\d+$/.test(p.customer.customerId) ? { customer_id: Number(p.customer.customerId) } : {}),
|
|
96
119
|
...(p.customer?.email ? { billing: { email: p.customer.email } } : {}),
|
package/src/store-settings.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { ConnectorUnauthorizedError, StoreSettingsError, minorUnitDigits, woocommerceTax } from '@tallyui/core';
|
|
2
2
|
import type { ServerCapabilities, StoreSettings, SyncContext } from '@tallyui/core';
|
|
3
3
|
|
|
4
|
-
/** The highest order.create version the WooCommerce transport maps: discounts and
|
|
5
|
-
export const WOO_ORDER_CREATE_VERSION =
|
|
4
|
+
/** The highest order.create version the WooCommerce transport maps: v4's net discounts, and v5's fees, shipping and custom lines (WCPOS push/orders; orders #146, #161). */
|
|
5
|
+
export const WOO_ORDER_CREATE_VERSION = 5;
|
|
6
6
|
|
|
7
7
|
export async function wooStoreSettings(context: SyncContext): Promise<StoreSettings> {
|
|
8
8
|
try {
|
package/src/traits/product.ts
CHANGED
|
@@ -107,4 +107,8 @@ export const wooProductTraits: ProductTraits = {
|
|
|
107
107
|
|
|
108
108
|
getCategoryNames: (doc) =>
|
|
109
109
|
(doc.categories ?? []).map((cat: any) => cat.name).filter(Boolean),
|
|
110
|
+
|
|
111
|
+
getCategories: (doc) =>
|
|
112
|
+
(doc.categories ?? []).filter((cat: any) => cat.name)
|
|
113
|
+
.map((cat: any) => ({ id: cat.id != null ? String(cat.id) : cat.name, name: cat.name })),
|
|
110
114
|
};
|