onroute-policy-engine 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/README.md +5 -4
  2. package/dist/enum/accessory-vehicle-type.d.ts +4 -0
  3. package/dist/enum/accessory-vehicle-type.js +8 -0
  4. package/dist/enum/facts.d.ts +5 -1
  5. package/dist/enum/facts.js +4 -0
  6. package/dist/enum/index.d.ts +2 -0
  7. package/dist/enum/index.js +5 -1
  8. package/dist/enum/permit-app-info.d.ts +8 -2
  9. package/dist/enum/permit-app-info.js +8 -2
  10. package/dist/enum/validation-result-code.d.ts +2 -1
  11. package/dist/enum/validation-result-code.js +1 -0
  12. package/dist/enum/validation-result-type.d.ts +2 -1
  13. package/dist/enum/validation-result-type.js +1 -0
  14. package/dist/enum/vehicle-types.d.ts +4 -0
  15. package/dist/enum/vehicle-types.js +8 -0
  16. package/dist/helper/facts.helper.d.ts +2 -9
  17. package/dist/helper/facts.helper.js +63 -26
  18. package/dist/helper/lists.helper.d.ts +13 -0
  19. package/dist/helper/lists.helper.js +32 -1
  20. package/dist/helper/rules-engine.helper.js +32 -2
  21. package/dist/policy-engine.d.ts +82 -2
  22. package/dist/policy-engine.js +516 -6
  23. package/dist/rule-operator/custom-operators.js +5 -1
  24. package/dist/types/commodity.d.ts +10 -3
  25. package/dist/types/cost-rule.d.ts +4 -0
  26. package/dist/types/cost-rule.js +2 -0
  27. package/dist/types/index.d.ts +4 -2
  28. package/dist/types/permit-type.d.ts +2 -1
  29. package/dist/types/policy-definition.d.ts +2 -1
  30. package/dist/types/range-matrix.d.ts +9 -0
  31. package/dist/types/range-matrix.js +2 -0
  32. package/dist/types/region-size-override.d.ts +3 -3
  33. package/dist/types/self-issuable.d.ts +1 -1
  34. package/dist/types/size-dimension.d.ts +7 -7
  35. package/dist/types/vehicle-type.d.ts +1 -0
  36. package/dist/types/vehicle.d.ts +11 -5
  37. package/dist/validation-result.d.ts +1 -0
  38. package/dist/validation-results.d.ts +5 -0
  39. package/dist/validation-results.js +10 -1
  40. package/package.json +3 -3
@@ -35,10 +35,9 @@ class Policy {
35
35
  else {
36
36
  // Add facts specific to this run of the validation (e.g. validation
37
37
  // date for comparison against start date of the permit).
38
- (0, facts_helper_1.addRuntimeFacts)(engine);
39
- const permitFacts = (0, facts_helper_1.transformPermitFacts)(permit);
38
+ (0, facts_helper_1.addRuntimeFacts)(engine, this);
40
39
  // Run the json-rules-engine against the permit facts
41
- const engineResult = await engine.run(permitFacts);
40
+ const engineResult = await engine.run(permit);
42
41
  // Wrap the json-rules-engine result in a ValidationResult object
43
42
  return new validation_results_1.ValidationResults(engineResult);
44
43
  }
@@ -63,13 +62,507 @@ class Policy {
63
62
  * Gets a list of all configured commodities in the policy definition.
64
63
  * @param permitTypeId Return only commodities valid for this permit type.
65
64
  * If not supplied, return all commodities configured in policy. If permit
66
- * type does not require commodity (e.g. 'TROS'), return all commodities.
65
+ * type does not require commodity (e.g. 'TROS'), return empty map.
67
66
  * @returns Map of commodity IDs to commodity names.
68
67
  */
69
68
  getCommodities(permitTypeId) {
70
69
  const permitType = this.getPermitTypeDefinition(permitTypeId);
71
- const commodities = (0, lists_helper_1.extractIdentifiedObjects)(this.policyDefinition.commodities, permitType === null || permitType === void 0 ? void 0 : permitType.allowedCommodities);
72
- return commodities;
70
+ if (!permitTypeId) {
71
+ // Permit type not supplied, return all commodities
72
+ return (0, lists_helper_1.extractIdentifiedObjects)(this.policyDefinition.commodities);
73
+ }
74
+ else if (!permitType) {
75
+ // Permit type invalid, throw error
76
+ throw new Error(`Invalid permit type supplied: '${permitTypeId}'`);
77
+ }
78
+ else if (!permitType.commodityRequired) {
79
+ return new Map();
80
+ }
81
+ else {
82
+ // Commodities for oversize permits are those which have at least one
83
+ // power unit defined in the size dimension object of the commodity.
84
+ const commoditiesForOS = (0, lists_helper_1.extractIdentifiedObjects)(this.policyDefinition.commodities.filter((c) => {
85
+ if (c.size) {
86
+ if (c.size.powerUnits) {
87
+ if (c.size.powerUnits.length > 0) {
88
+ return true;
89
+ }
90
+ }
91
+ }
92
+ return false;
93
+ }), permitType.allowedCommodities);
94
+ // TODO when implementing overweight. Stub for now.
95
+ const commoditiesForOW = new Map();
96
+ if (permitType.sizeDimensionRequired &&
97
+ permitType.weightDimensionRequired) {
98
+ return (0, lists_helper_1.intersectIdMaps)(commoditiesForOS, commoditiesForOW);
99
+ }
100
+ else if (permitType.sizeDimensionRequired) {
101
+ return commoditiesForOS;
102
+ }
103
+ else if (permitType.weightDimensionRequired) {
104
+ return commoditiesForOW;
105
+ }
106
+ else {
107
+ // This permit type requires commodity selection, but does not
108
+ // require size or weight dimensions. For these permit types, the
109
+ // allowedCommodities must be configured in policy. Return these.
110
+ const commodities = (0, lists_helper_1.extractIdentifiedObjects)(this.policyDefinition.commodities, permitType.allowedCommodities);
111
+ return commodities;
112
+ }
113
+ }
114
+ }
115
+ /**
116
+ * Gets a list of all allowable vehicle types for the given permit type
117
+ * and commodity. Includes all power units and trailers.
118
+ * @param permitTypeId ID of the permit type to get vehicles for
119
+ * @param commodityId ID of the commodity to get vehicles for
120
+ * @returns Map of two separate maps, one keyed on 'powerUnits' and the
121
+ * other keyed on 'trailers'. Each map consists of a string id for the
122
+ * vehicle, and a string common name for the vehicle.
123
+ */
124
+ getPermittableVehicleTypes(permitTypeId, commodityId) {
125
+ var _a, _b, _c, _d;
126
+ if (!permitTypeId || !commodityId) {
127
+ throw new Error('Missing permitTypeId and/or commodityId');
128
+ }
129
+ const permitType = this.getPermitTypeDefinition(permitTypeId);
130
+ if (!permitType) {
131
+ throw new Error(`Invalid permit type: '${permitTypeId}'`);
132
+ }
133
+ if (!permitType.commodityRequired) {
134
+ // If commodity is not required, this method cannot calculate the
135
+ // permittable vehicle types since they will not be configured.
136
+ throw new Error(`Permit type '${permitTypeId}' does not require a commodity`);
137
+ }
138
+ let puTypes;
139
+ let trTypes;
140
+ const allowableCommodities = this.getCommodities(permitTypeId);
141
+ if (!allowableCommodities.has(commodityId)) {
142
+ // If the commodity is not allowed for the permit type, no power
143
+ // units or trailers are allowed.
144
+ puTypes = new Map();
145
+ trTypes = new Map();
146
+ }
147
+ else {
148
+ const commodity = this.getCommodityDefinition(commodityId);
149
+ if (!commodity) {
150
+ throw new Error(`Commodity id '${commodityId}' is not correctly configured in the policy definition`);
151
+ }
152
+ const puTypeIdsOS = (_b = (_a = commodity.size) === null || _a === void 0 ? void 0 : _a.powerUnits) === null || _b === void 0 ? void 0 : _b.map((p) => p.type);
153
+ const trTypeIdsOS = [];
154
+ (_d = (_c = commodity.size) === null || _c === void 0 ? void 0 : _c.powerUnits) === null || _d === void 0 ? void 0 : _d.forEach((pu) => {
155
+ const trForPu = pu.trailers.map((t) => t.type);
156
+ if (trForPu)
157
+ trTypeIdsOS.concat(trForPu);
158
+ });
159
+ // TODO: implement along with overweight permits. Stub for now.
160
+ const puTypeIdsOW = [];
161
+ const trTypeIdsOW = [];
162
+ const puTypesOS = (0, lists_helper_1.extractIdentifiedObjects)(this.policyDefinition.vehicleTypes.powerUnitTypes, puTypeIdsOS);
163
+ const trTypesOS = (0, lists_helper_1.extractIdentifiedObjects)(this.policyDefinition.vehicleTypes.trailerTypes, trTypeIdsOS);
164
+ const puTypesOW = (0, lists_helper_1.extractIdentifiedObjects)(this.policyDefinition.vehicleTypes.powerUnitTypes, puTypeIdsOW);
165
+ const trTypesOW = (0, lists_helper_1.extractIdentifiedObjects)(this.policyDefinition.vehicleTypes.trailerTypes, trTypeIdsOW);
166
+ if (permitType.sizeDimensionRequired &&
167
+ permitType.weightDimensionRequired) {
168
+ puTypes = (0, lists_helper_1.intersectIdMaps)(puTypesOS, puTypesOW);
169
+ trTypes = (0, lists_helper_1.intersectIdMaps)(trTypesOS, trTypesOW);
170
+ }
171
+ else if (permitType.sizeDimensionRequired) {
172
+ puTypes = puTypesOS;
173
+ trTypes = trTypesOS;
174
+ }
175
+ else if (permitType.weightDimensionRequired) {
176
+ puTypes = puTypesOW;
177
+ trTypes = trTypesOW;
178
+ }
179
+ else {
180
+ // This permit type requires commodity selection, but does not
181
+ // require size or weight dimensions. For these permit types, the
182
+ // allowedVehicles must be configured in policy. Return all power
183
+ // units and trailers from this list.
184
+ puTypes = (0, lists_helper_1.extractIdentifiedObjects)(this.policyDefinition.vehicleTypes.powerUnitTypes, permitType.allowedVehicles);
185
+ trTypes = (0, lists_helper_1.extractIdentifiedObjects)(this.policyDefinition.vehicleTypes.trailerTypes, permitType.allowedVehicles);
186
+ }
187
+ }
188
+ const vehicleTypes = new Map();
189
+ vehicleTypes.set(enum_1.VehicleTypes.PowerUnits, puTypes);
190
+ vehicleTypes.set(enum_1.VehicleTypes.Trailers, trTypes);
191
+ return vehicleTypes;
192
+ }
193
+ /**
194
+ * Gets a list of all allowable power unit types for the given permit type
195
+ * and commodity.
196
+ * @param permitTypeId ID of the permit type to get power units for
197
+ * @param commodityId ID of the commodity to get power units for
198
+ * @returns Map of power unit IDs to power unit names.
199
+ */
200
+ getPermittablePowerUnitTypes(permitTypeId, commodityId) {
201
+ const vehicleTypes = this.getPermittableVehicleTypes(permitTypeId, commodityId);
202
+ const puTypes = vehicleTypes.get(enum_1.VehicleTypes.PowerUnits);
203
+ return puTypes !== null && puTypes !== void 0 ? puTypes : new Map();
204
+ }
205
+ /**
206
+ * Gets the next permittable vehicles based on the supplied permit type,
207
+ * commodity, and current configuration.
208
+ * If the permit type does not require commodity or if the commodity
209
+ * is not valid for the permit type, this will return an empty map
210
+ * (meaning no vehicles are permittable).
211
+ * @param permitTypeId ID of the permit type to get vehicles for
212
+ * @param commodityId ID of the commodity to get vehicles for
213
+ * @param currentConfiguration Current vehicle configuration to which will be
214
+ * added the next permittable vehicle
215
+ * @returns Map of vehicle ID to vehicle name. Includes both power units and
216
+ * trailers in a single return value (if applicable).
217
+ */
218
+ getNextPermittableVehicles(permitTypeId, commodityId, currentConfiguration) {
219
+ var _a, _b, _c, _d;
220
+ if (!permitTypeId || !commodityId || !currentConfiguration) {
221
+ throw new Error('Missing permitTypeId and/or commodityId and/or currentConfiguration');
222
+ }
223
+ const permitType = this.getPermitTypeDefinition(permitTypeId);
224
+ if (!permitType) {
225
+ throw new Error(`Invalid permit type: '${permitTypeId}'`);
226
+ }
227
+ if (!permitType.commodityRequired) {
228
+ // If commodity is not required, this method cannot calculate the
229
+ // permittable vehicle types since they will not be configured.
230
+ throw new Error(`Permit type '${permitTypeId}' does not require a commodity`);
231
+ }
232
+ const commodity = this.getCommodityDefinition(commodityId);
233
+ if (!commodity) {
234
+ throw new Error(`Invalid commodity type: '${commodityId}'`);
235
+ }
236
+ if (!this.isConfigurationValid(permitTypeId, commodityId, currentConfiguration, true)) {
237
+ // Invalid configuration, no vehicles permitted
238
+ return new Map();
239
+ }
240
+ let vehicleTypes = new Map();
241
+ let vehicleTypeIds;
242
+ const allVehicles = this.policyDefinition.vehicleTypes.powerUnitTypes.concat(this.policyDefinition.vehicleTypes.trailerTypes);
243
+ if (currentConfiguration.length == 0) {
244
+ // The current configuration is empty, so return only the
245
+ // allowable power units.
246
+ vehicleTypeIds = (_b = (_a = commodity.size) === null || _a === void 0 ? void 0 : _a.powerUnits) === null || _b === void 0 ? void 0 : _b.map((p) => p.type);
247
+ }
248
+ else {
249
+ const powerUnit = (_d = (_c = commodity.size) === null || _c === void 0 ? void 0 : _c.powerUnits) === null || _d === void 0 ? void 0 : _d.find((p) => p.type == currentConfiguration[0]);
250
+ const trailerIds = powerUnit === null || powerUnit === void 0 ? void 0 : powerUnit.trailers.map((t) => t.type);
251
+ if (currentConfiguration.length == 1) {
252
+ // Just a power unit, return the list of trailerIds for the
253
+ // power unit, plus jeep if any of the trailer Ids allow
254
+ // a jeep.
255
+ if ((powerUnit === null || powerUnit === void 0 ? void 0 : powerUnit.trailers) &&
256
+ (powerUnit === null || powerUnit === void 0 ? void 0 : powerUnit.trailers.filter((t) => t.jeep).length) > 0) {
257
+ trailerIds === null || trailerIds === void 0 ? void 0 : trailerIds.push(enum_1.AccessoryVehicleType.Jeep);
258
+ }
259
+ vehicleTypeIds = trailerIds;
260
+ }
261
+ else {
262
+ const lastVehicleId = currentConfiguration[currentConfiguration.length - 1];
263
+ switch (lastVehicleId) {
264
+ case enum_1.AccessoryVehicleType.Jeep:
265
+ // If there is one jeep, there can be more
266
+ trailerIds === null || trailerIds === void 0 ? void 0 : trailerIds.push(enum_1.AccessoryVehicleType.Jeep);
267
+ vehicleTypeIds = trailerIds;
268
+ break;
269
+ case enum_1.AccessoryVehicleType.Booster:
270
+ // If there is one booster, there can be more
271
+ vehicleTypeIds = [enum_1.AccessoryVehicleType.Booster];
272
+ break;
273
+ default:
274
+ {
275
+ const trailer = powerUnit === null || powerUnit === void 0 ? void 0 : powerUnit.trailers.find((t) => t.type == lastVehicleId);
276
+ if (trailer && trailer.booster) {
277
+ vehicleTypeIds = [enum_1.AccessoryVehicleType.Booster];
278
+ }
279
+ else {
280
+ vehicleTypeIds = new Array();
281
+ }
282
+ }
283
+ break;
284
+ }
285
+ }
286
+ }
287
+ vehicleTypes = (0, lists_helper_1.extractIdentifiedObjects)(allVehicles, vehicleTypeIds);
288
+ return vehicleTypes;
289
+ }
290
+ /**
291
+ * Returns whether the supplied configuration is valid for the given permit type
292
+ * and commodity. If the permit type does not require a commodity, this method
293
+ * will return false. This will not validate incomplete configurations - if the
294
+ * current configuration has only a power unit this will return false even if the
295
+ * power unit is acceptable because there is no trailer which is mandatory.
296
+ * If this is called for a permit type that does not require commodity, or with
297
+ * a commodity not permitted for the permit type, it will return false.
298
+ * @param permitTypeId ID of the permit type to validate the configuration against
299
+ * @param commodityId ID of the commodity used for the configuration
300
+ * @param currentConfiguration Current vehicle configuration to validate
301
+ * @param validatePartial Whether to validate a partial configuration (e.g. one
302
+ * that does not include a trailer). This will just return whether or not there
303
+ * are any invalid vehicles in the configuration. If true, an empty current
304
+ * configuration will return true from this method.
305
+ */
306
+ isConfigurationValid(permitTypeId, commodityId, currentConfiguration, validatePartial = false) {
307
+ var _a, _b;
308
+ if (!permitTypeId || !commodityId || !currentConfiguration) {
309
+ throw new Error('Missing permitTypeId and/or commodityId and/or currentConfiguration');
310
+ }
311
+ const permitType = this.getPermitTypeDefinition(permitTypeId);
312
+ if (!permitType) {
313
+ throw new Error(`Invalid permit type: '${permitTypeId}'`);
314
+ }
315
+ if (!permitType.commodityRequired) {
316
+ // If commodity is not required, this method cannot calculate the
317
+ // permittable vehicle types since they will not be configured.
318
+ throw new Error(`Permit type '${permitTypeId}' does not require a commodity`);
319
+ }
320
+ const commodity = this.getCommodityDefinition(commodityId);
321
+ if (!commodity) {
322
+ throw new Error(`Invalid commodity type: '${commodityId}'`);
323
+ }
324
+ if (currentConfiguration.length == 0) {
325
+ // The current configuration is empty. Fine for partial, but
326
+ // invalid as a complete configuration.
327
+ return validatePartial;
328
+ }
329
+ const powerUnit = (_b = (_a = commodity.size) === null || _a === void 0 ? void 0 : _a.powerUnits) === null || _b === void 0 ? void 0 : _b.find((p) => p.type == currentConfiguration[0]);
330
+ if (!powerUnit) {
331
+ // The power unit is not allowed for the commodity
332
+ return false;
333
+ }
334
+ let trailerIds = powerUnit.trailers.map((t) => t.type);
335
+ let jeepAllowed = true;
336
+ let trailerAllowed = true;
337
+ let boosterAllowed = false;
338
+ for (let i = 1; i < currentConfiguration.length; i++) {
339
+ switch (currentConfiguration[i]) {
340
+ case enum_1.AccessoryVehicleType.Jeep:
341
+ if (!jeepAllowed) {
342
+ return false;
343
+ }
344
+ // Filter allowed trailers to only those that allow jeeps
345
+ trailerIds = powerUnit.trailers
346
+ .filter((t) => t.jeep)
347
+ .map((t) => t.type);
348
+ break;
349
+ case enum_1.AccessoryVehicleType.Booster:
350
+ if (!boosterAllowed) {
351
+ return false;
352
+ }
353
+ break;
354
+ default:
355
+ if (!trailerAllowed) {
356
+ return false;
357
+ }
358
+ {
359
+ const trailer = powerUnit.trailers.find((t) => t.type == currentConfiguration[i]);
360
+ if (!trailer || !trailerIds.includes(currentConfiguration[i])) {
361
+ // This trailer is not permitted for this power unit
362
+ return false;
363
+ }
364
+ jeepAllowed = false;
365
+ trailerAllowed = false;
366
+ boosterAllowed = trailer.booster;
367
+ }
368
+ break;
369
+ }
370
+ }
371
+ // We are still here, so configuration is valid. If there is a trailer
372
+ // it is a valid final configuration, otherwise it is a valid partial
373
+ // configuration.
374
+ return !trailerAllowed || validatePartial;
375
+ }
376
+ /**
377
+ * Gets the maximum size dimensions for a given permit type, commodity,
378
+ * and vehicle configuration. A vehicle configuration's size dimension is
379
+ * dictated by the configuration on the trailer. For configurations that
380
+ * are just a power unit, a pseudo trailer type 'NONE' is used and the
381
+ * size dimension is configured on that. Accessory category trailers
382
+ * (e.g. jeeps and boosters) are not used for configuration since they
383
+ * do not impact the size dimension in policy.
384
+ * @param permitTypeId ID of the permit type to get size dimension for
385
+ * @param commodityId ID of the commodity to get size dimension for
386
+ * @param currentConfiguration Current vehicle configuration to get size dimension for
387
+ * @param regions List of regions the vehicle will be traveling in. If not
388
+ * supplied this defaults to the most restrictive size dimension (if multiple
389
+ * are configured).
390
+ * @returns SizeDimension for the given permit type, commodity, and configuration
391
+ */
392
+ getSizeDimension(permitTypeId, commodityId, configuration, regions) {
393
+ var _a, _b, _c;
394
+ // Initialize the sizeDimension with global defaults
395
+ let sizeDimension = null;
396
+ // Validate that the configuration is permittable
397
+ if (this.isConfigurationValid(permitTypeId, commodityId, configuration)) {
398
+ // Get the power unit that has the size configuration
399
+ const commodity = this.getCommodityDefinition(commodityId);
400
+ const powerUnit = (_b = (_a = commodity === null || commodity === void 0 ? void 0 : commodity.size) === null || _a === void 0 ? void 0 : _a.powerUnits) === null || _b === void 0 ? void 0 : _b.find((pu) => pu.type == configuration[0]);
401
+ if (!powerUnit) {
402
+ throw new Error(`Configuration error: could not find power unit '${configuration[0]}'`);
403
+ }
404
+ // Get the last trailer in the configuration that can be used for size calculations
405
+ const sizeTrailer = Array.from(configuration)
406
+ .reverse()
407
+ .find((vehicleId) => {
408
+ var _a;
409
+ const trailerType = (_a = this.policyDefinition.vehicleTypes.trailerTypes) === null || _a === void 0 ? void 0 : _a.find((v) => v.id == vehicleId);
410
+ return !(trailerType === null || trailerType === void 0 ? void 0 : trailerType.ignoreForSizeDimensions);
411
+ });
412
+ if (sizeTrailer) {
413
+ // Get the trailer size dimension array for the commodity
414
+ const trailer = (_c = powerUnit.trailers) === null || _c === void 0 ? void 0 : _c.find((t) => t.type == sizeTrailer);
415
+ let sizeDimensions;
416
+ if (trailer &&
417
+ trailer.sizeDimensions &&
418
+ trailer.sizeDimensions.length > 0) {
419
+ sizeDimensions = trailer.sizeDimensions;
420
+ }
421
+ else {
422
+ sizeDimensions = new Array();
423
+ }
424
+ const sizeDimensionConfigured = this.selectCorrectSizeDimension(sizeDimensions, configuration, sizeTrailer);
425
+ if (sizeDimensionConfigured) {
426
+ // Adjust the size dimensions for the regions travelled, if needed
427
+ if (!regions) {
428
+ // If we are not provided a list of regions, assume we are
429
+ // traveling through all regions (will take the most restrictive
430
+ // of all dimensions in all cases).
431
+ console.log('Assuming all regions for size dimension lookup');
432
+ regions = this.policyDefinition.geographicRegions.map((g) => g.id);
433
+ }
434
+ else {
435
+ console.log(`Using '${regions}' as regions for size lookup`);
436
+ }
437
+ const valueOverrides = [];
438
+ regions === null || regions === void 0 ? void 0 : regions.forEach((r) => {
439
+ var _a, _b, _c, _d;
440
+ let valueOverride;
441
+ // Check to see if this region has specific size dimensions
442
+ const regionOverride = (_a = sizeDimensionConfigured.regions) === null || _a === void 0 ? void 0 : _a.find((cr) => cr.region == r);
443
+ if (!regionOverride) {
444
+ // The region travelled does not have an override, so it assumes
445
+ // the dimensions of the bc default.
446
+ valueOverride = {
447
+ region: r,
448
+ h: sizeDimensionConfigured.h,
449
+ w: sizeDimensionConfigured.w,
450
+ l: sizeDimensionConfigured.l,
451
+ };
452
+ }
453
+ else {
454
+ // There is a region override with one or more dimensions. Use this
455
+ // value preferentially, using default dimension if not supplied
456
+ valueOverride = {
457
+ region: r,
458
+ h: (_b = regionOverride.h) !== null && _b !== void 0 ? _b : sizeDimensionConfigured.h,
459
+ w: (_c = regionOverride.w) !== null && _c !== void 0 ? _c : sizeDimensionConfigured.w,
460
+ l: (_d = regionOverride.l) !== null && _d !== void 0 ? _d : sizeDimensionConfigured.l,
461
+ };
462
+ }
463
+ valueOverrides.push(valueOverride);
464
+ });
465
+ // At this point we have a complete set of size dimensions for each of
466
+ // the regions that will be traversed. Take the minimum value of each
467
+ // dimension for the final value.
468
+ const minimumOverrides = valueOverrides.reduce((accumulator, currentValue) => {
469
+ if (typeof currentValue.h !== 'undefined') {
470
+ if (typeof accumulator.h === 'undefined') {
471
+ accumulator.h = currentValue.h;
472
+ }
473
+ else {
474
+ accumulator.h = Math.min(accumulator.h, currentValue.h);
475
+ }
476
+ }
477
+ if (typeof currentValue.w !== 'undefined') {
478
+ if (typeof accumulator.w === 'undefined') {
479
+ accumulator.w = currentValue.w;
480
+ }
481
+ else {
482
+ accumulator.w = Math.min(accumulator.w, currentValue.w);
483
+ }
484
+ }
485
+ if (typeof currentValue.l !== 'undefined') {
486
+ if (typeof accumulator.l === 'undefined') {
487
+ accumulator.l = currentValue.l;
488
+ }
489
+ else {
490
+ accumulator.l = Math.min(accumulator.l, currentValue.l);
491
+ }
492
+ }
493
+ return accumulator;
494
+ }, { region: '' });
495
+ sizeDimension = {
496
+ rp: sizeDimensionConfigured.rp,
497
+ fp: sizeDimensionConfigured.fp,
498
+ h: minimumOverrides.h,
499
+ w: minimumOverrides.w,
500
+ l: minimumOverrides.l,
501
+ };
502
+ }
503
+ else {
504
+ console.log('Size dimension not configured for trailer');
505
+ }
506
+ }
507
+ else {
508
+ console.log('Could not locate trailer to use for size dimension');
509
+ }
510
+ }
511
+ else {
512
+ console.log('Configuration is invalid, returning null size dimension');
513
+ }
514
+ return sizeDimension;
515
+ }
516
+ /**
517
+ * Selects the correct size dimension from a list of potential candidates, based
518
+ * on the modifier of each dimension. If none of the modifiers match, returns
519
+ * null.
520
+ * @param sizeDimensions Size dimension options to choose from
521
+ * @param currentConfiguration The full vehicle configuration
522
+ */
523
+ selectCorrectSizeDimension(sizeDimensions, configuration, sizeTrailer) {
524
+ var _a;
525
+ let matchingDimension = null;
526
+ if ((sizeDimensions === null || sizeDimensions === void 0 ? void 0 : sizeDimensions.length) > 0 && (configuration === null || configuration === void 0 ? void 0 : configuration.length) > 0) {
527
+ for (const sizeDimension of sizeDimensions) {
528
+ if (!sizeDimension.modifiers) {
529
+ // This dimension has no modifiers, so it is the default if none of
530
+ // the other specific modifiers match.
531
+ matchingDimension = sizeDimension;
532
+ console.log('Using default size dimension, no modifiers specified');
533
+ }
534
+ else {
535
+ const sizeTrailerIndex = configuration.findIndex((c) => sizeTrailer == c);
536
+ const isMatch = (_a = sizeDimension.modifiers) === null || _a === void 0 ? void 0 : _a.every((m) => {
537
+ if (!m.type) {
538
+ return false;
539
+ }
540
+ switch (m.position) {
541
+ case enum_1.RelativePosition.First.toString():
542
+ return configuration[0] == m.type;
543
+ case enum_1.RelativePosition.Last.toString():
544
+ return configuration[configuration.length - 1] == m.type;
545
+ case enum_1.RelativePosition.Before.toString():
546
+ return configuration[sizeTrailerIndex - 1] == m.type;
547
+ case enum_1.RelativePosition.After.toString():
548
+ return configuration[sizeTrailerIndex + 1] == m.type;
549
+ default:
550
+ return false;
551
+ }
552
+ });
553
+ // As soon as we find a dimension with matching modifiers,
554
+ // set it and return it.
555
+ if (isMatch) {
556
+ matchingDimension = sizeDimension;
557
+ break;
558
+ }
559
+ }
560
+ }
561
+ }
562
+ else {
563
+ console.log('Either size dimensions or configuration array is null, no matching dimension returned');
564
+ }
565
+ return matchingDimension;
73
566
  }
74
567
  /**
75
568
  * Gets a list of all configured power unit types in the policy definition.
@@ -104,5 +597,22 @@ class Policy {
104
597
  return null;
105
598
  }
106
599
  }
600
+ /**
601
+ * Gets a full Commodity definition by ID
602
+ * @param type Type ID of the commodity to return.
603
+ * @returns Commodity definition for the supplied ID.
604
+ */
605
+ getCommodityDefinition(type) {
606
+ let commodity;
607
+ if (this.policyDefinition.commodities) {
608
+ commodity = this.policyDefinition.commodities.find((c) => c.id === type);
609
+ }
610
+ if (commodity) {
611
+ return commodity;
612
+ }
613
+ else {
614
+ return null;
615
+ }
616
+ }
107
617
  }
108
618
  exports.Policy = Policy;
@@ -16,7 +16,11 @@ function dateStringValidator(a) {
16
16
  }
17
17
  const CustomOperators = [];
18
18
  exports.CustomOperators = CustomOperators;
19
- CustomOperators.push(new json_rules_engine_1.Operator('stringMinimumLength', (a, b) => a.trim().length >= b, stringValidator));
19
+ CustomOperators.push(new json_rules_engine_1.Operator('stringMinimumLength', (a, b) => {
20
+ if (!a)
21
+ return false;
22
+ return a.trim().length >= b;
23
+ }, stringValidator));
20
24
  CustomOperators.push(new json_rules_engine_1.Operator('dateLessThan', (a, b) => {
21
25
  const firstDate = (0, dayjs_1.default)(a, enum_1.PermitAppInfo.PermitDateFormat.toString());
22
26
  const secondDate = (0, dayjs_1.default)(b, enum_1.PermitAppInfo.PermitDateFormat.toString());
@@ -1,5 +1,12 @@
1
- import { IdentifiedObject, PowerUnit, Trailer } from 'onroute-policy-engine/types';
1
+ import { IdentifiedObject, VehicleSizeConfiguration, PowerUnitWeight, TrailerWeight } from 'onroute-policy-engine/types';
2
2
  export type Commodity = IdentifiedObject & {
3
- powerUnits?: Array<PowerUnit>;
4
- trailers?: Array<Trailer>;
3
+ size?: CommoditySize;
4
+ weight?: CommodityWeight;
5
+ };
6
+ export type CommoditySize = {
7
+ powerUnits?: Array<VehicleSizeConfiguration>;
8
+ };
9
+ export type CommodityWeight = {
10
+ powerUnits?: Array<PowerUnitWeight>;
11
+ trailers?: Array<TrailerWeight>;
5
12
  };
@@ -0,0 +1,4 @@
1
+ export type CostRule = {
2
+ fact: string;
3
+ params: Record<string, any>;
4
+ };
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -1,14 +1,16 @@
1
- export { Commodity } from './commodity';
1
+ export { Commodity, CommoditySize, CommodityWeight } from './commodity';
2
+ export { CostRule } from './cost-rule';
2
3
  export { DimensionModifier } from './dimension-modifier';
3
4
  export { PermitFacts } from './facts';
4
5
  export { GeographicRegion } from './geographic-region';
5
6
  export { IdentifiedObject } from './identified-object';
6
7
  export { PermitType } from './permit-type';
7
8
  export { PolicyDefinition } from './policy-definition';
9
+ export { Matrix, RangeMatrix } from './range-matrix';
8
10
  export { RegionSizeOverride } from './region-size-override';
9
11
  export { SelfIssuable } from './self-issuable';
10
12
  export { SizeDimension } from './size-dimension';
11
13
  export { VehicleCategory, PowerUnitCategory, TrailerCategory, VehicleCategories, } from './vehicle-category';
12
14
  export { VehicleType, PowerUnitType, TrailerType, VehicleTypes, } from './vehicle-type';
13
- export { Vehicle, PowerUnit, Trailer } from './vehicle';
15
+ export { Vehicle, VehicleSizeConfiguration, TrailerSize, PowerUnitWeight, TrailerWeight, } from './vehicle';
14
16
  export { WeightDimension, PowerUnitWeightDimension, TrailerWeightDimension, DefaultWeightDimensions, } from './weight-dimension';
@@ -1,5 +1,5 @@
1
1
  import { RuleProperties } from 'json-rules-engine';
2
- import { IdentifiedObject } from 'onroute-policy-engine/types';
2
+ import { CostRule, IdentifiedObject } from 'onroute-policy-engine/types';
3
3
  export type PermitType = IdentifiedObject & {
4
4
  routingRequired: boolean;
5
5
  weightDimensionRequired: boolean;
@@ -8,4 +8,5 @@ export type PermitType = IdentifiedObject & {
8
8
  allowedVehicles: Array<string>;
9
9
  allowedCommodities?: Array<string>;
10
10
  rules?: Array<RuleProperties>;
11
+ costRules?: Array<CostRule>;
11
12
  };
@@ -1,4 +1,4 @@
1
- import { GeographicRegion, PermitType, DefaultWeightDimensions, VehicleTypes, Commodity, SizeDimension, VehicleCategories } from 'onroute-policy-engine/types';
1
+ import { GeographicRegion, PermitType, DefaultWeightDimensions, VehicleTypes, Commodity, SizeDimension, VehicleCategories, RangeMatrix } from 'onroute-policy-engine/types';
2
2
  import { RuleProperties } from 'json-rules-engine';
3
3
  export type PolicyDefinition = {
4
4
  version: string;
@@ -10,4 +10,5 @@ export type PolicyDefinition = {
10
10
  vehicleCategories: VehicleCategories;
11
11
  vehicleTypes: VehicleTypes;
12
12
  commodities: Array<Commodity>;
13
+ rangeMatrices?: Array<RangeMatrix>;
13
14
  };
@@ -0,0 +1,9 @@
1
+ import { IdentifiedObject } from 'onroute-policy-engine/types';
2
+ export type RangeMatrix = IdentifiedObject & {
3
+ matrix: Matrix[];
4
+ };
5
+ export type Matrix = {
6
+ min?: number;
7
+ max?: number;
8
+ value: number;
9
+ };
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -1,6 +1,6 @@
1
1
  export type RegionSizeOverride = {
2
2
  region: string;
3
- width?: number;
4
- height?: number;
5
- length?: number;
3
+ w?: number;
4
+ h?: number;
5
+ l?: number;
6
6
  };
@@ -1,3 +1,3 @@
1
1
  export type SelfIssuable = {
2
- canSelfIssue?: boolean;
2
+ selfIssue?: boolean;
3
3
  };