@shushed/helpers 0.0.226-fix-erp-631-20260105165305 → 0.0.226

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.
Files changed (42) hide show
  1. package/dist/cjs/contracts/index.js +5 -1
  2. package/dist/cjs/contracts/product-draft.schema.json +159 -5
  3. package/dist/cjs/contracts/purchase-orderline.schema.json +135 -0
  4. package/dist/cjs/contracts/stock-movement.schema.json +144 -0
  5. package/dist/cjs/dist-dereferenced/index.js +5 -1
  6. package/dist/cjs/dist-dereferenced/messages/product-draft.js +1 -1
  7. package/dist/cjs/dist-dereferenced/product-draft.js +1 -1
  8. package/dist/cjs/dist-dereferenced/purchase-orderline.js +4 -0
  9. package/dist/cjs/dist-dereferenced/stock-movement.js +4 -0
  10. package/dist/cjs/dist-types/purchase-orderline.js +2 -0
  11. package/dist/cjs/dist-types/stock-movement.js +2 -0
  12. package/dist/cjs/src-public/airtable.js +96 -59
  13. package/dist/cjs/src-public/bcOrder.js +60 -77
  14. package/dist/cjs/src-public/centra.js +56 -5
  15. package/dist/cjs/src-public/env.js +1 -1
  16. package/dist/cjs/src-public/index.js +3 -1
  17. package/dist/cjs/src-public/pubsub.js +35 -10
  18. package/dist/cjs/src-public/runtime.js +48 -9
  19. package/dist/cjs/src-public/sitoo.js +349 -0
  20. package/dist/cjs/src-public/utils.js +3 -0
  21. package/dist/package.json +3 -2
  22. package/dist/types/contracts/index.d.ts +2 -0
  23. package/dist/types/dist-dereferenced/index.d.ts +2 -0
  24. package/dist/types/dist-dereferenced/messages/product-draft.d.ts +148 -1
  25. package/dist/types/dist-dereferenced/product-draft.d.ts +148 -1
  26. package/dist/types/dist-dereferenced/purchase-orderline.d.ts +114 -0
  27. package/dist/types/dist-dereferenced/stock-movement.d.ts +110 -0
  28. package/dist/types/dist-types/index.d.ts +2 -0
  29. package/dist/types/dist-types/messages/product-draft.d.ts +23 -4
  30. package/dist/types/dist-types/product-draft.d.ts +23 -4
  31. package/dist/types/dist-types/purchase-orderline.d.ts +31 -0
  32. package/dist/types/dist-types/stock-movement.d.ts +30 -0
  33. package/dist/types/src-public/airtable.d.ts +0 -9
  34. package/dist/types/src-public/centra.d.ts +3 -0
  35. package/dist/types/src-public/env.d.ts +1 -1
  36. package/dist/types/src-public/index.d.ts +1 -0
  37. package/dist/types/src-public/pubsub.d.ts +4 -1
  38. package/dist/types/src-public/runtime.d.ts +17 -4
  39. package/dist/types/src-public/sitoo.d.ts +254 -0
  40. package/dist/types/src-public/types.d.ts +6 -0
  41. package/dist/types/src-public/utils.d.ts +1 -1
  42. package/package.json +4 -3
@@ -42,71 +42,107 @@ class AirtableHelper extends runtime_1.default {
42
42
  }
43
43
  return existingRecord;
44
44
  }
45
- async updateMultiple(payload, options = {}, callIdx = 0, collectedResult = {
46
- updatedRecords: [],
47
- createdRecords: [],
48
- records: []
49
- }) {
45
+ async updateMultiple(payload, options = {}) {
50
46
  let response = null;
51
47
  const tableUrl = `https://api.airtable.com/v0/${this.baseId}/${this.tableId}`;
52
- const currentBatch = payload.slice(callIdx * 10, (callIdx + 1) * 10);
53
- try {
54
- response = await fetch(`${tableUrl}`, {
55
- method: "PATCH",
56
- headers: {
57
- Authorization: `Bearer ${this.apiKey}`,
58
- "Content-Type": "application/json",
59
- },
60
- body: JSON.stringify({
61
- performUpsert: {
62
- fieldsToMergeOn: (options.fieldsToMergeOn ?? [this.primaryKeyFieldName]).map(x => this.dictionary[x] || x),
63
- },
64
- returnFieldsByFieldId: true,
65
- typecast: options.typecast || false,
66
- records: currentBatch.map(x => {
67
- const recordId = x.$recordId;
68
- const fieldsWithoutRecordId = { ...x };
69
- delete fieldsWithoutRecordId.$recordId;
70
- const record = {
71
- fields: AirtableHelper.convertToDictionary(this.dictionary, this.primaryKeyWritable === false
72
- ? AirtableHelper.removePrimaryKey(fieldsWithoutRecordId, this.primaryKeyFieldName)
73
- : fieldsWithoutRecordId),
74
- };
75
- if (recordId) {
76
- record.id = recordId;
77
- }
78
- return record;
79
- })
80
- }),
48
+ const batchSize = 10;
49
+ let collectedResult = {
50
+ updatedRecords: [],
51
+ createdRecords: [],
52
+ records: []
53
+ };
54
+ const fieldsToMergeOn = (options.fieldsToMergeOn || [this.primaryKeyFieldName]);
55
+ const fieldsToMergeOnIds = fieldsToMergeOn.map(x => this.dictionary[x] || x);
56
+ let callIdx = 0;
57
+ const payloadDeduplicated = payload.filter((x, idx, self) => idx === self.findIndex((t) => fieldsToMergeOn.every(field => (0, lodash_isequal_1.default)(t[field], x[field]))));
58
+ const maxCallIdx = Math.ceil(payloadDeduplicated.length / batchSize);
59
+ while (callIdx < maxCallIdx) {
60
+ const currentBatch = payloadDeduplicated.slice(callIdx * batchSize, (callIdx + 1) * batchSize);
61
+ const recordsInBatch = currentBatch.map(x => {
62
+ const recordId = x.$recordId;
63
+ const fieldsWithoutRecordId = { ...x };
64
+ delete fieldsWithoutRecordId.$recordId;
65
+ const record = {
66
+ fields: AirtableHelper.convertToDictionary(this.dictionary, this.primaryKeyWritable === false
67
+ ? AirtableHelper.removePrimaryKey(fieldsWithoutRecordId, this.primaryKeyFieldName)
68
+ : fieldsWithoutRecordId),
69
+ };
70
+ if (recordId) {
71
+ record.id = recordId;
72
+ }
73
+ return record;
81
74
  });
82
- if (!response.ok && response) {
83
- const text = await response.text().catch(() => `${response?.status || 'unknown'}`);
84
- throw new Error(text);
75
+ try {
76
+ response = await fetch(`${tableUrl}`, {
77
+ method: "PATCH",
78
+ headers: {
79
+ Authorization: `Bearer ${this.apiKey}`,
80
+ "Content-Type": "application/json",
81
+ },
82
+ body: JSON.stringify({
83
+ performUpsert: {
84
+ fieldsToMergeOn: fieldsToMergeOnIds,
85
+ },
86
+ returnFieldsByFieldId: true,
87
+ typecast: options.typecast || false,
88
+ records: recordsInBatch
89
+ }),
90
+ });
91
+ if (!response.ok && response) {
92
+ const text = await response.text().catch(() => `${response?.status || 'unknown'}`);
93
+ throw new Error(text);
94
+ }
95
+ const resp = (await response.json());
96
+ collectedResult = {
97
+ updatedRecords: collectedResult.updatedRecords.concat(resp.updatedRecords),
98
+ createdRecords: collectedResult.createdRecords.concat(resp.createdRecords),
99
+ records: collectedResult.records.concat(resp.records.map(x => AirtableHelper.translateFields(this.dictionary, x)))
100
+ };
85
101
  }
86
- const resp = (await response.json());
87
- const nextCollectedResult = {
88
- updatedRecords: collectedResult.updatedRecords.concat(resp.updatedRecords),
89
- createdRecords: collectedResult.createdRecords.concat(resp.createdRecords),
90
- records: collectedResult.records.concat(resp.records.map(x => AirtableHelper.translateFields(this.dictionary, x)))
91
- };
92
- if (payload.length > (callIdx + 1) * 10) {
93
- return this.updateMultiple(payload, options, callIdx + 1, nextCollectedResult);
102
+ catch (err) {
103
+ const errorMessage = `Failed to update records in ${this.tableId} table (baseId: ${this.baseId}). Status: ${response?.status || 'unknown'}. Error: ${err.message}`;
104
+ const batchErrors = currentBatch.map(() => new Error(errorMessage));
105
+ collectedResult = {
106
+ updatedRecords: collectedResult.updatedRecords,
107
+ createdRecords: collectedResult.createdRecords,
108
+ records: collectedResult.records.concat(batchErrors)
109
+ };
110
+ }
111
+ finally {
112
+ callIdx += 1;
94
113
  }
95
- return nextCollectedResult;
96
114
  }
97
- catch (err) {
98
- const errorMessage = `Failed to update records in ${this.tableId} table (baseId: ${this.baseId}). Status: ${response?.status || 'unknown'}. Error: ${err.message}`;
99
- const batchErrors = currentBatch.map(() => new Error(errorMessage));
100
- const nextCollectedResult = {
101
- updatedRecords: collectedResult.updatedRecords,
102
- createdRecords: collectedResult.createdRecords,
103
- records: collectedResult.records.concat(batchErrors)
104
- };
105
- if (payload.length > (callIdx + 1) * 10) {
106
- return this.updateMultiple(payload, options, callIdx + 1, nextCollectedResult);
115
+ const resultInOrder = [];
116
+ for (let i = 0; i < payload.length; i++) {
117
+ let j = 0;
118
+ let foundMatchingRecord = null;
119
+ while (j < collectedResult.records.length && foundMatchingRecord === null) {
120
+ let isMatching = true;
121
+ let k = 0;
122
+ while (k < fieldsToMergeOn.length && isMatching) {
123
+ const field = fieldsToMergeOn[k];
124
+ if (!(0, lodash_isequal_1.default)(collectedResult.records[j].fields[field], payload[i][field])) {
125
+ isMatching = false;
126
+ }
127
+ k += 1;
128
+ }
129
+ if (isMatching) {
130
+ foundMatchingRecord = j;
131
+ }
132
+ j += 1;
133
+ }
134
+ if (foundMatchingRecord !== null) {
135
+ resultInOrder.push(collectedResult.records[foundMatchingRecord]);
136
+ }
137
+ else {
138
+ resultInOrder.push(new Error(`Record ${payload[i][this.primaryKeyFieldName]} does not match any record in the response`));
107
139
  }
108
- return nextCollectedResult;
109
140
  }
141
+ return {
142
+ updatedRecords: collectedResult.updatedRecords,
143
+ createdRecords: collectedResult.createdRecords,
144
+ records: resultInOrder
145
+ };
110
146
  }
111
147
  async upsert(payload) {
112
148
  const existingRecord = await this.getExistingRecord(payload);
@@ -258,9 +294,10 @@ class AirtableHelper extends runtime_1.default {
258
294
  const escapeFormulaValue = (value) => {
259
295
  return value.replace(/"/g, '\\"');
260
296
  };
297
+ const dedupedKeys = keys.filter((x, idx, self) => self.indexOf(x) === idx);
261
298
  const batchSize = 50;
262
- for (let i = 0; i < keys.length; i += batchSize) {
263
- const batch = keys.slice(i, i + batchSize);
299
+ for (let i = 0; i < dedupedKeys.length; i += batchSize) {
300
+ const batch = dedupedKeys.slice(i, i + batchSize);
264
301
  const orConditions = batch.map(key => `${this.dictionary[this.primaryKeyFieldName]} = "${escapeFormulaValue(key)}"`).join(', ');
265
302
  const formula = `OR(${orConditions})`;
266
303
  const records = await this.getExistingRecords(formula);
@@ -169,33 +169,30 @@ function transformMasterOrder(payload) {
169
169
  if (desc)
170
170
  skuByDescription[desc] = sku;
171
171
  });
172
- const isShippableItem = (line) => {
173
- const itemNo = String(line?.item_no || line?.no || '');
174
- const variantCode = String(line?.variant_code || '');
175
- const amount = Number(line?.amount || line?.amount_excl_vat || 0);
176
- const amountGross = Number(line?.amount_including_vat || line?.amount_incl_vat || 0);
177
- const quantity = Number(line?.quantity || 0);
178
- const hasStyleAndColour = itemNo.includes('-');
179
- const hasSize = variantCode.trim().length > 0;
180
- const hasValue = amount > 0 || amountGross > 0 || quantity > 0;
181
- const hasQuantity = quantity > 0;
182
- return Boolean(hasStyleAndColour && hasSize && hasValue && hasQuantity);
172
+ const isItemish = (type, itemNo, desc) => {
173
+ const t = String(type || '').toLowerCase();
174
+ return t.includes('item') || itemNo.includes('-') || desc.includes(' - ');
183
175
  };
184
176
  const returnedQtyBySku = {};
185
177
  (payload.credit_memos || []).forEach((cm) => {
186
178
  (cm.credit_memo_lines || []).forEach((l) => {
187
- if (!isShippableItem(l)) {
188
- const desc = String(l?.description || '');
189
- if (desc && skuByDescription[desc]) {
190
- const sku = skuByDescription[desc];
191
- returnedQtyBySku[sku] = (returnedQtyBySku[sku] || 0) + (l.quantity || 0);
192
- }
179
+ const itemNo = String(l?.item_no || l?.no || '');
180
+ const desc = String(l?.description || '');
181
+ if (itemNo.toUpperCase() === 'X DELCHR' || itemNo.toUpperCase() === 'REFUND')
182
+ return;
183
+ if ((l?.quantity || 0) <= 0)
193
184
  return;
185
+ const typeStr = String(l?.type || '').toLowerCase();
186
+ const isTypeItem = typeStr.includes('item');
187
+ if (isTypeItem) {
188
+ const variant = String(l?.variant_code || '');
189
+ const sku = variant ? `${itemNo}-${variant}` : itemNo;
190
+ returnedQtyBySku[sku] = (returnedQtyBySku[sku] || 0) + (l.quantity || 0);
191
+ }
192
+ else if (desc && skuByDescription[desc]) {
193
+ const sku = skuByDescription[desc];
194
+ returnedQtyBySku[sku] = (returnedQtyBySku[sku] || 0) + (l.quantity || 0);
194
195
  }
195
- const itemNo = String(l?.item_no || l?.no || '');
196
- const variant = String(l?.variant_code || '');
197
- const sku = variant ? `${itemNo}-${variant}` : itemNo;
198
- returnedQtyBySku[sku] = (returnedQtyBySku[sku] || 0) + (l.quantity || 0);
199
196
  });
200
197
  });
201
198
  (payload.sales || []).forEach((sale) => {
@@ -203,9 +200,14 @@ function transformMasterOrder(payload) {
203
200
  if (!docTypeRaw.toLowerCase().includes('return'))
204
201
  return;
205
202
  (sale.sale_lines || []).forEach((l) => {
206
- if (!isShippableItem(l))
207
- return;
208
203
  const itemNo = String(l?.item_no || l?.no || '');
204
+ const desc = String(l?.description || '');
205
+ if (itemNo.toUpperCase() === 'X DELCHR' || itemNo.toUpperCase() === 'REFUND')
206
+ return;
207
+ if ((l?.quantity || 0) <= 0)
208
+ return;
209
+ if (!isItemish(l?.type, itemNo, desc))
210
+ return;
209
211
  const variant = String(l?.variant_code || '');
210
212
  const sku = variant ? `${itemNo}-${variant}` : itemNo;
211
213
  returnedQtyBySku[sku] = (returnedQtyBySku[sku] || 0) + (l.quantity || 0);
@@ -235,12 +237,18 @@ function transformMasterOrder(payload) {
235
237
  const shippedQtyBySku = {};
236
238
  (payload.shipments || []).forEach((s) => {
237
239
  (s.shipment_lines || []).forEach((l) => {
238
- if (!isShippableItem(l))
239
- return;
240
240
  const itemNo = String(l?.item_no || l?.no || "");
241
+ if (itemNo.toUpperCase() === 'X DELCHR')
242
+ return;
243
+ const desc = String(l?.description || '');
244
+ const looksProduct = itemNo.includes('-') || desc.includes(' - ');
245
+ const qty = Number(l?.quantity || 0);
246
+ if (!looksProduct)
247
+ return;
248
+ if (qty <= 0)
249
+ return;
241
250
  const variant = String(l?.variant_code || "");
242
251
  const sku = variant ? `${itemNo}-${variant}` : itemNo;
243
- const qty = Number(l?.quantity || 0);
244
252
  shippedQtyBySku[sku] = (shippedQtyBySku[sku] || 0) + qty;
245
253
  });
246
254
  });
@@ -511,30 +519,19 @@ function transformMasterOrder(payload) {
511
519
  const d = new Date(source.includes("T") ? source : `${source}T00:00:00Z`);
512
520
  return isNaN(d.getTime()) ? new Date().toISOString() : d.toISOString();
513
521
  };
514
- const mapCourierName = (shippingAgentCode) => {
515
- if (!shippingAgentCode || typeof shippingAgentCode !== 'string')
516
- return null;
517
- const code = shippingAgentCode.toUpperCase().trim();
518
- if (code === 'GLOBALE' || code === 'ROYALMAIL' || code === 'EVRI' || code === 'WAITROSE') {
519
- return code;
520
- }
521
- return null;
522
- };
523
- const mapClassName = (serviceCode) => {
524
- if (!serviceCode || typeof serviceCode !== 'string')
525
- return 'STANDARD';
526
- const code = serviceCode.toUpperCase().trim();
527
- if (code.includes('24') || code.includes('EXPRESS') || code.includes('PRIORITY')) {
528
- return 'PRIORITY';
529
- }
530
- return 'STANDARD';
531
- };
532
522
  const mappedShipments = (payload.shipments || []).map((s) => {
533
523
  const rawLines = (s.shipment_lines || [])
534
- .filter((l) => isShippableItem(l));
524
+ .filter((l) => (l?.quantity ?? 0) > 0)
525
+ .filter((l) => String(l?.item_no || '').toUpperCase() !== 'X DELCHR')
526
+ .filter((l) => {
527
+ const itemNo = String(l?.item_no || '');
528
+ const desc = String(l?.description || '');
529
+ const looksProduct = itemNo.includes('-') || desc.includes(' - ');
530
+ return looksProduct;
531
+ });
535
532
  const qtyBySku = new Map();
536
533
  rawLines.forEach((l) => {
537
- const itemNo = String(l?.item_no || l?.no || '');
534
+ const itemNo = String(l?.item_no || '');
538
535
  const variant = String(l?.variant_code || '');
539
536
  const sku = variant ? `${itemNo}-${variant}` : itemNo;
540
537
  const q = Number(l?.quantity || 0);
@@ -553,9 +550,6 @@ function transformMasterOrder(payload) {
553
550
  status: statusVal,
554
551
  type: statusVal === "SHIPPED" ? "SHIPPED-OUTBOUND" : undefined,
555
552
  shipped_by: "TORQUE",
556
- courier_name: mapCourierName(s.shipping_agent_code),
557
- tracking_code: (s.torque_tracking_id && typeof s.torque_tracking_id === 'string') ? s.torque_tracking_id : null,
558
- class_name: mapClassName(s.shipping_agent_service_code),
559
553
  items: shipmentLines,
560
554
  amount_net: money(0),
561
555
  amount_gross: money(0),
@@ -564,27 +558,28 @@ function transformMasterOrder(payload) {
564
558
  discount_amount_percent: 0,
565
559
  };
566
560
  });
567
- const shipmentsBySku = new Map();
568
- (payload.shipments || []).forEach((s) => {
569
- (s.shipment_lines || []).forEach((sl) => {
570
- if (!isShippableItem(sl))
571
- return;
572
- const itemNo = String(sl?.item_no || sl?.no || '');
573
- const variant = String(sl?.variant_code || '');
574
- const sku = variant ? `${itemNo}-${variant}` : itemNo;
575
- if (!shipmentsBySku.has(sku)) {
576
- shipmentsBySku.set(sku, s);
577
- }
578
- });
579
- });
580
561
  const returnShipments = (payload.credit_memos || [])
581
562
  .filter((cm) => {
582
563
  const lines = cm?.credit_memo_lines || [];
583
- return lines.some((l) => isShippableItem(l));
564
+ return lines.some((l) => {
565
+ const itemNo = String(l?.item_no || l?.no || "");
566
+ const upper = itemNo.toUpperCase();
567
+ if (upper === 'X DELCHR' || upper === 'REFUND')
568
+ return false;
569
+ const looksProduct = itemNo.includes('-');
570
+ return (l?.quantity || 0) > 0 && looksProduct;
571
+ });
584
572
  })
585
573
  .map((cm, idx) => {
586
574
  const rawLines = (cm.credit_memo_lines || [])
587
- .filter((l) => isShippableItem(l));
575
+ .filter((l) => (l?.quantity || 0) > 0)
576
+ .filter((l) => {
577
+ const itemNo = String(l?.item_no || l?.no || '');
578
+ const upper = itemNo.toUpperCase();
579
+ if (upper === 'X DELCHR' || upper === 'REFUND')
580
+ return false;
581
+ return itemNo.includes('-');
582
+ });
588
583
  const qtyBySku = new Map();
589
584
  rawLines.forEach((l) => {
590
585
  const itemNo = String(l?.item_no || l?.no || '');
@@ -594,7 +589,6 @@ function transformMasterOrder(payload) {
594
589
  qtyBySku.set(sku, (qtyBySku.get(sku) || 0) + q);
595
590
  });
596
591
  const skus = Array.from(qtyBySku.keys()).sort();
597
- const matchingShipment = skus.length > 0 ? shipmentsBySku.get(skus[0]) : null;
598
592
  const lines = skus.map((sku, i) => ({
599
593
  lineNumber: i + 1,
600
594
  sku,
@@ -608,17 +602,6 @@ function transformMasterOrder(payload) {
608
602
  status: "RECEIVED",
609
603
  type: "SHIPPED-RETURN",
610
604
  shipped_by: "CUSTOMER",
611
- courier_name: mapCourierName(matchingShipment?.shipping_agent_code || cm.shipping_agent_code),
612
- tracking_code: (() => {
613
- if (matchingShipment?.torque_tracking_id && typeof matchingShipment.torque_tracking_id === 'string') {
614
- return matchingShipment.torque_tracking_id;
615
- }
616
- if (cm.torque_tracking_id && typeof cm.torque_tracking_id === 'string') {
617
- return cm.torque_tracking_id;
618
- }
619
- return null;
620
- })(),
621
- class_name: mapClassName(matchingShipment?.shipping_agent_service_code || cm.shipping_agent_service_code),
622
605
  items: lines,
623
606
  amount_net: money(0),
624
607
  amount_gross: money(0),
@@ -783,7 +766,7 @@ function transformMasterOrder(payload) {
783
766
  return acc + (s.amount_incl_vat || 0);
784
767
  }
785
768
  const lineAmounts = (s.shipment_lines || []).reduce((lineAcc, l) => {
786
- if (!isShippableItem(l))
769
+ if (isDeliveryItem(l))
787
770
  return lineAcc;
788
771
  const orderLineNo = Number(l?.order_line_no || -1);
789
772
  let sourceLine = orderLineNo > 0 ? sourceLineMap.get(orderLineNo) : null;
@@ -6,7 +6,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  const crypto_1 = __importDefault(require("crypto"));
7
7
  const env_1 = __importDefault(require("./env"));
8
8
  const utils_1 = require("./utils");
9
- const CACHE_EXPIRATION_MS = 1000 * 60 * 60 * 3;
9
+ const CACHE_EXPIRATION_MS = 1000 * 60 * 5;
10
10
  class CentraHelper extends env_1.default {
11
11
  opts;
12
12
  shaToken;
@@ -99,6 +99,12 @@ class CentraHelper extends env_1.default {
99
99
  getCacheKeyForMarket(marketExternalId) {
100
100
  return `${this.getCacheKeyForMarkets()}_${marketExternalId}`;
101
101
  }
102
+ getCacheKeyForCampaigns() {
103
+ return `centra_${this.shaToken}_campaign_name`;
104
+ }
105
+ getCacheKeyForCampaign(campaignName) {
106
+ return `${this.getCacheKeyForCampaigns()}_${campaignName}`;
107
+ }
102
108
  getCacheKeyForSizeCharts() {
103
109
  return `centra_${this.shaToken}_size_chart_external_id`;
104
110
  }
@@ -130,7 +136,7 @@ class CentraHelper extends env_1.default {
130
136
  return `${this.getCacheKeyForCentraWarehouses()}_${warehouseExternalId}`;
131
137
  }
132
138
  getCacheKeyForCentraPricelists() {
133
- return `centra_${this.shaToken}_pricelist_external_id`;
139
+ return `centra_${this.shaToken}_pricelists_external_id`;
134
140
  }
135
141
  getCacheKeyForCentraPricelist(pricelistExternalId) {
136
142
  return `${this.getCacheKeyForCentraPricelists()}_${pricelistExternalId}`;
@@ -218,7 +224,7 @@ class CentraHelper extends env_1.default {
218
224
  return result;
219
225
  }
220
226
  async fetchCentraCampaigns(names) {
221
- const limit = (names?.length || 0) > 200 ? 200 : (names?.length || 0);
227
+ const limit = names ? (names.length > 200 ? 200 : names.length) : 200;
222
228
  let nextCursor = null;
223
229
  const result = {};
224
230
  do {
@@ -270,7 +276,7 @@ class CentraHelper extends env_1.default {
270
276
  nextCursor = null;
271
277
  }
272
278
  if (campaignConnection && campaignConnection.edges?.length) {
273
- for (let i = campaignConnection.edges.length; i < (names?.length || 0); i++) {
279
+ for (let i = 0; i < campaignConnection.edges.length; i++) {
274
280
  const { node } = campaignConnection.edges[i];
275
281
  result[node.name] = node;
276
282
  }
@@ -721,7 +727,9 @@ class CentraHelper extends env_1.default {
721
727
  }
722
728
  else {
723
729
  for (const warehouse of warehouses) {
724
- warehouseToSet[warehouse.externalId] = warehouse;
730
+ if (warehouse.externalId) {
731
+ warehouseToSet[warehouse.externalId] = warehouse;
732
+ }
725
733
  }
726
734
  await this.set(Object.entries(warehouseToSet).filter(([_, value]) => !(value instanceof Error)).map(([key, value]) => ({ name: this.getCacheKeyForCentraWarehouse(key), value: JSON.stringify(value) })), 'env', {
727
735
  ephemeralMs: CACHE_EXPIRATION_MS,
@@ -797,6 +805,49 @@ class CentraHelper extends env_1.default {
797
805
  }
798
806
  return Object.assign({}, pricelistInCache, pricelistToSet);
799
807
  }
808
+ async getCentraCampaigns(alwaysFetch = false) {
809
+ let campaignInCache = {};
810
+ let dedupedCampaignNamesInCache = [];
811
+ let campaignsToFetch = null;
812
+ if (!alwaysFetch) {
813
+ const campaignNamesInCache = await (this.get(this.getCacheKeyForCampaigns(), 'env', {
814
+ isEphemeral: true,
815
+ encrypted: false,
816
+ }).then(x => x ? JSON.parse(x) : null));
817
+ if (campaignNamesInCache) {
818
+ dedupedCampaignNamesInCache = campaignNamesInCache.filter((x, index, self) => self.indexOf(x) === index);
819
+ campaignInCache = Object.fromEntries(Object.entries(await this.get(dedupedCampaignNamesInCache.map((x) => this.getCacheKeyForCampaign(x)), 'env', {
820
+ isEphemeral: true,
821
+ encrypted: false,
822
+ })).map(([key, value]) => [key, value ? JSON.parse(value || 'null') : undefined]).filter(([_, value]) => value));
823
+ campaignsToFetch = dedupedCampaignNamesInCache.filter(x => !campaignInCache[x]);
824
+ }
825
+ }
826
+ const campaignToSet = {};
827
+ if (!campaignsToFetch || campaignsToFetch.length) {
828
+ const campaigns = await this.fetchCentraCampaigns();
829
+ if (CentraHelper.isCentraErrors(campaigns)) {
830
+ return new Error(`Failed to fetch campaigns: ${campaigns.errors.map((x) => x.message).join(', ')}`);
831
+ }
832
+ else {
833
+ for (const campaign of Object.values(campaigns)) {
834
+ campaignToSet[campaign.name] = campaign;
835
+ }
836
+ await this.set(Object.entries(campaignToSet).filter(([_, value]) => !(value instanceof Error)).map(([key, value]) => ({ name: this.getCacheKeyForCampaign(key), value: JSON.stringify(value) })), 'env', {
837
+ ephemeralMs: CACHE_EXPIRATION_MS,
838
+ encrypted: false,
839
+ });
840
+ await this.set([{
841
+ name: this.getCacheKeyForCampaigns(),
842
+ value: JSON.stringify(Object.keys(campaignToSet)),
843
+ }], 'env', {
844
+ ephemeralMs: CACHE_EXPIRATION_MS,
845
+ encrypted: false,
846
+ });
847
+ }
848
+ }
849
+ return Object.assign({}, campaignInCache, campaignToSet);
850
+ }
800
851
  async getCentraMarkets(alwaysFetch = false) {
801
852
  let marketInCache = {};
802
853
  let dedupedMarketNamesInCache = [];
@@ -567,7 +567,7 @@ class EnvEngine extends runtime_1.default {
567
567
  batchSize: 1,
568
568
  subscriptionFilter: null,
569
569
  batchWindowSeconds: 10,
570
- }, Object.fromEntries(Object.entries(rnPartialConfig).filter(x => !(x === null || x === undefined))));
570
+ }, Object.fromEntries(Object.entries(rnPartialConfig).filter(([x]) => !(x === null || x === undefined))));
571
571
  return rnConfig;
572
572
  }
573
573
  createOrUpdateRespectfulNudge(subscriptionName, rnPartialConfig) {
@@ -17,7 +17,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
17
17
  return (mod && mod.__esModule) ? mod : { "default": mod };
18
18
  };
19
19
  Object.defineProperty(exports, "__esModule", { value: true });
20
- exports.RedisConnectionError = exports.RateLimit = exports.setHeaders = exports.BCOrderHelper = exports.DatoHelper = exports.AirtableHelper = exports.CentraHelper = exports.BigQueryHelper = exports.JWKSHelper = exports.CloudTasksHelper = exports.Secrets = exports.SchedulerHelper = exports.Logging = exports.Runtime = exports.PubSubHelper = exports.EnvEngine = exports.validate = void 0;
20
+ exports.SitooHelper = exports.RedisConnectionError = exports.RateLimit = exports.setHeaders = exports.BCOrderHelper = exports.DatoHelper = exports.AirtableHelper = exports.CentraHelper = exports.BigQueryHelper = exports.JWKSHelper = exports.CloudTasksHelper = exports.Secrets = exports.SchedulerHelper = exports.Logging = exports.Runtime = exports.PubSubHelper = exports.EnvEngine = exports.validate = void 0;
21
21
  var validate_1 = require("./validate");
22
22
  Object.defineProperty(exports, "validate", { enumerable: true, get: function () { return __importDefault(validate_1).default; } });
23
23
  __exportStar(require("./sanitize"), exports);
@@ -52,3 +52,5 @@ Object.defineProperty(exports, "setHeaders", { enumerable: true, get: function (
52
52
  var rateLimit_1 = require("./rateLimit");
53
53
  Object.defineProperty(exports, "RateLimit", { enumerable: true, get: function () { return __importDefault(rateLimit_1).default; } });
54
54
  Object.defineProperty(exports, "RedisConnectionError", { enumerable: true, get: function () { return rateLimit_1.RedisConnectionError; } });
55
+ var sitoo_1 = require("./sitoo");
56
+ Object.defineProperty(exports, "SitooHelper", { enumerable: true, get: function () { return __importDefault(sitoo_1).default; } });
@@ -79,19 +79,44 @@ class PubSubHelper extends runtime_1.default {
79
79
  }
80
80
  const topic = pubSubPublishTopics[options.topicName];
81
81
  const batches = payloads.length > 5 ? (0, lodash_chunk_1.default)(payloads, 10) : [payloads];
82
- const attributes = {
83
- ...options.extraAttributes,
84
- source_system: options.sourceSystem,
85
- target_system: options.targetSystem,
86
- buildship_id: this.systemEnvName + '-' + this.workflowId + '-' + this.triggerId,
87
- };
82
+ let nextAttributes = {};
88
83
  const messageIds = [];
89
84
  for (let i = 0; i < batches.length; i++) {
90
85
  const batch = batches[i];
91
- const results = await Promise.allSettled(batch.map((x) => topic.publishMessage({
92
- data: Buffer.from(JSON.stringify(x)),
93
- attributes: Object.fromEntries(Object.entries(options.attributesGenerator ? Object.assign({}, attributes, options.attributesGenerator(x)) : attributes).filter(([_, v]) => v !== undefined && v !== null).map(([k, v]) => [k, `${v}`]))
94
- })));
86
+ const results = await Promise.allSettled(batch.map((x) => {
87
+ if (options.attributesGenerator) {
88
+ nextAttributes = Object.fromEntries(Object.entries(options.attributesGenerator(x)).filter(([_, v]) => v !== undefined && v !== null).map(([k, v]) => [k, typeof v === 'boolean' ? (v ? 'yes' : 'no') : `${v}`]));
89
+ }
90
+ if (typeof options.extraAttributes === 'function') {
91
+ nextAttributes = Object.fromEntries(Object.entries(options.extraAttributes ? Object.assign({}, nextAttributes, options.extraAttributes(x)) : nextAttributes).filter(([_, v]) => v !== undefined && v !== null).map(([k, v]) => [k, typeof v === 'boolean' ? (v ? 'yes' : 'no') : `${v}`]));
92
+ }
93
+ else if (typeof options.extraAttributes === 'object') {
94
+ for (const key in options.extraAttributes) {
95
+ let resultForKey;
96
+ if (typeof options.extraAttributes[key] === 'function') {
97
+ resultForKey = options.extraAttributes[key](x);
98
+ }
99
+ else {
100
+ resultForKey = options.extraAttributes[key];
101
+ }
102
+ if (typeof resultForKey === 'boolean') {
103
+ nextAttributes[key] = resultForKey ? 'yes' : 'no';
104
+ }
105
+ else {
106
+ nextAttributes[key] = `${resultForKey}`;
107
+ }
108
+ }
109
+ nextAttributes = Object.assign(nextAttributes, {
110
+ source_system: options.sourceSystem,
111
+ target_system: options.targetSystem,
112
+ buildship_id: this.systemEnvName + '-' + this.workflowId + '-' + this.triggerId,
113
+ });
114
+ }
115
+ return topic.publishMessage({
116
+ data: Buffer.from(JSON.stringify(x)),
117
+ attributes: nextAttributes
118
+ });
119
+ }));
95
120
  for (const result of results) {
96
121
  if (result.status === 'fulfilled') {
97
122
  messageIds.push(result.value);