@shushed/helpers 0.0.222 → 0.0.223-redis-fix-20251222110722

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.
@@ -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.Order = exports.StockMovement = exports.MarketingPref = exports.CustomerSegment = exports.Asset = exports.DevelopmentColour = exports.Category = exports.ProductCategory = exports.Product = exports.ProductDraft = exports.Total = exports.Money = exports.Country = exports.Currency = void 0;
20
+ exports.Order = exports.MarketingPref = exports.CustomerSegment = exports.Asset = exports.DevelopmentColour = exports.Category = exports.ProductCategory = exports.Product = exports.ProductDraft = exports.Total = exports.Money = exports.Country = exports.Currency = void 0;
21
21
  var currency_schema_json_1 = require("./currency.schema.json");
22
22
  Object.defineProperty(exports, "Currency", { enumerable: true, get: function () { return __importDefault(currency_schema_json_1).default; } });
23
23
  var country_schema_json_1 = require("./country.schema.json");
@@ -42,8 +42,6 @@ var customer_segment_schema_json_1 = require("./customer-segment.schema.json");
42
42
  Object.defineProperty(exports, "CustomerSegment", { enumerable: true, get: function () { return __importDefault(customer_segment_schema_json_1).default; } });
43
43
  var marketing_preferences_schema_json_1 = require("./marketing-preferences.schema.json");
44
44
  Object.defineProperty(exports, "MarketingPref", { enumerable: true, get: function () { return __importDefault(marketing_preferences_schema_json_1).default; } });
45
- var stock_movement_schema_json_1 = require("./stock-movement.schema.json");
46
- Object.defineProperty(exports, "StockMovement", { enumerable: true, get: function () { return __importDefault(stock_movement_schema_json_1).default; } });
47
45
  __exportStar(require("./order"), exports);
48
46
  var order_schema_json_1 = require("./order.schema.json");
49
47
  Object.defineProperty(exports, "Order", { enumerable: true, get: function () { return __importDefault(order_schema_json_1).default; } });
@@ -36,7 +36,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
36
36
  return (mod && mod.__esModule) ? mod : { "default": mod };
37
37
  };
38
38
  Object.defineProperty(exports, "__esModule", { value: true });
39
- exports.Order = exports.Messages = exports.Total = exports.Stock = exports.StockMovement = exports.Product = exports.ProductDraft = exports.ProductCategory = exports.Price = exports.Money = exports.MarketingPreferences = exports.DevelopmentColour = exports.CustomerSegment = exports.Currency = exports.Country = exports.Category = exports.Asset = void 0;
39
+ exports.Order = exports.Messages = exports.Total = exports.Stock = exports.Product = exports.ProductDraft = exports.ProductCategory = exports.Price = exports.Money = exports.MarketingPreferences = exports.DevelopmentColour = exports.CustomerSegment = exports.Currency = exports.Country = exports.Category = exports.Asset = void 0;
40
40
  var asset_1 = require("./asset");
41
41
  Object.defineProperty(exports, "Asset", { enumerable: true, get: function () { return __importDefault(asset_1).default; } });
42
42
  var category_1 = require("./category");
@@ -61,8 +61,6 @@ var product_draft_1 = require("./product-draft");
61
61
  Object.defineProperty(exports, "ProductDraft", { enumerable: true, get: function () { return __importDefault(product_draft_1).default; } });
62
62
  var product_1 = require("./product");
63
63
  Object.defineProperty(exports, "Product", { enumerable: true, get: function () { return __importDefault(product_1).default; } });
64
- var stock_movement_1 = require("./stock-movement");
65
- Object.defineProperty(exports, "StockMovement", { enumerable: true, get: function () { return __importDefault(stock_movement_1).default; } });
66
64
  var stock_1 = require("./stock");
67
65
  Object.defineProperty(exports, "Stock", { enumerable: true, get: function () { return __importDefault(stock_1).default; } });
68
66
  var total_1 = require("./total");
@@ -42,107 +42,71 @@ class AirtableHelper extends runtime_1.default {
42
42
  }
43
43
  return existingRecord;
44
44
  }
45
- async updateMultiple(payload, options = {}) {
45
+ async updateMultiple(payload, options = {}, callIdx = 0, collectedResult = {
46
+ updatedRecords: [],
47
+ createdRecords: [],
48
+ records: []
49
+ }) {
46
50
  let response = null;
47
51
  const tableUrl = `https://api.airtable.com/v0/${this.baseId}/${this.tableId}`;
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;
74
- });
75
- try {
76
- response = await fetch(`${tableUrl}`, {
77
- method: "PATCH",
78
- headers: {
79
- Authorization: `Bearer ${this.apiKey}`,
80
- "Content-Type": "application/json",
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),
81
63
  },
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
- };
101
- }
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
- };
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
+ }),
81
+ });
82
+ if (!response.ok && response) {
83
+ const text = await response.text().catch(() => `${response?.status || 'unknown'}`);
84
+ throw new Error(text);
110
85
  }
111
- finally {
112
- callIdx += 1;
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);
113
94
  }
95
+ return nextCollectedResult;
114
96
  }
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`));
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);
139
107
  }
108
+ return nextCollectedResult;
140
109
  }
141
- return {
142
- updatedRecords: collectedResult.updatedRecords,
143
- createdRecords: collectedResult.createdRecords,
144
- records: resultInOrder
145
- };
146
110
  }
147
111
  async upsert(payload) {
148
112
  const existingRecord = await this.getExistingRecord(payload);
@@ -294,10 +258,9 @@ class AirtableHelper extends runtime_1.default {
294
258
  const escapeFormulaValue = (value) => {
295
259
  return value.replace(/"/g, '\\"');
296
260
  };
297
- const dedupedKeys = keys.filter((x, idx, self) => self.indexOf(x) === idx);
298
261
  const batchSize = 50;
299
- for (let i = 0; i < dedupedKeys.length; i += batchSize) {
300
- const batch = dedupedKeys.slice(i, i + batchSize);
262
+ for (let i = 0; i < keys.length; i += batchSize) {
263
+ const batch = keys.slice(i, i + batchSize);
301
264
  const orConditions = batch.map(key => `${this.dictionary[this.primaryKeyFieldName]} = "${escapeFormulaValue(key)}"`).join(', ');
302
265
  const formula = `OR(${orConditions})`;
303
266
  const records = await this.getExistingRecords(formula);
@@ -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 * 5;
9
+ const CACHE_EXPIRATION_MS = 1000 * 60 * 60 * 3;
10
10
  class CentraHelper extends env_1.default {
11
11
  opts;
12
12
  shaToken;
@@ -99,12 +99,6 @@ 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
- }
108
102
  getCacheKeyForSizeCharts() {
109
103
  return `centra_${this.shaToken}_size_chart_external_id`;
110
104
  }
@@ -136,7 +130,7 @@ class CentraHelper extends env_1.default {
136
130
  return `${this.getCacheKeyForCentraWarehouses()}_${warehouseExternalId}`;
137
131
  }
138
132
  getCacheKeyForCentraPricelists() {
139
- return `centra_${this.shaToken}_pricelists_external_id`;
133
+ return `centra_${this.shaToken}_pricelist_external_id`;
140
134
  }
141
135
  getCacheKeyForCentraPricelist(pricelistExternalId) {
142
136
  return `${this.getCacheKeyForCentraPricelists()}_${pricelistExternalId}`;
@@ -224,7 +218,7 @@ class CentraHelper extends env_1.default {
224
218
  return result;
225
219
  }
226
220
  async fetchCentraCampaigns(names) {
227
- const limit = names ? (names.length > 200 ? 200 : names.length) : 200;
221
+ const limit = (names?.length || 0) > 200 ? 200 : (names?.length || 0);
228
222
  let nextCursor = null;
229
223
  const result = {};
230
224
  do {
@@ -276,7 +270,7 @@ class CentraHelper extends env_1.default {
276
270
  nextCursor = null;
277
271
  }
278
272
  if (campaignConnection && campaignConnection.edges?.length) {
279
- for (let i = 0; i < campaignConnection.edges.length; i++) {
273
+ for (let i = campaignConnection.edges.length; i < (names?.length || 0); i++) {
280
274
  const { node } = campaignConnection.edges[i];
281
275
  result[node.name] = node;
282
276
  }
@@ -727,9 +721,7 @@ class CentraHelper extends env_1.default {
727
721
  }
728
722
  else {
729
723
  for (const warehouse of warehouses) {
730
- if (warehouse.externalId) {
731
- warehouseToSet[warehouse.externalId] = warehouse;
732
- }
724
+ warehouseToSet[warehouse.externalId] = warehouse;
733
725
  }
734
726
  await this.set(Object.entries(warehouseToSet).filter(([_, value]) => !(value instanceof Error)).map(([key, value]) => ({ name: this.getCacheKeyForCentraWarehouse(key), value: JSON.stringify(value) })), 'env', {
735
727
  ephemeralMs: CACHE_EXPIRATION_MS,
@@ -805,49 +797,6 @@ class CentraHelper extends env_1.default {
805
797
  }
806
798
  return Object.assign({}, pricelistInCache, pricelistToSet);
807
799
  }
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
- }
851
800
  async getCentraMarkets(alwaysFetch = false) {
852
801
  let marketInCache = {};
853
802
  let dedupedMarketNamesInCache = [];
@@ -155,12 +155,15 @@ class EnvEngine extends runtime_1.default {
155
155
  Object.assign(this.globalInMemoryRef[level], Object.fromEntries(Object.entries(objEncrypted).map(([k, v]) => [k, `${expirseAt}/${now}/${v}`])));
156
156
  }
157
157
  if (!options.ignoreStores?.includes('redis')) {
158
- (async () => {
158
+ try {
159
159
  const redisClient = await (0, redisClient_1.getConnectedRedisClient)(this.systemEnvName).catch(() => null);
160
160
  if (redisClient) {
161
- Promise.allSettled(Object.entries(objEncrypted).map(([k, v]) => redisClient.set(k, `${expirseAt}/${now}/${v}`, { PX: expirseAt - Date.now() })));
161
+ await Promise.allSettled(Object.entries(objEncrypted).map(([k, v]) => redisClient.set(k, `${expirseAt}/${now}/${v}`, { PX: expirseAt - Date.now() })));
162
162
  }
163
- })().catch(() => this.logging.error(`Failed to set the ${envs.map(x => x.name).join(', ')} in Redis`));
163
+ }
164
+ catch {
165
+ this.logging.error(`Failed to set the ${envs.map(x => x.name).join(', ')} in Redis`);
166
+ }
164
167
  }
165
168
  }
166
169
  else {
@@ -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.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;
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;
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,5 +52,3 @@ 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; } });
package/dist/package.json CHANGED
@@ -5,7 +5,6 @@
5
5
  "license": "UNLICENSED",
6
6
  "description": "",
7
7
  "dependencies": {
8
- "@google-cloud/firestore": "^7.11.1",
9
8
  "@hackylabs/deep-redact": "^2.2.1",
10
9
  "ajv": "^8.17.1",
11
10
  "ajv-formats": "^3.0.1",
@@ -13,15 +12,15 @@
13
12
  "jose": "^6.0.11",
14
13
  "lodash.chunk": "^4.2.0",
15
14
  "lodash.clonedeep": "^4.5.0",
16
- "lodash.groupby": "^4.6.0",
17
15
  "lodash.isequal": "^4.5.0",
18
16
  "lodash.omit": "^4.5.0",
19
17
  "lodash.pick": "^4.4.0",
20
18
  "mime-types": "^3.0.1",
21
19
  "p-limit": "^7.1.1",
22
20
  "rate-limiter-flexible": "^7.2.0",
21
+ "uuid": "^11.1.0",
23
22
  "redis": "^5.6.0",
24
- "uuid": "^11.1.0"
23
+ "@google-cloud/firestore": "^7.11.1"
25
24
  },
26
25
  "files": [
27
26
  "dist"
@@ -10,7 +10,6 @@ export { default as DevelopmentColour } from './development-colour.schema.json';
10
10
  export { default as Asset } from './asset.schema.json';
11
11
  export { default as CustomerSegment } from './customer-segment.schema.json';
12
12
  export { default as MarketingPref } from './marketing-preferences.schema.json';
13
- export { default as StockMovement } from './stock-movement.schema.json';
14
13
  export * from './order';
15
14
  export { default as Order } from './order.schema.json';
16
15
  export * from './messages';
@@ -10,7 +10,6 @@ export { default as Price } from './price';
10
10
  export { default as ProductCategory } from './product-category';
11
11
  export { default as ProductDraft } from './product-draft';
12
12
  export { default as Product } from './product';
13
- export { default as StockMovement } from './stock-movement';
14
13
  export { default as Stock } from './stock';
15
14
  export { default as Total } from './total';
16
15
  export * as Messages from './messages';
@@ -10,7 +10,6 @@ export { default as Price } from './price';
10
10
  export { default as ProductCategory } from './product-category';
11
11
  export { default as ProductDraft } from './product-draft';
12
12
  export { default as Product } from './product';
13
- export { default as StockMovement } from './stock-movement';
14
13
  export { default as Stock } from './stock';
15
14
  export { default as Total } from './total';
16
15
  export * as Messages from './messages';
@@ -45,6 +45,15 @@ declare class AirtableHelper<T extends Record<string, string>, K extends keyof T
45
45
  fieldsToMergeOn?: Array<keyof T>;
46
46
  primaryKeyWritable?: boolean;
47
47
  typecast?: boolean;
48
+ }, callIdx?: number, collectedResult?: {
49
+ updatedRecords: Array<string>;
50
+ createdRecords: Array<string>;
51
+ records: Array<{
52
+ id: string;
53
+ fields: {
54
+ [key in T[keyof T]]: any;
55
+ };
56
+ } | Error>;
48
57
  }): Promise<{
49
58
  updatedRecords: Array<string>;
50
59
  createdRecords: Array<string>;
@@ -140,8 +140,6 @@ export default class CentraHelper extends EnvEngine {
140
140
  getCacheKeyForCountryCode(countryCode: string): string;
141
141
  getCacheKeyForMarkets(): string;
142
142
  getCacheKeyForMarket(marketExternalId: string): string;
143
- getCacheKeyForCampaigns(): string;
144
- getCacheKeyForCampaign(campaignName: string): string;
145
143
  getCacheKeyForSizeCharts(): string;
146
144
  getCacheKeyForCountries(): string;
147
145
  getCacheKeyForCountry(countryIso2Code: string): string;
@@ -165,7 +163,6 @@ export default class CentraHelper extends EnvEngine {
165
163
  private fetchCentraVariants;
166
164
  getCentraWarehouses(externalIds?: string[] | null | undefined, alwaysFetch?: boolean): Promise<Error | Record<string, Error | BasicCentraWarehouse>>;
167
165
  getCentraPricelists(names?: string[] | null | undefined, alwaysFetch?: boolean): Promise<Error | Record<string, Error | BasicPricelist>>;
168
- getCentraCampaigns(alwaysFetch?: boolean): Promise<Error | Record<string, Error | BasicCentraMarket>>;
169
166
  getCentraMarkets(alwaysFetch?: boolean): Promise<Error | Record<string, Error | BasicCentraMarket>>;
170
167
  getCentraCountries(iso2Codes?: string[] | null | undefined, alwaysFetch?: boolean): Promise<Error | Record<string, Error | BasicCentraCountry>>;
171
168
  getCentraSizeCharts(externalIds?: string[] | null | undefined, alwaysFetch?: boolean): Promise<Error | Record<string, Error | BasicCentraSizeChart>>;
@@ -17,4 +17,3 @@ export { default as setHeaders } from './setHeaders';
17
17
  export { type CentraError, type CentraErrors, type BasicCentraCountry, type BasicCentraMarket, type BasicCentraSizeChart, type BasicPricelist, type BasicCentraWarehouse, type BasicCentraCampaign, type BasicCentraProduct, type BasicCentraVariant, type BasicCentraDisplay } from './centra';
18
18
  export { default as RateLimit, RedisConnectionError } from './rateLimit';
19
19
  export { type TriggerOnCreateOptions, type TriggerOnExecuteOptions, type NodeOptions, type RNConfiguration, type TriggerExtraOptions } from './types';
20
- export { default as SitooHelper, type UnitCost, type StockMovementInput, type BasicSitooWarehouse, type BasicSitooStore, type BasicWarehouseTransaction, type WarehouseTransactionEnvelope, type WarehouseTransactionSyncOptions } from './sitoo';
package/package.json CHANGED
@@ -1,11 +1,10 @@
1
1
  {
2
2
  "name": "@shushed/helpers",
3
- "version": "0.0.222",
3
+ "version": "0.0.223-redis-fix-20251222110722",
4
4
  "author": "",
5
5
  "license": "UNLICENSED",
6
6
  "description": "",
7
7
  "dependencies": {
8
- "@google-cloud/firestore": "^7.11.1",
9
8
  "@hackylabs/deep-redact": "^2.2.1",
10
9
  "ajv": "^8.17.1",
11
10
  "ajv-formats": "^3.0.1",
@@ -13,15 +12,15 @@
13
12
  "jose": "^6.0.11",
14
13
  "lodash.chunk": "^4.2.0",
15
14
  "lodash.clonedeep": "^4.5.0",
16
- "lodash.groupby": "^4.6.0",
17
15
  "lodash.isequal": "^4.5.0",
18
16
  "lodash.omit": "^4.5.0",
19
17
  "lodash.pick": "^4.4.0",
20
18
  "mime-types": "^3.0.1",
21
19
  "p-limit": "^7.1.1",
22
20
  "rate-limiter-flexible": "^7.2.0",
21
+ "uuid": "^11.1.0",
23
22
  "redis": "^5.6.0",
24
- "uuid": "^11.1.0"
23
+ "@google-cloud/firestore": "^7.11.1"
25
24
  },
26
25
  "files": [
27
26
  "dist"
@@ -1,144 +0,0 @@
1
- {
2
- "$schema": "http://json-schema.org/draft-07/schema#",
3
- "$id": "https://shushed.example.com/stock-move.schema.json",
4
- "title": "Stock Movement Schema",
5
- "type": "object",
6
- "additionalProperties": false,
7
- "properties": {
8
- "style_id": {
9
- "type": "string",
10
- "minLength": 1,
11
- "faker": {
12
- "custom.style": []
13
- },
14
- "description": "Style Code - Example: 000697"
15
- },
16
- "colour_id": {
17
- "type": "string",
18
- "minLength": 1,
19
- "faker": {
20
- "custom.color": []
21
- },
22
- "description": "Colour Code - Example: 3203"
23
- },
24
- "size_code": {
25
- "type": "string",
26
- "minLength": 1,
27
- "description": "Variant code, e.g., XS, M, XL, ONE, or numeric like 10, 37, 40"
28
- },
29
- "location_code": {
30
- "type": "string",
31
- "pattern": "^\\d{3}-[A-Z]+$|^[A-Z]{2,}-?[A-Z]*$|^[A-Z]{2,}\\d{3}$",
32
- "description": "Valid location code like 001-DR, JL200, HEADOFFICE"
33
- },
34
- "source_system": {
35
- "type": "string",
36
- "enum": [
37
- "bc",
38
- "centra",
39
- "sitoo"
40
- ]
41
- },
42
- "quantity": {
43
- "type": "integer",
44
- "minimum": 0,
45
- "description": "Quantity of stock available on hand"
46
- },
47
- "description": {
48
- "type": "string"
49
- },
50
- "entry_no": {
51
- "type": "string",
52
- "description": "BC Entry no. Should be empty for other systems"
53
- },
54
- "external_entry_no": {
55
- "type": "string",
56
- "description": "External Entry no. Might be empty for other systems"
57
- },
58
- "document_no": {
59
- "type": "string",
60
- "description": "BC Document no. Should be empty for other systems"
61
- },
62
- "external_document_no": {
63
- "type": "string",
64
- "description": "Original document i.e. Sitoo ID that caused the change"
65
- },
66
- "type": {
67
- "type": "string",
68
- "enum": [
69
- "transfer",
70
- "negative adjustment",
71
- "positive adjustment",
72
- "purchase",
73
- "sale"
74
- ]
75
- },
76
- "unit_cost": {
77
- "oneOf": [
78
- {
79
- "$ref": "https://shushed.example.com/money.schema.json"
80
- },
81
- {
82
- "type": "null"
83
- }
84
- ],
85
- "description": "Cost per unit, can be null"
86
- },
87
- "document_date": {
88
- "type": "string",
89
- "format": "date",
90
- "description": "Date of the creation of the document. Preferably creation time of the item ledger entry",
91
- "faker": {
92
- "date.between": [
93
- "2024-01-01",
94
- "2025-04-30"
95
- ]
96
- }
97
- },
98
- "posting_date": {
99
- "type": "string",
100
- "format": "date",
101
- "description": "Date of posting the record in BC. This should be empty for non-BC systems. Preferably creation time of the item ledger entry",
102
- "faker": {
103
- "date.between": [
104
- "2024-01-01",
105
- "2025-04-30"
106
- ]
107
- }
108
- },
109
- "created_at": {
110
- "type": "string",
111
- "format": "date-time",
112
- "description": "Date time of the creation of the record. Preferably creation time of the item ledger entry",
113
- "faker": {
114
- "date.between": [
115
- "2024-01-01T00:00:00.000Z",
116
- "2025-04-30T00:00:00.000Z"
117
- ]
118
- }
119
- },
120
- "last_modified_at": {
121
- "type": "string",
122
- "format": "date-time",
123
- "description": "Datetime of the last created_at in the history",
124
- "faker": {
125
- "date.between": [
126
- "2024-01-01T00:00:00.000Z",
127
- "2025-04-30T00:00:00.000Z"
128
- ]
129
- }
130
- }
131
- },
132
- "required": [
133
- "style_id",
134
- "source_system",
135
- "location_code",
136
- "colour_id",
137
- "size_code",
138
- "last_modified_at",
139
- "quantity",
140
- "created_at",
141
- "document_date",
142
- "type"
143
- ]
144
- }
@@ -1,4 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- const schema = { "$schema": "http://json-schema.org/draft-07/schema#", "title": "Stock Movement Schema", "type": "object", "additionalProperties": false, "properties": { "style_id": { "type": "string", "minLength": 1 }, "colour_id": { "type": "string", "minLength": 1 }, "size_code": { "type": "string", "minLength": 1 }, "location_code": { "type": "string", "pattern": "^\\d{3}-[A-Z]+$|^[A-Z]{2,}-?[A-Z]*$|^[A-Z]{2,}\\d{3}$" }, "source_system": { "type": "string", "enum": ["bc", "centra", "sitoo"] }, "quantity": { "type": "integer", "minimum": 0 }, "entry_no": { "type": "string" }, "external_entry_no": { "type": "string" }, "document_no": { "type": "string" }, "external_document_no": { "type": "string" }, "type": { "type": "string", "enum": ["transfer", "negative adjustment", "positive adjustment", "purchase", "sale"] }, "unit_cost": { "oneOf": [{ "$schema": "http://json-schema.org/draft-07/schema#", "title": "Money", "type": "object", "additionalProperties": false, "properties": { "value": { "type": "integer", "minimum": 0 }, "decimal_places": { "type": "integer", "minimum": 0, "maximum": 4 }, "currency": { "title": "Currency", "$schema": "http://json-schema.org/draft-07/schema#", "type": "string", "enum": ["AFN", "ALL", "DZD", "USD", "EUR", "AOA", "XCD", "ARS", "AMD", "AWG", "AUD", "AZN", "BSD", "BHD", "BDT", "BBD", "BYN", "BZD", "XOF", "BMD", "BTN", "INR", "BOB", "BOV", "BAM", "BWP", "NOK", "BRL", "BND", "BGN", "BIF", "CVE", "KHR", "XAF", "CAD", "KYD", "CLF", "CLP", "CNY", "COP", "COU", "KMF", "CDF", "NZD", "CRC", "CUC", "CUP", "ANG", "CZK", "DKK", "DJF", "DOP", "EGP", "SVC", "ERN", "ETB", "FKP", "FJD", "XPF", "GMD", "GEL", "GHS", "GIP", "GTQ", "GBP", "GNF", "GYD", "HTG", "HNL", "HKD", "HUF", "ISK", "IDR", "XDR", "IRR", "IQD", "ILS", "JMD", "JPY", "JOD", "KZT", "KES", "KPW", "KRW", "KWD", "KGS", "LAK", "LBP", "LSL", "ZAR", "LRD", "LYD", "CHF", "MOP", "MGA", "MWK", "MYR", "MVR", "MRU", "MUR", "XUA", "MXN", "MXV", "MDL", "MNT", "MAD", "MZN", "MMK", "NAD", "NPR", "NIO", "NGN", "OMR", "PKR", "PAB", "PGK", "PYG", "PEN", "PHP", "PLN", "QAR", "MKD", "RON", "RUB", "RWF", "SHP", "WST", "STN", "SAR", "RSD", "SCR", "SLE", "SGD", "XSU", "SBD", "SOS", "SSP", "LKR", "SDG", "SRD", "SZL", "SEK", "CHE", "CHW", "SYP", "TWD", "TJS", "TZS", "THB", "TOP", "TTD", "TND", "TRY", "TMT", "UGX", "UAH", "AED", "USN", "UYI", "UYU", "UZS", "VUV", "VEF", "VED", "VND", "YER", "ZMW", "ZWL"] }, "lcy_value": { "type": "integer", "minimum": 0 }, "lcy_currency": { "title": "LCY Currency", "$schema": "http://json-schema.org/draft-07/schema#", "type": "string", "enum": ["AFN", "ALL", "DZD", "USD", "EUR", "AOA", "XCD", "ARS", "AMD", "AWG", "AUD", "AZN", "BSD", "BHD", "BDT", "BBD", "BYN", "BZD", "XOF", "BMD", "BTN", "INR", "BOB", "BOV", "BAM", "BWP", "NOK", "BRL", "BND", "BGN", "BIF", "CVE", "KHR", "XAF", "CAD", "KYD", "CLF", "CLP", "CNY", "COP", "COU", "KMF", "CDF", "NZD", "CRC", "CUC", "CUP", "ANG", "CZK", "DKK", "DJF", "DOP", "EGP", "SVC", "ERN", "ETB", "FKP", "FJD", "XPF", "GMD", "GEL", "GHS", "GIP", "GTQ", "GBP", "GNF", "GYD", "HTG", "HNL", "HKD", "HUF", "ISK", "IDR", "XDR", "IRR", "IQD", "ILS", "JMD", "JPY", "JOD", "KZT", "KES", "KPW", "KRW", "KWD", "KGS", "LAK", "LBP", "LSL", "ZAR", "LRD", "LYD", "CHF", "MOP", "MGA", "MWK", "MYR", "MVR", "MRU", "MUR", "XUA", "MXN", "MXV", "MDL", "MNT", "MAD", "MZN", "MMK", "NAD", "NPR", "NIO", "NGN", "OMR", "PKR", "PAB", "PGK", "PYG", "PEN", "PHP", "PLN", "QAR", "MKD", "RON", "RUB", "RWF", "SHP", "WST", "STN", "SAR", "RSD", "SCR", "SLE", "SGD", "XSU", "SBD", "SOS", "SSP", "LKR", "SDG", "SRD", "SZL", "SEK", "CHE", "CHW", "SYP", "TWD", "TJS", "TZS", "THB", "TOP", "TTD", "TND", "TRY", "TMT", "UGX", "UAH", "AED", "USN", "UYI", "UYU", "UZS", "VUV", "VEF", "VED", "VND", "YER", "ZMW", "ZWL"] }, "lcy_decimal_places": { "type": "integer", "minimum": 0, "maximum": 4 } }, "required": ["value", "currency", "lcy_value", "lcy_currency", "decimal_places", "lcy_decimal_places"] }, { "type": "null" }] }, "document_date": { "type": "string", "format": "date" }, "posting_date": { "type": "string", "format": "date" }, "created_at": { "type": "string", "format": "date-time" }, "last_modified_at": { "type": "string", "format": "date-time" } }, "required": ["style_id", "source_system", "location_code", "colour_id", "size_code", "last_modified_at", "quantity", "created_at", "document_date", "type"], "$id": "https://shushed.example.com/stock-move.schema.json" };
4
- exports.default = schema;
@@ -1,2 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
@@ -1,279 +0,0 @@
1
- "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- const crypto_1 = __importDefault(require("crypto"));
7
- const lodash_groupby_1 = __importDefault(require("lodash.groupby"));
8
- const env_1 = __importDefault(require("./env"));
9
- const SITOO_TR_TYPE_MANUAL_IN = 10;
10
- const SITOO_TR_TYPE_MANUAL_OUT = 20;
11
- const BATCH_SIZE = 900;
12
- class SitooHelper extends env_1.default {
13
- opts;
14
- shaToken;
15
- baseUrl;
16
- siteId;
17
- constructor(options, opts) {
18
- super(options);
19
- this.opts = opts;
20
- this.shaToken = crypto_1.default.createHash('sha256').update(opts.accessToken).digest('hex').slice(0, 8);
21
- this.baseUrl = opts.sitooBaseUrl.replace(/\/$/, '');
22
- this.siteId = opts.sitooSiteId;
23
- }
24
- async getSitooWarehouses() {
25
- const url = `${this.baseUrl}/sites/${this.siteId}/warehouses.json`;
26
- const response = await fetch(url, {
27
- method: 'GET',
28
- headers: {
29
- 'Authorization': `Basic ${this.opts.accessToken}`,
30
- 'Content-Type': 'application/json',
31
- },
32
- });
33
- if (!response.ok) {
34
- const errorText = await response.text().catch(() => 'Unknown error');
35
- throw new Error(`Sitoo API error: ${response.status} ${response.statusText} - ${errorText}`);
36
- }
37
- const data = await response.json();
38
- const nameToId = {};
39
- const warehouses = {};
40
- for (const warehouse of data.items) {
41
- nameToId[warehouse.name] = warehouse.warehouseid;
42
- warehouses[warehouse.warehouseid] = warehouse;
43
- }
44
- return { nameToId, warehouses };
45
- }
46
- async fetchWarehouseTransactionsWithFilter(params) {
47
- const queryParams = new URLSearchParams();
48
- queryParams.set('num', (params.num || 100).toString());
49
- if (params.transactiontype !== undefined) {
50
- queryParams.set('transactiontype', params.transactiontype.toString());
51
- }
52
- if (params.datecreatedfrom !== undefined) {
53
- queryParams.set('datecreatedfrom', params.datecreatedfrom.toString());
54
- }
55
- if (params.start !== undefined) {
56
- queryParams.set('start', params.start.toString());
57
- }
58
- queryParams.set('sort', params.ascending ? 'warehousetransactionid' : '-warehousetransactionid');
59
- const url = `${this.baseUrl}/sites/${this.siteId}/warehouses/${params.warehouseid}/warehousetransactions.json?${queryParams.toString()}`;
60
- const response = await fetch(url, {
61
- method: 'GET',
62
- headers: {
63
- 'Authorization': `Basic ${this.opts.accessToken}`,
64
- 'Content-Type': 'application/json',
65
- },
66
- });
67
- if (!response.ok) {
68
- const errorText = await response.text().catch(() => 'Unknown error');
69
- throw new Error(`Sitoo API error: ${response.status} ${response.statusText} - ${errorText}`);
70
- }
71
- return await response.json();
72
- }
73
- async searchTransactionsInApi(warehouseId, entryNosToFind, transactionType, datecreatedfrom) {
74
- const found = new Map();
75
- let lowestTransactionId = undefined;
76
- if (entryNosToFind.size === 0) {
77
- return { found, lowestTransactionId };
78
- }
79
- let hasMore = true;
80
- const pageSize = 100;
81
- const currentDateFrom = datecreatedfrom;
82
- let currentStart = 0;
83
- while (hasMore && found.size < entryNosToFind.size) {
84
- const envelope = await this.fetchWarehouseTransactionsWithFilter({
85
- warehouseid: warehouseId,
86
- datecreatedfrom: currentDateFrom,
87
- transactiontype: transactionType,
88
- ascending: false,
89
- num: pageSize,
90
- start: currentStart,
91
- });
92
- const transactions = envelope.items;
93
- if (transactions.length === 0) {
94
- hasMore = false;
95
- break;
96
- }
97
- for (const transaction of transactions) {
98
- if (lowestTransactionId === undefined || transaction.warehousetransactionid < lowestTransactionId) {
99
- lowestTransactionId = transaction.warehousetransactionid;
100
- }
101
- if (transaction.description) {
102
- for (const entryNo of entryNosToFind) {
103
- if (!found.has(entryNo) && transaction.description.includes(`'${entryNo}'`)) {
104
- found.set(entryNo, transaction);
105
- }
106
- }
107
- }
108
- if (found.size === entryNosToFind.size) {
109
- break;
110
- }
111
- }
112
- currentStart = currentStart ? currentStart + transactions.length : transactions.length;
113
- if (transactions.length < pageSize) {
114
- hasMore = false;
115
- }
116
- }
117
- return { found, lowestTransactionId };
118
- }
119
- async batchAddWarehouseTransactions(transactions) {
120
- if (transactions.length === 0)
121
- return [];
122
- if (transactions.length > BATCH_SIZE) {
123
- throw new Error(`Batch size exceeds maximum of ${BATCH_SIZE}. Got ${transactions.length}`);
124
- }
125
- const url = `${this.baseUrl}/sites/${this.siteId}/warehousetransactions.json`;
126
- const response = await fetch(url, {
127
- method: 'POST',
128
- headers: {
129
- 'Authorization': `Basic ${this.opts.accessToken}`,
130
- 'Content-Type': 'application/json',
131
- },
132
- body: JSON.stringify(transactions),
133
- });
134
- if (!response.ok) {
135
- const errorText = await response.text().catch(() => 'Unknown error');
136
- throw new Error(`Sitoo API error: ${response.status} ${response.statusText} - ${errorText}`);
137
- }
138
- const result = await response.json();
139
- return result;
140
- }
141
- async syncWarehouseTransactions(items, options) {
142
- if (items.length === 0) {
143
- return [];
144
- }
145
- const locationCodeToWarehouse = (await this.getSitooWarehouses()).nameToId;
146
- const foundEntryNos = new Map();
147
- const results = {};
148
- const entryNoToIdx = {};
149
- const entriesByWarehouseAndTransactionType = new Map();
150
- for (let i = 0; i < items.length; i++) {
151
- const entry = items[i];
152
- const warehouseId = locationCodeToWarehouse[entry.location_code];
153
- const transactionType = entry.type === 'transfer' ? (entry.quantity > 0 ? SITOO_TR_TYPE_MANUAL_IN : SITOO_TR_TYPE_MANUAL_OUT) : undefined;
154
- entryNoToIdx[entry.entry_no] = i;
155
- if (transactionType === undefined) {
156
- results[i] = {
157
- error: false,
158
- message: `Not supported transaction type: ${entry.type}`,
159
- };
160
- continue;
161
- }
162
- if (warehouseId === undefined) {
163
- results[i] = {
164
- error: false,
165
- message: `Not supported warehouse in Sitoo: ${entry.location_code}`,
166
- };
167
- continue;
168
- }
169
- const key = `${warehouseId}-${transactionType}`;
170
- if (!entriesByWarehouseAndTransactionType.has(key)) {
171
- entriesByWarehouseAndTransactionType.set(key, []);
172
- }
173
- entriesByWarehouseAndTransactionType.get(key).push(entry);
174
- }
175
- for (const [_, warehouseEntries] of entriesByWarehouseAndTransactionType) {
176
- if (warehouseEntries.length > 0) {
177
- let datecreatedfrom = undefined;
178
- for (const entry of warehouseEntries) {
179
- const entryCreatedAt = Math.floor(new Date(entry.created_at).getTime() / 1000);
180
- if (datecreatedfrom === undefined || entryCreatedAt < datecreatedfrom) {
181
- datecreatedfrom = entryCreatedAt;
182
- }
183
- }
184
- const { found: apiFound } = await this.searchTransactionsInApi(locationCodeToWarehouse[warehouseEntries[0].location_code], new Set(warehouseEntries.map(e => e.entry_no.toString())), warehouseEntries[0].quantity > 0 ? SITOO_TR_TYPE_MANUAL_IN : SITOO_TR_TYPE_MANUAL_OUT, datecreatedfrom);
185
- for (const [entryNo, transaction] of apiFound) {
186
- foundEntryNos.set(entryNo, transaction.warehousetransactionid);
187
- }
188
- }
189
- }
190
- for (const [entryNo, transactionId] of foundEntryNos) {
191
- const idx = entryNoToIdx[entryNo];
192
- results[idx] = {
193
- error: false,
194
- message: `Found transaction in Sitoo: ${transactionId} for the entry ${entryNo}`,
195
- };
196
- }
197
- const batches = [[]];
198
- for (const [_, warehouseEntries] of entriesByWarehouseAndTransactionType) {
199
- for (let i = 0; i < warehouseEntries.length; i += 1) {
200
- if (!foundEntryNos.has(warehouseEntries[i].entry_no)) {
201
- if ((batches[batches.length - 1].length + 1) >= BATCH_SIZE) {
202
- batches.push([]);
203
- }
204
- batches[batches.length - 1].push(warehouseEntries[i]);
205
- }
206
- }
207
- }
208
- const hasMissingEntries = batches[0].length > 0;
209
- if (!options?.preview && hasMissingEntries) {
210
- for (let i = 0; i < batches.length; i++) {
211
- const warehouseEntities = (0, lodash_groupby_1.default)(batches[i], e => `${locationCodeToWarehouse[e.location_code]}-${e.quantity > 0 ? 'positive' : 'negative'}`);
212
- const entryNosInBatches = {};
213
- const transactionsToCreate = [];
214
- for (const k in warehouseEntities) {
215
- const entriesToCreate = warehouseEntities[k];
216
- const warehouseId = locationCodeToWarehouse[entriesToCreate[0].location_code];
217
- const entryType = entriesToCreate[0].quantity > 0 ? SITOO_TR_TYPE_MANUAL_IN : SITOO_TR_TYPE_MANUAL_OUT;
218
- entryNosInBatches[transactionsToCreate.length] = entriesToCreate.map(e => e.entry_no);
219
- transactionsToCreate.push({
220
- warehouseid: warehouseId,
221
- transactiontype: entryType,
222
- description: entriesToCreate.map(e => `'${e.entry_no}'`).join(', '),
223
- items: entriesToCreate.map(e => ({
224
- sku: [e.style_id, e.colour_id, e.size_code].filter(Boolean).join('-'),
225
- decimalquantity: `${e.quantity.toFixed(3)}`,
226
- moneypricein: (((e.unit_cost?.value ?? 0) / 100) * e.quantity).toFixed(2),
227
- })),
228
- });
229
- }
230
- try {
231
- const createdIds = await this.batchAddWarehouseTransactions(transactionsToCreate);
232
- for (let j = 0; j < transactionsToCreate.length; j++) {
233
- const transactionId = createdIds[j];
234
- for (let k = 0; k < entryNosInBatches[j].length; k += 1) {
235
- const entryNo = entryNosInBatches[j][k];
236
- const idx = entryNoToIdx[entryNo];
237
- results[idx] = {
238
- entryNo: entryNo,
239
- transactionId: transactionId,
240
- };
241
- }
242
- }
243
- }
244
- catch (error) {
245
- for (const entry of batches[i]) {
246
- const idx = entryNoToIdx[entry.entry_no];
247
- results[idx] = error;
248
- }
249
- }
250
- }
251
- }
252
- else {
253
- for (let i = 0; i < batches.length; i++) {
254
- for (let j = 0; j < batches[i].length; j += 1) {
255
- const entry = batches[i][j];
256
- const idx = entryNoToIdx[entry.entry_no];
257
- const warehouseId = locationCodeToWarehouse[entry.location_code];
258
- const transactionType = entry.quantity > 0 ? SITOO_TR_TYPE_MANUAL_IN : SITOO_TR_TYPE_MANUAL_OUT;
259
- results[idx] = {
260
- error: false,
261
- message: `In Preview Mode. ${entry.entry_no} scheduled to be created as transction type: ${transactionType} in the warehouse: ${warehouseId}. Batch: ${i}`
262
- };
263
- }
264
- }
265
- }
266
- const resultsAsArray = [];
267
- for (let i = 0; i < items.length; i += 1) {
268
- const result = results[i];
269
- if (!result) {
270
- resultsAsArray.push(new Error(`${items[i].entry_no} got missed in processing`));
271
- }
272
- else {
273
- resultsAsArray.push(result);
274
- }
275
- }
276
- return resultsAsArray;
277
- }
278
- }
279
- exports.default = SitooHelper;
@@ -1,110 +0,0 @@
1
- declare const schema: {
2
- readonly $schema: "http://json-schema.org/draft-07/schema#";
3
- readonly title: "Stock Movement Schema";
4
- readonly type: "object";
5
- readonly additionalProperties: false;
6
- readonly properties: {
7
- readonly style_id: {
8
- readonly type: "string";
9
- readonly minLength: 1;
10
- };
11
- readonly colour_id: {
12
- readonly type: "string";
13
- readonly minLength: 1;
14
- };
15
- readonly size_code: {
16
- readonly type: "string";
17
- readonly minLength: 1;
18
- };
19
- readonly location_code: {
20
- readonly type: "string";
21
- readonly pattern: "^\\d{3}-[A-Z]+$|^[A-Z]{2,}-?[A-Z]*$|^[A-Z]{2,}\\d{3}$";
22
- };
23
- readonly source_system: {
24
- readonly type: "string";
25
- readonly enum: readonly ["bc", "centra", "sitoo"];
26
- };
27
- readonly quantity: {
28
- readonly type: "integer";
29
- readonly minimum: 0;
30
- };
31
- readonly entry_no: {
32
- readonly type: "string";
33
- };
34
- readonly external_entry_no: {
35
- readonly type: "string";
36
- };
37
- readonly document_no: {
38
- readonly type: "string";
39
- };
40
- readonly external_document_no: {
41
- readonly type: "string";
42
- };
43
- readonly type: {
44
- readonly type: "string";
45
- readonly enum: readonly ["transfer", "negative adjustment", "positive adjustment", "purchase", "sale"];
46
- };
47
- readonly unit_cost: {
48
- readonly oneOf: readonly [{
49
- readonly $schema: "http://json-schema.org/draft-07/schema#";
50
- readonly title: "Money";
51
- readonly type: "object";
52
- readonly additionalProperties: false;
53
- readonly properties: {
54
- readonly value: {
55
- readonly type: "integer";
56
- readonly minimum: 0;
57
- };
58
- readonly decimal_places: {
59
- readonly type: "integer";
60
- readonly minimum: 0;
61
- readonly maximum: 4;
62
- };
63
- readonly currency: {
64
- readonly title: "Currency";
65
- readonly $schema: "http://json-schema.org/draft-07/schema#";
66
- readonly type: "string";
67
- readonly enum: readonly ["AFN", "ALL", "DZD", "USD", "EUR", "AOA", "XCD", "ARS", "AMD", "AWG", "AUD", "AZN", "BSD", "BHD", "BDT", "BBD", "BYN", "BZD", "XOF", "BMD", "BTN", "INR", "BOB", "BOV", "BAM", "BWP", "NOK", "BRL", "BND", "BGN", "BIF", "CVE", "KHR", "XAF", "CAD", "KYD", "CLF", "CLP", "CNY", "COP", "COU", "KMF", "CDF", "NZD", "CRC", "CUC", "CUP", "ANG", "CZK", "DKK", "DJF", "DOP", "EGP", "SVC", "ERN", "ETB", "FKP", "FJD", "XPF", "GMD", "GEL", "GHS", "GIP", "GTQ", "GBP", "GNF", "GYD", "HTG", "HNL", "HKD", "HUF", "ISK", "IDR", "XDR", "IRR", "IQD", "ILS", "JMD", "JPY", "JOD", "KZT", "KES", "KPW", "KRW", "KWD", "KGS", "LAK", "LBP", "LSL", "ZAR", "LRD", "LYD", "CHF", "MOP", "MGA", "MWK", "MYR", "MVR", "MRU", "MUR", "XUA", "MXN", "MXV", "MDL", "MNT", "MAD", "MZN", "MMK", "NAD", "NPR", "NIO", "NGN", "OMR", "PKR", "PAB", "PGK", "PYG", "PEN", "PHP", "PLN", "QAR", "MKD", "RON", "RUB", "RWF", "SHP", "WST", "STN", "SAR", "RSD", "SCR", "SLE", "SGD", "XSU", "SBD", "SOS", "SSP", "LKR", "SDG", "SRD", "SZL", "SEK", "CHE", "CHW", "SYP", "TWD", "TJS", "TZS", "THB", "TOP", "TTD", "TND", "TRY", "TMT", "UGX", "UAH", "AED", "USN", "UYI", "UYU", "UZS", "VUV", "VEF", "VED", "VND", "YER", "ZMW", "ZWL"];
68
- };
69
- readonly lcy_value: {
70
- readonly type: "integer";
71
- readonly minimum: 0;
72
- };
73
- readonly lcy_currency: {
74
- readonly title: "LCY Currency";
75
- readonly $schema: "http://json-schema.org/draft-07/schema#";
76
- readonly type: "string";
77
- readonly enum: readonly ["AFN", "ALL", "DZD", "USD", "EUR", "AOA", "XCD", "ARS", "AMD", "AWG", "AUD", "AZN", "BSD", "BHD", "BDT", "BBD", "BYN", "BZD", "XOF", "BMD", "BTN", "INR", "BOB", "BOV", "BAM", "BWP", "NOK", "BRL", "BND", "BGN", "BIF", "CVE", "KHR", "XAF", "CAD", "KYD", "CLF", "CLP", "CNY", "COP", "COU", "KMF", "CDF", "NZD", "CRC", "CUC", "CUP", "ANG", "CZK", "DKK", "DJF", "DOP", "EGP", "SVC", "ERN", "ETB", "FKP", "FJD", "XPF", "GMD", "GEL", "GHS", "GIP", "GTQ", "GBP", "GNF", "GYD", "HTG", "HNL", "HKD", "HUF", "ISK", "IDR", "XDR", "IRR", "IQD", "ILS", "JMD", "JPY", "JOD", "KZT", "KES", "KPW", "KRW", "KWD", "KGS", "LAK", "LBP", "LSL", "ZAR", "LRD", "LYD", "CHF", "MOP", "MGA", "MWK", "MYR", "MVR", "MRU", "MUR", "XUA", "MXN", "MXV", "MDL", "MNT", "MAD", "MZN", "MMK", "NAD", "NPR", "NIO", "NGN", "OMR", "PKR", "PAB", "PGK", "PYG", "PEN", "PHP", "PLN", "QAR", "MKD", "RON", "RUB", "RWF", "SHP", "WST", "STN", "SAR", "RSD", "SCR", "SLE", "SGD", "XSU", "SBD", "SOS", "SSP", "LKR", "SDG", "SRD", "SZL", "SEK", "CHE", "CHW", "SYP", "TWD", "TJS", "TZS", "THB", "TOP", "TTD", "TND", "TRY", "TMT", "UGX", "UAH", "AED", "USN", "UYI", "UYU", "UZS", "VUV", "VEF", "VED", "VND", "YER", "ZMW", "ZWL"];
78
- };
79
- readonly lcy_decimal_places: {
80
- readonly type: "integer";
81
- readonly minimum: 0;
82
- readonly maximum: 4;
83
- };
84
- };
85
- readonly required: readonly ["value", "currency", "lcy_value", "lcy_currency", "decimal_places", "lcy_decimal_places"];
86
- }, {
87
- readonly type: "null";
88
- }];
89
- };
90
- readonly document_date: {
91
- readonly type: "string";
92
- readonly format: "date";
93
- };
94
- readonly posting_date: {
95
- readonly type: "string";
96
- readonly format: "date";
97
- };
98
- readonly created_at: {
99
- readonly type: "string";
100
- readonly format: "date-time";
101
- };
102
- readonly last_modified_at: {
103
- readonly type: "string";
104
- readonly format: "date-time";
105
- };
106
- };
107
- readonly required: readonly ["style_id", "source_system", "location_code", "colour_id", "size_code", "last_modified_at", "quantity", "created_at", "document_date", "type"];
108
- readonly $id: "https://shushed.example.com/stock-move.schema.json";
109
- };
110
- export default schema;
@@ -1,30 +0,0 @@
1
- export type Currency = ("AFN" | "ALL" | "DZD" | "USD" | "EUR" | "AOA" | "XCD" | "ARS" | "AMD" | "AWG" | "AUD" | "AZN" | "BSD" | "BHD" | "BDT" | "BBD" | "BYN" | "BZD" | "XOF" | "BMD" | "BTN" | "INR" | "BOB" | "BOV" | "BAM" | "BWP" | "NOK" | "BRL" | "BND" | "BGN" | "BIF" | "CVE" | "KHR" | "XAF" | "CAD" | "KYD" | "CLF" | "CLP" | "CNY" | "COP" | "COU" | "KMF" | "CDF" | "NZD" | "CRC" | "CUC" | "CUP" | "ANG" | "CZK" | "DKK" | "DJF" | "DOP" | "EGP" | "SVC" | "ERN" | "ETB" | "FKP" | "FJD" | "XPF" | "GMD" | "GEL" | "GHS" | "GIP" | "GTQ" | "GBP" | "GNF" | "GYD" | "HTG" | "HNL" | "HKD" | "HUF" | "ISK" | "IDR" | "XDR" | "IRR" | "IQD" | "ILS" | "JMD" | "JPY" | "JOD" | "KZT" | "KES" | "KPW" | "KRW" | "KWD" | "KGS" | "LAK" | "LBP" | "LSL" | "ZAR" | "LRD" | "LYD" | "CHF" | "MOP" | "MGA" | "MWK" | "MYR" | "MVR" | "MRU" | "MUR" | "XUA" | "MXN" | "MXV" | "MDL" | "MNT" | "MAD" | "MZN" | "MMK" | "NAD" | "NPR" | "NIO" | "NGN" | "OMR" | "PKR" | "PAB" | "PGK" | "PYG" | "PEN" | "PHP" | "PLN" | "QAR" | "MKD" | "RON" | "RUB" | "RWF" | "SHP" | "WST" | "STN" | "SAR" | "RSD" | "SCR" | "SLE" | "SGD" | "XSU" | "SBD" | "SOS" | "SSP" | "LKR" | "SDG" | "SRD" | "SZL" | "SEK" | "CHE" | "CHW" | "SYP" | "TWD" | "TJS" | "TZS" | "THB" | "TOP" | "TTD" | "TND" | "TRY" | "TMT" | "UGX" | "UAH" | "AED" | "USN" | "UYI" | "UYU" | "UZS" | "VUV" | "VEF" | "VED" | "VND" | "YER" | "ZMW" | "ZWL");
2
- export type LCYCurrency = ("AFN" | "ALL" | "DZD" | "USD" | "EUR" | "AOA" | "XCD" | "ARS" | "AMD" | "AWG" | "AUD" | "AZN" | "BSD" | "BHD" | "BDT" | "BBD" | "BYN" | "BZD" | "XOF" | "BMD" | "BTN" | "INR" | "BOB" | "BOV" | "BAM" | "BWP" | "NOK" | "BRL" | "BND" | "BGN" | "BIF" | "CVE" | "KHR" | "XAF" | "CAD" | "KYD" | "CLF" | "CLP" | "CNY" | "COP" | "COU" | "KMF" | "CDF" | "NZD" | "CRC" | "CUC" | "CUP" | "ANG" | "CZK" | "DKK" | "DJF" | "DOP" | "EGP" | "SVC" | "ERN" | "ETB" | "FKP" | "FJD" | "XPF" | "GMD" | "GEL" | "GHS" | "GIP" | "GTQ" | "GBP" | "GNF" | "GYD" | "HTG" | "HNL" | "HKD" | "HUF" | "ISK" | "IDR" | "XDR" | "IRR" | "IQD" | "ILS" | "JMD" | "JPY" | "JOD" | "KZT" | "KES" | "KPW" | "KRW" | "KWD" | "KGS" | "LAK" | "LBP" | "LSL" | "ZAR" | "LRD" | "LYD" | "CHF" | "MOP" | "MGA" | "MWK" | "MYR" | "MVR" | "MRU" | "MUR" | "XUA" | "MXN" | "MXV" | "MDL" | "MNT" | "MAD" | "MZN" | "MMK" | "NAD" | "NPR" | "NIO" | "NGN" | "OMR" | "PKR" | "PAB" | "PGK" | "PYG" | "PEN" | "PHP" | "PLN" | "QAR" | "MKD" | "RON" | "RUB" | "RWF" | "SHP" | "WST" | "STN" | "SAR" | "RSD" | "SCR" | "SLE" | "SGD" | "XSU" | "SBD" | "SOS" | "SSP" | "LKR" | "SDG" | "SRD" | "SZL" | "SEK" | "CHE" | "CHW" | "SYP" | "TWD" | "TJS" | "TZS" | "THB" | "TOP" | "TTD" | "TND" | "TRY" | "TMT" | "UGX" | "UAH" | "AED" | "USN" | "UYI" | "UYU" | "UZS" | "VUV" | "VEF" | "VED" | "VND" | "YER" | "ZMW" | "ZWL");
3
- export interface __MainSchema {
4
- style_id: string;
5
- colour_id: string;
6
- size_code: string;
7
- location_code: string;
8
- source_system: ("bc" | "centra" | "sitoo");
9
- quantity: number;
10
- description?: string;
11
- entry_no?: string;
12
- external_entry_no?: string;
13
- document_no?: string;
14
- external_document_no?: string;
15
- type: ("transfer" | "negative adjustment" | "positive adjustment" | "purchase" | "sale");
16
- unit_cost?: (Money | null);
17
- document_date: string;
18
- posting_date?: string;
19
- created_at: string;
20
- last_modified_at: string;
21
- }
22
- export interface Money {
23
- value: number;
24
- decimal_places: number;
25
- currency: Currency;
26
- lcy_value: number;
27
- lcy_currency: LCYCurrency;
28
- lcy_decimal_places: number;
29
- }
30
- export default __MainSchema;
@@ -1,119 +0,0 @@
1
- import EnvEngine from './env';
2
- import Runtime from './runtime';
3
- export interface UnitCost {
4
- value: number;
5
- decimal_places: number;
6
- currency: string;
7
- lcy_value: number;
8
- lcy_currency: string;
9
- lcy_decimal_places: number;
10
- }
11
- export interface StockMovementInput {
12
- style_id: string;
13
- colour_id: string;
14
- size_code: string;
15
- location_code: string;
16
- last_updated_at?: string;
17
- entry_no: string;
18
- quantity: number;
19
- document_no: string;
20
- external_document_no?: string;
21
- type: 'transfer' | 'negative adjustment' | 'positive adjustment' | 'purchase' | 'sale';
22
- unit_cost?: UnitCost;
23
- document_date?: string;
24
- posting_date?: string;
25
- created_at: string;
26
- }
27
- export type BasicSitooWarehouse = {
28
- warehouseid: number;
29
- warehousetype: string;
30
- usetype: string;
31
- storeid: number;
32
- sellable: boolean;
33
- name: string;
34
- externalId: number;
35
- };
36
- export type BasicSitooStore = {
37
- storeid: number;
38
- name: string;
39
- externalId: number;
40
- };
41
- export type BasicWarehouseTransaction = {
42
- warehousetransactionid: number;
43
- warehouseid: number;
44
- datecreated: number;
45
- transactiontype: number;
46
- description: string;
47
- orderdeliveryid: number | null;
48
- shipmentid: number | null;
49
- externalid: string | null;
50
- reasoncode: string | null;
51
- items: Array<{
52
- decimalquantity: string;
53
- decimaltotal: string;
54
- moneypricein: string;
55
- moneytotal: string;
56
- moneyvalue: string;
57
- sku: string;
58
- }>;
59
- };
60
- export type WarehouseTransactionEnvelope = {
61
- totalcount: number;
62
- items: BasicWarehouseTransaction[];
63
- };
64
- export interface WarehouseTransactionSyncOptions {
65
- preview?: boolean;
66
- skuGenerator?: (styleId: string, colourId: string, sizeCode: string) => string;
67
- }
68
- export interface WarehouseTransactionSyncResult {
69
- found: Array<{
70
- entryNo: string;
71
- transactionId: number;
72
- }>;
73
- missing: string[];
74
- created: Array<{
75
- entryNo: string;
76
- transactionId: number;
77
- }>;
78
- errors: Array<{
79
- entryNo: string;
80
- error: string;
81
- }>;
82
- firestoreUpdated: boolean;
83
- skipped: Array<{
84
- entryNo: string;
85
- reason: string;
86
- }>;
87
- }
88
- export interface FirestoreCacheDocument {
89
- [entryNo: string]: number;
90
- }
91
- export default class SitooHelper extends EnvEngine {
92
- protected opts: {
93
- accessToken: string;
94
- sitooBaseUrl: string;
95
- sitooSiteId: number;
96
- };
97
- shaToken: string;
98
- baseUrl: string;
99
- siteId: number;
100
- constructor(options: ConstructorParameters<typeof Runtime>[0], opts: {
101
- accessToken: string;
102
- sitooBaseUrl: string;
103
- sitooSiteId: number;
104
- });
105
- getSitooWarehouses(): Promise<{
106
- nameToId: Record<string, number>;
107
- warehouses: Record<number, BasicSitooWarehouse>;
108
- }>;
109
- private fetchWarehouseTransactionsWithFilter;
110
- private searchTransactionsInApi;
111
- private batchAddWarehouseTransactions;
112
- syncWarehouseTransactions(items: StockMovementInput[], options?: WarehouseTransactionSyncOptions): Promise<Array<{
113
- entryNo: string;
114
- transactionId: number;
115
- } | Error | {
116
- error: boolean;
117
- message: string;
118
- }>>;
119
- }