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
|
|
612
|
-
compareAtPrice: (val === null || val === void 0 ? void 0 : val.compareAtPrice
|
|
613
|
-
salePrice: (val === null || val === void 0 ? void 0 : val.salePrice
|
|
614
|
-
isOnSale: (val === null || val === void 0 ? void 0 : val.isOnSale
|
|
615
|
-
sku: (val === null || val === void 0 ? void 0 : val.sku
|
|
616
|
-
taxable: (val === null || val === void 0 ? void 0 : val.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
|
|
624
|
-
.
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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)
|
|
701
|
-
price: (
|
|
702
|
-
compareAtPrice: (
|
|
703
|
-
salePrice: (
|
|
704
|
-
isOnSale: (
|
|
705
|
-
sku: (
|
|
706
|
-
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) || [];
|
package/lib/models/Product.js
CHANGED
|
@@ -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';
|