rerobe-js-orm 4.5.2 → 4.5.4

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.
@@ -95,6 +95,28 @@ export default class ProductFormState extends FormState {
95
95
  }[];
96
96
  toRemove: string[];
97
97
  };
98
+ getVariantIds(): string[];
99
+ getVariantRows(): VariantInventory[];
100
+ bulkEditVariantFields(patch: Partial<VariantInventory>, selector?: {
101
+ ids?: string[];
102
+ predicate?: (v: VariantInventory) => boolean;
103
+ }): void;
104
+ bulkEditVariantLocationAmount(location: {
105
+ id: string;
106
+ name: string;
107
+ }, amount: number, options?: {
108
+ ids?: string[];
109
+ predicate?: (v: VariantInventory) => boolean;
110
+ mode?: 'set' | 'increment';
111
+ field?: 'availableAmount' | 'incomingAmount';
112
+ }): void;
113
+ bulkEditVariantSkus(updates: {
114
+ id?: string;
115
+ options?: {
116
+ [k: string]: string;
117
+ };
118
+ sku: string;
119
+ }[]): void;
98
120
  private aggregateInventoryLocationsFromVariants;
99
121
  private fieldFactory;
100
122
  }
@@ -604,27 +604,34 @@ class ProductFormState extends FormState_1.default {
604
604
  this.utilities.makeRandId(28);
605
605
  const existingInd = this.fields.variantInventory.selectedValues.findIndex((i) => i.id === id);
606
606
  const existing = existingInd !== -1 ? this.fields.variantInventory.selectedValues[existingInd] : {};
607
+ const coalesce = (v, fallback = null) => v !== undefined ? v : fallback;
607
608
  const next = {
608
609
  id,
609
610
  options: val.options || existing.options || {},
610
- locations: (val === null || val === void 0 ? void 0 : val.locations) !== undefined ? val.locations : existing.locations,
611
- price: (val === null || val === void 0 ? void 0 : val.price) !== undefined ? val.price : existing.price,
612
- compareAtPrice: (val === null || val === void 0 ? void 0 : val.compareAtPrice) !== undefined ? val.compareAtPrice : existing.compareAtPrice,
613
- salePrice: (val === null || val === void 0 ? void 0 : val.salePrice) !== undefined ? val.salePrice : existing.salePrice,
614
- isOnSale: (val === null || val === void 0 ? void 0 : val.isOnSale) !== undefined ? val.isOnSale : existing.isOnSale,
615
- sku: (val === null || val === void 0 ? void 0 : val.sku) !== undefined ? val.sku : existing.sku,
616
- taxable: (val === null || val === void 0 ? void 0 : val.taxable) !== undefined ? val.taxable : existing.taxable,
611
+ locations: (val === null || val === void 0 ? void 0 : val.locations) !== undefined ? val.locations : coalesce(existing.locations, null),
612
+ price: coalesce(val === null || val === void 0 ? void 0 : val.price, coalesce(existing.price, null)),
613
+ compareAtPrice: coalesce(val === null || val === void 0 ? void 0 : val.compareAtPrice, coalesce(existing.compareAtPrice, null)),
614
+ salePrice: coalesce(val === null || val === void 0 ? void 0 : val.salePrice, coalesce(existing.salePrice, null)),
615
+ isOnSale: coalesce(val === null || val === void 0 ? void 0 : val.isOnSale, coalesce(existing.isOnSale, null)),
616
+ sku: coalesce(val === null || val === void 0 ? void 0 : val.sku, coalesce(existing.sku, null)),
617
+ taxable: coalesce(val === null || val === void 0 ? void 0 : val.taxable, coalesce(existing.taxable, null)),
618
+ costPerItem: coalesce(val === null || val === void 0 ? void 0 : val.costPerItem, coalesce(existing.costPerItem, null)),
619
+ continueSellingWhenOutOfStock: coalesce(val === null || val === void 0 ? void 0 : val.continueSellingWhenOutOfStock, coalesce(existing.continueSellingWhenOutOfStock, null)),
620
+ shopifyId: coalesce(val === null || val === void 0 ? void 0 : val.shopifyId, coalesce(existing.shopifyId, null)),
617
621
  };
618
622
  const foundInd = this.fields.variantInventory.selectedValues.findIndex((i) => i.id === id);
619
623
  if (foundInd !== -1) {
620
624
  const foundI = this.fields.variantInventory.selectedValues[foundInd];
621
625
  let shouldToggleRemove = false;
622
626
  if ((val === null || val === void 0 ? void 0 : val.locations) !== undefined) {
623
- const norm = (arr = []) => [...arr]
624
- .filter((l) => l && l.id)
625
- .sort((a, b) => (a.id > b.id ? 1 : a.id < b.id ? -1 : 0))
626
- .map((l) => `${l.id}:${Number(l.availableAmount) || 0}:${Number(l.incomingAmount) || 0}`)
627
- .join('|');
627
+ const norm = (arr) => {
628
+ const list = Array.isArray(arr) ? arr : [];
629
+ return [...list]
630
+ .filter((l) => l && l.id)
631
+ .sort((a, b) => (a.id > b.id ? 1 : a.id < b.id ? -1 : 0))
632
+ .map((l) => `${l.id}:${Number(l.availableAmount) || 0}:${Number(l.incomingAmount) || 0}`)
633
+ .join('|');
634
+ };
628
635
  shouldToggleRemove = norm(foundI.locations) === norm(next.locations);
629
636
  }
630
637
  if (shouldToggleRemove) {
@@ -648,7 +655,7 @@ class ProductFormState extends FormState_1.default {
648
655
  // aggregate main inventoryLocations from variant locations (sum by location id)
649
656
  this.aggregateInventoryLocationsFromVariants();
650
657
  }
651
- // Public: define axes (≤3) for generating combinations
658
+ // Define axes (≤3) for generating combinations
652
659
  setVariantAxes(axes = {}) {
653
660
  const axisKeys = Object.keys(axes || {});
654
661
  if (axisKeys.length > 3) {
@@ -665,7 +672,7 @@ class ProductFormState extends FormState_1.default {
665
672
  }, {});
666
673
  }
667
674
  }
668
- // Public: regenerate variants from axes, preserving existing per-variant fields where options match
675
+ // Regenerate variants from axes, preserving existing per-variant fields where options match
669
676
  generateVariantsFromAxes(defaults = {}) {
670
677
  const axes = this.variantAxes || {};
671
678
  const axisKeys = Object.keys(axes);
@@ -691,19 +698,23 @@ class ProductFormState extends FormState_1.default {
691
698
  const combos = [];
692
699
  cartesian(axisKeys, 0, {}, combos);
693
700
  const regenerated = combos.map((opts) => {
694
- var _a, _b, _c, _d, _e, _f;
701
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
695
702
  const prev = this.getExistingVariantByOptions(opts);
696
703
  const id = (prev === null || prev === void 0 ? void 0 : prev.id) || this.utilities.makeRandId(28);
704
+ const nz = (v) => (v === undefined ? null : v);
697
705
  const merged = {
698
706
  id,
699
707
  options: opts,
700
- locations: (prev === null || prev === void 0 ? void 0 : prev.locations) || defaults.locations,
701
- price: (_a = prev === null || prev === void 0 ? void 0 : prev.price) !== null && _a !== void 0 ? _a : defaults.price,
702
- compareAtPrice: (_b = prev === null || prev === void 0 ? void 0 : prev.compareAtPrice) !== null && _b !== void 0 ? _b : defaults.compareAtPrice,
703
- salePrice: (_c = prev === null || prev === void 0 ? void 0 : prev.salePrice) !== null && _c !== void 0 ? _c : defaults.salePrice,
704
- isOnSale: (_d = prev === null || prev === void 0 ? void 0 : prev.isOnSale) !== null && _d !== void 0 ? _d : defaults.isOnSale,
705
- sku: (_e = prev === null || prev === void 0 ? void 0 : prev.sku) !== null && _e !== void 0 ? _e : defaults.sku,
706
- taxable: (_f = prev === null || prev === void 0 ? void 0 : prev.taxable) !== null && _f !== void 0 ? _f : defaults.taxable,
708
+ locations: nz((_a = prev === null || prev === void 0 ? void 0 : prev.locations) !== null && _a !== void 0 ? _a : defaults.locations),
709
+ price: nz((_b = prev === null || prev === void 0 ? void 0 : prev.price) !== null && _b !== void 0 ? _b : defaults.price),
710
+ compareAtPrice: nz((_c = prev === null || prev === void 0 ? void 0 : prev.compareAtPrice) !== null && _c !== void 0 ? _c : defaults.compareAtPrice),
711
+ salePrice: nz((_d = prev === null || prev === void 0 ? void 0 : prev.salePrice) !== null && _d !== void 0 ? _d : defaults.salePrice),
712
+ isOnSale: nz((_e = prev === null || prev === void 0 ? void 0 : prev.isOnSale) !== null && _e !== void 0 ? _e : defaults.isOnSale),
713
+ sku: nz((_f = prev === null || prev === void 0 ? void 0 : prev.sku) !== null && _f !== void 0 ? _f : defaults.sku),
714
+ taxable: nz((_g = prev === null || prev === void 0 ? void 0 : prev.taxable) !== null && _g !== void 0 ? _g : defaults.taxable),
715
+ costPerItem: nz((_h = prev === null || prev === void 0 ? void 0 : prev.costPerItem) !== null && _h !== void 0 ? _h : defaults.costPerItem),
716
+ continueSellingWhenOutOfStock: nz((_j = prev === null || prev === void 0 ? void 0 : prev.continueSellingWhenOutOfStock) !== null && _j !== void 0 ? _j : defaults.continueSellingWhenOutOfStock),
717
+ shopifyId: nz((_k = prev === null || prev === void 0 ? void 0 : prev.shopifyId) !== null && _k !== void 0 ? _k : defaults.shopifyId),
707
718
  };
708
719
  return merged;
709
720
  });
@@ -837,6 +848,130 @@ class ProductFormState extends FormState_1.default {
837
848
  const toRemove = curRows.filter((r) => !nextKeySet.has(JSON.stringify(r.options || {}))).map((r) => r.id);
838
849
  return { toAdd, toRemove };
839
850
  }
851
+ // UI: expose variant ids and rows
852
+ getVariantIds() {
853
+ return this.fields.variantInventory.selectedValues.map((v) => v.id);
854
+ }
855
+ getVariantRows() {
856
+ return this.fields.variantInventory.selectedValues.slice();
857
+ }
858
+ // Bulk edit user-mutable variant fields (excludes inventory locations and options/id)
859
+ bulkEditVariantFields(patch, selector) {
860
+ const allowedKeys = [
861
+ 'price',
862
+ 'compareAtPrice',
863
+ 'salePrice',
864
+ 'isOnSale',
865
+ 'sku',
866
+ 'taxable',
867
+ 'costPerItem',
868
+ 'continueSellingWhenOutOfStock',
869
+ ];
870
+ const rows = this.fields.variantInventory.selectedValues;
871
+ const shouldEdit = (v) => {
872
+ if ((selector === null || selector === void 0 ? void 0 : selector.ids) && selector.ids.length)
873
+ return selector.ids.includes(v.id);
874
+ if (selector === null || selector === void 0 ? void 0 : selector.predicate)
875
+ return !!selector.predicate(v);
876
+ return true; // default: all
877
+ };
878
+ rows.forEach((v, idx) => {
879
+ if (!shouldEdit(v))
880
+ return;
881
+ const next = Object.assign({}, v);
882
+ allowedKeys.forEach((k) => {
883
+ if (patch[k] !== undefined) {
884
+ const val = patch[k];
885
+ next[k] = val === undefined ? null : val;
886
+ }
887
+ });
888
+ this.fields.variantInventory.selectedValues[idx] = next;
889
+ });
890
+ // Recompute aggregates (locations unchanged, so totals stay the same but we keep logic consistent)
891
+ const totalQty = this.fields.variantInventory.selectedValues.reduce((acc, cur) => {
892
+ const locs = cur.locations;
893
+ const sum = Array.isArray(locs) ? locs.reduce((a, l) => a + (Number(l === null || l === void 0 ? void 0 : l.availableAmount) || 0), 0) : 0;
894
+ return acc + sum;
895
+ }, 0);
896
+ this.fields.quantity.inputValue = totalQty;
897
+ this.fields.availableForSale.selectedValue = totalQty > 0;
898
+ this.aggregateInventoryLocationsFromVariants();
899
+ }
900
+ // Bulk edit inventory amount for a specific location across selected variants
901
+ // mode: 'set' (default) to overwrite, 'increment' to add delta
902
+ bulkEditVariantLocationAmount(location, amount, options) {
903
+ const rows = this.fields.variantInventory.selectedValues;
904
+ const shouldEdit = (v) => {
905
+ if ((options === null || options === void 0 ? void 0 : options.ids) && options.ids.length)
906
+ return options.ids.includes(v.id);
907
+ if (options === null || options === void 0 ? void 0 : options.predicate)
908
+ return !!options.predicate(v);
909
+ return true; // default all
910
+ };
911
+ const mode = (options === null || options === void 0 ? void 0 : options.mode) || 'set';
912
+ const targetField = (options === null || options === void 0 ? void 0 : options.field) || 'availableAmount';
913
+ rows.forEach((v, idx) => {
914
+ if (!shouldEdit(v))
915
+ return;
916
+ const locs = (v.locations || []);
917
+ const existingIdx = locs.findIndex((l) => l && l.id === location.id);
918
+ if (existingIdx !== -1) {
919
+ const current = locs[existingIdx];
920
+ const nextVal = mode === 'increment' ? Number(current[targetField] || 0) + Number(amount) : Number(amount);
921
+ const updated = Object.assign(Object.assign({}, current), { [targetField]: nextVal });
922
+ locs.splice(existingIdx, 1, updated);
923
+ }
924
+ else {
925
+ const base = {
926
+ id: location.id,
927
+ name: location.name,
928
+ availableAmount: 0,
929
+ incomingAmount: 0,
930
+ };
931
+ base[targetField] = mode === 'increment' ? Number(amount) : Number(amount);
932
+ locs.push(base);
933
+ }
934
+ this.fields.variantInventory.selectedValues[idx] = Object.assign(Object.assign({}, v), { locations: locs });
935
+ });
936
+ // Recompute aggregates
937
+ const totalQty = this.fields.variantInventory.selectedValues.reduce((acc, cur) => {
938
+ const locs = cur.locations;
939
+ const sum = Array.isArray(locs) ? locs.reduce((a, l) => a + (Number(l === null || l === void 0 ? void 0 : l.availableAmount) || 0), 0) : 0;
940
+ return acc + sum;
941
+ }, 0);
942
+ this.fields.quantity.inputValue = totalQty;
943
+ this.fields.availableForSale.selectedValue = totalQty > 0;
944
+ this.aggregateInventoryLocationsFromVariants();
945
+ }
946
+ // Bulk edit SKUs per selected variant individually
947
+ bulkEditVariantSkus(updates) {
948
+ if (!Array.isArray(updates) || !updates.length)
949
+ return;
950
+ const rows = this.fields.variantInventory.selectedValues;
951
+ const indexById = {};
952
+ rows.forEach((v, i) => (indexById[v.id] = i));
953
+ updates.forEach((u) => {
954
+ let idx = -1;
955
+ if (u.id && indexById[u.id] !== undefined) {
956
+ idx = indexById[u.id];
957
+ }
958
+ else if (u.options) {
959
+ idx = rows.findIndex((v) => this.isSameOptions(v.options, u.options));
960
+ }
961
+ if (idx !== -1) {
962
+ const cur = rows[idx] || {};
963
+ this.fields.variantInventory.selectedValues[idx] = Object.assign(Object.assign({}, cur), { sku: u.sku });
964
+ }
965
+ });
966
+ // Aggregates unchanged but keep consistent calc
967
+ const totalQty = this.fields.variantInventory.selectedValues.reduce((acc, cur) => {
968
+ const locs = cur.locations;
969
+ const sum = Array.isArray(locs) ? locs.reduce((a, l) => a + (Number(l === null || l === void 0 ? void 0 : l.availableAmount) || 0), 0) : 0;
970
+ return acc + sum;
971
+ }, 0);
972
+ this.fields.quantity.inputValue = totalQty;
973
+ this.fields.availableForSale.selectedValue = totalQty > 0;
974
+ }
840
975
  aggregateInventoryLocationsFromVariants() {
841
976
  var _a;
842
977
  const variants = ((_a = this.fields.variantInventory) === null || _a === void 0 ? void 0 : _a.selectedValues) || [];
@@ -406,6 +406,21 @@ class Product extends Base_1.default {
406
406
  const basePrice = ((_b = (_a = v.price) !== null && _a !== void 0 ? _a : this.attributes.price) !== null && _b !== void 0 ? _b : '0');
407
407
  const variant = Object.assign(Object.assign({}, baseVariantFields()), { optionValues,
408
408
  inventoryQuantities });
409
+ // Override inventory policy if allowed to continue selling
410
+ if (v.continueSellingWhenOutOfStock === true) {
411
+ variant.inventoryPolicy = 'CONTINUE';
412
+ }
413
+ else if (v.continueSellingWhenOutOfStock === false) {
414
+ variant.inventoryPolicy = 'DENY';
415
+ }
416
+ // Per-variant cost override
417
+ if (v.costPerItem !== undefined && v.costPerItem !== null) {
418
+ variant.inventoryItem.cost = this.utilities.sanitizeNumber(v.costPerItem, 0);
419
+ }
420
+ // Include variant id when present (for updates)
421
+ if (v.shopifyId) {
422
+ variant.id = v.shopifyId;
423
+ }
409
424
  if (v.sku) {
410
425
  variant.sku = v.sku;
411
426
  }
@@ -156,13 +156,16 @@ type VariantOptions = Record<string, string>;
156
156
  type VariantInventory = {
157
157
  id: string;
158
158
  options: VariantOptions;
159
- locations?: InventoryLocation[];
160
- price?: string;
161
- compareAtPrice?: string;
162
- salePrice?: string;
163
- isOnSale?: 'yes' | 'no';
164
- sku?: string;
165
- taxable?: 'yes' | 'no';
159
+ locations?: InventoryLocation[] | null;
160
+ price?: string | null;
161
+ compareAtPrice?: string | null;
162
+ salePrice?: string | null;
163
+ isOnSale?: 'yes' | 'no' | null;
164
+ sku?: string | null;
165
+ taxable?: 'yes' | 'no' | null;
166
+ shopifyId?: string | null;
167
+ costPerItem?: string | null;
168
+ continueSellingWhenOutOfStock?: boolean | null;
166
169
  };
167
170
  type ProductSellStatusKeys = 'SELL_REQUEST_REVIEW' | 'REJECTED' | 'HOLD' | 'ACCEPTED' | 'DROP_OFF_AT_REROBE' | 'SHIP_TO_REROBE' | 'AT_HOME_PICK_UP' | 'QUALITY_CONTROL' | 'LISTED' | 'SOLD' | 'RETURNED' | 'SOLD_SELLER_TO_BE_PAID' | 'SOLD_SELLER_PAID' | 'HIDDEN' | 'BOOKED' | 'OPEN';
168
171
  type ProductPublishKeys = 'USER_PUBLISHED' | 'MERCHANT_PUBLISHED';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rerobe-js-orm",
3
- "version": "4.5.2",
3
+ "version": "4.5.4",
4
4
  "description": "ReRobe's Javascript ORM Framework",
5
5
  "main": "lib/index.js",
6
6
  "types": "lib/index.d.ts",