sgerp-frontend-lib 0.1.4 → 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 (81) hide show
  1. package/dist/constants/timezones.d.ts +7 -2
  2. package/dist/constants/timezones.d.ts.map +1 -1
  3. package/dist/constants/timezones.js +17 -2
  4. package/dist/index.d.ts +1 -0
  5. package/dist/index.d.ts.map +1 -1
  6. package/dist/index.js +2 -0
  7. package/dist/locales/locale-server.d.ts +1 -1
  8. package/dist/locales/locale-server.d.ts.map +1 -1
  9. package/dist/locales/locale-server.js +5 -0
  10. package/dist/locales/locale.d.ts +1 -1
  11. package/dist/locales/locale.d.ts.map +1 -1
  12. package/dist/locales/locale.js +6 -2
  13. package/dist/locales/locale_en.d.ts.map +1 -1
  14. package/dist/locales/locale_en.js +236 -1
  15. package/dist/locales/locale_id.d.ts +2 -0
  16. package/dist/locales/locale_id.d.ts.map +1 -0
  17. package/dist/locales/locale_id.js +2473 -0
  18. package/dist/locales/locale_ja.d.ts.map +1 -1
  19. package/dist/locales/locale_ja.js +188 -1
  20. package/dist/locales/locale_ms.d.ts.map +1 -1
  21. package/dist/locales/locale_ms.js +60 -1
  22. package/dist/locales/locale_tl.d.ts +2 -0
  23. package/dist/locales/locale_tl.d.ts.map +1 -0
  24. package/dist/locales/locale_tl.js +1122 -0
  25. package/dist/sgerp/collection.d.ts.map +1 -1
  26. package/dist/sgerp/collection.js +18 -4
  27. package/dist/sgerp/domains.d.ts +16 -2
  28. package/dist/sgerp/domains.d.ts.map +1 -1
  29. package/dist/sgerp/domains.js +47 -1
  30. package/dist/sgerp/hooks/use-selection-state.d.ts +37 -0
  31. package/dist/sgerp/hooks/use-selection-state.d.ts.map +1 -0
  32. package/dist/sgerp/hooks/use-selection-state.js +121 -0
  33. package/dist/sgerp/hooks/use-simulation-data.d.ts +105 -0
  34. package/dist/sgerp/hooks/use-simulation-data.d.ts.map +1 -0
  35. package/dist/sgerp/hooks/use-simulation-data.js +146 -0
  36. package/dist/sgerp/hooks/use-simulation-state.d.ts +80 -0
  37. package/dist/sgerp/hooks/use-simulation-state.d.ts.map +1 -0
  38. package/dist/sgerp/hooks/use-simulation-state.js +161 -0
  39. package/dist/sgerp/index.d.ts +10 -3
  40. package/dist/sgerp/index.d.ts.map +1 -1
  41. package/dist/sgerp/index.js +30 -2
  42. package/dist/sgerp/simulation-logic/fetchUtils.d.ts +18 -0
  43. package/dist/sgerp/simulation-logic/fetchUtils.d.ts.map +1 -1
  44. package/dist/sgerp/simulation-logic/fetchUtils.js +196 -0
  45. package/dist/sgerp/simulation-logic/index.d.ts +7 -2
  46. package/dist/sgerp/simulation-logic/index.d.ts.map +1 -1
  47. package/dist/sgerp/simulation-logic/index.js +18 -1
  48. package/dist/sgerp/simulation-logic/liveUpdates.d.ts +102 -0
  49. package/dist/sgerp/simulation-logic/liveUpdates.d.ts.map +1 -0
  50. package/dist/sgerp/simulation-logic/liveUpdates.js +87 -0
  51. package/dist/sgerp/simulation-logic/prepareSimulationData.d.ts +36 -0
  52. package/dist/sgerp/simulation-logic/prepareSimulationData.d.ts.map +1 -0
  53. package/dist/sgerp/simulation-logic/prepareSimulationData.js +34 -0
  54. package/dist/sgerp/simulation-logic/routeCalculationUtils.d.ts +7 -1
  55. package/dist/sgerp/simulation-logic/routeCalculationUtils.d.ts.map +1 -1
  56. package/dist/sgerp/simulation-logic/routeCalculationUtils.js +81 -0
  57. package/dist/sgerp/simulation-logic/vrpStatsCalculator.d.ts +52 -0
  58. package/dist/sgerp/simulation-logic/vrpStatsCalculator.d.ts.map +1 -0
  59. package/dist/sgerp/simulation-logic/vrpStatsCalculator.js +247 -0
  60. package/dist/sgerp/simulation-logic/vrpStatsUtils.d.ts +17 -0
  61. package/dist/sgerp/simulation-logic/vrpStatsUtils.d.ts.map +1 -0
  62. package/dist/sgerp/simulation-logic/vrpStatsUtils.js +48 -0
  63. package/dist/sgerp/utils/timezone.d.ts +15 -0
  64. package/dist/sgerp/utils/timezone.d.ts.map +1 -0
  65. package/dist/sgerp/utils/timezone.js +19 -0
  66. package/dist/sgerp/vrptoolbox-analyzer.d.ts +39 -0
  67. package/dist/sgerp/vrptoolbox-analyzer.d.ts.map +1 -0
  68. package/dist/sgerp/vrptoolbox-analyzer.js +210 -0
  69. package/dist/vrptoolbox/collection.d.ts +16 -0
  70. package/dist/vrptoolbox/collection.d.ts.map +1 -0
  71. package/dist/vrptoolbox/collection.js +62 -0
  72. package/dist/vrptoolbox/index.d.ts +7 -0
  73. package/dist/vrptoolbox/index.d.ts.map +1 -0
  74. package/dist/vrptoolbox/index.js +24 -0
  75. package/dist/vrptoolbox/types/job.d.ts +46 -0
  76. package/dist/vrptoolbox/types/job.d.ts.map +1 -0
  77. package/dist/vrptoolbox/types/job.js +5 -0
  78. package/dist/vrptoolbox/vrptoolbox-utils.d.ts +133 -0
  79. package/dist/vrptoolbox/vrptoolbox-utils.d.ts.map +1 -0
  80. package/dist/vrptoolbox/vrptoolbox-utils.js +207 -0
  81. package/package.json +9 -4
@@ -0,0 +1,247 @@
1
+ "use strict";
2
+ /**
3
+ * VRPToolbox Statistics Calculator
4
+ * Processes VRP payload and result to calculate fleet statistics, timeline data, and utilization
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.calculateFleetStats = calculateFleetStats;
8
+ exports.processVRPStatistics = processVRPStatistics;
9
+ const vrpStatsUtils_1 = require("./vrpStatsUtils");
10
+ const VEHICLE_COLORS = [
11
+ 'red',
12
+ 'blue',
13
+ 'green',
14
+ 'orange',
15
+ 'purple',
16
+ 'brown',
17
+ 'magenta',
18
+ 'teal',
19
+ ];
20
+ /**
21
+ * Calculate fleet-wide statistics from processed vehicles
22
+ */
23
+ function calculateFleetStats(vehicles) {
24
+ let totalSlack = 0;
25
+ let totalDistance = 0;
26
+ let totalDrivingTime = 0;
27
+ let totalBreakTime = 0;
28
+ vehicles.forEach((vehicle) => {
29
+ totalDistance += parseFloat(vehicle.totalDistance || '0');
30
+ vehicle.timelineWaypoints.forEach((wp) => {
31
+ totalSlack += wp.adjustedSlack || 0;
32
+ totalDrivingTime += wp.travel_time_from_prev || 0;
33
+ totalBreakTime += wp.nodeBreakDuration || 0;
34
+ });
35
+ });
36
+ return {
37
+ totalSlack: (0, vrpStatsUtils_1.formatSeconds)(totalSlack),
38
+ totalDistance: totalDistance.toFixed(2),
39
+ totalDrivingTime: (0, vrpStatsUtils_1.formatSeconds)(totalDrivingTime),
40
+ totalBreakTime: (0, vrpStatsUtils_1.formatSeconds)(totalBreakTime),
41
+ };
42
+ }
43
+ /**
44
+ * Process VRP result and payload to generate vehicle statistics
45
+ */
46
+ async function processVRPStatistics(result, payload) {
47
+ if (!result || !result.vehicles) {
48
+ console.warn('No vehicles in result', result);
49
+ return [];
50
+ }
51
+ const inputVehicles = (payload === null || payload === void 0 ? void 0 : payload.vehicles) || [];
52
+ const vehicleList = Array.isArray(result.vehicles)
53
+ ? result.vehicles
54
+ .filter((v) => v.nodes && v.nodes.length > 0) // Only vehicles with nodes
55
+ .map((v) => ({
56
+ agent_id: v.agent_id,
57
+ nodes: v.nodes,
58
+ }))
59
+ : Object.entries(result.vehicles)
60
+ .filter(([id, nodes]) => nodes && nodes.length > 0) // Only vehicles with nodes
61
+ .map(([id, nodes]) => ({
62
+ agent_id: id,
63
+ nodes: nodes,
64
+ }));
65
+ const vehicles = await Promise.all(vehicleList.map(async (vehicleData, index) => {
66
+ var _a, _b, _c, _d;
67
+ const inputVehicle = inputVehicles.find((v) => v.agent_id === vehicleData.agent_id);
68
+ let vehicleBreakDuration = 0;
69
+ if (inputVehicle &&
70
+ typeof inputVehicle.dynamic_break_duration === 'number') {
71
+ vehicleBreakDuration = inputVehicle.dynamic_break_duration;
72
+ }
73
+ let sortedNodes = JSON.parse(JSON.stringify(vehicleData.nodes));
74
+ sortedNodes.sort((a, b) => new Date(a.scheduled_ts).getTime() - new Date(b.scheduled_ts).getTime());
75
+ // Add start/end nodes if vehicle has start/end times
76
+ if (inputVehicle) {
77
+ if (inputVehicle.start_time) {
78
+ const startNode = ((payload === null || payload === void 0 ? void 0 : payload.nodes) || []).find((n) => { var _a; return n.uid === ((_a = inputVehicle.partial_route) === null || _a === void 0 ? void 0 : _a[0]); }) || vehicleData.nodes[0];
79
+ if (startNode) {
80
+ sortedNodes.unshift({
81
+ lat: startNode.lat,
82
+ lon: startNode.lon,
83
+ scheduled_ts: inputVehicle.start_time,
84
+ location_name: 'Start',
85
+ node_type: 'vehicle_start',
86
+ service_time: 0,
87
+ });
88
+ }
89
+ }
90
+ if (inputVehicle.end_time) {
91
+ const lastRouteUid = (_a = inputVehicle.partial_route) === null || _a === void 0 ? void 0 : _a[((_b = inputVehicle.partial_route) === null || _b === void 0 ? void 0 : _b.length) - 1];
92
+ const endNode = ((payload === null || payload === void 0 ? void 0 : payload.nodes) || []).find((n) => n.uid === lastRouteUid) ||
93
+ vehicleData.nodes[vehicleData.nodes.length - 1];
94
+ if (endNode) {
95
+ sortedNodes.push({
96
+ lat: endNode.lat,
97
+ lon: endNode.lon,
98
+ scheduled_ts: inputVehicle.end_time,
99
+ location_name: 'End',
100
+ node_type: 'vehicle_end',
101
+ service_time: 0,
102
+ });
103
+ }
104
+ }
105
+ }
106
+ // Process nodes with travel times, breaks, and slack
107
+ const processedNodes = sortedNodes.map((node, idx, array) => {
108
+ const prevNode = array[idx - 1];
109
+ let prevNodeDepartureTime;
110
+ if (prevNode) {
111
+ let prevNodeActualBreakDuration = 0;
112
+ let prevNodeEffectiveSlack = 0;
113
+ if (typeof prevNode.dynamic_break_time === 'number') {
114
+ prevNodeActualBreakDuration = prevNode.dynamic_break_time;
115
+ prevNodeEffectiveSlack = prevNode.slack || 0;
116
+ }
117
+ else {
118
+ const prevNodeBreakCount = prevNode.dynamic_break || 0;
119
+ prevNodeActualBreakDuration =
120
+ prevNodeBreakCount * vehicleBreakDuration;
121
+ prevNodeEffectiveSlack = Math.max(0, (prevNode.slack || 0) - prevNodeActualBreakDuration);
122
+ }
123
+ const prevScheduled = new Date(prevNode.scheduled_ts);
124
+ const departureMs = prevScheduled.getTime() +
125
+ ((prevNode.service_time || 0) +
126
+ prevNodeEffectiveSlack +
127
+ prevNodeActualBreakDuration) *
128
+ 1000;
129
+ prevNodeDepartureTime = new Date(departureMs);
130
+ }
131
+ let travel_time_from_prev = 0;
132
+ if (prevNode && node.node_type !== 'vehicle_end') {
133
+ const nodeScheduled = new Date(node.scheduled_ts);
134
+ if (prevNodeDepartureTime) {
135
+ travel_time_from_prev =
136
+ (nodeScheduled.getTime() - prevNodeDepartureTime.getTime()) /
137
+ 1000;
138
+ }
139
+ }
140
+ let nodeBreakDuration = 0;
141
+ let adjustedSlack = 0;
142
+ if (typeof node.dynamic_break_time === 'number') {
143
+ nodeBreakDuration = node.dynamic_break_time;
144
+ adjustedSlack = node.slack || 0;
145
+ }
146
+ else {
147
+ const nodeBreakCount = node.dynamic_break || 0;
148
+ nodeBreakDuration = nodeBreakCount * vehicleBreakDuration;
149
+ adjustedSlack = Math.max(0, (node.slack || 0) - nodeBreakDuration);
150
+ }
151
+ return Object.assign(Object.assign({}, node), { travel_time_from_prev,
152
+ nodeBreakDuration,
153
+ adjustedSlack, service_time: node.service_time || 0 });
154
+ });
155
+ // Calculate total distance (try multiple methods)
156
+ let totalDistance = 0;
157
+ const lastNode = processedNodes[processedNodes.length - 1];
158
+ // Method 1: Use scheduled_cumulative_dist if available
159
+ if (lastNode &&
160
+ typeof lastNode.scheduled_cumulative_dist === 'number') {
161
+ totalDistance = lastNode.scheduled_cumulative_dist / 1000;
162
+ }
163
+ // Method 2: Fallback to Euclidean distance
164
+ else if (processedNodes.length > 1) {
165
+ for (let i = 1; i < processedNodes.length; i++) {
166
+ const prev = processedNodes[i - 1];
167
+ const curr = processedNodes[i];
168
+ totalDistance += (0, vrpStatsUtils_1.getDistanceFromLatLonInKm)(prev.lat, prev.lon, curr.lat, curr.lon);
169
+ }
170
+ }
171
+ // Calculate total time
172
+ let totalTime = 0;
173
+ if (processedNodes.length > 0 && lastNode) {
174
+ const startTime = new Date(processedNodes[0].scheduled_ts);
175
+ const endTime = new Date(lastNode.scheduled_ts);
176
+ totalTime = (endTime.getTime() - startTime.getTime()) / 1000;
177
+ }
178
+ // Calculate utilization over time
179
+ const utilizationData = [];
180
+ const vehicleCapacity = (inputVehicle === null || inputVehicle === void 0 ? void 0 : inputVehicle.capacity) || (inputVehicle === null || inputVehicle === void 0 ? void 0 : inputVehicle.limits) || {};
181
+ const totalCapacity = Object.values(vehicleCapacity).reduce((sum, val) => sum + val, 0);
182
+ const currentLoad = {};
183
+ // Initialize load for prebook_cvrptw mode
184
+ if (((_d = (_c = payload.engine_settings) === null || _c === void 0 ? void 0 : _c.calculation_parameters) === null || _d === void 0 ? void 0 : _d.scheduling_mode) ===
185
+ 'prebook_cvrptw') {
186
+ const inputNodes = payload.nodes || [];
187
+ const allDemands = inputNodes
188
+ .filter((n) => n.node_type === 'dropoff' &&
189
+ n.demand &&
190
+ vehicleData.nodes.some((vn) => vn.booking_uid && vn.booking_uid === n.booking_uid))
191
+ .flatMap((n) => Object.entries(n.demand));
192
+ for (const [key, value] of allDemands) {
193
+ currentLoad[key] = (currentLoad[key] || 0) + Math.abs(value);
194
+ }
195
+ }
196
+ else {
197
+ Object.keys(vehicleCapacity).forEach((key) => (currentLoad[key] = 0));
198
+ }
199
+ // Track utilization at each node
200
+ processedNodes.forEach((node) => {
201
+ const originalNode = (payload.nodes || []).find((n) => n.uid === node.uid || n.id === node.uid);
202
+ if (originalNode === null || originalNode === void 0 ? void 0 : originalNode.demand) {
203
+ if (node.node_type === 'dropoff') {
204
+ for (const [key, value] of Object.entries(originalNode.demand)) {
205
+ currentLoad[key] =
206
+ (currentLoad[key] || 0) - Math.abs(value);
207
+ }
208
+ }
209
+ else if (node.node_type === 'pickup') {
210
+ for (const [key, value] of Object.entries(originalNode.demand)) {
211
+ currentLoad[key] =
212
+ (currentLoad[key] || 0) + Math.abs(value);
213
+ }
214
+ }
215
+ }
216
+ let utilizationAfter = 0;
217
+ if (totalCapacity > 0) {
218
+ const totalLoad = Object.values(currentLoad).reduce((sum, val) => sum + val, 0);
219
+ utilizationAfter = (totalLoad / totalCapacity) * 100;
220
+ }
221
+ utilizationData.push({
222
+ time: node.scheduled_ts,
223
+ utilization: Math.max(0, Math.min(100, utilizationAfter)),
224
+ });
225
+ });
226
+ // Calculate vehicle-specific stats
227
+ const vehicleStats = processedNodes.reduce((acc, node) => {
228
+ acc.slack += node.adjustedSlack || 0;
229
+ acc.breakTime += node.nodeBreakDuration || 0;
230
+ acc.drivingTime += node.travel_time_from_prev || 0;
231
+ return acc;
232
+ }, { slack: 0, breakTime: 0, drivingTime: 0 });
233
+ return {
234
+ vehicleId: vehicleData.agent_id,
235
+ vehicleName: (inputVehicle === null || inputVehicle === void 0 ? void 0 : inputVehicle.service_number) || vehicleData.agent_id,
236
+ timelineWaypoints: processedNodes,
237
+ utilizationData: utilizationData,
238
+ color: VEHICLE_COLORS[index % VEHICLE_COLORS.length],
239
+ totalDistance: totalDistance.toFixed(2),
240
+ totalTime: (0, vrpStatsUtils_1.formatSeconds)(totalTime),
241
+ totalSlack: (0, vrpStatsUtils_1.formatSeconds)(vehicleStats.slack),
242
+ totalBreakTime: (0, vrpStatsUtils_1.formatSeconds)(vehicleStats.breakTime),
243
+ totalDrivingTime: (0, vrpStatsUtils_1.formatSeconds)(vehicleStats.drivingTime),
244
+ };
245
+ }));
246
+ return vehicles;
247
+ }
@@ -0,0 +1,17 @@
1
+ /**
2
+ * VRPToolbox Statistics Utilities
3
+ * Helper functions for calculating and formatting VRP statistics
4
+ */
5
+ /**
6
+ * Format seconds into HH:MM:SS format
7
+ */
8
+ export declare function formatSeconds(seconds: number): string;
9
+ /**
10
+ * Calculate distance between two lat/lon coordinates in kilometers (Haversine formula)
11
+ */
12
+ export declare function getDistanceFromLatLonInKm(lat1: number, lon1: number, lat2: number, lon2: number): number;
13
+ /**
14
+ * Convert timestamp to display Date (handles timezone conversions)
15
+ */
16
+ export declare function toDisplayDate(ts: string): Date;
17
+ //# sourceMappingURL=vrpStatsUtils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vrpStatsUtils.d.ts","sourceRoot":"","sources":["../../../sgerplib/sgerp/simulation-logic/vrpStatsUtils.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;GAEG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAUrD;AAED;;GAEG;AACH,wBAAgB,yBAAyB,CACvC,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,GACX,MAAM,CAaR;AAMD;;GAEG;AACH,wBAAgB,aAAa,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,CAc9C"}
@@ -0,0 +1,48 @@
1
+ "use strict";
2
+ /**
3
+ * VRPToolbox Statistics Utilities
4
+ * Helper functions for calculating and formatting VRP statistics
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.formatSeconds = formatSeconds;
8
+ exports.getDistanceFromLatLonInKm = getDistanceFromLatLonInKm;
9
+ exports.toDisplayDate = toDisplayDate;
10
+ /**
11
+ * Format seconds into HH:MM:SS format
12
+ */
13
+ function formatSeconds(seconds) {
14
+ const hours = Math.floor(seconds / 3600);
15
+ const minutes = Math.floor((seconds % 3600) / 60);
16
+ const remainingSeconds = Math.floor(seconds % 60);
17
+ const formattedHours = String(hours).padStart(2, '0');
18
+ const formattedMinutes = String(minutes).padStart(2, '0');
19
+ const formattedSeconds = String(remainingSeconds).padStart(2, '0');
20
+ return `${formattedHours}:${formattedMinutes}:${formattedSeconds}`;
21
+ }
22
+ /**
23
+ * Calculate distance between two lat/lon coordinates in kilometers (Haversine formula)
24
+ */
25
+ function getDistanceFromLatLonInKm(lat1, lon1, lat2, lon2) {
26
+ const R = 6371; // Earth's radius in km
27
+ const dLat = deg2rad(lat2 - lat1);
28
+ const dLon = deg2rad(lon2 - lon1);
29
+ const a = Math.sin(dLat / 2) * Math.sin(dLat / 2) +
30
+ Math.cos(deg2rad(lat1)) *
31
+ Math.cos(deg2rad(lat2)) *
32
+ Math.sin(dLon / 2) *
33
+ Math.sin(dLon / 2);
34
+ const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
35
+ const d = R * c;
36
+ return d;
37
+ }
38
+ function deg2rad(deg) {
39
+ return deg * (Math.PI / 180);
40
+ }
41
+ /**
42
+ * Convert timestamp to display Date (handles timezone conversions)
43
+ */
44
+ function toDisplayDate(ts) {
45
+ // Parse as UTC and return as local Date object
46
+ const date = new Date(ts);
47
+ return new Date(Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate(), date.getUTCHours(), date.getUTCMinutes(), date.getUTCSeconds(), date.getUTCMilliseconds()));
48
+ }
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Timezone utility functions
3
+ */
4
+ /**
5
+ * Get the effective timezone based on the current mode
6
+ * In VRPToolbox mode, uses the VRP timezone selector
7
+ * Otherwise, uses the project timezone (or falls back to UTC)
8
+ *
9
+ * @param mode - The current effective mode ('vrptoolbox' or other)
10
+ * @param vrpTimezone - The timezone selected in VRPToolbox mode
11
+ * @param projectTimezone - The project's configured timezone
12
+ * @returns The effective timezone string (e.g., 'UTC', 'Asia/Singapore')
13
+ */
14
+ export declare function getEffectiveTimezone(mode: string, vrpTimezone: string, projectTimezone?: string): string;
15
+ //# sourceMappingURL=timezone.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"timezone.d.ts","sourceRoot":"","sources":["../../../sgerplib/sgerp/utils/timezone.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH;;;;;;;;;GASG;AACH,wBAAgB,oBAAoB,CAClC,IAAI,EAAE,MAAM,EACZ,WAAW,EAAE,MAAM,EACnB,eAAe,CAAC,EAAE,MAAM,GACvB,MAAM,CAER"}
@@ -0,0 +1,19 @@
1
+ "use strict";
2
+ /**
3
+ * Timezone utility functions
4
+ */
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.getEffectiveTimezone = getEffectiveTimezone;
7
+ /**
8
+ * Get the effective timezone based on the current mode
9
+ * In VRPToolbox mode, uses the VRP timezone selector
10
+ * Otherwise, uses the project timezone (or falls back to UTC)
11
+ *
12
+ * @param mode - The current effective mode ('vrptoolbox' or other)
13
+ * @param vrpTimezone - The timezone selected in VRPToolbox mode
14
+ * @param projectTimezone - The project's configured timezone
15
+ * @returns The effective timezone string (e.g., 'UTC', 'Asia/Singapore')
16
+ */
17
+ function getEffectiveTimezone(mode, vrpTimezone, projectTimezone) {
18
+ return mode === 'vrptoolbox' ? vrpTimezone : (projectTimezone || 'UTC');
19
+ }
@@ -0,0 +1,39 @@
1
+ interface Vehicle {
2
+ agent_id: string;
3
+ characteristics?: Record<string, any>;
4
+ start_time?: number;
5
+ end_time?: number;
6
+ geofence_ids: any[];
7
+ assigned_nodes: any[];
8
+ labels?: string[];
9
+ capacity: number | Record<string, number>;
10
+ }
11
+ interface Node {
12
+ node_type: string;
13
+ vehicle_characteristics: Record<string, any>;
14
+ open_time_ts: number;
15
+ close_time_ts: number;
16
+ geofence_id: any[];
17
+ geofence_ids?: any[];
18
+ demand: number | Record<string, number>;
19
+ vehicle_labels?: {
20
+ or?: string[];
21
+ and?: string[];
22
+ };
23
+ booking_uid: string;
24
+ }
25
+ interface PipelineObject {
26
+ request: {
27
+ vehicles: Vehicle[];
28
+ nodes: Node[];
29
+ };
30
+ result: {
31
+ rejected_bookings: {
32
+ uid: string;
33
+ }[];
34
+ vehicles: Record<string, any>;
35
+ };
36
+ }
37
+ export declare function analyzePipelineObject(pipelineObject: PipelineObject): Record<string, any>;
38
+ export type { PipelineObject, Vehicle, Node };
39
+ //# sourceMappingURL=vrptoolbox-analyzer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vrptoolbox-analyzer.d.ts","sourceRoot":"","sources":["../../sgerplib/sgerp/vrptoolbox-analyzer.ts"],"names":[],"mappings":"AAAA,UAAU,OAAO;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,eAAe,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IACtC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,GAAG,EAAE,CAAC;IACpB,cAAc,EAAE,GAAG,EAAE,CAAC;IACtB,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,QAAQ,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC3C;AAED,UAAU,IAAI;IACZ,SAAS,EAAE,MAAM,CAAC;IAClB,uBAAuB,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAC7C,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,GAAG,EAAE,CAAC;IACnB,YAAY,CAAC,EAAE,GAAG,EAAE,CAAC;IACrB,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACxC,cAAc,CAAC,EAAE;QACf,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC;QACd,GAAG,CAAC,EAAE,MAAM,EAAE,CAAC;KAChB,CAAC;IACF,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,UAAU,cAAc;IACtB,OAAO,EAAE;QACP,QAAQ,EAAE,OAAO,EAAE,CAAC;QACpB,KAAK,EAAE,IAAI,EAAE,CAAC;KACf,CAAC;IACF,MAAM,EAAE;QACN,iBAAiB,EAAE;YAAE,GAAG,EAAE,MAAM,CAAA;SAAE,EAAE,CAAC;QACrC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;KAC/B,CAAC;CACH;AAwQD,wBAAgB,qBAAqB,CACnC,cAAc,EAAE,cAAc,GAC7B,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAkCrB;AAED,YAAY,EAAE,cAAc,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC"}
@@ -0,0 +1,210 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.analyzePipelineObject = analyzePipelineObject;
4
+ const DEFAULT_SEAT_TYPE = "passenger";
5
+ function getCapacity(value, defaultSeatType = DEFAULT_SEAT_TYPE) {
6
+ return typeof value === "object" ? value : { [DEFAULT_SEAT_TYPE]: value };
7
+ }
8
+ function checkCharacteristics(node, vehicle) {
9
+ const reasons = [];
10
+ const messages = [];
11
+ if (!vehicle.characteristics) {
12
+ return [reasons, messages];
13
+ }
14
+ const vehicleChars = vehicle.characteristics;
15
+ const agentId = vehicle.agent_id;
16
+ const nodeType = node.node_type;
17
+ Object.entries(node.vehicle_characteristics).forEach(([char, value]) => {
18
+ const vehicleVar = vehicleChars[char];
19
+ if (vehicleVar === undefined) {
20
+ reasons.push({
21
+ violation: `${char}_availability_violation`,
22
+ vehicle_value: vehicleVar,
23
+ char: char,
24
+ vehicle_characteristics: vehicleChars,
25
+ });
26
+ messages.push(`${nodeType} can not be assigned to vehicle ${agentId} because characteristic ${char} is not available in vehicle ${JSON.stringify(vehicleChars)}`);
27
+ }
28
+ else if (typeof value === "object") {
29
+ const minVal = value.min;
30
+ const maxVal = value.max;
31
+ if (minVal !== undefined && vehicleVar < minVal) {
32
+ reasons.push({
33
+ violation: `${char}_min_violation`,
34
+ vehicle_value: vehicleVar,
35
+ required_min: minVal,
36
+ vehicle: agentId,
37
+ });
38
+ messages.push(`${nodeType} can not be assigned to vehicle ${agentId} because of ${char}.min violation: ${vehicleVar} < ${minVal}`);
39
+ }
40
+ if (maxVal !== undefined && vehicleVar > maxVal) {
41
+ reasons.push({
42
+ violation: `${char}_max_violation`,
43
+ vehicle_value: vehicleVar,
44
+ required_max: maxVal,
45
+ vehicle: agentId,
46
+ });
47
+ messages.push(`${nodeType} can not be assigned to vehicle ${agentId} because of ${char}.max violation: ${vehicleVar} > ${maxVal}`);
48
+ }
49
+ }
50
+ else if (value !== vehicleVar) {
51
+ reasons.push({
52
+ violation: `${char}_value_violation`,
53
+ required_value: value,
54
+ vehicle_value: vehicleVar,
55
+ });
56
+ messages.push(`can not be assigned to vehicle ${agentId} - ${char}_value_violation`);
57
+ }
58
+ });
59
+ return [reasons, messages];
60
+ }
61
+ function checkStartTimeEndTimeConstraints(node, vehicle) {
62
+ const reasons = [];
63
+ const messages = [];
64
+ const agentId = vehicle.agent_id;
65
+ const nodeType = node.node_type;
66
+ if (vehicle.start_time !== undefined) {
67
+ if (vehicle.start_time > node.close_time_ts) {
68
+ reasons.push({
69
+ violation: "start_time_violation",
70
+ node_close_time: node.close_time_ts,
71
+ vehicle_start_time: vehicle.start_time,
72
+ });
73
+ messages.push(`${nodeType} can not be assigned to vehicle ${agentId} - start_time_violation: ${vehicle.start_time} > ${node.close_time_ts}`);
74
+ }
75
+ }
76
+ if (vehicle.end_time !== undefined) {
77
+ if (vehicle.end_time < node.open_time_ts) {
78
+ reasons.push({
79
+ violation: "end_time_violation",
80
+ node_open_time: node.open_time_ts,
81
+ vehicle_end_time: vehicle.end_time,
82
+ });
83
+ messages.push(`${nodeType} can not be assigned to vehicle ${agentId} - end_time_violation: ${vehicle.end_time} < ${node.open_time_ts}`);
84
+ }
85
+ }
86
+ return [reasons, messages];
87
+ }
88
+ function checkGeofenceConstraints(node, vehicle) {
89
+ const reasons = [];
90
+ const messages = [];
91
+ const agentId = vehicle.agent_id;
92
+ if (node.geofence_ids === undefined && node.geofence_id) {
93
+ node.geofence_ids = [node.geofence_id];
94
+ }
95
+ if (node.geofence_ids && node.geofence_ids.length > 0 && node.geofence_ids[0] !== null) {
96
+ const vehicleGeofenceIds = new Set(vehicle.geofence_ids);
97
+ const nodeGeofenceIds = new Set(node.geofence_ids);
98
+ if (vehicleGeofenceIds.size > 0 &&
99
+ !Array.from(nodeGeofenceIds).some((id) => vehicleGeofenceIds.has(id))) {
100
+ reasons.push({
101
+ violation: "geofence_violation",
102
+ node_geofence_ids: node.geofence_ids,
103
+ vehicle_geofence_ids: vehicle.geofence_ids,
104
+ });
105
+ messages.push(`can not be assigned to vehicle ${agentId} - geofence violations: ${JSON.stringify([...vehicleGeofenceIds])} vs ${JSON.stringify([...nodeGeofenceIds])}`);
106
+ }
107
+ }
108
+ return [reasons, messages];
109
+ }
110
+ function checkVehicleAssignedNodes(node, vehicle) {
111
+ const reasons = [];
112
+ const messages = [];
113
+ const agentId = vehicle.agent_id;
114
+ if (vehicle.assigned_nodes.length > 0) {
115
+ reasons.push({
116
+ violation: "vehicle_has_assigned_nodes",
117
+ });
118
+ messages.push(`vehicle ${agentId} has assigned nodes. Possibly change the first solution strategy to 16 or clear vehicles`);
119
+ }
120
+ return [reasons, messages];
121
+ }
122
+ function checkVehicleLabels(node, vehicle) {
123
+ const reasons = [];
124
+ const messages = [];
125
+ const agentId = vehicle.agent_id;
126
+ const nodeLabels = node.vehicle_labels;
127
+ if (!nodeLabels) {
128
+ return [reasons, messages];
129
+ }
130
+ const labelOrValues = new Set(nodeLabels.or || []);
131
+ const labelAndValues = new Set(nodeLabels.and || []);
132
+ const vehicleLabels = new Set(vehicle.labels || []);
133
+ if (labelOrValues.size > 0) {
134
+ if (!Array.from(labelOrValues).some((label) => vehicleLabels.has(label))) {
135
+ reasons.push({
136
+ violation: "vehicle and booking labels do not satisfy each other",
137
+ });
138
+ messages.push(`vehicle ${agentId} has labels ${JSON.stringify([...vehicleLabels])}. Booking: ${JSON.stringify(nodeLabels)}`);
139
+ }
140
+ }
141
+ if (labelAndValues.size > 0) {
142
+ if (!Array.from(labelAndValues).every((label) => vehicleLabels.has(label))) {
143
+ reasons.push({
144
+ violation: "vehicle and booking labels do not satisfy each other",
145
+ });
146
+ messages.push(`vehicle ${agentId} has labels ${JSON.stringify([...vehicleLabels])}. Booking: ${JSON.stringify(nodeLabels)}`);
147
+ }
148
+ }
149
+ return [reasons, messages];
150
+ }
151
+ function analyzeVehicles(nodeGroup, vehicles) {
152
+ const possibleReasons = {
153
+ messages: [],
154
+ impossibleVehicles: new Set(),
155
+ comments: [],
156
+ };
157
+ nodeGroup.forEach((node) => {
158
+ vehicles.forEach((vehicle) => {
159
+ const [reasons1, messages1] = checkCharacteristics(node, vehicle);
160
+ const [reasons2, messages2] = checkStartTimeEndTimeConstraints(node, vehicle);
161
+ const [reasons3, messages3] = checkGeofenceConstraints(node, vehicle);
162
+ const [reasons4, messages4] = checkVehicleAssignedNodes(node, vehicle);
163
+ const [reasons5, messages5] = checkVehicleLabels(node, vehicle);
164
+ if (reasons1.length > 0 ||
165
+ reasons2.length > 0 ||
166
+ reasons3.length > 0 ||
167
+ reasons4.length > 0 ||
168
+ reasons5.length > 0) {
169
+ possibleReasons.messages.push(...messages1, ...messages2, ...messages3, ...messages4, ...messages5);
170
+ possibleReasons.impossibleVehicles.add(vehicle.agent_id);
171
+ }
172
+ });
173
+ });
174
+ possibleReasons.impossibleVehicles = Array.from(possibleReasons.impossibleVehicles);
175
+ possibleReasons.impossibleVehiclesCount =
176
+ possibleReasons.impossibleVehicles.length;
177
+ if (possibleReasons.impossibleVehiclesCount === vehicles.length) {
178
+ possibleReasons.comments.push("all vehicles are impossible");
179
+ }
180
+ else {
181
+ const possibleVehicles = vehicles
182
+ .filter((vehicle) => !possibleReasons.impossibleVehicles.includes(vehicle.agent_id))
183
+ .map((vehicle) => vehicle.agent_id);
184
+ possibleReasons.comments.push({ possibleVehicles });
185
+ }
186
+ return possibleReasons;
187
+ }
188
+ function analyzePipelineObject(pipelineObject) {
189
+ const { vehicles, nodes } = pipelineObject.request;
190
+ const { rejected_bookings, vehicles: resultedVehicles } = pipelineObject.result;
191
+ const rejectedUids = new Set(rejected_bookings.map((b) => b.uid));
192
+ const rejectedNodes = nodes.filter((node) => rejectedUids.has(node.booking_uid));
193
+ const emptyVehicleUids = new Set(Object.entries(resultedVehicles)
194
+ .filter(([_, item]) => !item)
195
+ .map(([key, _]) => key));
196
+ const emptyVehicles = vehicles; // .filter(v => emptyVehicleUids.has(v.agent_id));
197
+ const possibleReasons = {};
198
+ const bookingNodes = {};
199
+ rejectedNodes.forEach((node) => {
200
+ const { booking_uid } = node;
201
+ if (!bookingNodes[booking_uid]) {
202
+ bookingNodes[booking_uid] = [];
203
+ }
204
+ bookingNodes[booking_uid].push(node);
205
+ });
206
+ Object.entries(bookingNodes).forEach(([booking_uid, node_group]) => {
207
+ possibleReasons[booking_uid] = analyzeVehicles(node_group, emptyVehicles);
208
+ });
209
+ return possibleReasons;
210
+ }
@@ -0,0 +1,16 @@
1
+ import type { OptimizationJob } from './types/job';
2
+ import { Collection } from '../sgerp/collection';
3
+ /**
4
+ * Collection class for VRP Toolbox Optimization Jobs
5
+ */
6
+ export declare class OptimizationJobCollection extends Collection<OptimizationJob> {
7
+ private baseUrl;
8
+ private username?;
9
+ private password?;
10
+ constructor(baseUrl: string, username?: string, password?: string);
11
+ /**
12
+ * Set authentication credentials
13
+ */
14
+ setCredentials(username: string, password: string): void;
15
+ }
16
+ //# sourceMappingURL=collection.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"collection.d.ts","sourceRoot":"","sources":["../../sgerplib/vrptoolbox/collection.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAA0B,MAAM,aAAa,CAAC;AAE3E,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAqBjD;;GAEG;AACH,qBAAa,yBAA0B,SAAQ,UAAU,CAAC,eAAe,CAAC;IACxE,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,QAAQ,CAAC,CAAS;IAC1B,OAAO,CAAC,QAAQ,CAAC,CAAS;gBAEd,OAAO,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM;IAqCjE;;OAEG;IACH,cAAc,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI;CAIzD"}