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,496 @@
1
+ const jsPDF = require('jspdf')
2
+ require('jspdf-autotable')
3
+ const { headerFromUser } = require('./util/pdfDocument')
4
+ const { getStyle } = require('./reportStyle')
5
+ const { getUserPartner } = require('fleetmap-partners')
6
+ const {
7
+ convertToLocaleString, getTranslations,
8
+ getLanguage, isClientSide
9
+ } = require('./util/utils')
10
+ const traccarHelper = require('./util/traccar')
11
+ const { devicesToProcess } = require('./util/device')
12
+ const automaticReports = require('./automaticReports')
13
+ const { getDriverName, getDriverData } = require('./util/driver')
14
+ const { default: axios } = require('axios')
15
+
16
+ const fileName = 'LocationReport'
17
+
18
+ async function createLocationReport (from, to, userData, traccar) {
19
+ console.log('Create LocationReport')
20
+ const reportData = []
21
+
22
+ if (userData.byDriver) {
23
+ console.log('ByDriver')
24
+ if (isClientSide()) {
25
+ const cookie = await axios.get('/pinmeapi/cookie/get', { withCredentials: true }).then(d => d.data)
26
+ const url = `https://${process.env.SERVER_HOST}/pinmeapi/reports/location-report`
27
+ return axios.post(url, { from, to, userData, cookie }).then(d => d.data)
28
+ }
29
+ const report = await createLocationReportByDriver(from, to, userData, traccar)
30
+ reportData.push(report)
31
+ } else if (userData.byGroup) {
32
+ console.log('ByGroup')
33
+ const allData = await createLocationReportByGroup(from, to, userData, traccar)
34
+ reportData.push(...allData)
35
+ } else {
36
+ console.log('ByDevice')
37
+ const report = await createLocationReportByDevice(from, to, userData, traccar)
38
+ reportData.push(report)
39
+ }
40
+
41
+ return reportData
42
+ }
43
+
44
+ async function createLocationReportByDriver (from, to, userData, traccar) {
45
+ // server side only
46
+ const url = `http://${process.env.TRACCAR_HOST}/api/devices`
47
+ const devices = await traccar.axios.get(url).then(d => d.data)
48
+ console.log(devices.length)
49
+
50
+ if (!devices.length) { return { drivers: [] } }
51
+
52
+ const deviceCount = 0
53
+ const results = []
54
+ const batchSize = 50
55
+ for (let i = 0; i < devices.length; i += batchSize) {
56
+ const batch = devices.slice(i, i + batchSize)
57
+ const batchResults = await Promise.all(batch.map(device => _getDriverRoute(traccar, from, to, [device], deviceCount, devices, userData)))
58
+ const filtered = batchResults.filter(r => r && r.length)
59
+ console.log(i, '/', devices.length, 'pushing', filtered, batchResults)
60
+ filtered.forEach(r => {
61
+ r = r[0]
62
+ if (r.driver && r.positions) {
63
+ const driver = results.find(d => d.driver && d.driver.id === r.driver.id)
64
+ if (driver) {
65
+ driver.positions = driver.positions.concat(r.positions).sort((a, b) => new Date(a.fixTime).getTime() - new Date(b.fixTime).getTime())
66
+ } else {
67
+ results.push(r)
68
+ }
69
+ }
70
+ })
71
+ }
72
+ return { drivers: results }
73
+ }
74
+
75
+ async function _getDriverRoute (traccar, from, to, slice, deviceCount, devices, userData, retry = 3) {
76
+ const url = 'https://rntba7aqifqx3iywrenb7pq6km0liwsr.lambda-url.us-east-1.on.aws/pinmeapi/process-report/get-driver-route'
77
+ return traccar.axios.post(url, {
78
+ from,
79
+ to,
80
+ slice,
81
+ deviceCount,
82
+ devices,
83
+ userData
84
+ }, { timeout: 1000 * 60 * 5 })
85
+ .catch((e) => {
86
+ if (--retry) {
87
+ return _getDriverRoute(traccar, from, to, slice, deviceCount, devices, userData, retry)
88
+ } else {
89
+ console.error('giving up', slice[0].id, 'after 3 retries', e.message)
90
+ }
91
+ })
92
+ .then(r => (r && r.data) || [])
93
+ }
94
+
95
+ async function getDriverRoute (traccar, from, to, slice, deviceCount, devices, userData) {
96
+ const allInOne = await traccarHelper.getAllInOne(
97
+ traccar, from, to, slice, true, false, false, false,
98
+ deviceCount,
99
+ devices.length,
100
+ 5,
101
+ 1,
102
+ undefined,
103
+ false,
104
+ true
105
+ )
106
+ return processDrivers(from, to, userData, allInOne)
107
+ }
108
+
109
+ async function createLocationReportByGroup (from, to, userData, traccar) {
110
+ const reportData = []
111
+
112
+ for (const g of userData.groups) {
113
+ const devices = userData.devices.filter(d => d.groupId === g.id)
114
+
115
+ if (devices.length > 0) {
116
+ const groupData = {
117
+ devices: [],
118
+ group: g,
119
+ xpert: devices.filter(d => d.attributes.xpert).length > 0
120
+ }
121
+
122
+ const response = await traccar.reports.reportsRouteGet(from, to, null, [g.id])
123
+ const data = response.data
124
+
125
+ devices.sort((a, b) => (a.name > b.name) ? 1 : -1)
126
+
127
+ if (data.length > 0) {
128
+ console.log('Locations:' + data.length)
129
+ groupData.devices = processDevices(from, to, devices, userData.drivers, data)
130
+
131
+ reportData.push(groupData)
132
+ }
133
+ }
134
+ }
135
+
136
+ const allData = await createLocationReportByDevice(from, to, userData, traccar)
137
+ reportData.push(allData)
138
+
139
+ return reportData
140
+ }
141
+
142
+ async function createLocationReportByDevice (from, to, userData, traccar) {
143
+ const devices = devicesToProcess(userData)
144
+
145
+ const allData = {
146
+ devices: [],
147
+ xpert: devices.filter(d => d.attributes.xpert).length > 0
148
+ }
149
+
150
+ const sliced = automaticReports.sliceArray(devices, 5)
151
+
152
+ let deviceCount = 0
153
+ for (const slice of sliced) {
154
+ const allInOne = await traccarHelper.getAllInOne(traccar, from, to, slice, true, false, false, false, deviceCount, devices.length)
155
+ const routeData = allInOne.route
156
+
157
+ console.log('Locations:' + routeData.length)
158
+
159
+ if (routeData.length > 0) {
160
+ allData.devices.push(...processDevices(from, to, slice, {
161
+ route: routeData
162
+ }, userData))
163
+ }
164
+ deviceCount = deviceCount + slice.length
165
+ }
166
+
167
+ return allData
168
+ }
169
+
170
+ function processDevices (from, to, devices, data) {
171
+ const devicesResult = []
172
+
173
+ for (const d of devices) {
174
+ const lighterPositions = data.route.filter(t => t.deviceId === d.id)
175
+ if (lighterPositions.length > 0) {
176
+ devicesResult.push({
177
+ device: d,
178
+ from,
179
+ to,
180
+ positions: lighterPositions
181
+ })
182
+ }
183
+ }
184
+
185
+ return devicesResult
186
+ }
187
+
188
+ async function processDrivers (from, to, userData, data) {
189
+ const result = []
190
+ for (const d of userData.drivers) {
191
+ const { route } = await getDriverData(d, data, userData)
192
+
193
+ if (route.length > 0) {
194
+ route.sort((a, b) => new Date(a.fixTime).getTime() - new Date(b.fixTime).getTime())
195
+
196
+ route.forEach(p => {
197
+ const device = userData.devices.find(d => d.id === p.deviceId)
198
+ if (device) {
199
+ p.vehicleName = device.name
200
+ }
201
+ })
202
+
203
+ const driverData = {
204
+ driver: d,
205
+ from,
206
+ to,
207
+ positions: route
208
+ }
209
+ console.log('pushing', route.length, 'for', d.name)
210
+ result.push(driverData)
211
+ }
212
+ }
213
+ return result
214
+ }
215
+
216
+ async function exportLocationReportToPDF (userData, reportData) {
217
+ console.log('Export to PDF')
218
+
219
+ const translations = getTranslations(userData)
220
+ const lang = getLanguage(userData)
221
+ const timezone = userData.user.attributes.timezone
222
+
223
+ const positionsData = userData.byDriver ? reportData.drivers : reportData.devices
224
+
225
+ const headers = userData.byDriver
226
+ ? [
227
+ translations.report.date,
228
+ translations.report.address,
229
+ translations.report.speed,
230
+ translations.report.ignition,
231
+ translations.report.vehicle
232
+ ]
233
+ : [
234
+ translations.report.date,
235
+ translations.report.address,
236
+ translations.report.speed,
237
+ translations.report.ignition,
238
+ translations.report.driver
239
+ ]
240
+
241
+ if (userData.includeTemperature) {
242
+ headers.push(translations.report.temperature)
243
+ headers.push(translations.report.temperature2 || 'Temperature 2')
244
+ }
245
+
246
+ if (positionsData) {
247
+ if (userData.includeDigitalPorts) {
248
+ headers.push(
249
+ getDigitalPort1Label(
250
+ userData.byDriver ? positionsData[0].driver : positionsData[0].device,
251
+ translations,
252
+ positionsData[0].device.attributes
253
+ ))
254
+ headers.push(
255
+ getDigitalPort2Label(
256
+ userData.byDriver ? positionsData[0].driver : positionsData[0].device,
257
+ translations,
258
+ positionsData[0].device.attributes))
259
+ }
260
+ headers.push('Latitude')
261
+ headers.push('Longitude')
262
+
263
+ let first = true
264
+ // eslint-disable-next-line new-cap
265
+ const doc = new jsPDF.jsPDF('l')
266
+ await headerFromUser(doc, translations.report.titleLocationReport, userData.user)
267
+
268
+ positionsData.forEach(d => {
269
+ const data = []
270
+ const name = userData.byDriver ? d.driver.name : deviceName(d.device)
271
+ const group = userData.byDriver ? '' : deviceGroupName(userData.groups, d.device, translations)
272
+
273
+ let space = 0
274
+ if (!first) {
275
+ doc.addPage()
276
+ } else {
277
+ first = false
278
+ space = 10
279
+ }
280
+ doc.setFontSize(13)
281
+ doc.text(name, 20, space + 20)
282
+ doc.setFontSize(11)
283
+ doc.text(group, 200, space + 20)
284
+ doc.text(convertToLocaleString(d.from, lang, timezone) + ' - ' + convertToLocaleString(d.to, lang, timezone), 20, space + 25)
285
+ d.positions.forEach(a => {
286
+ const temp = [
287
+ getLocationDate(a, userData.user),
288
+ a.address,
289
+ Math.round(a.speed * 1.85200),
290
+ a.attributes.ignition ? translations.report.ignitionOn : translations.report.ignitionOff,
291
+ userData.byDriver ? a.vehicleName : getDriverName(a.attributes.driverUniqueId, a.fixTime, userData)
292
+ ]
293
+
294
+ if (userData.includeTemperature) {
295
+ temp.push(getTempValue(a, 'temp1'))
296
+ temp.push(getTempValue(a, 'temp2'))
297
+ }
298
+
299
+ if (userData.includeDigitalPorts) {
300
+ temp.push(getDigitalPortValue(a, 1, translations, d.device.attributes))
301
+ temp.push(getDigitalPortValue(a, 2, translations, d.device.attributes))
302
+ }
303
+ temp.push(a.latitude)
304
+ temp.push(a.longitude)
305
+ data.push(temp)
306
+ })
307
+
308
+ const footValues = [
309
+ 'Total:' + d.positions.length,
310
+ '', getMaxSpeed(d.positions)
311
+ ]
312
+
313
+ const style = getStyle(getUserPartner(userData.user))
314
+ doc.autoTable({
315
+ head: [headers],
316
+ body: data,
317
+ foot: [footValues],
318
+ showFoot: 'lastPage',
319
+ headStyles: {
320
+ fillColor: style.pdfHeaderColor,
321
+ textColor: style.pdfHeaderTextColor,
322
+ fontSize: 10
323
+ },
324
+ bodyStyles: {
325
+ fillColor: style.pdfBodyColor,
326
+ textColor: style.pdfBodyTextColor,
327
+ fontSize: 8
328
+ },
329
+ footStyles: {
330
+ fillColor: style.pdfFooterColor,
331
+ textColor: style.pdfFooterTextColor,
332
+ fontSize: 9
333
+ },
334
+ startY: space + 35
335
+ })
336
+ })
337
+
338
+ return doc
339
+ }
340
+ }
341
+
342
+ function getTempValue (row, sensor) {
343
+ return (row.attributes[sensor] !== 175 && row.attributes[sensor]) || ''
344
+ }
345
+
346
+ function getDigitalPort1Label (row, translations, dAttributes) {
347
+ return (dAttributes && dAttributes.sensor1) || (row.attributes && row.attributes.sensor1) || translations.report.digital_ports1
348
+ }
349
+
350
+ function getDigitalPort2Label (row, translations, dAttributes) {
351
+ return (dAttributes && dAttributes.sensor2) || (row.attributes && row.attributes.sensor2) || translations.report.digital_ports2
352
+ }
353
+
354
+ function getDigitalPortValue (row, index, translations, dAttributes) {
355
+ switch (index) {
356
+ case 1:
357
+ if (row.attributes && row.attributes.door1 !== undefined) {
358
+ return translations.report[row.attributes.door1]
359
+ }
360
+ if (row.attributes && row.attributes.sensor !== undefined) {
361
+ return row.attributes.sensor
362
+ ? dAttributes.sensor1on || row.attributes.sensor
363
+ : dAttributes.sensor1off || row.attributes.sensor
364
+ }
365
+ if (row.attributes && row.attributes[dAttributes.sensor1Attribute] !== undefined) {
366
+ return row.attributes[dAttributes.sensor1Attribute]
367
+ ? dAttributes.sensor1on || row.attributes.sensor
368
+ : dAttributes.sensor1off || row.attributes.sensor
369
+ }
370
+ break
371
+ case 2:
372
+ if (row.attributes && row.attributes[dAttributes.sensor2Attribute] !== undefined) {
373
+ return row.attributes[dAttributes.sensor2Attribute]
374
+ ? dAttributes.sensor2on || row.attributes.sensor
375
+ : dAttributes.sensor2off || row.attributes.sensor
376
+ }
377
+ if (row.attributes && row.attributes.door2) {
378
+ return translations.report[row.attributes.door2]
379
+ }
380
+ return row.attributes && row.attributes.rain ? translations.report[row.attributes.rain] : ''
381
+ default:
382
+ return ''
383
+ }
384
+ }
385
+
386
+ function exportLocationReportToExcel (userData, reportData) {
387
+ console.log('Export to Excel')
388
+
389
+ const translations = getTranslations(userData)
390
+
391
+ const positionsData = userData.byDriver ? reportData.drivers : reportData.devices
392
+
393
+ const settings = {
394
+ sheetName: translations.report.titleLocationReport, // The name of the sheet
395
+ fileName // The name of the spreadsheet
396
+ }
397
+ const headers = userData.byDriver
398
+ ? [
399
+ { label: translations.report.driver, value: 'driver' },
400
+ { label: translations.report.date, value: 'date' },
401
+ { label: translations.report.address, value: 'address' },
402
+ { label: translations.report.speed, value: 'speed' },
403
+ { label: translations.report.ignition, value: 'ignition' },
404
+ { label: translations.report.vehicle, value: 'name' },
405
+ { label: translations.report.temperature, value: 'temperature' }
406
+ ]
407
+ : [
408
+ { label: translations.report.vehicle, value: 'name' },
409
+ { label: translations.report.group, value: 'group' },
410
+ { label: translations.report.date, value: 'date' },
411
+ { label: translations.report.address, value: 'address' },
412
+ { label: translations.report.speed, value: 'speed' },
413
+ { label: translations.report.ignition, value: 'ignition' },
414
+ { label: translations.report.driver, value: 'driver' }
415
+ ]
416
+
417
+ if (userData.includeTemperature) {
418
+ headers.push({ label: translations.report.temperature, value: 'temperature' })
419
+ headers.push({ label: translations.report.temperature2, value: 'temperature2' })
420
+ }
421
+
422
+ headers.push({ label: 'Latitude', value: 'latitude' })
423
+ headers.push({ label: 'Longitude', value: 'longitude' })
424
+
425
+ let data = []
426
+ if (positionsData) {
427
+ if (userData.includeDigitalPorts) {
428
+ headers.push({ label: getDigitalPort1Label(userData.byDriver ? positionsData[0].driver : positionsData[0].device, translations), value: 'digitalport1' })
429
+ headers.push({ label: getDigitalPort2Label(userData.byDriver ? positionsData[0].driver : positionsData[0].device, translations), value: 'digitalport2' })
430
+ }
431
+
432
+ positionsData.forEach(d => {
433
+ data = data.concat(d.positions.map(a => {
434
+ return {
435
+ name: userData.byDriver ? a.vehicleName : d.device.name,
436
+ group: userData.byDriver ? '' : deviceGroupName(userData.groups, d.device),
437
+ date: getLocationDate(a, userData.user),
438
+ address: a.address,
439
+ speed: Math.round(a.speed * 1.85200),
440
+ ignition: a.attributes.ignition ? translations.report.ignitionOn : translations.report.ignitionOff,
441
+ driver: userData.byDriver ? d.driver.name : getDriverName(a.attributes.driverUniqueId, a.fixTime, userData),
442
+ temperature: getTempValue(a, 'temp1'),
443
+ temperature2: getTempValue(a, 'temp2'),
444
+ digitalport1: d.device && getDigitalPortValue(a, 1, translations, d.device.attributes),
445
+ digitalport2: d.device && getDigitalPortValue(a, 2, translations, d.device.attributes),
446
+ latitude: a.latitude,
447
+ longitude: a.longitude
448
+ }
449
+ }))
450
+ })
451
+ return {
452
+ headers,
453
+ data,
454
+ settings
455
+ }
456
+ }
457
+ }
458
+
459
+ function deviceGroupName (groups, device, translation) {
460
+ const group = groups.find(g => device.groupId === g.id)
461
+ if (group) {
462
+ if (translation) {
463
+ return translation.report.group + ': ' + group.name
464
+ } else {
465
+ return group.name
466
+ }
467
+ }
468
+ return ''
469
+ }
470
+
471
+ function deviceName (device) {
472
+ return device.name + (device.attributes.license_plate ? ', ' + device.attributes.license_plate : '') + (device.model ? ', ' + device.model : '')
473
+ }
474
+
475
+ function getLocationDate (location, user) {
476
+ return convertToLocaleString(location.fixTime, user.attributes.lang, user.attributes.timezone)
477
+ }
478
+
479
+ function getMaxSpeed (data) {
480
+ const values = data.map(item => {
481
+ return item.speed
482
+ })
483
+ const max = values.reduce(function (a, b) {
484
+ return Math.max(a, b)
485
+ })
486
+ return Math.round(max * 1.85200)
487
+ }
488
+
489
+ exports.createLocationReport = createLocationReport
490
+ exports.exportLocationReportToPDF = exportLocationReportToPDF
491
+ exports.exportLocationReportToExcel = exportLocationReportToExcel
492
+ exports.create = createLocationReport
493
+ exports.exportToPDF = exportLocationReportToPDF
494
+ exports.exportToExcel = exportLocationReportToExcel
495
+ exports.getDigitalPortValue = getDigitalPortValue
496
+ exports.getDriverRoute = getDriverRoute