rerobe-js-orm 4.4.8 → 4.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.
@@ -4,7 +4,7 @@ const ProductFactory_1 = require("./ProductFactory");
4
4
  const Product_1 = require("../../models/Product");
5
5
  class ProductFromFormState extends ProductFactory_1.default {
6
6
  createProduct(props) {
7
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31;
7
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32;
8
8
  const productAttributes = {
9
9
  availableForSale: props.fields.availableForSale.selectedValue,
10
10
  description: props.fields.description.inputValue,
@@ -28,19 +28,20 @@ class ProductFromFormState extends ProductFactory_1.default {
28
28
  variantId: ((_j = props.props) === null || _j === void 0 ? void 0 : _j.variantId) || '',
29
29
  reservedBy: ((_k = props.props) === null || _k === void 0 ? void 0 : _k.reservedBy) || [],
30
30
  options: props.fields.options.selectedValue,
31
+ variantInventory: ((_l = props.fields.variantInventory) === null || _l === void 0 ? void 0 : _l.selectedValues) || [],
31
32
  costPerItem: props.fields.costPerItem.inputValue,
32
33
  compareAtPrice: props.fields.compareAtPrice.inputValue,
33
34
  quantity: props.fields.quantity.inputValue,
34
- merchants: ((_l = props.props) === null || _l === void 0 ? void 0 : _l.merchants) || [],
35
- merchantId: ((_m = props.props) === null || _m === void 0 ? void 0 : _m.merchantId) || '',
35
+ merchants: ((_m = props.props) === null || _m === void 0 ? void 0 : _m.merchants) || [],
36
+ merchantId: ((_o = props.props) === null || _o === void 0 ? void 0 : _o.merchantId) || '',
36
37
  editorAuthorizations: props.fields.editorAuthorizations.selectedValue,
37
38
  rfidTag: props.fields.rfidTag.inputValue || '',
38
39
  sku: props.fields.sku.inputValue || '',
39
- metaDataDescription: ((_o = props.fields.metaDataDescription) === null || _o === void 0 ? void 0 : _o.inputValue) || '',
40
- metaDataTitle: ((_p = props.fields.metaDataTitle) === null || _p === void 0 ? void 0 : _p.inputValue) || '',
40
+ metaDataDescription: ((_p = props.fields.metaDataDescription) === null || _p === void 0 ? void 0 : _p.inputValue) || '',
41
+ metaDataTitle: ((_q = props.fields.metaDataTitle) === null || _q === void 0 ? void 0 : _q.inputValue) || '',
41
42
  };
42
43
  const productFilterAttributes = {
43
- documentId: (_q = props.props) === null || _q === void 0 ? void 0 : _q.documentId,
44
+ documentId: (_r = props.props) === null || _r === void 0 ? void 0 : _r.documentId,
44
45
  brand: props.fields.brand.selectedValue,
45
46
  measurementCategory: props.fields.measurementCategory.selectedValue,
46
47
  size: props.fields.size.selectedValue,
@@ -61,10 +62,10 @@ class ProductFromFormState extends ProductFactory_1.default {
61
62
  featuredCollections: props.fields.featuredCollections.selectedValues || [],
62
63
  primaryAgeCategory: props.fields.primaryAgeCategory.selectedValue || '',
63
64
  tags: props.fields.tags.selectedValues || [],
64
- systemTags: ((_r = props.props) === null || _r === void 0 ? void 0 : _r.systemTags) || [],
65
+ systemTags: ((_s = props.props) === null || _s === void 0 ? void 0 : _s.systemTags) || [],
65
66
  };
66
67
  const consignmentAttributes = {
67
- sellRequestId: ((_s = props.props) === null || _s === void 0 ? void 0 : _s.sellRequestId) || '',
68
+ sellRequestId: ((_t = props.props) === null || _t === void 0 ? void 0 : _t.sellRequestId) || '',
68
69
  publishType: props.fields.publishType.selectedValue,
69
70
  conditionRemarks: props.fields.conditionRemarks.inputValue,
70
71
  productRemarks: props.fields.productRemarks.inputValue,
@@ -77,14 +78,14 @@ class ProductFromFormState extends ProductFactory_1.default {
77
78
  nextAvailableSeason: props.fields.nextAvailableSeason.selectedValue,
78
79
  notesForProductionTeam: props.fields.notesForProductionTeam.inputValue,
79
80
  reviewedBy: props.fields.reviewedBy.selectedValue,
80
- sellRequestImageUrls: ((_t = props.props) === null || _t === void 0 ? void 0 : _t.sellRequestImageUrls) || [],
81
+ sellRequestImageUrls: ((_u = props.props) === null || _u === void 0 ? void 0 : _u.sellRequestImageUrls) || [],
81
82
  salesChannel: props.fields.salesChannel.selectedValues || [],
82
83
  sizeComment: props.fields.sizeComment.selectedValue,
83
- salePrice: ((_u = props.props) === null || _u === void 0 ? void 0 : _u.salePrice) || '',
84
+ salePrice: ((_v = props.props) === null || _v === void 0 ? void 0 : _v.salePrice) || '',
84
85
  isOnSale: props.fields.isOnSale.selectedValue || '',
85
86
  discountType: props.fields.discountType.selectedValue || '',
86
87
  discountValue: props.fields.discountValue.inputValue || '',
87
- selectedForClearance: ((_v = props.props) === null || _v === void 0 ? void 0 : _v.selectedForClearance) || '',
88
+ selectedForClearance: ((_w = props.props) === null || _w === void 0 ? void 0 : _w.selectedForClearance) || '',
88
89
  locationIds: props.fields.locationIds.selectedValues || [],
89
90
  inventoryLocations: props.fields.inventoryLocations.selectedValues || [],
90
91
  majorDefects: props.fields.majorDefects.selectedValues || [], // ToDo: Deprecate
@@ -92,44 +93,44 @@ class ProductFromFormState extends ProductFactory_1.default {
92
93
  careInstructions: props.fields.careInstructions.inputValue || '',
93
94
  };
94
95
  const timestampAttributes = {
95
- createdAtTimestamp: (_w = props.props) === null || _w === void 0 ? void 0 : _w.createdAtTimestamp,
96
- updatedAtTimestamp: (_x = props.props) === null || _x === void 0 ? void 0 : _x.updatedAtTimestamp,
97
- sellRequestReviewTimestamp: (_y = props.props) === null || _y === void 0 ? void 0 : _y.sellRequestReviewTimestamp,
98
- sellRequestReviewDraftTimestamp: (_z = props.props) === null || _z === void 0 ? void 0 : _z.sellRequestReviewDraftTimestamp,
99
- rejectedTimestamp: (_0 = props.props) === null || _0 === void 0 ? void 0 : _0.rejectedTimestamp,
100
- holdTimestamp: (_1 = props.props) === null || _1 === void 0 ? void 0 : _1.holdTimestamp,
101
- acceptedTimestamp: (_2 = props.props) === null || _2 === void 0 ? void 0 : _2.acceptedTimestamp,
102
- sellerToDropOffTimestamp: (_3 = props.props) === null || _3 === void 0 ? void 0 : _3.sellerToDropOffTimestamp,
103
- sellerToShipTimestamp: (_4 = props.props) === null || _4 === void 0 ? void 0 : _4.sellerToShipTimestamp,
104
- sellerToGetPickUpTimestamp: (_5 = props.props) === null || _5 === void 0 ? void 0 : _5.sellerToGetPickUpTimestamp,
105
- qualityControlTimestamp: (_6 = props.props) === null || _6 === void 0 ? void 0 : _6.qualityControlTimestamp,
106
- pendingPublicationTimestamp: (_7 = props.props) === null || _7 === void 0 ? void 0 : _7.pendingPublicationTimestamp,
107
- listedTimestamp: (_8 = props.props) === null || _8 === void 0 ? void 0 : _8.listedTimestamp,
108
- clearanceTimestamp: (_9 = props.props) === null || _9 === void 0 ? void 0 : _9.clearanceTimestamp,
109
- reservedTimestamp: (_10 = props.props) === null || _10 === void 0 ? void 0 : _10.reservedTimestamp,
110
- soldTimestamp: (_11 = props.props) === null || _11 === void 0 ? void 0 : _11.soldTimestamp,
111
- returnedTimestamp: (_12 = props.props) === null || _12 === void 0 ? void 0 : _12.returnedTimestamp,
112
- soldSellerSelfPayTimestamp: (_13 = props.props) === null || _13 === void 0 ? void 0 : _13.soldSellerSelfPayTimestamp,
113
- soldSellerPayoutProcessingTimestamp: (_14 = props.props) === null || _14 === void 0 ? void 0 : _14.soldSellerPayoutProcessingTimestamp,
114
- soldSellerPayoutFailedTimestamp: (_15 = props.props) === null || _15 === void 0 ? void 0 : _15.soldSellerPayoutFailedTimestamp,
115
- soldSellerToBePaidTimestamp: (_16 = props.props) === null || _16 === void 0 ? void 0 : _16.soldSellerToBePaidTimestamp,
116
- soldSellerPaidTimestamp: (_17 = props.props) === null || _17 === void 0 ? void 0 : _17.soldSellerPaidTimestamp,
117
- sellerSelfRejectTimestamp: (_18 = props.props) === null || _18 === void 0 ? void 0 : _18.sellerSelfRejectTimestamp,
118
- liquidationRequestedTimestamp: (_19 = props.props) === null || _19 === void 0 ? void 0 : _19.liquidationRequestedTimestamp,
119
- sellerLiquidatedTimestamp: (_20 = props.props) === null || _20 === void 0 ? void 0 : _20.sellerLiquidatedTimestamp,
120
- archivedTimestamp: (_21 = props.props) === null || _21 === void 0 ? void 0 : _21.archivedTimestamp,
121
- liquidationReadyTimestamp: (_22 = props.props) === null || _22 === void 0 ? void 0 : _22.liquidationReadyTimestamp,
122
- sellerDonatedTimestamp: (_23 = props.props) === null || _23 === void 0 ? void 0 : _23.sellerDonatedTimestamp,
123
- merchantDonatedTimestamp: (_24 = props.props) === null || _24 === void 0 ? void 0 : _24.merchantDonatedTimestamp,
124
- draftTimestamp: (_25 = props.props) === null || _25 === void 0 ? void 0 : _25.draftTimestamp,
125
- hiddenTimestamp: (_26 = props.props) === null || _26 === void 0 ? void 0 : _26.hiddenTimestamp,
126
- bookedTimestamp: (_27 = props.props) === null || _27 === void 0 ? void 0 : _27.bookedTimestamp,
127
- openTimestamp: (_28 = props.props) === null || _28 === void 0 ? void 0 : _28.openTimestamp,
96
+ createdAtTimestamp: (_x = props.props) === null || _x === void 0 ? void 0 : _x.createdAtTimestamp,
97
+ updatedAtTimestamp: (_y = props.props) === null || _y === void 0 ? void 0 : _y.updatedAtTimestamp,
98
+ sellRequestReviewTimestamp: (_z = props.props) === null || _z === void 0 ? void 0 : _z.sellRequestReviewTimestamp,
99
+ sellRequestReviewDraftTimestamp: (_0 = props.props) === null || _0 === void 0 ? void 0 : _0.sellRequestReviewDraftTimestamp,
100
+ rejectedTimestamp: (_1 = props.props) === null || _1 === void 0 ? void 0 : _1.rejectedTimestamp,
101
+ holdTimestamp: (_2 = props.props) === null || _2 === void 0 ? void 0 : _2.holdTimestamp,
102
+ acceptedTimestamp: (_3 = props.props) === null || _3 === void 0 ? void 0 : _3.acceptedTimestamp,
103
+ sellerToDropOffTimestamp: (_4 = props.props) === null || _4 === void 0 ? void 0 : _4.sellerToDropOffTimestamp,
104
+ sellerToShipTimestamp: (_5 = props.props) === null || _5 === void 0 ? void 0 : _5.sellerToShipTimestamp,
105
+ sellerToGetPickUpTimestamp: (_6 = props.props) === null || _6 === void 0 ? void 0 : _6.sellerToGetPickUpTimestamp,
106
+ qualityControlTimestamp: (_7 = props.props) === null || _7 === void 0 ? void 0 : _7.qualityControlTimestamp,
107
+ pendingPublicationTimestamp: (_8 = props.props) === null || _8 === void 0 ? void 0 : _8.pendingPublicationTimestamp,
108
+ listedTimestamp: (_9 = props.props) === null || _9 === void 0 ? void 0 : _9.listedTimestamp,
109
+ clearanceTimestamp: (_10 = props.props) === null || _10 === void 0 ? void 0 : _10.clearanceTimestamp,
110
+ reservedTimestamp: (_11 = props.props) === null || _11 === void 0 ? void 0 : _11.reservedTimestamp,
111
+ soldTimestamp: (_12 = props.props) === null || _12 === void 0 ? void 0 : _12.soldTimestamp,
112
+ returnedTimestamp: (_13 = props.props) === null || _13 === void 0 ? void 0 : _13.returnedTimestamp,
113
+ soldSellerSelfPayTimestamp: (_14 = props.props) === null || _14 === void 0 ? void 0 : _14.soldSellerSelfPayTimestamp,
114
+ soldSellerPayoutProcessingTimestamp: (_15 = props.props) === null || _15 === void 0 ? void 0 : _15.soldSellerPayoutProcessingTimestamp,
115
+ soldSellerPayoutFailedTimestamp: (_16 = props.props) === null || _16 === void 0 ? void 0 : _16.soldSellerPayoutFailedTimestamp,
116
+ soldSellerToBePaidTimestamp: (_17 = props.props) === null || _17 === void 0 ? void 0 : _17.soldSellerToBePaidTimestamp,
117
+ soldSellerPaidTimestamp: (_18 = props.props) === null || _18 === void 0 ? void 0 : _18.soldSellerPaidTimestamp,
118
+ sellerSelfRejectTimestamp: (_19 = props.props) === null || _19 === void 0 ? void 0 : _19.sellerSelfRejectTimestamp,
119
+ liquidationRequestedTimestamp: (_20 = props.props) === null || _20 === void 0 ? void 0 : _20.liquidationRequestedTimestamp,
120
+ sellerLiquidatedTimestamp: (_21 = props.props) === null || _21 === void 0 ? void 0 : _21.sellerLiquidatedTimestamp,
121
+ archivedTimestamp: (_22 = props.props) === null || _22 === void 0 ? void 0 : _22.archivedTimestamp,
122
+ liquidationReadyTimestamp: (_23 = props.props) === null || _23 === void 0 ? void 0 : _23.liquidationReadyTimestamp,
123
+ sellerDonatedTimestamp: (_24 = props.props) === null || _24 === void 0 ? void 0 : _24.sellerDonatedTimestamp,
124
+ merchantDonatedTimestamp: (_25 = props.props) === null || _25 === void 0 ? void 0 : _25.merchantDonatedTimestamp,
125
+ draftTimestamp: (_26 = props.props) === null || _26 === void 0 ? void 0 : _26.draftTimestamp,
126
+ hiddenTimestamp: (_27 = props.props) === null || _27 === void 0 ? void 0 : _27.hiddenTimestamp,
127
+ bookedTimestamp: (_28 = props.props) === null || _28 === void 0 ? void 0 : _28.bookedTimestamp,
128
+ openTimestamp: (_29 = props.props) === null || _29 === void 0 ? void 0 : _29.openTimestamp,
128
129
  };
129
130
  const payoutAttributes = {
130
- payoutMethod: (_29 = props.props) === null || _29 === void 0 ? void 0 : _29.payoutMethod,
131
- payoutSystem: (_30 = props.props) === null || _30 === void 0 ? void 0 : _30.payoutSystem,
132
- payoutReference: (_31 = props.props) === null || _31 === void 0 ? void 0 : _31.payoutReference,
131
+ payoutMethod: (_30 = props.props) === null || _30 === void 0 ? void 0 : _30.payoutMethod,
132
+ payoutSystem: (_31 = props.props) === null || _31 === void 0 ? void 0 : _31.payoutSystem,
133
+ payoutReference: (_32 = props.props) === null || _32 === void 0 ? void 0 : _32.payoutReference,
133
134
  };
134
135
  return new Product_1.default(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({}, productAttributes), productFilterAttributes), consignmentAttributes), timestampAttributes), payoutAttributes));
135
136
  }
@@ -59,5 +59,8 @@ export default class ProductFormState extends FormState {
59
59
  private discountValueChangeHandler;
60
60
  private calculateSalePrice;
61
61
  private inventoryLocationsChangeHandler;
62
+ private generateVariantId;
63
+ private variantInventoryChangeHandler;
64
+ private aggregateInventoryLocationsFromVariants;
62
65
  private fieldFactory;
63
66
  }
@@ -85,8 +85,10 @@ class ProductFormState extends FormState_1.default {
85
85
  this.fields.metaDataTitle = this.fieldFactory('textInput', 'metaDataTitle');
86
86
  this.fields.metaDataDescription = this.fieldFactory('textInput', 'metaDataDescription');
87
87
  this.fields.handle = this.fieldFactory('textInput', 'handle');
88
- // Product Classification (UNIQUE, MULTI, TEMPLATE)
88
+ // Product Classification (UNIQUE, MULTIVARIANT, TEMPLATE)
89
89
  this.fields.productClass = this.fieldFactory('singleSelect', 'productClass', options_1.productClassOptions);
90
+ // Variant inventory per option-combination (max 3 axes)
91
+ this.fields.variantInventory = this.fieldFactory('multiSelect', 'variantInventory');
90
92
  }
91
93
  autoCreateTitle() {
92
94
  return this.createProduct().autoCreateTitle();
@@ -569,6 +571,84 @@ class ProductFormState extends FormState_1.default {
569
571
  this.multiSelectChangeHandler('inventoryLocations', val);
570
572
  }
571
573
  }
574
+ generateVariantId(options) {
575
+ const entries = Object.entries(options || {}).sort(([a], [b]) => (a > b ? 1 : a < b ? -1 : 0));
576
+ return entries.map(([k, v]) => `${this.utilities.camelCase(k)}=${String(v).trim()}`).join('|');
577
+ }
578
+ variantInventoryChangeHandler(val) {
579
+ const numAxes = (val === null || val === void 0 ? void 0 : val.options) ? Object.keys(val.options).length : 0;
580
+ if (numAxes > 3) {
581
+ return; // silently ignore invalid (>3 axes)
582
+ }
583
+ const id = (val === null || val === void 0 ? void 0 : val.id) || this.generateVariantId((val === null || val === void 0 ? void 0 : val.options) || {});
584
+ const next = {
585
+ id,
586
+ options: val.options || {},
587
+ locations: val === null || val === void 0 ? void 0 : val.locations,
588
+ };
589
+ const existingIds = this.fields.variantInventory.selectedValues.map((i) => i.id);
590
+ if (existingIds.includes(id)) {
591
+ const foundInd = this.fields.variantInventory.selectedValues.findIndex((i) => i.id === id);
592
+ const foundI = this.fields.variantInventory.selectedValues[foundInd];
593
+ const sameLocations = (() => {
594
+ const norm = (arr = []) => [...arr]
595
+ .filter((l) => l && l.id)
596
+ .sort((a, b) => (a.id > b.id ? 1 : a.id < b.id ? -1 : 0))
597
+ .map((l) => `${l.id}:${Number(l.availableAmount) || 0}:${Number(l.incomingAmount) || 0}`)
598
+ .join('|');
599
+ return norm(foundI.locations) === norm(next.locations);
600
+ })();
601
+ if (sameLocations) {
602
+ // same locations => toggle remove
603
+ this.fields.variantInventory.selectedValues.splice(foundInd, 1);
604
+ }
605
+ else {
606
+ // update quantity/options
607
+ this.fields.variantInventory.selectedValues.splice(foundInd, 1, next);
608
+ }
609
+ }
610
+ else {
611
+ this.multiSelectChangeHandler('variantInventory', next);
612
+ }
613
+ // aggregate updates
614
+ const totalQty = this.fields.variantInventory.selectedValues.reduce((acc, cur) => {
615
+ const locs = cur.locations;
616
+ const sum = Array.isArray(locs) ? locs.reduce((a, l) => a + (Number(l === null || l === void 0 ? void 0 : l.availableAmount) || 0), 0) : 0;
617
+ return acc + sum;
618
+ }, 0);
619
+ this.fields.quantity.inputValue = totalQty;
620
+ this.fields.availableForSale.selectedValue = totalQty > 0;
621
+ // aggregate main inventoryLocations from variant locations (sum by location id)
622
+ this.aggregateInventoryLocationsFromVariants();
623
+ }
624
+ aggregateInventoryLocationsFromVariants() {
625
+ var _a;
626
+ const variants = ((_a = this.fields.variantInventory) === null || _a === void 0 ? void 0 : _a.selectedValues) || [];
627
+ const aggregateMap = {};
628
+ variants
629
+ .filter((v) => Array.isArray(v === null || v === void 0 ? void 0 : v.locations))
630
+ .forEach((v) => {
631
+ (v.locations || []).forEach((loc) => {
632
+ if (!loc || !loc.id || !loc.name)
633
+ return;
634
+ if (!aggregateMap[loc.id]) {
635
+ aggregateMap[loc.id] = {
636
+ id: loc.id,
637
+ name: loc.name,
638
+ availableAmount: Number(loc.availableAmount) || 0,
639
+ incomingAmount: Number(loc.incomingAmount) || 0,
640
+ };
641
+ }
642
+ else {
643
+ aggregateMap[loc.id].availableAmount += Number(loc.availableAmount) || 0;
644
+ aggregateMap[loc.id].incomingAmount += Number(loc.incomingAmount) || 0;
645
+ }
646
+ });
647
+ });
648
+ const aggregated = Object.values(aggregateMap);
649
+ this.fields.inventoryLocations.selectedValues = aggregated;
650
+ this.fields.inventoryLocations.valid = aggregated.length > 0;
651
+ }
572
652
  fieldFactory(fieldType, fieldKey, fieldOptions) {
573
653
  var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x;
574
654
  let options = fieldOptions || [];
@@ -859,6 +939,9 @@ class ProductFormState extends FormState_1.default {
859
939
  if (fieldKey === 'inventoryLocations') {
860
940
  onChangeHandler = (val) => this.inventoryLocationsChangeHandler(val);
861
941
  }
942
+ if (fieldKey === 'variantInventory') {
943
+ onChangeHandler = (val) => this.variantInventoryChangeHandler(val);
944
+ }
862
945
  return {
863
946
  options,
864
947
  hidden,
@@ -1062,17 +1062,27 @@ const productClassOptions = [
1062
1062
  {
1063
1063
  label: 'Unique',
1064
1064
  value: 'UNIQUE',
1065
- description: 'One-of-a-kind, no variants',
1065
+ description: 'One-of-a-kind product with no variants (e.g., vintage clothing, collectibles).',
1066
1066
  },
1067
1067
  {
1068
- label: 'Multiple',
1069
- value: 'MULTI',
1070
- description: 'Many duplicates of the same product (e.g., gift cards, shopping bags, etc.)',
1068
+ label: 'Multi-Variant',
1069
+ value: 'MULTIVARIANT',
1070
+ description: 'A product that has multiple variants (e.g., size, color, style).',
1071
1071
  },
1072
1072
  {
1073
1073
  label: 'Template',
1074
1074
  value: 'TEMPLATE',
1075
- description: 'Blueprint for cloning a variant of the product at checkout',
1075
+ description: 'A blueprint used to quickly clone or create a variant of the product at checkout.',
1076
+ },
1077
+ {
1078
+ label: 'Supply',
1079
+ value: 'SUPPLY',
1080
+ description: 'Order-related inventory items such as shopping bags or other sellable supplies.',
1081
+ },
1082
+ {
1083
+ label: 'Digital',
1084
+ value: 'DIGITAL',
1085
+ description: 'Non-physical products that never run out of stock (e.g., gift cards, downloads).',
1076
1086
  },
1077
1087
  ];
1078
1088
  exports.productClassOptions = productClassOptions;
@@ -11,6 +11,25 @@ export default class Product extends Base {
11
11
  toObj(): CompleteProduct;
12
12
  toProductInputObjForShopify(location?: string): any;
13
13
  toProductInputObjForShopifyV2(locationId: string, weightUnit?: string): any[];
14
+ toProductInputObjForShopifyV3(locationId: string, weightUnit?: string): {
15
+ title: string;
16
+ status: string;
17
+ productOptions: {
18
+ name: string;
19
+ values: {
20
+ name: string;
21
+ }[];
22
+ }[];
23
+ descriptionHtml: string;
24
+ productType: string;
25
+ vendor: string;
26
+ tags: (string | null)[];
27
+ files: {
28
+ originalSource: string;
29
+ contentType: string;
30
+ }[];
31
+ variants: any[];
32
+ };
14
33
  toObjForTextTranslation(): TranslatableAttributes;
15
34
  toObjForTypesense(isCanonicalMerchant?: boolean): TypesenseProductObj;
16
35
  buildUpdatedFieldsForSalesChannel(): {
@@ -29,6 +29,7 @@ class Product extends Base_1.default {
29
29
  variantId: (props === null || props === void 0 ? void 0 : props.variantId) || '',
30
30
  reservedBy: (props === null || props === void 0 ? void 0 : props.reservedBy) || [],
31
31
  options: (props === null || props === void 0 ? void 0 : props.options) || {},
32
+ variantInventory: (props === null || props === void 0 ? void 0 : props.variantInventory) || [],
32
33
  costPerItem: (props === null || props === void 0 ? void 0 : props.costPerItem) || '',
33
34
  compareAtPrice: (props === null || props === void 0 ? void 0 : props.compareAtPrice) || '',
34
35
  quantity: (props === null || props === void 0 ? void 0 : props.quantity) || 0,
@@ -289,6 +290,150 @@ class Product extends Base_1.default {
289
290
  }
290
291
  return [productInputObj];
291
292
  }
293
+ toProductInputObjForShopifyV3(locationId, weightUnit) {
294
+ var _a;
295
+ const title = this.attributes.title || `Ribbn Product ${this.attributes.handle || this.filterAttributes.documentId}`;
296
+ const inInventoryStatuses = [
297
+ product_constants_1.PRODUCT_STATES.draft,
298
+ product_constants_1.PRODUCT_STATES.qualityControl,
299
+ product_constants_1.PRODUCT_STATES.pendingPublication,
300
+ product_constants_1.PRODUCT_STATES.listed,
301
+ product_constants_1.PRODUCT_STATES.availableInStore,
302
+ product_constants_1.PRODUCT_STATES.hold,
303
+ product_constants_1.PRODUCT_STATES.archived,
304
+ ];
305
+ const baseVariantFields = () => ({
306
+ inventoryPolicy: 'DENY',
307
+ inventoryItem: {
308
+ cost: this.utilities.sanitizeNumber(this.attributes.costPerItem, 0),
309
+ tracked: true,
310
+ measurement: {
311
+ weight: {
312
+ weight: parseFloat(this.attributes.weight) || parseFloat('999'),
313
+ weightUnit: weightUnit || 'GRAMS',
314
+ },
315
+ },
316
+ },
317
+ sku: this.attributes.sku,
318
+ price: this.attributes.price || '0',
319
+ compareAtPrice: this.attributes.price || '0',
320
+ taxable: this.attributes.isTaxable === 'yes' ? true : false,
321
+ });
322
+ const passedTags = ((_a = this.filterAttributes.tags) === null || _a === void 0 ? void 0 : _a.length) ? this.filterAttributes.tags : [];
323
+ const tags = [
324
+ this.filterAttributes.documentId ? `ribbnId:${this.filterAttributes.documentId}` : null,
325
+ ...passedTags,
326
+ ].filter(Boolean);
327
+ const files = this.attributes.imageUrls
328
+ ? this.attributes.imageUrls.map((image) => ({
329
+ originalSource: image.trim(),
330
+ contentType: 'IMAGE',
331
+ }))
332
+ : [];
333
+ const variantsFromInventory = Array.isArray(this.attributes.variantInventory)
334
+ ? this.attributes.variantInventory
335
+ : [];
336
+ if (variantsFromInventory.length === 0) {
337
+ // Fallback: single-variant product (legacy behavior)
338
+ const variant = Object.assign(Object.assign({}, baseVariantFields()), { inventoryQuantities: [
339
+ {
340
+ locationId: `gid://shopify/Location/${locationId}`,
341
+ name: 'available',
342
+ quantity: 0,
343
+ },
344
+ ], optionValues: [{ optionName: 'Variant', name: title }] });
345
+ if (inInventoryStatuses.indexOf(this.consignmentAttributes.status) !== -1) {
346
+ variant.inventoryQuantities[0].quantity = 1;
347
+ }
348
+ if (this.consignmentAttributes.salePrice &&
349
+ Boolean(this.timestampAttributes.clearanceTimestamp) &&
350
+ this.consignmentAttributes.isOnSale === 'yes') {
351
+ variant.price = this.consignmentAttributes.salePrice;
352
+ variant.compareAtPrice = this.attributes.price || variant.compareAtPrice;
353
+ }
354
+ return {
355
+ title,
356
+ status: 'DRAFT',
357
+ productOptions: [{ name: 'Variant', values: [{ name: title }] }],
358
+ descriptionHtml: this.attributes.description,
359
+ productType: this.filterAttributes.productType,
360
+ vendor: this.attributes.vendorId,
361
+ tags,
362
+ files,
363
+ variants: [variant],
364
+ };
365
+ }
366
+ // Multi-variant: derive axes and values from variantInventory
367
+ const axisOrder = [];
368
+ const axisValuesMap = {};
369
+ variantsFromInventory.forEach((v) => {
370
+ Object.keys(v.options || {}).forEach((axis) => {
371
+ if (!axisOrder.includes(axis))
372
+ axisOrder.push(axis);
373
+ if (!axisValuesMap[axis])
374
+ axisValuesMap[axis] = new Set();
375
+ const value = String(v.options[axis]);
376
+ if (value)
377
+ axisValuesMap[axis].add(value);
378
+ });
379
+ });
380
+ const limitedAxes = axisOrder.slice(0, 3);
381
+ const productOptions = limitedAxes.map((axis) => ({
382
+ name: axis,
383
+ values: Array.from(axisValuesMap[axis] || new Set()).map((name) => ({ name })),
384
+ }));
385
+ const variants = variantsFromInventory.map((v) => {
386
+ var _a, _b, _c, _d, _e, _f, _g;
387
+ const optionValues = limitedAxes.map((axis) => ({
388
+ optionName: axis,
389
+ name: String(v.options[axis] || ''),
390
+ }));
391
+ const inventoryQuantities = Array.isArray(v.locations) && v.locations.length > 0
392
+ ? [
393
+ {
394
+ locationId: `gid://shopify/Location/${locationId}`,
395
+ name: 'available',
396
+ quantity: (v.locations || []).reduce((acc, l) => acc + (Number(l === null || l === void 0 ? void 0 : l.availableAmount) || 0), 0),
397
+ },
398
+ ]
399
+ : [
400
+ {
401
+ locationId: `gid://shopify/Location/${locationId}`,
402
+ name: 'available',
403
+ quantity: inInventoryStatuses.indexOf(this.consignmentAttributes.status) !== -1 ? 1 : 0,
404
+ },
405
+ ];
406
+ const basePrice = ((_b = (_a = v.price) !== null && _a !== void 0 ? _a : this.attributes.price) !== null && _b !== void 0 ? _b : '0');
407
+ const variant = Object.assign(Object.assign({}, baseVariantFields()), { optionValues,
408
+ inventoryQuantities });
409
+ if (v.sku) {
410
+ variant.sku = v.sku;
411
+ }
412
+ if (v.taxable !== undefined) {
413
+ variant.taxable = v.taxable === 'yes';
414
+ }
415
+ variant.price = basePrice;
416
+ variant.compareAtPrice = ((_c = v.compareAtPrice) !== null && _c !== void 0 ? _c : basePrice);
417
+ const onSale = ((_d = v.isOnSale) !== null && _d !== void 0 ? _d : this.consignmentAttributes.isOnSale);
418
+ const salePrice = ((_e = v.salePrice) !== null && _e !== void 0 ? _e : this.consignmentAttributes.salePrice);
419
+ if (salePrice && Boolean(this.timestampAttributes.clearanceTimestamp) && onSale === 'yes') {
420
+ variant.price = salePrice;
421
+ variant.compareAtPrice = ((_g = (_f = v.compareAtPrice) !== null && _f !== void 0 ? _f : this.attributes.price) !== null && _g !== void 0 ? _g : basePrice);
422
+ }
423
+ return variant;
424
+ });
425
+ return {
426
+ title,
427
+ status: 'DRAFT',
428
+ productOptions,
429
+ descriptionHtml: this.attributes.description,
430
+ productType: this.filterAttributes.productType,
431
+ vendor: this.attributes.vendorId,
432
+ tags,
433
+ files,
434
+ variants,
435
+ };
436
+ }
292
437
  toObjForTextTranslation() {
293
438
  return {
294
439
  description: this.attributes.description || this.FIELD_NOT_TRANSLATABLE_KEY,
@@ -30,7 +30,8 @@ type ProductAttributes = {
30
30
  metaDataTitle?: string;
31
31
  metaDataDescription?: string;
32
32
  isTaxable?: string;
33
- productClass?: 'UNIQUE' | 'MULTI' | 'TEMPLATE' | string;
33
+ productClass?: 'UNIQUE' | 'MULTIVARIANT' | 'TEMPLATE' | 'SUPPLY' | 'DIGITAL' | string;
34
+ variantInventory?: VariantInventory[];
34
35
  };
35
36
  type ProductFilterAttributes = {
36
37
  documentId: string;
@@ -151,6 +152,18 @@ type MaterialComposition = {
151
152
  type EditorAuthorization = {
152
153
  [key: string]: string[];
153
154
  };
155
+ type VariantOptions = Record<string, string>;
156
+ type VariantInventory = {
157
+ id: string;
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';
166
+ };
154
167
  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';
155
168
  type ProductPublishKeys = 'USER_PUBLISHED' | 'MERCHANT_PUBLISHED';
156
169
  type ShopifyProduct = ProductAttributes & ProductFilterAttributes;
@@ -186,6 +199,7 @@ type ProductAttributesFormFields = {
186
199
  handle: TextInputFormField<string>;
187
200
  isTaxable: SingleSelectFormField<string>;
188
201
  productClass: SingleSelectFormField<string>;
202
+ variantInventory: MultiSelectFormField<VariantInventory>;
189
203
  };
190
204
  type ProductFilterAttributesFormFields = {
191
205
  brand: SingleSelectFormField<string>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rerobe-js-orm",
3
- "version": "4.4.8",
3
+ "version": "4.5.0",
4
4
  "description": "ReRobe's Javascript ORM Framework",
5
5
  "main": "lib/index.js",
6
6
  "types": "lib/index.d.ts",