onroute-policy-engine 2.22.1 → 2.24.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.
@@ -0,0 +1,8 @@
1
+ /**
2
+ * This is the default description used for each cost object,
3
+ * when there is no explicit description provided (eg. Non-STWSE permit costs).
4
+ *
5
+ * NOTE: DO NOT change the value of this constant, as the value is being used elsewhere
6
+ * throughout the backend.
7
+ */
8
+ export declare const DEFAULT_COST_DESCRIPTION = "Calculated permit cost";
@@ -0,0 +1,11 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DEFAULT_COST_DESCRIPTION = void 0;
4
+ /**
5
+ * This is the default description used for each cost object,
6
+ * when there is no explicit description provided (eg. Non-STWSE permit costs).
7
+ *
8
+ * NOTE: DO NOT change the value of this constant, as the value is being used elsewhere
9
+ * throughout the backend.
10
+ */
11
+ exports.DEFAULT_COST_DESCRIPTION = 'Calculated permit cost';
@@ -0,0 +1,9 @@
1
+ export declare const BASE_OVERLOAD_LIMIT = 28000;
2
+ export declare const EXTRA_WEIGHT_INTERVAL = 900;
3
+ export declare const EXTRA_RATE_INCREMENT = 1.85;
4
+ export declare const DEFAULT_MAX_RATE = 21.4;
5
+ export declare const MINIMUM_OVERLOAD_FEE = 25;
6
+ export declare const OVERLOAD_RATES_PER_10KM: {
7
+ max: number;
8
+ rate: number;
9
+ }[];
@@ -0,0 +1,38 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.OVERLOAD_RATES_PER_10KM = exports.MINIMUM_OVERLOAD_FEE = exports.DEFAULT_MAX_RATE = exports.EXTRA_RATE_INCREMENT = exports.EXTRA_WEIGHT_INTERVAL = exports.BASE_OVERLOAD_LIMIT = void 0;
4
+ exports.BASE_OVERLOAD_LIMIT = 28000;
5
+ exports.EXTRA_WEIGHT_INTERVAL = 900;
6
+ exports.EXTRA_RATE_INCREMENT = 1.85;
7
+ exports.DEFAULT_MAX_RATE = 21.4;
8
+ exports.MINIMUM_OVERLOAD_FEE = 25;
9
+ // Overload permit fee rate table mapping up to 28,000 kg
10
+ exports.OVERLOAD_RATES_PER_10KM = [
11
+ { max: 2000, rate: 0.95 },
12
+ { max: 3000, rate: 1.15 },
13
+ { max: 4000, rate: 1.4 },
14
+ { max: 5000, rate: 1.6 },
15
+ { max: 6000, rate: 1.85 },
16
+ { max: 7000, rate: 2.15 },
17
+ { max: 8000, rate: 2.45 },
18
+ { max: 9000, rate: 2.95 },
19
+ { max: 10000, rate: 3.35 },
20
+ { max: 11000, rate: 3.75 },
21
+ { max: 12000, rate: 4.25 },
22
+ { max: 13000, rate: 4.95 },
23
+ { max: 14000, rate: 5.6 },
24
+ { max: 15000, rate: 6.25 },
25
+ { max: 16000, rate: 7.25 },
26
+ { max: 17000, rate: 8.25 },
27
+ { max: 18000, rate: 9.15 },
28
+ { max: 19000, rate: 10.1 },
29
+ { max: 20000, rate: 10.9 },
30
+ { max: 21000, rate: 11.85 },
31
+ { max: 22000, rate: 12.7 },
32
+ { max: 23000, rate: 13.95 },
33
+ { max: 24000, rate: 14.95 },
34
+ { max: 25000, rate: 16.1 },
35
+ { max: 26000, rate: 17.85 },
36
+ { max: 27000, rate: 19.85 },
37
+ { max: 28000, rate: 21.4 },
38
+ ];
@@ -1,6 +1 @@
1
1
  export declare const AXLE_CALC_RESULTS_FACT = "axleCalcResults";
2
- export declare const BASE_OVERLOAD_LIMIT = 28000;
3
- export declare const EXTRA_WEIGHT_INTERVAL = 900;
4
- export declare const EXTRA_RATE_INCREMENT = 1.85;
5
- export declare const DEFAULT_MAX_RATE = 21.4;
6
- export declare const MINIMUM_OVERLOAD_FEE = 25;
@@ -1,9 +1,4 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.MINIMUM_OVERLOAD_FEE = exports.DEFAULT_MAX_RATE = exports.EXTRA_RATE_INCREMENT = exports.EXTRA_WEIGHT_INTERVAL = exports.BASE_OVERLOAD_LIMIT = exports.AXLE_CALC_RESULTS_FACT = void 0;
3
+ exports.AXLE_CALC_RESULTS_FACT = void 0;
4
4
  exports.AXLE_CALC_RESULTS_FACT = 'axleCalcResults';
5
- exports.BASE_OVERLOAD_LIMIT = 28000;
6
- exports.EXTRA_WEIGHT_INTERVAL = 900;
7
- exports.EXTRA_RATE_INCREMENT = 1.85;
8
- exports.DEFAULT_MAX_RATE = 21.4;
9
- exports.MINIMUM_OVERLOAD_FEE = 25;
@@ -17,6 +17,7 @@ export declare enum CostFacts {
17
17
  CostPerKilometre = "costPerKilometre",
18
18
  CostPerMonth = "costPerMonth",
19
19
  FixedCost = "fixedCost",
20
- OverloadCost = "overloadCost",
20
+ OverloadGvwCost = "overloadGvwCost",
21
+ OverloadWeightCost = "overloadWeightCost",
21
22
  RangeMatrixCostLookup = "rangeMatrixCostLookup"
22
23
  }
@@ -22,6 +22,7 @@ var CostFacts;
22
22
  CostFacts["CostPerKilometre"] = "costPerKilometre";
23
23
  CostFacts["CostPerMonth"] = "costPerMonth";
24
24
  CostFacts["FixedCost"] = "fixedCost";
25
- CostFacts["OverloadCost"] = "overloadCost";
25
+ CostFacts["OverloadGvwCost"] = "overloadGvwCost";
26
+ CostFacts["OverloadWeightCost"] = "overloadWeightCost";
26
27
  CostFacts["RangeMatrixCostLookup"] = "rangeMatrixCostLookup";
27
28
  })(CostFacts || (exports.CostFacts = CostFacts = {}));
@@ -7,6 +7,7 @@ export declare enum PolicyCheckId {
7
7
  AxleGroupMaximumLegalWeightThreshold = "axle-group-maximum-legal-weight-threshold",
8
8
  BoosterAxleLimit = "booster-axle-limit",
9
9
  BridgeFormula = "bridge-formula",
10
+ CheckLegalWeight = "check-legal-weight",
10
11
  CheckPermittableWeight = "check-permittable-weight",
11
12
  DriveJeepLoadEqualization = "drive-jeep-load-equalization",
12
13
  MaxTireLoad = "max-tire-load",
@@ -12,6 +12,7 @@ var PolicyCheckId;
12
12
  PolicyCheckId["AxleGroupMaximumLegalWeightThreshold"] = "axle-group-maximum-legal-weight-threshold";
13
13
  PolicyCheckId["BoosterAxleLimit"] = "booster-axle-limit";
14
14
  PolicyCheckId["BridgeFormula"] = "bridge-formula";
15
+ PolicyCheckId["CheckLegalWeight"] = "check-legal-weight";
15
16
  PolicyCheckId["CheckPermittableWeight"] = "check-permittable-weight";
16
17
  PolicyCheckId["DriveJeepLoadEqualization"] = "drive-jeep-load-equalization";
17
18
  PolicyCheckId["MaxTireLoad"] = "max-tire-load";
@@ -4,6 +4,7 @@ export declare enum ValidationResultCode {
4
4
  PolicyValidationError = "policy-validation-error",
5
5
  ConfigurationInvalid = "configuration-invalid",
6
6
  GeneralResult = "general-result",
7
+ DimensionOversize = "dimension-oversize",
7
8
  CostValue = "cost-value",
8
9
  IsLcvCarrier = "lcv-carrier",
9
10
  NoFeeClient = "no-fee-client"
@@ -8,6 +8,7 @@ var ValidationResultCode;
8
8
  ValidationResultCode["PolicyValidationError"] = "policy-validation-error";
9
9
  ValidationResultCode["ConfigurationInvalid"] = "configuration-invalid";
10
10
  ValidationResultCode["GeneralResult"] = "general-result";
11
+ ValidationResultCode["DimensionOversize"] = "dimension-oversize";
11
12
  ValidationResultCode["CostValue"] = "cost-value";
12
13
  ValidationResultCode["IsLcvCarrier"] = "lcv-carrier";
13
14
  ValidationResultCode["NoFeeClient"] = "no-fee-client";
@@ -9,6 +9,7 @@ const quarterOfYear_1 = __importDefault(require("dayjs/plugin/quarterOfYear"));
9
9
  const enum_1 = require("onroute-policy-engine/enum");
10
10
  const policy_check_helper_1 = require("./policy-check.helper");
11
11
  const stgvwi_1 = require("../constants/stgvwi");
12
+ const overload_1 = require("../constants/overload");
12
13
  dayjs_1.default.extend(quarterOfYear_1.default);
13
14
  const getAxleCalculationInputs = async (policy, almanac) => {
14
15
  var _a, _b;
@@ -28,6 +29,28 @@ const getAxleCalculationInputs = async (policy, almanac) => {
28
29
  licensedGVW: vehicleDetails.licensedGVW || 0,
29
30
  };
30
31
  };
32
+ const getOverloadCost = (overloadKg, totalDistance) => {
33
+ let ratePer10km = 0;
34
+ if (overloadKg <= overload_1.BASE_OVERLOAD_LIMIT) {
35
+ const match = overload_1.OVERLOAD_RATES_PER_10KM.find((r) => overloadKg <= r.max);
36
+ ratePer10km = match ? match.rate : overload_1.DEFAULT_MAX_RATE;
37
+ }
38
+ else {
39
+ // For overload greater than BASE_OVERLOAD_LIMIT kg
40
+ const extraWeight = overloadKg - overload_1.BASE_OVERLOAD_LIMIT;
41
+ const intervals = Math.ceil(extraWeight / overload_1.EXTRA_WEIGHT_INTERVAL);
42
+ ratePer10km = overload_1.DEFAULT_MAX_RATE + intervals * overload_1.EXTRA_RATE_INCREMENT;
43
+ }
44
+ // Fee calculation: rate * (totalDistance / 10)
45
+ const distanceUnits = Math.ceil(totalDistance / 10);
46
+ let totalFee = ratePer10km * distanceUnits;
47
+ // Apply minimum fee rule
48
+ if (totalFee < overload_1.MINIMUM_OVERLOAD_FEE) {
49
+ totalFee = overload_1.MINIMUM_OVERLOAD_FEE;
50
+ }
51
+ // Round to the nearest dollar (0.50 rounds up)
52
+ return Math.round(totalFee);
53
+ };
31
54
  /**
32
55
  * Adds runtime facts for the validation. For example, adds the
33
56
  * validation date for comparison against startDate of the permit.
@@ -308,7 +331,7 @@ function addRuntimeFacts(engine, policy) {
308
331
  * Add runtime fact to calculate the overload fee for STGVWI permits
309
332
  * based on the Licensed GVW Increase and Total Distance.
310
333
  */
311
- engine.addFact(enum_1.CostFacts.OverloadCost.toString(), async function (params, almanac) {
334
+ engine.addFact(enum_1.CostFacts.OverloadGvwCost.toString(), async function (params, almanac) {
312
335
  const actualGVW = await almanac.factValue(enum_1.PermitAppInfo.PermitData, {}, 'vehicleConfiguration.actualGVW');
313
336
  const licensedGVW = await almanac.factValue(enum_1.PermitAppInfo.PermitData, {}, 'vehicleDetails.licensedGVW');
314
337
  const distance = await almanac.factValue(enum_1.PermitAppInfo.PermitData, {}, enum_1.PermitAppInfo.TotalDistance);
@@ -316,55 +339,18 @@ function addRuntimeFacts(engine, policy) {
316
339
  if (overloadKg <= 0 || !distance) {
317
340
  return 0;
318
341
  }
319
- let ratePer10km = 0;
320
- if (overloadKg <= stgvwi_1.BASE_OVERLOAD_LIMIT) {
321
- // Overload permit fee rate table mapping up to 28,000 kg
322
- const rates = [
323
- { max: 2000, rate: 0.95 },
324
- { max: 3000, rate: 1.15 },
325
- { max: 4000, rate: 1.4 },
326
- { max: 5000, rate: 1.6 },
327
- { max: 6000, rate: 1.85 },
328
- { max: 7000, rate: 2.15 },
329
- { max: 8000, rate: 2.45 },
330
- { max: 9000, rate: 2.95 },
331
- { max: 10000, rate: 3.35 },
332
- { max: 11000, rate: 3.75 },
333
- { max: 12000, rate: 4.25 },
334
- { max: 13000, rate: 4.95 },
335
- { max: 14000, rate: 5.6 },
336
- { max: 15000, rate: 6.25 },
337
- { max: 16000, rate: 7.25 },
338
- { max: 17000, rate: 8.25 },
339
- { max: 18000, rate: 9.15 },
340
- { max: 19000, rate: 10.1 },
341
- { max: 20000, rate: 10.9 },
342
- { max: 21000, rate: 11.85 },
343
- { max: 22000, rate: 12.7 },
344
- { max: 23000, rate: 13.95 },
345
- { max: 24000, rate: 14.95 },
346
- { max: 25000, rate: 16.1 },
347
- { max: 26000, rate: 17.85 },
348
- { max: 27000, rate: 19.85 },
349
- { max: 28000, rate: 21.4 },
350
- ];
351
- const match = rates.find((r) => overloadKg <= r.max);
352
- ratePer10km = match ? match.rate : stgvwi_1.DEFAULT_MAX_RATE;
353
- }
354
- else {
355
- // For overload greater than BASE_OVERLOAD_LIMIT kg
356
- const extraWeight = overloadKg - stgvwi_1.BASE_OVERLOAD_LIMIT;
357
- const intervals = Math.ceil(extraWeight / stgvwi_1.EXTRA_WEIGHT_INTERVAL);
358
- ratePer10km = stgvwi_1.DEFAULT_MAX_RATE + intervals * stgvwi_1.EXTRA_RATE_INCREMENT;
359
- }
360
- // Fee calculation: rate * (distance / 10)
361
- const distanceUnits = Math.ceil(distance / 10);
362
- let totalFee = ratePer10km * distanceUnits;
363
- // Apply minimum fee rule
364
- if (totalFee < stgvwi_1.MINIMUM_OVERLOAD_FEE) {
365
- totalFee = stgvwi_1.MINIMUM_OVERLOAD_FEE;
342
+ return getOverloadCost(overloadKg, distance);
343
+ });
344
+ /**
345
+ * Add runtime fact to calculate the overload fee for STWSE permits
346
+ * based on the Weight over 27.5m and Total Distance.
347
+ */
348
+ engine.addFact(enum_1.CostFacts.OverloadWeightCost.toString(), async function (params, almanac) {
349
+ const overloadWeight = await almanac.factValue(enum_1.PermitAppInfo.PermitData, {}, 'vehicleConfiguration.overloadWeight');
350
+ const totalDistance = await almanac.factValue(enum_1.PermitAppInfo.PermitData, {}, enum_1.PermitAppInfo.TotalDistance);
351
+ if (overloadWeight <= 0 || !totalDistance) {
352
+ return 0;
366
353
  }
367
- // Round to the nearest dollar (0.50 rounds up)
368
- return Math.round(totalFee);
354
+ return getOverloadCost(overloadWeight, totalDistance);
369
355
  });
370
356
  }
@@ -16,6 +16,14 @@ import { Policy } from 'onroute-policy-engine';
16
16
  type PolicyCheck = (policy: Policy, vehicleConfiguration: Array<string>, axleConfiguration: Array<AxleConfiguration>) => Array<PolicyCheckResult>;
17
17
  /** Applies the standard lower-of rule or the exact 7.16(g) exception. */
18
18
  export declare function getMaximumLegalAxleGroupWeightThreshold(individualLegalWeightSum: number, ctr717TableValue: number, isTandemDriveSingleJeepException: boolean): number;
19
+ /**
20
+ * Validates each axle unit against its legal weight maximum.
21
+ *
22
+ * The feature-defined steering and drive limits are authoritative for the
23
+ * standard truck/truck-tractor and PME subtypes. Other axle units retain
24
+ * their configured legal weight lookup.
25
+ */
26
+ export declare function CheckLegalWeight(policy: Policy, vehicleConfiguration: Array<string>, axleConfiguration: Array<AxleConfiguration>): Array<PolicyCheckResult>;
19
27
  /**
20
28
  * Validates every contiguous axle-unit group within 8 m, excluding groups
21
29
  * beginning at axle unit 1, against the CTR 7.17 maximum legal weight.
@@ -301,6 +309,7 @@ export declare function CheckDriveJeepLoadEqualization(_policy: Policy, vehicleC
301
309
  * Currently includes:
302
310
  * - AxleGroupMaximumLegalWeightThreshold: Validates axle groups within 8 m against CTR 7.17
303
311
  * - BridgeFormula: Validates axle groups against bridge formula requirements
312
+ * - CheckLegalWeight: Validates axle units against legal weight limits
304
313
  * - CheckPermittableWeight: Validates total vehicle weight against permit limits
305
314
  * - MaxTireLoad: Validates tire load capacity for each axle unit
306
315
  * - MinDriveAxleWeight: Validates minimum weight requirements for drive axles
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.policyCheckMap = void 0;
4
4
  exports.getMaximumLegalAxleGroupWeightThreshold = getMaximumLegalAxleGroupWeightThreshold;
5
+ exports.CheckLegalWeight = CheckLegalWeight;
5
6
  exports.CheckAxleGroupMaximumLegalWeightThreshold = CheckAxleGroupMaximumLegalWeightThreshold;
6
7
  exports.CheckNumberOfAxles = CheckNumberOfAxles;
7
8
  exports.CheckBridgeFormula = CheckBridgeFormula;
@@ -18,6 +19,27 @@ exports.CheckDriveJeepLoadEqualization = CheckDriveJeepLoadEqualization;
18
19
  const dimensions_helper_1 = require("./dimensions.helper");
19
20
  const ctr_717_helper_1 = require("./ctr-717.helper");
20
21
  const enum_1 = require("../enum");
22
+ const POWER_UNIT_AXLE_CODE_MULTIPLIER = 10;
23
+ const LEGAL_WEIGHT_MAXIMUM_KG = {
24
+ SINGLE_AXLE: 9100,
25
+ STANDARD_TRUCK_TRACTOR_STEER: 6000,
26
+ STANDARD_SINGLE_STEER_WITH_TRIDEM_DRIVE: 7300,
27
+ TANDEM_AXLE: 17000,
28
+ STANDARD_TANDEM_STEER_WITH_TRIDEM_DRIVE: 13600,
29
+ PME_TANDEM_STEER_WITH_TRIDEM_DRIVE: 15200,
30
+ TRIDEM_AXLE: 24000,
31
+ };
32
+ const PERMITTABLE_WEIGHT_MAXIMUM_KG = {
33
+ SINGLE_STEER: 9100,
34
+ SINGLE_NON_STEER: 11000,
35
+ TANDEM: 23000,
36
+ STANDARD_TRIDEM: 28000,
37
+ QUALIFYING_TRIDEM: 29000,
38
+ };
39
+ const QUALIFYING_TRIDEM_SPREAD_CM = {
40
+ MINIMUM: 240,
41
+ MAXIMUM: 370,
42
+ };
21
43
  /** Applies the standard lower-of rule or the exact 7.16(g) exception. */
22
44
  function getMaximumLegalAxleGroupWeightThreshold(individualLegalWeightSum, ctr717TableValue, isTandemDriveSingleJeepException) {
23
45
  const tandemDriveSingleJeepMinimumKg = 24000;
@@ -34,7 +56,7 @@ function getApplicableLegalWeightThreshold(policy, vehicleConfiguration, axleCon
34
56
  return undefined;
35
57
  }
36
58
  const weights = vehicleIndex === 0
37
- ? policy.getDefaultPowerUnitWeight(vehicleType, axleConfiguration[0].numberOfAxles * 10 +
59
+ ? policy.getDefaultPowerUnitWeight(vehicleType, axleConfiguration[0].numberOfAxles * POWER_UNIT_AXLE_CODE_MULTIPLIER +
38
60
  axleConfiguration[1].numberOfAxles)
39
61
  : policy.getDefaultTrailerWeight(vehicleType, axle.numberOfAxles);
40
62
  if (weights.length === 0) {
@@ -42,6 +64,98 @@ function getApplicableLegalWeightThreshold(policy, vehicleConfiguration, axleCon
42
64
  }
43
65
  return (_a = policy.selectCorrectWeightDimension(weights, vehicleConfiguration, axleConfiguration, axleIndex)) === null || _a === void 0 ? void 0 : _a.legal;
44
66
  }
67
+ function getAxleUnitVehicleIndexLookup(policy, vehicleConfiguration, axleConfiguration) {
68
+ return axleConfiguration.some((axleUnit) => axleUnit.vehicleIndex !== undefined)
69
+ ? (0, dimensions_helper_1.getAxleUnitVehicleIndexes)(policy, vehicleConfiguration, axleConfiguration)
70
+ : axleConfiguration.map((_, axleIndex) => axleIndex < 2 ? 0 : axleIndex - 1);
71
+ }
72
+ function getConfiguredAxleUnitWeightThreshold(policy, vehicleConfiguration, axleConfiguration, axleUnitVehicleIndexes, axleIndex, threshold) {
73
+ var _a;
74
+ var _b;
75
+ const vehicleIndex = axleUnitVehicleIndexes[axleIndex];
76
+ const vehicleType = vehicleConfiguration[vehicleIndex];
77
+ const axleUnit = axleConfiguration[axleIndex];
78
+ if (!vehicleType || !axleUnit) {
79
+ return 0;
80
+ }
81
+ const weights = vehicleIndex === 0
82
+ ? policy.getDefaultPowerUnitWeight(vehicleType, axleConfiguration[0].numberOfAxles * POWER_UNIT_AXLE_CODE_MULTIPLIER +
83
+ axleConfiguration[1].numberOfAxles)
84
+ : policy.getDefaultTrailerWeight(vehicleType, axleUnit.numberOfAxles);
85
+ if (weights.length === 0) {
86
+ return 0;
87
+ }
88
+ return ((_b = (_a = policy.selectCorrectWeightDimension(weights, vehicleConfiguration, axleConfiguration, axleIndex)) === null || _a === void 0 ? void 0 : _a[threshold]) !== null && _b !== void 0 ? _b : 0);
89
+ }
90
+ function isFeatureLegalWeightPowerUnit(vehicleType) {
91
+ return (vehicleType === 'REGTRCK' ||
92
+ vehicleType === 'TRKTRAC' ||
93
+ vehicleType === 'TRCKPME' ||
94
+ vehicleType === 'TRACPME');
95
+ }
96
+ function getFeatureLegalPowerUnitWeightThreshold(vehicleType, axleConfiguration, axleIndex) {
97
+ const steerAxle = axleConfiguration[0];
98
+ const driveAxle = axleConfiguration[1];
99
+ const hasPme = vehicleType === 'TRCKPME' || vehicleType === 'TRACPME';
100
+ if (axleIndex === 0) {
101
+ if (steerAxle.numberOfAxles === 1 &&
102
+ (driveAxle.numberOfAxles === 1 || driveAxle.numberOfAxles === 2)) {
103
+ return hasPme || vehicleType === 'REGTRCK'
104
+ ? LEGAL_WEIGHT_MAXIMUM_KG.SINGLE_AXLE
105
+ : LEGAL_WEIGHT_MAXIMUM_KG.STANDARD_TRUCK_TRACTOR_STEER;
106
+ }
107
+ if (steerAxle.numberOfAxles === 1 && driveAxle.numberOfAxles === 3) {
108
+ return hasPme
109
+ ? LEGAL_WEIGHT_MAXIMUM_KG.SINGLE_AXLE
110
+ : LEGAL_WEIGHT_MAXIMUM_KG.STANDARD_SINGLE_STEER_WITH_TRIDEM_DRIVE;
111
+ }
112
+ if (steerAxle.numberOfAxles === 2 && driveAxle.numberOfAxles === 2) {
113
+ return LEGAL_WEIGHT_MAXIMUM_KG.TANDEM_AXLE;
114
+ }
115
+ if (steerAxle.numberOfAxles === 2 && driveAxle.numberOfAxles === 3) {
116
+ return hasPme
117
+ ? LEGAL_WEIGHT_MAXIMUM_KG.PME_TANDEM_STEER_WITH_TRIDEM_DRIVE
118
+ : LEGAL_WEIGHT_MAXIMUM_KG.STANDARD_TANDEM_STEER_WITH_TRIDEM_DRIVE;
119
+ }
120
+ }
121
+ if (axleIndex === 1) {
122
+ return {
123
+ 1: LEGAL_WEIGHT_MAXIMUM_KG.SINGLE_AXLE,
124
+ 2: LEGAL_WEIGHT_MAXIMUM_KG.TANDEM_AXLE,
125
+ 3: LEGAL_WEIGHT_MAXIMUM_KG.TRIDEM_AXLE,
126
+ }[driveAxle.numberOfAxles];
127
+ }
128
+ return undefined;
129
+ }
130
+ /**
131
+ * Validates each axle unit against its legal weight maximum.
132
+ *
133
+ * The feature-defined steering and drive limits are authoritative for the
134
+ * standard truck/truck-tractor and PME subtypes. Other axle units retain
135
+ * their configured legal weight lookup.
136
+ */
137
+ function CheckLegalWeight(policy, vehicleConfiguration, axleConfiguration) {
138
+ const policyId = enum_1.PolicyCheckId.CheckLegalWeight;
139
+ const powerUnitType = vehicleConfiguration[0];
140
+ const axleUnitVehicleIndexes = getAxleUnitVehicleIndexLookup(policy, vehicleConfiguration, axleConfiguration);
141
+ return axleConfiguration.map((axleUnit, axleIndex) => {
142
+ const featureThreshold = axleUnitVehicleIndexes[axleIndex] === 0 &&
143
+ isFeatureLegalWeightPowerUnit(powerUnitType)
144
+ ? getFeatureLegalPowerUnitWeightThreshold(powerUnitType, axleConfiguration, axleIndex)
145
+ : undefined;
146
+ const legalWeight = featureThreshold !== null && featureThreshold !== void 0 ? featureThreshold : getConfiguredAxleUnitWeightThreshold(policy, vehicleConfiguration, axleConfiguration, axleUnitVehicleIndexes, axleIndex, 'legal');
147
+ const result = axleUnit.axleUnitWeight <= legalWeight;
148
+ const axleUnitNumber = axleIndex + 1;
149
+ return {
150
+ id: policyId,
151
+ message: `Weight for axle unit ${axleUnitNumber} ${result ? 'is legal' : `must not exceed ${legalWeight} kgs`}`,
152
+ result: result ? enum_1.PolicyCheckResultType.Pass : enum_1.PolicyCheckResultType.Fail,
153
+ axleUnit: axleUnitNumber,
154
+ actualWeight: axleUnit.axleUnitWeight,
155
+ thresholdWeight: legalWeight,
156
+ };
157
+ });
158
+ }
45
159
  /**
46
160
  * Validates every contiguous axle-unit group within 8 m, excluding groups
47
161
  * beginning at axle unit 1, against the CTR 7.17 maximum legal weight.
@@ -172,6 +286,37 @@ function CheckNumberOfAxles(_policy, _vehicleConfiguration, axleConfiguration) {
172
286
  });
173
287
  return policyCheckResults;
174
288
  }
289
+ function getBridgeFormulaInputFailure(axleConfiguration) {
290
+ for (let axleIndex = 0; axleIndex < axleConfiguration.length; axleIndex++) {
291
+ const axleUnit = axleConfiguration[axleIndex];
292
+ const axleUnitNumber = axleIndex + 1;
293
+ let message;
294
+ if (!Number.isFinite(axleUnit.axleUnitWeight) ||
295
+ axleUnit.axleUnitWeight <= 0) {
296
+ message = `Axle unit weight for axle unit ${axleUnitNumber} must be a finite number greater than 0.`;
297
+ }
298
+ else if (axleUnit.numberOfAxles > 1 &&
299
+ (!Number.isFinite(axleUnit.axleSpread) ||
300
+ axleUnit.axleSpread <= 0)) {
301
+ message = `Axle spread for axle unit ${axleUnitNumber} must be a finite number greater than 0 when the unit has multiple axles.`;
302
+ }
303
+ else if (axleIndex > 0 &&
304
+ (!Number.isFinite(axleUnit.interaxleSpacing) ||
305
+ axleUnit.interaxleSpacing <= 0)) {
306
+ message = `Axle spacing before axle unit ${axleUnitNumber} must be a finite number greater than 0.`;
307
+ }
308
+ if (message) {
309
+ return {
310
+ id: enum_1.PolicyCheckId.BridgeFormula,
311
+ message,
312
+ result: enum_1.PolicyCheckResultType.Fail,
313
+ startAxleUnit: axleUnitNumber,
314
+ endAxleUnit: axleUnitNumber,
315
+ };
316
+ }
317
+ }
318
+ return undefined;
319
+ }
175
320
  /**
176
321
  * Performs bridge formula calculations on axle groups and returns policy check results.
177
322
  *
@@ -201,6 +346,10 @@ function CheckNumberOfAxles(_policy, _vehicleConfiguration, axleConfiguration) {
201
346
  function CheckBridgeFormula(policy, _vehicleConfiguration, axleConfiguration) {
202
347
  const policyCheckResults = new Array();
203
348
  const policyId = enum_1.PolicyCheckId.BridgeFormula;
349
+ const inputFailure = getBridgeFormulaInputFailure(axleConfiguration);
350
+ if (inputFailure) {
351
+ return [inputFailure];
352
+ }
204
353
  const bridgeCalcResults = policy.calculateBridge(axleConfiguration);
205
354
  bridgeCalcResults.forEach((br) => {
206
355
  const message = `Axle group ${br.startAxleUnit} to ${br.endAxleUnit} ${br.success ? 'passes' : 'does not pass'} bridge formula.`;
@@ -338,77 +487,51 @@ function CheckNumTiresPerAxle(_policy, _vehicleConfiguration, axleConfiguration)
338
487
  * @see SingleAxleDimension
339
488
  */
340
489
  function CheckPermittableWeight(policy, vehicleConfiguration, axleConfiguration) {
341
- const policyCheckResults = new Array();
342
490
  const policyId = enum_1.PolicyCheckId.CheckPermittableWeight;
343
- const hasVehicleIndexes = axleConfiguration.some((axleUnit) => axleUnit.vehicleIndex !== undefined);
344
- // Legacy John Fletcher implementation, restored from:
345
- // git show e4a9d6badd025dec844df43379f776a4995ad75b^:src/helper/policy-check.helper.ts
346
- if (!hasVehicleIndexes) {
347
- const singleAxleDimensions = [];
348
- vehicleConfiguration.forEach((vc, i) => {
349
- let weight;
350
- if (i === 0) {
351
- const powerUnitAxles = axleConfiguration[0].numberOfAxles * 10 +
352
- axleConfiguration[1].numberOfAxles;
353
- weight = policy.getDefaultPowerUnitWeight(vc, powerUnitAxles);
354
- const steerAxleDimension = policy.selectCorrectWeightDimension(weight, vehicleConfiguration, axleConfiguration, 0) || {};
355
- singleAxleDimensions.push(steerAxleDimension);
356
- const driveAxleDimension = policy.selectCorrectWeightDimension(weight, vehicleConfiguration, axleConfiguration, 1) || {};
357
- singleAxleDimensions.push(driveAxleDimension);
491
+ const axleUnitVehicleIndexes = getAxleUnitVehicleIndexLookup(policy, vehicleConfiguration, axleConfiguration);
492
+ return axleConfiguration.map((axleUnit, axleIndex) => {
493
+ const vehicleIndex = axleUnitVehicleIndexes[axleIndex];
494
+ const isSingleSteer = axleIndex === 0 && vehicleIndex === 0 && axleUnit.numberOfAxles === 1;
495
+ const isTandemSteer = axleIndex === 0 && vehicleIndex === 0 && axleUnit.numberOfAxles === 2;
496
+ let permittableWeight;
497
+ if (isSingleSteer) {
498
+ permittableWeight = PERMITTABLE_WEIGHT_MAXIMUM_KG.SINGLE_STEER;
499
+ }
500
+ else if (!isTandemSteer) {
501
+ if (axleUnit.numberOfAxles === 1) {
502
+ permittableWeight = PERMITTABLE_WEIGHT_MAXIMUM_KG.SINGLE_NON_STEER;
358
503
  }
359
- else if (i + 1 < axleConfiguration.length) {
360
- weight = policy.getDefaultTrailerWeight(vc, axleConfiguration[i + 1].numberOfAxles);
361
- const trailerDimension = policy.selectCorrectWeightDimension(weight, vehicleConfiguration, axleConfiguration, i + 1) || {};
362
- singleAxleDimensions.push(trailerDimension);
504
+ else if (axleUnit.numberOfAxles === 2) {
505
+ permittableWeight = PERMITTABLE_WEIGHT_MAXIMUM_KG.TANDEM;
363
506
  }
364
- });
365
- axleConfiguration.forEach((ac, i) => {
366
- const actualWeight = ac.axleUnitWeight;
367
- const permittableWeight = singleAxleDimensions[i].permittable || 0;
368
- const result = actualWeight <= permittableWeight;
369
- const axleUnit = i + 1;
370
- const message = `Weight for axle unit ${axleUnit} ${result ? 'is permittable' : `must not exceed ${permittableWeight} kgs`}`;
371
- policyCheckResults.push({
372
- id: policyId,
373
- message: message,
374
- result: result
375
- ? enum_1.PolicyCheckResultType.Pass
376
- : enum_1.PolicyCheckResultType.Fail,
377
- axleUnit: axleUnit,
378
- actualWeight: actualWeight,
379
- thresholdWeight: permittableWeight,
380
- });
381
- });
382
- return policyCheckResults;
383
- }
384
- // Explicit multi-axle ownership path supplied by the consuming application.
385
- // This is the new way, using { vehicleIndex: num }
386
- const axleUnitVehicleIndexes = (0, dimensions_helper_1.getAxleUnitVehicleIndexes)(policy, vehicleConfiguration, axleConfiguration);
387
- axleConfiguration.forEach((ac, i) => {
388
- const vehicleIndex = axleUnitVehicleIndexes[i];
389
- const vehicleType = vehicleConfiguration[vehicleIndex];
390
- const weight = vehicleIndex === 0
391
- ? policy.getDefaultPowerUnitWeight(vehicleType, axleConfiguration[0].numberOfAxles * 10 +
392
- axleConfiguration[1].numberOfAxles)
393
- : policy.getDefaultTrailerWeight(vehicleType, ac.numberOfAxles);
394
- const axleDimension = weight.length > 0
395
- ? policy.selectCorrectWeightDimension(weight, vehicleConfiguration, axleConfiguration, i) || {}
396
- : {};
397
- const actualWeight = ac.axleUnitWeight;
398
- const permittableWeight = axleDimension.permittable || 0;
399
- const result = actualWeight <= permittableWeight;
400
- const axleUnit = i + 1;
401
- const message = `Weight for axle unit ${axleUnit} ${result ? 'is permittable' : `must not exceed ${permittableWeight} kgs`}`;
402
- policyCheckResults.push({
507
+ else if (axleUnit.numberOfAxles === 3) {
508
+ const spreadQualifies = axleUnit.axleSpread !== undefined &&
509
+ axleUnit.axleSpread >= QUALIFYING_TRIDEM_SPREAD_CM.MINIMUM &&
510
+ axleUnit.axleSpread <= QUALIFYING_TRIDEM_SPREAD_CM.MAXIMUM;
511
+ const nextVehicleIndex = vehicleIndex + 1;
512
+ const hasImmediatelyFollowingBooster = vehicleConfiguration[nextVehicleIndex] ===
513
+ enum_1.AccessoryVehicleType.Booster;
514
+ const boosterAxle = axleConfiguration.find((_, candidateIndex) => candidateIndex > axleIndex &&
515
+ axleUnitVehicleIndexes[candidateIndex] === nextVehicleIndex);
516
+ const boosterQualifies = !hasImmediatelyFollowingBooster || (boosterAxle === null || boosterAxle === void 0 ? void 0 : boosterAxle.numberOfAxles) === 1;
517
+ permittableWeight =
518
+ spreadQualifies && boosterQualifies
519
+ ? PERMITTABLE_WEIGHT_MAXIMUM_KG.QUALIFYING_TRIDEM
520
+ : PERMITTABLE_WEIGHT_MAXIMUM_KG.STANDARD_TRIDEM;
521
+ }
522
+ }
523
+ permittableWeight !== null && permittableWeight !== void 0 ? permittableWeight : (permittableWeight = getConfiguredAxleUnitWeightThreshold(policy, vehicleConfiguration, axleConfiguration, axleUnitVehicleIndexes, axleIndex, 'permittable'));
524
+ const result = axleUnit.axleUnitWeight <= permittableWeight;
525
+ const axleUnitNumber = axleIndex + 1;
526
+ return {
403
527
  id: policyId,
404
- message: message,
528
+ message: `Weight for axle unit ${axleUnitNumber} ${result ? 'is permittable' : `must not exceed ${permittableWeight} kgs`}`,
405
529
  result: result ? enum_1.PolicyCheckResultType.Pass : enum_1.PolicyCheckResultType.Fail,
406
- axleUnit: axleUnit,
407
- actualWeight: actualWeight,
530
+ axleUnit: axleUnitNumber,
531
+ actualWeight: axleUnit.axleUnitWeight,
408
532
  thresholdWeight: permittableWeight,
409
- });
533
+ };
410
534
  });
411
- return policyCheckResults;
412
535
  }
413
536
  /**
414
537
  * Validates minimum steer axle weight requirements for single steer, tridem drive power units.
@@ -1092,6 +1215,7 @@ function CheckDriveJeepLoadEqualization(_policy, vehicleConfiguration, axleConfi
1092
1215
  * Currently includes:
1093
1216
  * - AxleGroupMaximumLegalWeightThreshold: Validates axle groups within 8 m against CTR 7.17
1094
1217
  * - BridgeFormula: Validates axle groups against bridge formula requirements
1218
+ * - CheckLegalWeight: Validates axle units against legal weight limits
1095
1219
  * - CheckPermittableWeight: Validates total vehicle weight against permit limits
1096
1220
  * - MaxTireLoad: Validates tire load capacity for each axle unit
1097
1221
  * - MinDriveAxleWeight: Validates minimum weight requirements for drive axles
@@ -1116,6 +1240,7 @@ exports.policyCheckMap = new Map([
1116
1240
  enum_1.PolicyCheckId.AxleGroupMaximumLegalWeightThreshold,
1117
1241
  CheckAxleGroupMaximumLegalWeightThreshold,
1118
1242
  ],
1243
+ [enum_1.PolicyCheckId.CheckLegalWeight, CheckLegalWeight],
1119
1244
  [enum_1.PolicyCheckId.CheckPermittableWeight, CheckPermittableWeight],
1120
1245
  [enum_1.PolicyCheckId.MaxTireLoad, CheckMaxTireLoad],
1121
1246
  [enum_1.PolicyCheckId.MinDriveAxleWeight, CheckMinDriveAxleWeight],
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.getRulesEngines = getRulesEngines;
4
4
  const json_rules_engine_1 = require("json-rules-engine");
5
5
  const custom_operators_1 = require("../rule-operator/custom-operators");
6
+ const cost_1 = require("../constants/cost");
6
7
  /**
7
8
  * Gets a json-rules-engine with all onRouteBC custom operators added,
8
9
  * and all policy rules that apply to all permit types added.
@@ -51,7 +52,7 @@ function getRulesEngines(policy) {
51
52
  event: {
52
53
  type: 'cost',
53
54
  params: {
54
- message: 'Calculated permit cost',
55
+ message: c.params.description || cost_1.DEFAULT_COST_DESCRIPTION,
55
56
  code: 'cost-value',
56
57
  cost: c,
57
58
  },
@@ -21,6 +21,7 @@ const dimensions_helper_1 = require("./helper/dimensions.helper");
21
21
  const dimensions_helper_2 = require("./helper/dimensions.helper");
22
22
  const display_code_helper_1 = require("./helper/display-code-helper");
23
23
  const policy_check_helper_1 = require("./helper/policy-check.helper");
24
+ const cost_1 = require("./constants/cost");
24
25
  /** Class representing commercial vehicle policy. */
25
26
  class Policy {
26
27
  /**
@@ -92,11 +93,22 @@ class Policy {
92
93
  // has a no fee flag set.
93
94
  if ((_d = this.specialAuthorizations) === null || _d === void 0 ? void 0 : _d.noFeeType) {
94
95
  const originalPermitCost = validationResults === null || validationResults === void 0 ? void 0 : validationResults.cost.reduce((accumulator, { cost }) => accumulator + (cost !== null && cost !== void 0 ? cost : 0), 0);
95
- // Clear the cost array and replace with zero cost
96
- validationResults.cost.length = 0;
97
- const newPermitCostResult = new validation_result_1.ValidationResult(enum_1.ValidationResultType.Cost, enum_1.ValidationResultCode.CostValue, 'Calculated permit cost');
98
- newPermitCostResult.cost = 0;
99
- validationResults.cost.push(newPermitCostResult);
96
+ if (permit.permitType !== "STWSE") {
97
+ // Clear the cost array and replace with zero cost
98
+ validationResults.cost.length = 0;
99
+ const newPermitCostResult = new validation_result_1.ValidationResult(enum_1.ValidationResultType.Cost, enum_1.ValidationResultCode.CostValue, cost_1.DEFAULT_COST_DESCRIPTION);
100
+ newPermitCostResult.cost = 0;
101
+ validationResults.cost.push(newPermitCostResult);
102
+ }
103
+ else {
104
+ // If permit type is STWSE, keep each of the cost objects, but set their costs to zero
105
+ const newPermitCosts = validationResults.cost.map(cost => {
106
+ const newPermitCostResult = new validation_result_1.ValidationResult(enum_1.ValidationResultType.Cost, enum_1.ValidationResultCode.CostValue, cost.message);
107
+ newPermitCostResult.cost = 0;
108
+ return newPermitCostResult;
109
+ });
110
+ validationResults.cost = newPermitCosts;
111
+ }
100
112
  // Add an informational message to the results
101
113
  validationResults.information.push(new validation_result_1.ValidationResult(enum_1.ValidationResultType.Information, enum_1.ValidationResultCode.NoFeeClient, `Client '${this.specialAuthorizations.companyId}' has no fee flag, original permit cost would otherwise be '${originalPermitCost}'`));
102
114
  }
package/dist/version.d.ts CHANGED
@@ -1 +1 @@
1
- export declare const version = "v2.22.1";
1
+ export declare const version = "v2.24.0";
package/dist/version.js CHANGED
@@ -2,4 +2,4 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.version = void 0;
4
4
  // Generated by genversion.
5
- exports.version = 'v2.22.1';
5
+ exports.version = 'v2.24.0';
package/package.json CHANGED
@@ -91,5 +91,5 @@
91
91
  ],
92
92
  "testEnvironment": "node"
93
93
  },
94
- "version": "v2.22.1"
94
+ "version": "v2.24.0"
95
95
  }