@softwear/latestcollectioncore 1.0.196 → 1.0.198

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.
@@ -1,8 +1,4 @@
1
1
  import { TransactionI, RaGI, HrTimeframeI, TimeGranularityE, SkuI } from './types';
2
- /**
3
- * The propFunction for FiltreX needs some data that can't be passed as parameters. That's why the 'globalsRelations' variable is needed.
4
- * TODO: Functional: Experiment with curried functions
5
- */
6
2
  declare function propFunction(propertyName: string, getPropertyName: Function, obj: Record<string, any>): any;
7
3
  declare function whichShardsToProcess(dateRange: any, granularity: 'year' | 'month'): any[];
8
4
  /**
@@ -29,16 +29,20 @@ let globalNrAggegationExpressions = 0; // tracks the # aggregations rows so we c
29
29
  * The propFunction for FiltreX needs some data that can't be passed as parameters. That's why the 'globalsRelations' variable is needed.
30
30
  * TODO: Functional: Experiment with curried functions
31
31
  */
32
+ function resolvePath(obj, path) {
33
+ return path.reduce((o, key) => o === null || o === void 0 ? void 0 : o[key], obj);
34
+ }
32
35
  function propFunction(propertyName, getPropertyName, obj) {
33
36
  var _a, _b;
34
37
  const positionOfDot = propertyName.indexOf('.');
35
38
  if (positionOfDot < 0)
36
39
  return obj[propertyName];
37
40
  const table = propertyName.substring(0, positionOfDot);
38
- const field = propertyName.substring(positionOfDot + 1);
41
+ const path = propertyName.substring(positionOfDot + 1).split('.');
39
42
  if (table == 'transaction')
40
- return obj[field];
41
- return ((_b = (_a = globalRelations[table]) === null || _a === void 0 ? void 0 : _a[obj[globalJoinableTables[table]]]) === null || _b === void 0 ? void 0 : _b[field]) || '~ ?';
43
+ return resolvePath(obj, path);
44
+ const joinedRecord = (_a = globalRelations[table]) === null || _a === void 0 ? void 0 : _a[obj[globalJoinableTables[table]]];
45
+ return (_b = resolvePath(joinedRecord, path)) !== null && _b !== void 0 ? _b : '~ ?';
42
46
  }
43
47
  const lower = (s) => s.toLowerCase();
44
48
  const upper = (s) => s.toUpperCase();
package/dist/consts.d.ts CHANGED
@@ -4,21 +4,25 @@ declare const SUBSCRIPTION_LIMITS: {
4
4
  readonly maxBarcodes: 1000;
5
5
  readonly maxDownloadBarcodes: 1000;
6
6
  readonly maxCustomers: 0;
7
+ readonly maxDataRetentionYears: 3;
7
8
  };
8
9
  readonly basiq: {
9
10
  readonly maxBarcodes: 100000;
10
11
  readonly maxDownloadBarcodes: 10000;
11
12
  readonly maxCustomers: 1000;
13
+ readonly maxDataRetentionYears: 3;
12
14
  };
13
15
  readonly pro: {
14
16
  readonly maxBarcodes: 200000;
15
17
  readonly maxDownloadBarcodes: 20000;
16
18
  readonly maxCustomers: 10000;
19
+ readonly maxDataRetentionYears: 5;
17
20
  };
18
21
  readonly ultimate: {
19
22
  readonly maxBarcodes: 500000;
20
23
  readonly maxDownloadBarcodes: 50000;
21
24
  readonly maxCustomers: 100000;
25
+ readonly maxDataRetentionYears: 7;
22
26
  };
23
27
  };
24
28
  declare const latestCollectionGLN: "8719332204995";
package/dist/consts.js CHANGED
@@ -22,21 +22,25 @@ const SUBSCRIPTION_LIMITS = {
22
22
  maxBarcodes: 1000,
23
23
  maxDownloadBarcodes: 1000,
24
24
  maxCustomers: 0,
25
+ maxDataRetentionYears: 3,
25
26
  },
26
27
  basiq: {
27
28
  maxBarcodes: 100000,
28
29
  maxDownloadBarcodes: 10000,
29
30
  maxCustomers: 1000,
31
+ maxDataRetentionYears: 3,
30
32
  },
31
33
  pro: {
32
34
  maxBarcodes: 200000,
33
35
  maxDownloadBarcodes: 20000,
34
36
  maxCustomers: 10000,
37
+ maxDataRetentionYears: 5,
35
38
  },
36
39
  ultimate: {
37
40
  maxBarcodes: 500000,
38
41
  maxDownloadBarcodes: 50000,
39
42
  maxCustomers: 100000,
43
+ maxDataRetentionYears: 7,
40
44
  },
41
45
  };
42
46
  exports.SUBSCRIPTION_LIMITS = SUBSCRIPTION_LIMITS;
@@ -0,0 +1,4 @@
1
+ export declare function getMaxDataRetentionYears(subscription: string): number;
2
+ export declare function getEarliestAllowedYear(maxRetentionYears: number, thisYear?: number): number;
3
+ export declare function isYearWithinDataRetention(year: number | string, maxRetentionYears: number, thisYear?: number): boolean;
4
+ export declare function isDateWithinDataRetention(date: string, maxRetentionYears: number, thisYear?: number): boolean;
@@ -0,0 +1,27 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.isDateWithinDataRetention = exports.isYearWithinDataRetention = exports.getEarliestAllowedYear = exports.getMaxDataRetentionYears = void 0;
4
+ const consts_1 = require("./consts");
5
+ function getMaxDataRetentionYears(subscription) {
6
+ var _a;
7
+ const limits = consts_1.SUBSCRIPTION_LIMITS[subscription];
8
+ return (_a = limits === null || limits === void 0 ? void 0 : limits.maxDataRetentionYears) !== null && _a !== void 0 ? _a : consts_1.SUBSCRIPTION_LIMITS.basiq.maxDataRetentionYears;
9
+ }
10
+ exports.getMaxDataRetentionYears = getMaxDataRetentionYears;
11
+ function getEarliestAllowedYear(maxRetentionYears, thisYear = new Date().getFullYear()) {
12
+ return thisYear - maxRetentionYears + 1;
13
+ }
14
+ exports.getEarliestAllowedYear = getEarliestAllowedYear;
15
+ function isYearWithinDataRetention(year, maxRetentionYears, thisYear = new Date().getFullYear()) {
16
+ const parsedYear = parseInt(String(year), 10);
17
+ if (Number.isNaN(parsedYear))
18
+ return true;
19
+ return parsedYear >= getEarliestAllowedYear(maxRetentionYears, thisYear);
20
+ }
21
+ exports.isYearWithinDataRetention = isYearWithinDataRetention;
22
+ function isDateWithinDataRetention(date, maxRetentionYears, thisYear = new Date().getFullYear()) {
23
+ if (!date)
24
+ return true;
25
+ return isYearWithinDataRetention(parseInt(date.substring(0, 4), 10), maxRetentionYears, thisYear);
26
+ }
27
+ exports.isDateWithinDataRetention = isDateWithinDataRetention;
package/dist/index.d.ts CHANGED
@@ -25,5 +25,6 @@ export * from './pdf';
25
25
  export type { GenPdfAlert, GenPdfOptions, PdfFontDefinition, PdfFontVariant, PdfImageAsset } from './reports';
26
26
  export * from './types';
27
27
  export * from './consts';
28
+ export * from './dataRetention';
28
29
  export { default as lcAxios, createAxiosInstance, axiosRetry, exponentialDelay, isAxiosError, } from './lcAxios';
29
30
  export type { AxiosRequestConfig, AxiosResponse, AxiosPromise, AxiosError, AxiosTransformer, AxiosRetryOptions, AxiosRequestConfigAny, } from './lcAxios';
package/dist/index.js CHANGED
@@ -69,6 +69,7 @@ Object.defineProperty(exports, "shouldSuppressZeroField", { enumerable: true, ge
69
69
  __exportStar(require("./pdf"), exports);
70
70
  __exportStar(require("./types"), exports);
71
71
  __exportStar(require("./consts"), exports);
72
+ __exportStar(require("./dataRetention"), exports);
72
73
  var lcAxios_1 = require("./lcAxios");
73
74
  Object.defineProperty(exports, "lcAxios", { enumerable: true, get: function () { return __importDefault(lcAxios_1).default; } });
74
75
  Object.defineProperty(exports, "createAxiosInstance", { enumerable: true, get: function () { return lcAxios_1.createAxiosInstance; } });
package/dist/types.d.ts CHANGED
@@ -230,6 +230,7 @@ interface BrandSettingI {
230
230
  orderMethods?: string[];
231
231
  propertyMapping?: PropertyMappingI[];
232
232
  fashionCloudId?: string;
233
+ tapprApiHost?: string;
233
234
  sizeSystemPrefix?: string;
234
235
  supplier?: string;
235
236
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@softwear/latestcollectioncore",
3
- "version": "1.0.196",
3
+ "version": "1.0.198",
4
4
  "description": "Core functions for LatestCollections applications",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -97,13 +97,18 @@ let globalNrAggegationExpressions = 0 // tracks the # aggregations rows so we ca
97
97
  * The propFunction for FiltreX needs some data that can't be passed as parameters. That's why the 'globalsRelations' variable is needed.
98
98
  * TODO: Functional: Experiment with curried functions
99
99
  */
100
+ function resolvePath(obj: Record<string, any> | undefined, path: string[]): any {
101
+ return path.reduce((o, key) => o?.[key], obj)
102
+ }
103
+
100
104
  function propFunction(propertyName: string, getPropertyName: Function, obj: Record<string, any>): any {
101
105
  const positionOfDot = propertyName.indexOf('.')
102
106
  if (positionOfDot < 0) return obj[propertyName]
103
107
  const table = propertyName.substring(0, positionOfDot)
104
- const field = propertyName.substring(positionOfDot + 1)
105
- if (table == 'transaction') return obj[field]
106
- return globalRelations[table]?.[obj[globalJoinableTables[table]]]?.[field] || '~ ?'
108
+ const path = propertyName.substring(positionOfDot + 1).split('.')
109
+ if (table == 'transaction') return resolvePath(obj, path)
110
+ const joinedRecord = globalRelations[table]?.[obj[globalJoinableTables[table]]]
111
+ return resolvePath(joinedRecord, path) ?? '~ ?'
107
112
  }
108
113
 
109
114
  const lower = (s: string) => s.toLowerCase()
package/src/consts.ts CHANGED
@@ -18,21 +18,25 @@ const SUBSCRIPTION_LIMITS = {
18
18
  maxBarcodes: 1000,
19
19
  maxDownloadBarcodes: 1000,
20
20
  maxCustomers: 0,
21
+ maxDataRetentionYears: 3,
21
22
  },
22
23
  basiq: {
23
24
  maxBarcodes: 100000,
24
25
  maxDownloadBarcodes: 10000,
25
26
  maxCustomers: 1000,
27
+ maxDataRetentionYears: 3,
26
28
  },
27
29
  pro: {
28
30
  maxBarcodes: 200000,
29
31
  maxDownloadBarcodes: 20000,
30
32
  maxCustomers: 10000,
33
+ maxDataRetentionYears: 5,
31
34
  },
32
35
  ultimate: {
33
36
  maxBarcodes: 500000,
34
37
  maxDownloadBarcodes: 50000,
35
38
  maxCustomers: 100000,
39
+ maxDataRetentionYears: 7,
36
40
  },
37
41
  } as const
38
42
  const latestCollectionGLN = '8719332204995' as const
@@ -0,0 +1,23 @@
1
+ import { SUBSCRIPTION_LIMITS } from './consts'
2
+
3
+ type SubscriptionKey = keyof typeof SUBSCRIPTION_LIMITS
4
+
5
+ export function getMaxDataRetentionYears(subscription: string): number {
6
+ const limits = SUBSCRIPTION_LIMITS[subscription as SubscriptionKey]
7
+ return limits?.maxDataRetentionYears ?? SUBSCRIPTION_LIMITS.basiq.maxDataRetentionYears
8
+ }
9
+
10
+ export function getEarliestAllowedYear(maxRetentionYears: number, thisYear = new Date().getFullYear()): number {
11
+ return thisYear - maxRetentionYears + 1
12
+ }
13
+
14
+ export function isYearWithinDataRetention(year: number | string, maxRetentionYears: number, thisYear = new Date().getFullYear()): boolean {
15
+ const parsedYear = parseInt(String(year), 10)
16
+ if (Number.isNaN(parsedYear)) return true
17
+ return parsedYear >= getEarliestAllowedYear(maxRetentionYears, thisYear)
18
+ }
19
+
20
+ export function isDateWithinDataRetention(date: string, maxRetentionYears: number, thisYear = new Date().getFullYear()): boolean {
21
+ if (!date) return true
22
+ return isYearWithinDataRetention(parseInt(date.substring(0, 4), 10), maxRetentionYears, thisYear)
23
+ }
package/src/index.ts CHANGED
@@ -25,6 +25,7 @@ export * from './pdf'
25
25
  export type { GenPdfAlert, GenPdfOptions, PdfFontDefinition, PdfFontVariant, PdfImageAsset } from './reports'
26
26
  export * from './types'
27
27
  export * from './consts'
28
+ export * from './dataRetention'
28
29
  export {
29
30
  default as lcAxios,
30
31
  createAxiosInstance,
package/src/types.ts CHANGED
@@ -244,6 +244,7 @@ interface BrandSettingI {
244
244
  orderMethods?: string[]
245
245
  propertyMapping?: PropertyMappingI[]
246
246
  fashionCloudId?: string
247
+ tapprApiHost?: string
247
248
  sizeSystemPrefix?: string
248
249
  supplier?: string
249
250
  }
@@ -271,6 +271,61 @@ describe('articleStatus', () => {
271
271
  expect(result[2].vector[articleStatus.MARGIN]).toBe(50)
272
272
  })
273
273
 
274
+ it('should resolve nested property paths like customer.invoiceAddress.countrycode when grouping', () => {
275
+ const CUSTOMERS = {
276
+ '*zBXU': { invoiceAddress: { countrycode: 'NL' } },
277
+ }
278
+ const rawAggregations = {}
279
+ articleStatus.runQuery(
280
+ { beginTimeStamp: 1569316408000, endTimeStamp: 1569316409000 + msYear },
281
+ rawAggregations,
282
+ true,
283
+ TRANSACTIONS,
284
+ ['customer.invoiceAddress.countrycode'],
285
+ SKUS,
286
+ {},
287
+ CUSTOMERS,
288
+ false,
289
+ '',
290
+ false
291
+ )
292
+
293
+ // Sales (custnr '*zBXU') resolve the nested path to 'NL',
294
+ // beginStock transactions have no custnr and fall back to '~ ?'
295
+ expect(Object.keys(rawAggregations)).toEqual(['** TOTAL **', '~ ?', 'NL'])
296
+ expect(rawAggregations['NL'][articleStatus.QTY_SOLD]).toBe(2)
297
+ })
298
+
299
+ it('should resolve nested property paths in filter expressions', () => {
300
+ const CUSTOMERS = {
301
+ '*zBXU': { invoiceAddress: { countrycode: 'NL' } },
302
+ }
303
+ const run = (countrycode) => {
304
+ const rawAggregations = {}
305
+ articleStatus.runQuery(
306
+ { beginTimeStamp: 1569316408000, endTimeStamp: 1569316409000 + msYear },
307
+ rawAggregations,
308
+ false,
309
+ TRANSACTIONS,
310
+ ['transaction.ean'],
311
+ SKUS,
312
+ {},
313
+ CUSTOMERS,
314
+ false,
315
+ 'customer.invoiceAddress.countrycode == "' + countrycode + '"',
316
+ false
317
+ )
318
+ return rawAggregations
319
+ }
320
+
321
+ const matching = run('NL')
322
+ expect(Object.keys(matching)).toEqual(['** TOTAL **', '000558000143', '8700558000143'])
323
+ expect(matching['000558000143'][articleStatus.QTY_SOLD]).toBe(1)
324
+
325
+ const nonMatching = run('DE')
326
+ expect(Object.keys(nonMatching)).toEqual([])
327
+ })
328
+
274
329
  it('should calculate shelfStock correctly with type 10 (beginShelfStock) and type 94 (picklist)', () => {
275
330
  // Test scenario:
276
331
  // - beginStock (type 0): 100 units
@@ -0,0 +1,21 @@
1
+ import { describe, expect, it } from 'vitest'
2
+ import { getEarliestAllowedYear, getMaxDataRetentionYears, isDateWithinDataRetention, isYearWithinDataRetention } from '../src/dataRetention'
3
+
4
+ describe('dataRetention', () => {
5
+ const thisYear = 2026
6
+
7
+ it('returns retention years per subscription', () => {
8
+ expect(getMaxDataRetentionYears('basiq')).toBe(3)
9
+ expect(getMaxDataRetentionYears('pro')).toBe(5)
10
+ expect(getMaxDataRetentionYears('ultimate')).toBe(7)
11
+ expect(getMaxDataRetentionYears('unknown')).toBe(3)
12
+ })
13
+
14
+ it('checks year and date access against retention', () => {
15
+ expect(getEarliestAllowedYear(3, thisYear)).toBe(2024)
16
+ expect(isYearWithinDataRetention(2024, 3, thisYear)).toBe(true)
17
+ expect(isYearWithinDataRetention(2023, 3, thisYear)).toBe(false)
18
+ expect(isDateWithinDataRetention('2023-12-31', 3, thisYear)).toBe(false)
19
+ expect(isDateWithinDataRetention('2024-01-01', 3, thisYear)).toBe(true)
20
+ })
21
+ })