@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 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 the customer (v3). It rises when the transport maps split payments (v4) and fees, shipping and custom lines (v5). */
6
- declare const WOO_ORDER_CREATE_VERSION = 3;
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 = 3;
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.3.0";
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
- if ((line.discountMinor ?? 0) > 0) {
885
- return { error: { code: "unsupported_tax_mode", message: "Discounted tax-inclusive lines are not supported for WooCommerce yet." } };
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
- line_items.push({ product_id: Number(line.variantId), quantity: line.quantity, subtotal, total });
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.0",
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.3.0"
29
+ "@tallyui/core": "^3.5.0"
30
30
  },
31
31
  "devDependencies": {
32
- "@tallyui/database": "3.3.0",
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 { lines: ReadonlyArray<{ id: string; netMicros?: string }> }
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
- if ((line.discountMinor ?? 0) > 0) {
60
- return { error: { code: 'unsupported_tax_mode', message: 'Discounted tax-inclusive lines are not supported for WooCommerce yet.' } };
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
- line_items.push({ product_id: Number(line.variantId), quantity: line.quantity, subtotal, total });
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 } } : {}),
@@ -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 the customer (v3). It rises when the transport maps split payments (v4) and fees, shipping and custom lines (v5). */
5
- export const WOO_ORDER_CREATE_VERSION = 3;
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 {
@@ -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
  };