fleetmap-reports 2.0.356 → 2.0.358

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 (88) hide show
  1. package/.claude/worktrees/eager-stonebraker-c08ac7/.eslintrc.js +15 -0
  2. package/.claude/worktrees/eager-stonebraker-c08ac7/lang/enGB.js +309 -0
  3. package/.claude/worktrees/eager-stonebraker-c08ac7/lang/esCL.js +297 -0
  4. package/.claude/worktrees/eager-stonebraker-c08ac7/lang/frFR.json +324 -0
  5. package/.claude/worktrees/eager-stonebraker-c08ac7/lang/index.js +35 -0
  6. package/.claude/worktrees/eager-stonebraker-c08ac7/lang/ptBR.js +301 -0
  7. package/.claude/worktrees/eager-stonebraker-c08ac7/lang/ptPT.js +310 -0
  8. package/.claude/worktrees/eager-stonebraker-c08ac7/package-lock.json +11944 -0
  9. package/.claude/worktrees/eager-stonebraker-c08ac7/package.json +45 -0
  10. package/.claude/worktrees/eager-stonebraker-c08ac7/src/activity-report.js +822 -0
  11. package/.claude/worktrees/eager-stonebraker-c08ac7/src/automaticReports.js +34 -0
  12. package/.claude/worktrees/eager-stonebraker-c08ac7/src/carbone/index.js +13 -0
  13. package/.claude/worktrees/eager-stonebraker-c08ac7/src/custom/vistawasteActivity-report.js +544 -0
  14. package/.claude/worktrees/eager-stonebraker-c08ac7/src/events-report.js +360 -0
  15. package/.claude/worktrees/eager-stonebraker-c08ac7/src/fuel-consumption-report.js +296 -0
  16. package/.claude/worktrees/eager-stonebraker-c08ac7/src/fuel-drop-report.js +128 -0
  17. package/.claude/worktrees/eager-stonebraker-c08ac7/src/here.js +172 -0
  18. package/.claude/worktrees/eager-stonebraker-c08ac7/src/idle-report.js +403 -0
  19. package/.claude/worktrees/eager-stonebraker-c08ac7/src/index.js +244 -0
  20. package/.claude/worktrees/eager-stonebraker-c08ac7/src/kms-report.js +549 -0
  21. package/.claude/worktrees/eager-stonebraker-c08ac7/src/location-report.js +496 -0
  22. package/.claude/worktrees/eager-stonebraker-c08ac7/src/machines-report.js +278 -0
  23. package/.claude/worktrees/eager-stonebraker-c08ac7/src/partnerReports/afriquia.js +129 -0
  24. package/.claude/worktrees/eager-stonebraker-c08ac7/src/partnerReports/dailyuse-report.js +80 -0
  25. package/.claude/worktrees/eager-stonebraker-c08ac7/src/partnerReports/delayedstart-report.js +223 -0
  26. package/.claude/worktrees/eager-stonebraker-c08ac7/src/partnerReports/driverranking-report.js +328 -0
  27. package/.claude/worktrees/eager-stonebraker-c08ac7/src/partnerReports/gpsjump-report.js +61 -0
  28. package/.claude/worktrees/eager-stonebraker-c08ac7/src/partnerReports/performance-report.js +359 -0
  29. package/.claude/worktrees/eager-stonebraker-c08ac7/src/refueling-report.js +345 -0
  30. package/.claude/worktrees/eager-stonebraker-c08ac7/src/reportStyle.js +34 -0
  31. package/.claude/worktrees/eager-stonebraker-c08ac7/src/sensor-report.js +447 -0
  32. package/.claude/worktrees/eager-stonebraker-c08ac7/src/speeding-report.js +663 -0
  33. package/.claude/worktrees/eager-stonebraker-c08ac7/src/stop-report.js +242 -0
  34. package/.claude/worktrees/eager-stonebraker-c08ac7/src/templates/ActivityReportTemplate.mjml +51 -0
  35. package/.claude/worktrees/eager-stonebraker-c08ac7/src/templates/EmptyReportTemplate.mjml +25 -0
  36. package/.claude/worktrees/eager-stonebraker-c08ac7/src/templates/EventsReportTemplate.mjml +75 -0
  37. package/.claude/worktrees/eager-stonebraker-c08ac7/src/templates/FuelDropReportTemplate.mjml +60 -0
  38. package/.claude/worktrees/eager-stonebraker-c08ac7/src/templates/IdleReportTemplate.mjml +54 -0
  39. package/.claude/worktrees/eager-stonebraker-c08ac7/src/templates/KmsReportGroupByDayTemplate.mjml +43 -0
  40. package/.claude/worktrees/eager-stonebraker-c08ac7/src/templates/KmsReportTemplate.mjml +44 -0
  41. package/.claude/worktrees/eager-stonebraker-c08ac7/src/templates/LocationReportTemplate.mjml +37 -0
  42. package/.claude/worktrees/eager-stonebraker-c08ac7/src/templates/RefuelingReportTemplate.mjml +60 -0
  43. package/.claude/worktrees/eager-stonebraker-c08ac7/src/templates/SensorReportTemplate.mjml +23 -0
  44. package/.claude/worktrees/eager-stonebraker-c08ac7/src/templates/SpeedingReportTemplate.mjml +80 -0
  45. package/.claude/worktrees/eager-stonebraker-c08ac7/src/templates/TripReportTemplate.mjml +97 -0
  46. package/.claude/worktrees/eager-stonebraker-c08ac7/src/templates/ZoneReportTemplate.mjml +52 -0
  47. package/.claude/worktrees/eager-stonebraker-c08ac7/src/templates/report.mjml +96 -0
  48. package/.claude/worktrees/eager-stonebraker-c08ac7/src/tests/activity.test.js +99 -0
  49. package/.claude/worktrees/eager-stonebraker-c08ac7/src/tests/carbone.test.js +20 -0
  50. package/.claude/worktrees/eager-stonebraker-c08ac7/src/tests/events.test.js +30 -0
  51. package/.claude/worktrees/eager-stonebraker-c08ac7/src/tests/fuel.test.js +35 -0
  52. package/.claude/worktrees/eager-stonebraker-c08ac7/src/tests/idle.test.js +16 -0
  53. package/.claude/worktrees/eager-stonebraker-c08ac7/src/tests/index.js +24 -0
  54. package/.claude/worktrees/eager-stonebraker-c08ac7/src/tests/index.test.js +203 -0
  55. package/.claude/worktrees/eager-stonebraker-c08ac7/src/tests/kms.test.js +74 -0
  56. package/.claude/worktrees/eager-stonebraker-c08ac7/src/tests/performance.test.js +36 -0
  57. package/.claude/worktrees/eager-stonebraker-c08ac7/src/tests/positions.test.js +67 -0
  58. package/.claude/worktrees/eager-stonebraker-c08ac7/src/tests/speeding.test.js +120 -0
  59. package/.claude/worktrees/eager-stonebraker-c08ac7/src/tests/translations.test.js +21 -0
  60. package/.claude/worktrees/eager-stonebraker-c08ac7/src/tests/trips.test.js +140 -0
  61. package/.claude/worktrees/eager-stonebraker-c08ac7/src/tests/zones.test.js +162 -0
  62. package/.claude/worktrees/eager-stonebraker-c08ac7/src/trip-report.js +641 -0
  63. package/.claude/worktrees/eager-stonebraker-c08ac7/src/util/device.js +13 -0
  64. package/.claude/worktrees/eager-stonebraker-c08ac7/src/util/driver.js +90 -0
  65. package/.claude/worktrees/eager-stonebraker-c08ac7/src/util/fuel.js +102 -0
  66. package/.claude/worktrees/eager-stonebraker-c08ac7/src/util/geofence.js +52 -0
  67. package/.claude/worktrees/eager-stonebraker-c08ac7/src/util/odoo.js +18 -0
  68. package/.claude/worktrees/eager-stonebraker-c08ac7/src/util/parallel.js +64 -0
  69. package/.claude/worktrees/eager-stonebraker-c08ac7/src/util/pdfDocument.js +140 -0
  70. package/.claude/worktrees/eager-stonebraker-c08ac7/src/util/route.js +78 -0
  71. package/.claude/worktrees/eager-stonebraker-c08ac7/src/util/timetable.js +30 -0
  72. package/.claude/worktrees/eager-stonebraker-c08ac7/src/util/traccar.js +213 -0
  73. package/.claude/worktrees/eager-stonebraker-c08ac7/src/util/traccar.worker.js +49 -0
  74. package/.claude/worktrees/eager-stonebraker-c08ac7/src/util/trips.js +270 -0
  75. package/.claude/worktrees/eager-stonebraker-c08ac7/src/util/utils.js +358 -0
  76. package/.claude/worktrees/eager-stonebraker-c08ac7/src/util/worker.js +14 -0
  77. package/.claude/worktrees/eager-stonebraker-c08ac7/src/util/xpert.js +58 -0
  78. package/.claude/worktrees/eager-stonebraker-c08ac7/src/word/index.js +95 -0
  79. package/.claude/worktrees/eager-stonebraker-c08ac7/src/zone-report.js +904 -0
  80. package/lang/enGB.js +18 -0
  81. package/lang/esCL.js +18 -0
  82. package/lang/frFR.json +7 -1
  83. package/lang/ptBR.js +18 -0
  84. package/lang/ptPT.js +18 -0
  85. package/package.json +1 -1
  86. package/src/partnerReports/driverranking-report.js +152 -49
  87. package/src/speeding-report.js +11 -1
  88. package/src/util/pdfDocument.js +2 -1
@@ -0,0 +1,822 @@
1
+ const automaticReports = require('./automaticReports')
2
+ const {
3
+ convertMS, getDates, getTranslations, weekDaySelected, convertToLocaleString, isClientSide,
4
+ getLanguage, executeServerSide
5
+ } = require('./util/utils')
6
+ const jsPDF = require('jspdf')
7
+ require('jspdf-autotable')
8
+ const {
9
+ headerFromUser, addTable,
10
+ pdfPageByDay
11
+ } = require('./util/pdfDocument')
12
+ const { getStyle } = require('./reportStyle')
13
+ const { getUserPartner } = require('fleetmap-partners')
14
+ const traccar = require('./util/traccar')
15
+ const { isInsideTimetable, isPartialInsideTimetable, calculateTrip, getDataByDay, getKms, calculateMultiDayTrips } = require('./util/trips')
16
+ const { devicesToProcess } = require('./util/device')
17
+ const { getDriverData } = require('./util/driver')
18
+ const { calculateConsumption } = require('./util/fuel')
19
+ const { getSummaryByDay } = require('./util/traccar')
20
+
21
+ const fileName = 'ActivityReport'
22
+
23
+ async function createActivityReport (from, to, userData, traccarInstance) {
24
+ console.log('Create ActivityReport')
25
+ const reportData = []
26
+
27
+ if (userData.byDriver) {
28
+ const allData = await createActivityReportByDriver(from, to, userData, traccarInstance)
29
+ reportData.push(allData)
30
+ } else if (userData.byGroup) {
31
+ const allData = await createActivityReportByGroup(from, to, userData, traccarInstance)
32
+ reportData.push(...allData)
33
+ } else {
34
+ const allData = await createActivityReportByDevice(from, to, userData, traccarInstance)
35
+ reportData.push(allData)
36
+ }
37
+
38
+ return reportData
39
+ }
40
+
41
+ async function createActivityReportByGroup (from, to, userData, traccarInstance) {
42
+ console.log('ByGroup')
43
+ const reportData = []
44
+ for (const g of userData.groups) {
45
+ const devices = userData.devices.filter(d => d.groupId === g.id)
46
+ console.log(g.name + ' devices:' + devices.length)
47
+ if (devices.length > 0) {
48
+ const groupData = {
49
+ devices: [],
50
+ group: g,
51
+ xpert: devices.filter(d => d.attributes.xpert).length > 0,
52
+ from,
53
+ to
54
+ }
55
+
56
+ const response = await traccarInstance.reports.reportsSummaryGet(from, to, null, [g.id])
57
+ const summary = response.data
58
+
59
+ const responseTrips = await traccarInstance.reports.reportsTripsGet(from, to, null, [g.id])
60
+ const trips = responseTrips.data
61
+
62
+ devices.sort((a, b) => (a.name > b.name) ? 1 : -1)
63
+
64
+ if (summary.length > 0) {
65
+ groupData.devices = await processDevices(from, to, devices, { summary, trips }, userData, traccarInstance)
66
+
67
+ reportData.push(groupData)
68
+ }
69
+ }
70
+ }
71
+
72
+ const withoutGroupDevices = await createActivityReportByDevice(from, to, userData, traccarInstance)
73
+ reportData.push(withoutGroupDevices)
74
+
75
+ return reportData
76
+ }
77
+
78
+ async function createActivityReportByDevice (from, to, userData, traccarInstance) {
79
+ console.log('createActivityReportByDevice')
80
+ const allDevices = devicesToProcess(userData)
81
+
82
+ const allData = {
83
+ devices: [],
84
+ xpert: allDevices.filter(d => d.attributes.xpert).length > 0,
85
+ from,
86
+ to
87
+ }
88
+
89
+ if (isClientSide() && allDevices.length > 20) {
90
+ const sliced = automaticReports.sliceArray(allDevices, 10)
91
+ await executeServerSide('activity-report', sliced, from, to, userData, allDevices.length, traccarInstance, 5).then(devices => {
92
+ allData.devices = devices
93
+ })
94
+ } else {
95
+ let summaryByDay = []
96
+ if (userData.allWeek && userData.groupByDay) {
97
+ const dates = getDates(from, to, userData.user.attributes.timezone || getUserPartner(userData.user).timezone)
98
+ summaryByDay = await getSummaryByDay(dates, userData, traccarInstance, allDevices, true, from, to)
99
+ }
100
+
101
+ let deviceCount = 0
102
+ const sliced = automaticReports.sliceArray(allDevices, 20)
103
+ for (const devices of sliced) {
104
+ const needRoute = userData.groupByDay || !userData.allWeek
105
+ const {
106
+ trips,
107
+ route,
108
+ summary
109
+ } = await traccar.getAllInOne(traccarInstance, from, to, devices, needRoute, true, false, !userData.groupByDay, deviceCount, allDevices.length, 10)
110
+
111
+ // Process report data
112
+ if (summary.length || trips.length) {
113
+ allData.devices = allData.devices.concat(await processDevices(from, to, devices, { summaryByDay, summary, trips, route }, userData, traccarInstance))
114
+ }
115
+ deviceCount += devices.length
116
+ }
117
+ }
118
+
119
+ return allData
120
+ }
121
+
122
+ async function createActivityReportByDriver (from, to, userData, traccarInstance) {
123
+ console.log('createActivityReportByDriver')
124
+ const allDevices = await traccarInstance.devices.devicesGet().then(d => d.data)
125
+
126
+ const allData = {
127
+ drivers: [],
128
+ from,
129
+ to
130
+ }
131
+ const driversData = new Map()
132
+
133
+ let deviceCount = 0
134
+ const sliced = automaticReports.sliceArray(allDevices, 10)
135
+ for (const devices of sliced) {
136
+ const data = await traccar.getAllInOne(traccarInstance, from, to, devices, true, true, false, false, deviceCount, allDevices.length)
137
+
138
+ for (const d of userData.drivers) {
139
+ const { trips, route } = await getDriverData(d, data, userData)
140
+ let driverData = driversData.get(d.id)
141
+ if (!driverData) {
142
+ driverData = { trips: [], route: [] }
143
+ driversData.set(d.id, driverData)
144
+ }
145
+ driverData.trips = driverData.trips.concat(...(trips || []))
146
+ driverData.route = driverData.route.concat(...(route || []))
147
+ }
148
+
149
+ deviceCount += devices.length
150
+ }
151
+
152
+ allData.drivers = processDrivers(from, to, userData.drivers, driversData, userData)
153
+
154
+ return allData
155
+ }
156
+
157
+ async function processDevices (from, to, devices, data, userData, traccarInstance) {
158
+ const devicesResult = []
159
+
160
+ for (const d of devices) {
161
+ const deviceTrips = data.trips.filter(t => t.deviceId === d.id)
162
+ const deviceRoute = data.route.filter(t => t.deviceId === d.id)
163
+
164
+ let uncompletedTripRoute = []
165
+ if (deviceRoute.length && deviceTrips.length && deviceRoute[deviceRoute.length - 1].attributes.ignition) {
166
+ uncompletedTripRoute = deviceRoute.filter(p => p.attributes.ignition && new Date(p.fixTime).getTime() > new Date(deviceTrips[deviceTrips.length - 1].endTime).getTime())
167
+ if (uncompletedTripRoute.length) {
168
+ deviceTrips.push(calculateTrip(d, uncompletedTripRoute))
169
+ }
170
+ }
171
+
172
+ const summary = []
173
+ if (userData.groupByDay) {
174
+ const trips = []
175
+ for (const t of deviceTrips) {
176
+ if (userData.allWeek || !userData.weekDays) {
177
+ trips.push(t)
178
+ continue
179
+ }
180
+ if (isInsideTimetable(t, userData)) {
181
+ trips.push(t)
182
+ continue
183
+ }
184
+ if (await isPartialInsideTimetable(d, t, userData, deviceRoute)) {
185
+ trips.push(t)
186
+ }
187
+ }
188
+ const dates = getDates(from, to, userData.user.attributes.timezone || getUserPartner(userData.user).timezone)
189
+ for (const date of dates) {
190
+ const { tripsByDay, routeByDay } = await getDataByDay(d, date, { trips, route: deviceRoute }, userData, traccarInstance)
191
+ const distance = getKms(tripsByDay)
192
+ if (!userData.allWeek && userData.weekDays && userData.dayHours) {
193
+ if (weekDaySelected(date, userData.weekDays)) {
194
+ summary.push({
195
+ date,
196
+ deviceId: d.id,
197
+ averageSpeed: distance > 0 ? ((tripsByDay.reduce((a, b) => a + (b.averageSpeed * b.distance), 0)) / distance) : 0,
198
+ distance,
199
+ engineHours: tripsByDay.reduce((a, b) => a + b.duration, 0),
200
+ maxSpeed: tripsByDay.reduce((a, b) => {
201
+ return a > b.maxSpeed ? a : b.maxSpeed
202
+ }, 0),
203
+ endOdometer: 0,
204
+ startOdometer: 0,
205
+ startTime: tripsByDay.length && tripsByDay[0].startTime,
206
+ startAddress: tripsByDay.length && tripsByDay[0].startAddress,
207
+ endTime: tripsByDay.length && tripsByDay[tripsByDay.length - 1].endTime,
208
+ endAddress: tripsByDay.length && tripsByDay[tripsByDay.length - 1].endAddress,
209
+ startTimeIsOut: tripsByDay.some(t => t.startTimeIsOut),
210
+ endTimeIsOut: tripsByDay.some(t => t.endTimeIsOut)
211
+ })
212
+ }
213
+ } else {
214
+ const summaryByDay = data.summaryByDay.find(a => a.date.getTime() === date.getTime())
215
+ const summaryByDevice = summaryByDay.summary.find(a => a.deviceId === d.id)
216
+
217
+ const summaryCurrentDay = { date, deviceId: d.id }
218
+ summaryCurrentDay.engineHours = tripsByDay.reduce((a, b) => a + b.duration, 0)
219
+ summaryCurrentDay.distance = summaryByDevice.distance
220
+ summaryCurrentDay.averageSpeed = distance > 0 ? ((tripsByDay.reduce((a, b) => a + (b.averageSpeed * b.distance), 0)) / distance) : 0
221
+ summaryCurrentDay.maxSpeed = tripsByDay.reduce((a, b) => { return a > b.maxSpeed ? a : b.maxSpeed }, 0)
222
+ summaryCurrentDay.convertedSpentFuel = calculateConsumption(d, { route: routeByDay })
223
+ summaryCurrentDay.startTime = tripsByDay.length && tripsByDay[0].startTime
224
+ summaryCurrentDay.startAddress = tripsByDay.length && tripsByDay[0].startAddress
225
+ summaryCurrentDay.endTime = tripsByDay.length && tripsByDay[tripsByDay.length - 1].endTime
226
+ summaryCurrentDay.endAddress = tripsByDay.length && tripsByDay[tripsByDay.length - 1].endAddress
227
+ summaryCurrentDay.startOdometer = summaryByDevice.startOdometer
228
+ summaryCurrentDay.endOdometer = summaryByDevice.endOdometer
229
+ summaryCurrentDay.startTimeIsOut = tripsByDay.some(t => t.startTimeIsOut)
230
+ summaryCurrentDay.endTimeIsOut = tripsByDay.some(t => t.endTimeIsOut)
231
+ summary.push(summaryCurrentDay)
232
+ }
233
+ }
234
+ } else {
235
+ if (!userData.allWeek && userData.weekDays && userData.dayHours) {
236
+ const trips = deviceTrips.filter(t => userData.allWeek || !userData.weekDays || isInsideTimetable(t, userData) || isPartialInsideTimetable(d, t, userData, deviceRoute))
237
+ const distance = getKms(trips)
238
+ summary.push({
239
+ deviceId: d.id,
240
+ averageSpeed: distance > 0 ? ((trips.reduce((a, b) => a + (b.averageSpeed * b.distance), 0)) / distance) : 0,
241
+ distance,
242
+ engineHours: trips.reduce((a, b) => a + b.duration, 0),
243
+ maxSpeed: trips.reduce((a, b) => {
244
+ return a > b.maxSpeed ? a : b.maxSpeed
245
+ }, 0),
246
+ startOdometer: trips.length ? trips[0].startOdometer : 0,
247
+ endOdometer: trips.length ? trips[trips.length - 1].endOdometer : 0,
248
+ startTime: trips.length && trips[0].startTime,
249
+ startAddress: trips.length && trips[0].startAddress,
250
+ endTime: trips.length && trips[trips.length - 1].endTime,
251
+ endAddress: trips.length && trips[trips.length - 1].endAddress
252
+ })
253
+ } else {
254
+ summary.push(...data.summary.filter(s => s.deviceId === d.id))
255
+
256
+ if (summary.length) {
257
+ summary.forEach(s => {
258
+ s.engineHours = deviceTrips.reduce((a, b) => a + b.duration, 0)
259
+ s.convertedSpentFuel = calculateConsumption(d, { route: deviceRoute, summary: s })
260
+ s.startTime = deviceTrips.length && deviceTrips[0].startTime
261
+ s.startAddress = deviceTrips.length && deviceTrips[0].startAddress
262
+ s.endTime = deviceTrips.length && deviceTrips[deviceTrips.length - 1].endTime
263
+ s.endAddress = deviceTrips.length && deviceTrips[deviceTrips.length - 1].endAddress
264
+ })
265
+ }
266
+ }
267
+ }
268
+
269
+ const deviceData = {
270
+ device: d,
271
+ summary
272
+ }
273
+
274
+ if ((deviceData.summary.length && deviceData.summary.some(s => s.startTime)) || !userData.onlyWithActivity) {
275
+ devicesResult.push(deviceData)
276
+ }
277
+ }
278
+
279
+ return devicesResult
280
+ }
281
+
282
+ function processDrivers (from, to, drivers, driversData, userData) {
283
+ const driversResult = []
284
+ drivers.forEach(d => {
285
+ const { trips, route } = driversData.get(d.id)
286
+ trips.sort((a, b) => new Date(a.startTime).getTime() - new Date(b.startTime).getTime())
287
+ if (trips.length > 0) {
288
+ const driverData = {
289
+ driver: d,
290
+ summary: []
291
+ }
292
+ if (userData.groupByDay) {
293
+ const dates = getDates(from, to)
294
+ for (const date of dates) {
295
+ const fromByDay = new Date(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0)
296
+ const toByDay = new Date(date.getFullYear(), date.getMonth(), date.getDate(), 23, 59, 59)
297
+
298
+ const tripsByDay = trips.filter(t => (new Date(t.startTime) > fromByDay && new Date(t.endTime) < toByDay) &&
299
+ (userData.allWeek || !userData.weekDays || isInsideTimetable(t, userData) || isPartialInsideTimetable(undefined, t, userData, route)))
300
+
301
+ calculateMultiDayTrips(trips, route, tripsByDay, fromByDay, toByDay, userData.devices)
302
+
303
+ driverData.summary.push({
304
+ date,
305
+ distance: tripsByDay.filter(t => t.distance > 0).reduce((a, b) => a + b.distance, 0),
306
+ engineHours: tripsByDay.reduce((a, b) => a + b.duration, 0),
307
+ maxSpeed: tripsByDay.reduce((a, b) => Math.max(a, b.maxSpeed), 0),
308
+ averageSpeed: tripsByDay.length ? Math.round(tripsByDay.reduce((a, b) => a + b.averageSpeed, 0) / tripsByDay.length) : 0,
309
+ startTime: tripsByDay.length && tripsByDay[0].startTime,
310
+ startAddress: tripsByDay.length && tripsByDay[0].startAddress,
311
+ endTime: tripsByDay.length && tripsByDay[tripsByDay.length - 1].endTime,
312
+ endAddress: tripsByDay.length && tripsByDay[tripsByDay.length - 1].endAddress
313
+ })
314
+ }
315
+ } else {
316
+ driverData.summary.push({
317
+ distance: trips.filter(t => t.distance > 0).reduce((a, b) => a + b.distance, 0),
318
+ engineHours: trips.reduce((a, b) => a + b.duration, 0),
319
+ maxSpeed: trips.reduce((a, b) => Math.max(a, b.maxSpeed), 0),
320
+ averageSpeed: Math.round(trips.reduce((a, b) => a + b.averageSpeed, 0) / trips.length),
321
+ startTime: trips.length && trips[0].startTime,
322
+ startAddress: trips.length && trips[0].startAddress,
323
+ endTime: trips.length && trips[trips.length - 1].endTime,
324
+ endAddress: trips.length && trips[trips.length - 1].endAddress
325
+ })
326
+ }
327
+ driversResult.push(driverData)
328
+ }
329
+ })
330
+
331
+ return driversResult
332
+ }
333
+
334
+ async function exportActivityReportToPDF (userData, reportData) {
335
+ console.log('Export to PDF')
336
+ const translations = getTranslations(userData)
337
+
338
+ if (reportData.devices || reportData.drivers) {
339
+ // eslint-disable-next-line new-cap
340
+ const doc = new jsPDF.jsPDF('l')
341
+ await headerFromUser(doc, translations.report.titleActivityReport, userData.user)
342
+
343
+ if (userData.groupByDay && userData.byDriver) {
344
+ exportActivityReportToPDFDriverGroupByDay(doc, translations, reportData, userData)
345
+ } else if (userData.groupByDay) {
346
+ exportActivityReportToPDFVehicleGroupByDay(doc, translations, reportData, userData)
347
+ } else if (userData.byDriver) {
348
+ exportActivityReportToPDFDriver(doc, translations, reportData, userData)
349
+ } else {
350
+ exportActivityReportToPDFVehicle(doc, translations, reportData, userData)
351
+ }
352
+
353
+ return doc
354
+ }
355
+ }
356
+
357
+ function exportActivityReportToPDFDriver (doc, translations, reportData, userData) {
358
+ const timezone = userData.user.attributes.timezone
359
+ const lang = getLanguage(userData)
360
+
361
+ const headers = [translations.report.driver,
362
+ translations.report.start,
363
+ translations.report.end,
364
+ translations.report.distance,
365
+ translations.report.speed + ' ' + translations.report.avgSpeed + ' (Km/h)',
366
+ translations.report.speed + ' ' + translations.report.maxSpeed + ' (Km/h)',
367
+ translations.report.driverHours]
368
+
369
+ doc.setFontSize(10)
370
+ doc.text(convertToLocaleString(reportData.from, lang, timezone) + ' - ' + convertToLocaleString(reportData.to, lang, timezone), 20, 30)
371
+
372
+ const data = []
373
+ reportData.drivers.forEach(d => {
374
+ const temp = [
375
+ d.driver.name,
376
+ d.summary[0].startTime ? new Date(d.summary[0].startTime).toLocaleString() : '',
377
+ d.summary[0].endTime ? new Date(d.summary[0].endTime).toLocaleString() : '',
378
+ Number((d.summary[0].distance / 1000).toFixed(1)),
379
+ Math.round(d.summary[0].averageSpeed * 1.85200),
380
+ Math.round(d.summary[0].maxSpeed * 1.85200),
381
+ convertMS(d.summary[0].engineHours)
382
+ ]
383
+ data.push(temp)
384
+ })
385
+
386
+ const footValues = ['Total:' + reportData.drivers.length,
387
+ '', '',
388
+ (reportData.drivers.reduce((a, b) => a + b.summary[0].distance, 0) / 1000).toFixed(0),
389
+ getSumAvgSpeed(reportData.drivers, userData),
390
+ getSumMaxSpeed(reportData.drivers, userData),
391
+ convertMS(reportData.drivers.reduce((a, b) => a + b.summary[0].engineHours, 0))]
392
+
393
+ const style = getStyle(getUserPartner(userData.user))
394
+ addTable(doc, headers, data, footValues, style, 35)
395
+ }
396
+
397
+ function exportActivityReportToPDFVehicle (doc, translations, reportData, userData) {
398
+ const timezone = userData.user.attributes.timezone
399
+ const lang = getLanguage(userData)
400
+
401
+ const headers = [translations.report.vehicle,
402
+ translations.report.group,
403
+ translations.report.start,
404
+ translations.report.end,
405
+ translations.report.distance,
406
+ translations.report.start + ' ' + translations.report.odometer,
407
+ translations.report.end + ' ' + translations.report.odometer,
408
+ translations.report.avgSpeed + ' (Km/h)',
409
+ translations.report.maxSpeed + ' (Km/h)',
410
+ translations.report.engineHours,
411
+ translations.report.spentFuel]
412
+
413
+ doc.setFontSize(10)
414
+ doc.text(convertToLocaleString(reportData.from, lang, timezone, userData.user) + ' - ' + convertToLocaleString(reportData.to, lang, timezone, userData.user), 20, 30)
415
+
416
+ const data = []
417
+ reportData.devices.forEach(d => {
418
+ const group = userData.groups.find(g => d.device.groupId === g.id)
419
+
420
+ if (d.summary) {
421
+ const temp = [
422
+ d.device.name,
423
+ group ? group.name : '',
424
+ d.summary[0].startTime ? new Date(d.summary[0].startTime).toLocaleString() : '',
425
+ d.summary[0].endTime ? new Date(d.summary[0].endTime).toLocaleString() : '',
426
+ Number((d.summary[0].distance / 1000).toFixed(1)),
427
+ Number((d.summary[0].startOdometer / 1000).toFixed(1)),
428
+ Number((d.summary[0].endOdometer / 1000).toFixed(1)),
429
+ Math.round(d.summary[0].averageSpeed * 1.85200),
430
+ Math.round(d.summary[0].maxSpeed * 1.85200),
431
+ convertMS(d.summary[0].engineHours),
432
+ Math.round(d.summary[0].convertedSpentFuel >= 0 ? d.summary[0].convertedSpentFuel : 0)
433
+ ]
434
+ data.push(temp)
435
+ }
436
+ })
437
+
438
+ const footValues = ['Total:' + reportData.devices.length,
439
+ '', '', '',
440
+ (reportData.devices.reduce((a, b) => a + b.summary[0].distance, 0) / 1000).toFixed(0),
441
+ '', '',
442
+ getSumAvgSpeed(reportData.devices, userData),
443
+ getSumMaxSpeed(reportData.devices, userData),
444
+ convertMS(reportData.devices.reduce((a, b) => a + b.summary[0].engineHours, 0)),
445
+ Math.round(getSumSpentFuel(reportData.devices, userData))]
446
+
447
+ const style = getStyle(getUserPartner(userData.user))
448
+ addTable(doc, headers, data, footValues, style, 35)
449
+ }
450
+
451
+ function exportActivityReportToPDFVehicleGroupByDay (doc, translations, reportData, userData) {
452
+ const headers = [translations.report.date,
453
+ translations.report.start,
454
+ translations.report.end,
455
+ translations.report.distance,
456
+ translations.report.start + ' ' + translations.report.odometer,
457
+ translations.report.end + ' ' + translations.report.odometer,
458
+ translations.report.avgSpeed + ' (Km/h)',
459
+ translations.report.maxSpeed + ' (Km/h)',
460
+ translations.report.engineHours,
461
+ translations.report.spentFuel]
462
+
463
+ const weekDays = [
464
+ translations.report.sunday,
465
+ translations.report.monday,
466
+ translations.report.tuesday,
467
+ translations.report.wednesday,
468
+ translations.report.thursday,
469
+ translations.report.friday,
470
+ translations.report.saturday
471
+ ]
472
+
473
+ reportData.devices.forEach(function (d, index) {
474
+ pdfPageByDay(userData, d, index, doc, translations, reportData)
475
+
476
+ const data = []
477
+ d.summary.forEach(s => {
478
+ const temp = [
479
+ new Date(s.date).toLocaleDateString() + ' - ' + weekDays[new Date(s.date).getDay()],
480
+ s.startTime ? new Date(s.startTime).toLocaleTimeString() : '',
481
+ s.endTime ? new Date(s.endTime).toLocaleTimeString() : '',
482
+ Number((s.distance / 1000).toFixed(1)),
483
+ Number((s.startOdometer / 1000).toFixed(1)),
484
+ Number((s.endOdometer / 1000).toFixed(1)),
485
+ Math.round(s.averageSpeed * 1.85200),
486
+ Math.round(s.maxSpeed * 1.85200),
487
+ convertMS(s.engineHours),
488
+ Math.round(s.convertedSpentFuel >= 0 ? s.convertedSpentFuel : 0)
489
+ ]
490
+
491
+ data.push(temp)
492
+ })
493
+
494
+ const footValues = ['Total:' + d.summary.length,
495
+ '', '',
496
+ (d.summary.reduce((a, b) => a + b.distance, 0) / 1000).toFixed(0),
497
+ '', '',
498
+ getSumAvgSpeed(d.summary, userData),
499
+ getSumMaxSpeed(d.summary, userData),
500
+ convertMS(d.summary.reduce((a, b) => a + b.engineHours, 0)),
501
+ Math.round(getSumSpentFuel(d.summary, userData))]
502
+
503
+ const style = getStyle(getUserPartner(userData.user))
504
+ addTable(doc, headers, data, footValues, style, 40)
505
+ })
506
+ }
507
+
508
+ function exportActivityReportToExcelVehicleGroupByDay (settings, translations, reportData, userData) {
509
+ const headers = [{ label: translations.report.vehicle, value: 'name' },
510
+ { label: translations.report.group, value: 'group' },
511
+ { label: translations.report.date, value: 'date' },
512
+ { label: translations.report.weekDay, value: 'weekday' },
513
+ { label: translations.report.start, value: 'start' },
514
+ { label: translations.report.end, value: 'end' },
515
+ { label: translations.report.distance, value: 'distance' },
516
+ { label: translations.report.start + ' ' + translations.report.odometer, value: 'startOdometer' },
517
+ { label: translations.report.end + ' ' + translations.report.odometer, value: 'endOdometer' },
518
+ { label: translations.report.speed + ' ' + translations.report.avgSpeed + ' (Km/h)', value: 'avgSpeed' },
519
+ { label: translations.report.speed + ' ' + translations.report.maxSpeed + ' (Km/h)', value: 'maxSpeed' },
520
+ { label: translations.report.engineHours, value: 'engineHours' },
521
+ { label: translations.report.spentFuel, value: 'spentFuel' }]
522
+
523
+ const weekDays = [
524
+ translations.report.sunday,
525
+ translations.report.monday,
526
+ translations.report.tuesday,
527
+ translations.report.wednesday,
528
+ translations.report.thursday,
529
+ translations.report.friday,
530
+ translations.report.saturday
531
+ ]
532
+
533
+ let data = []
534
+ if (reportData.devices) {
535
+ reportData.devices.forEach(d => {
536
+ const group = userData.groups.find(g => d.device.groupId === g.id)
537
+
538
+ data = data.concat([{}])
539
+ data = data.concat(d.summary.map(s => {
540
+ return {
541
+ name: d.device.name,
542
+ group: group ? group.name : '',
543
+ date: new Date(s.date).toLocaleDateString(),
544
+ weekday: weekDays[new Date(s.date).getDay()],
545
+ start: s.startTime ? new Date(s.startTime).toLocaleTimeString() : '',
546
+ end: s.endTime ? new Date(s.endTime).toLocaleTimeString() : '',
547
+ distance: Number((s.distance / 1000).toFixed(0)),
548
+ startOdometer: Number((s.startOdometer / 1000).toFixed(0)),
549
+ endOdometer: Number((s.endOdometer / 1000).toFixed(0)),
550
+ avgSpeed: Math.round(s.averageSpeed * 1.85200),
551
+ maxSpeed: Math.round(s.maxSpeed * 1.85200),
552
+ engineHours: convertMS(s.engineHours),
553
+ spentFuel: Math.round(s.convertedSpentFuel >= 0 ? s.convertedSpentFuel : 0)
554
+ }
555
+ }))
556
+ })
557
+ return {
558
+ headers,
559
+ data,
560
+ settings
561
+ }
562
+ }
563
+ }
564
+
565
+ function exportActivityReportToPDFDriverGroupByDay (doc, translations, reportData, userData) {
566
+ const headers = [translations.report.date,
567
+ translations.report.start,
568
+ translations.report.end,
569
+ translations.report.distance,
570
+ translations.report.avgSpeed + ' (Km/h)',
571
+ translations.report.maxSpeed + ' (Km/h)',
572
+ translations.report.engineHours]
573
+
574
+ const weekDays = [
575
+ translations.report.sunday,
576
+ translations.report.monday,
577
+ translations.report.tuesday,
578
+ translations.report.wednesday,
579
+ translations.report.thursday,
580
+ translations.report.friday,
581
+ translations.report.saturday
582
+ ]
583
+
584
+ reportData.drivers.forEach(function (d, index) {
585
+ const name = d.driver.name
586
+ const group = userData.groups.find(g => g.drivers.includes(d.id))
587
+
588
+ const space = index === 0 ? 8 : 0
589
+ if (index) {
590
+ doc.addPage()
591
+ }
592
+
593
+ doc.setFontSize(13)
594
+ doc.text(name, 20, space + 20)
595
+ doc.setFontSize(11)
596
+ doc.text(group ? translations.report.group + ': ' + group.name : '', 150, space + 20)
597
+ doc.text(new Date(reportData.from).toLocaleString() + ' - ' + new Date(reportData.to).toLocaleString(), 20, space + 25)
598
+
599
+ if (!userData.allWeek && userData.weekDays) {
600
+ doc.text((userData.weekDays.monday ? ' ' + translations.report.monday + ',' : '') +
601
+ (userData.weekDays.tuesday ? ' ' + translations.report.tuesday + ',' : '') +
602
+ (userData.weekDays.wednesday ? ' ' + translations.report.wednesday + ',' : '') +
603
+ (userData.weekDays.thursday ? ' ' + translations.report.thursday + ',' : '') +
604
+ (userData.weekDays.friday ? ' ' + translations.report.friday + ',' : '') +
605
+ (userData.weekDays.saturday ? ' ' + translations.report.saturday + ',' : '') +
606
+ (userData.weekDays.sunday ? ' ' + translations.report.sunday + ',' : '') +
607
+ ' das ' +
608
+ userData.dayHours.startTime + ' - ' + userData.dayHours.endTime, 20, space + 30)
609
+ }
610
+
611
+ const data = []
612
+ d.summary.forEach(s => {
613
+ const temp = [
614
+ new Date(s.date).toLocaleDateString() + ' - ' + weekDays[new Date(s.date).getDay()],
615
+ s.startTime ? new Date(s.startTime).toLocaleTimeString() : '',
616
+ s.endTime ? new Date(s.endTime).toLocaleTimeString() : '',
617
+ Number((s.distance / 1000).toFixed(1)),
618
+ Math.round(s.averageSpeed * 1.85200),
619
+ Math.round(s.maxSpeed * 1.85200),
620
+ convertMS(s.engineHours)
621
+ ]
622
+
623
+ data.push(temp)
624
+ })
625
+
626
+ const footValues = ['Total:' + d.summary.length,
627
+ '', '',
628
+ (d.summary.reduce((a, b) => a + b.distance, 0) / 1000).toFixed(0),
629
+ getSumAvgSpeed(d.summary, userData),
630
+ getSumMaxSpeed(d.summary, userData),
631
+ convertMS(d.summary.reduce((a, b) => a + b.engineHours, 0))]
632
+
633
+ const style = getStyle(getUserPartner(userData.user))
634
+ addTable(doc, headers, data, footValues, style, 40)
635
+ })
636
+ }
637
+
638
+ function exportActivityReportToExcelDriverGroupByDay (settings, translations, reportData, userData) {
639
+ const headers = [{ label: translations.report.driver, value: 'name' },
640
+ { label: translations.report.group, value: 'group' },
641
+ { label: translations.report.date, value: 'date' },
642
+ { label: translations.report.weekDay, value: 'weekday' },
643
+ { label: translations.report.start, value: 'start' },
644
+ { label: translations.report.end, value: 'end' },
645
+ { label: translations.report.distance, value: 'distance' },
646
+ { label: translations.report.speed + ' ' + translations.report.avgSpeed + ' (Km/h)', value: 'avgSpeed' },
647
+ { label: translations.report.speed + ' ' + translations.report.maxSpeed + ' (Km/h)', value: 'maxSpeed' },
648
+ { label: translations.report.engineHours, value: 'engineHours' }]
649
+
650
+ const weekDays = [
651
+ translations.report.sunday,
652
+ translations.report.monday,
653
+ translations.report.tuesday,
654
+ translations.report.wednesday,
655
+ translations.report.thursday,
656
+ translations.report.friday,
657
+ translations.report.saturday
658
+ ]
659
+
660
+ let data = []
661
+ if (reportData.drivers) {
662
+ reportData.drivers.forEach(d => {
663
+ const group = userData.groups.find(g => g.drivers.includes(d.id))
664
+
665
+ data = data.concat([{}])
666
+ data = data.concat(d.summary.map(s => {
667
+ return {
668
+ name: d.driver.name,
669
+ group: group ? group.name : '',
670
+ date: new Date(s.date).toLocaleDateString(),
671
+ weekday: weekDays[new Date(s.date).getDay()],
672
+ start: s.startTime ? new Date(s.startTime).toLocaleTimeString() : '',
673
+ end: s.endTime ? new Date(s.endTime).toLocaleTimeString() : '',
674
+ distance: Number((s.distance / 1000).toFixed(0)),
675
+ avgSpeed: Math.round(s.averageSpeed * 1.85200),
676
+ maxSpeed: Math.round(s.maxSpeed * 1.85200),
677
+ engineHours: convertMS(s.engineHours)
678
+ }
679
+ }))
680
+ })
681
+ return {
682
+ headers,
683
+ data,
684
+ settings
685
+ }
686
+ }
687
+ }
688
+
689
+ function exportActivityReportToExcelDriver (settings, translations, reportData) {
690
+ const headers = [{ label: translations.report.driver, value: 'driver' },
691
+ { label: translations.report.start, value: 'start' },
692
+ { label: translations.report.end, value: 'end' },
693
+ { label: translations.report.distance, value: 'distance' },
694
+ { label: translations.report.speed + ' ' + translations.report.avgSpeed + ' (Km/h)', value: 'avgSpeed' },
695
+ { label: translations.report.speed + ' ' + translations.report.maxSpeed + ' (Km/h)', value: 'maxSpeed' },
696
+ { label: translations.report.driverHours, value: 'engineHours' }]
697
+
698
+ let data = []
699
+ if (reportData.drivers) {
700
+ reportData.drivers.forEach(d => {
701
+ data = data.concat([{
702
+ driver: d.driver.name,
703
+ start: d.summary[0].startTime ? new Date(d.summary[0].startTime).toLocaleString() : '',
704
+ end: d.summary[0].endTime ? new Date(d.summary[0].endTime).toLocaleString() : '',
705
+ distance: Number((d.summary[0].distance / 1000).toFixed(0)),
706
+ avgSpeed: Math.round(d.summary[0].averageSpeed * 1.85200),
707
+ maxSpeed: Math.round(d.summary[0].maxSpeed * 1.85200),
708
+ engineHours: convertMS(d.summary[0].engineHours)
709
+ }])
710
+ })
711
+ return {
712
+ headers,
713
+ data,
714
+ settings
715
+ }
716
+ }
717
+ }
718
+
719
+ function exportActivityReportToExcelVehicle (settings, translations, reportData, userData) {
720
+ const headers = [{ label: translations.report.vehicle, value: 'name' },
721
+ { label: translations.report.group, value: 'group' },
722
+ { label: translations.report.start, value: 'start' },
723
+ { label: translations.report.end, value: 'end' },
724
+ { label: translations.report.distance, value: 'distance' },
725
+ { label: translations.report.start + ' ' + translations.report.odometer, value: 'startOdometer' },
726
+ { label: translations.report.end + ' ' + translations.report.odometer, value: 'endOdometer' },
727
+ { label: translations.report.speed + ' ' + translations.report.avgSpeed + ' (Km/h)', value: 'avgSpeed' },
728
+ { label: translations.report.speed + ' ' + translations.report.maxSpeed + ' (Km/h)', value: 'maxSpeed' },
729
+ { label: translations.report.engineHours, value: 'engineHours' },
730
+ { label: translations.report.spentFuel, value: 'spentFuel' }]
731
+
732
+ if (reportData.devices) {
733
+ let data = []
734
+ reportData.devices.forEach(d => {
735
+ const group = userData.groups.find(g => d.device.groupId === g.id)
736
+
737
+ if (d.summary) {
738
+ data = data.concat([{
739
+ name: d.device.name,
740
+ group: group ? group.name : '',
741
+ start: d.summary[0].startTime ? new Date(d.summary[0].startTime).toLocaleString() : '',
742
+ end: d.summary[0].endTime ? new Date(d.summary[0].endTime).toLocaleString() : '',
743
+ distance: Number((d.summary[0].distance / 1000).toFixed(0)),
744
+ startOdometer: Number((d.summary[0].startOdometer / 1000).toFixed(0)),
745
+ endOdometer: Number((d.summary[0].endOdometer / 1000).toFixed(0)),
746
+ avgSpeed: Math.round(d.summary[0].averageSpeed * 1.85200),
747
+ maxSpeed: Math.round(d.summary[0].maxSpeed * 1.85200),
748
+ engineHours: convertMS(d.summary[0].engineHours),
749
+ spentFuel: Math.round(d.summary[0].convertedSpentFuel >= 0 ? d.summary[0].convertedSpentFuel : 0)
750
+ }])
751
+ }
752
+ })
753
+ return {
754
+ headers,
755
+ data,
756
+ settings
757
+ }
758
+ }
759
+ }
760
+
761
+ function exportActivityReportToExcel (userData, reportData) {
762
+ console.log('Export to Excel')
763
+ const translations = getTranslations(userData)
764
+
765
+ const settings = {
766
+ sheetName: translations.report.titleActivityReport, // The name of the sheet
767
+ fileName // The name of the spreadsheet
768
+ }
769
+
770
+ if (userData.groupByDay && userData.byDriver) {
771
+ return exportActivityReportToExcelDriverGroupByDay(settings, translations, reportData, userData)
772
+ } else if (userData.groupByDay) {
773
+ return exportActivityReportToExcelVehicleGroupByDay(settings, translations, reportData, userData)
774
+ } else if (userData.byDriver) {
775
+ return exportActivityReportToExcelDriver(settings, translations, reportData, userData)
776
+ } else {
777
+ return exportActivityReportToExcelVehicle(settings, translations, reportData, userData)
778
+ }
779
+ }
780
+
781
+ function getSumAvgSpeed (data, userData) {
782
+ const values = data.map(item => {
783
+ if (userData.groupByDay) {
784
+ return Math.round(item.averageSpeed * 1.85200)
785
+ } else {
786
+ return Math.round(item.summary[0].averageSpeed * 1.85200)
787
+ }
788
+ }).filter(v => v !== 0)
789
+ return Math.round((values.reduce((a, b) => a + b, 0)) / values.length)
790
+ }
791
+
792
+ function getSumMaxSpeed (data, userData) {
793
+ const values = data.map(item => {
794
+ if (userData.groupByDay) {
795
+ return item.maxSpeed
796
+ } else {
797
+ return item.summary[0].maxSpeed
798
+ }
799
+ })
800
+ const max = values.reduce(function (a, b) {
801
+ return Math.max(a, b)
802
+ })
803
+ return Math.round(max * 1.85200)
804
+ }
805
+
806
+ function getSumSpentFuel (data, userData) {
807
+ const values = data.map(item => {
808
+ if (userData.groupByDay) {
809
+ return item.convertedSpentFuel >= 0 ? item.convertedSpentFuel : 0
810
+ } else {
811
+ return item.summary[0].convertedSpentFuel >= 0 ? item.summary[0].convertedSpentFuel : 0
812
+ }
813
+ })
814
+ return values.reduce((a, b) => a + b, 0)
815
+ }
816
+
817
+ exports.createActivityReport = createActivityReport
818
+ exports.exportActivityReportToPDF = exportActivityReportToPDF
819
+ exports.exportActivityReportToExcel = exportActivityReportToExcel
820
+ exports.create = createActivityReport
821
+ exports.exportToPDF = exportActivityReportToPDF
822
+ exports.exportToExcel = exportActivityReportToExcel