fleetmap-reports 2.0.358 → 2.0.359

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/package.json +1 -1
  2. package/src/partnerReports/driverranking-report.js +39 -7
  3. package/.claude/worktrees/eager-stonebraker-c08ac7/.eslintrc.js +0 -15
  4. package/.claude/worktrees/eager-stonebraker-c08ac7/lang/enGB.js +0 -309
  5. package/.claude/worktrees/eager-stonebraker-c08ac7/lang/esCL.js +0 -297
  6. package/.claude/worktrees/eager-stonebraker-c08ac7/lang/frFR.json +0 -324
  7. package/.claude/worktrees/eager-stonebraker-c08ac7/lang/index.js +0 -35
  8. package/.claude/worktrees/eager-stonebraker-c08ac7/lang/ptBR.js +0 -301
  9. package/.claude/worktrees/eager-stonebraker-c08ac7/lang/ptPT.js +0 -310
  10. package/.claude/worktrees/eager-stonebraker-c08ac7/package-lock.json +0 -11944
  11. package/.claude/worktrees/eager-stonebraker-c08ac7/package.json +0 -45
  12. package/.claude/worktrees/eager-stonebraker-c08ac7/src/activity-report.js +0 -822
  13. package/.claude/worktrees/eager-stonebraker-c08ac7/src/automaticReports.js +0 -34
  14. package/.claude/worktrees/eager-stonebraker-c08ac7/src/carbone/index.js +0 -13
  15. package/.claude/worktrees/eager-stonebraker-c08ac7/src/custom/vistawasteActivity-report.js +0 -544
  16. package/.claude/worktrees/eager-stonebraker-c08ac7/src/events-report.js +0 -360
  17. package/.claude/worktrees/eager-stonebraker-c08ac7/src/fuel-consumption-report.js +0 -296
  18. package/.claude/worktrees/eager-stonebraker-c08ac7/src/fuel-drop-report.js +0 -128
  19. package/.claude/worktrees/eager-stonebraker-c08ac7/src/here.js +0 -172
  20. package/.claude/worktrees/eager-stonebraker-c08ac7/src/idle-report.js +0 -403
  21. package/.claude/worktrees/eager-stonebraker-c08ac7/src/index.js +0 -244
  22. package/.claude/worktrees/eager-stonebraker-c08ac7/src/kms-report.js +0 -549
  23. package/.claude/worktrees/eager-stonebraker-c08ac7/src/location-report.js +0 -496
  24. package/.claude/worktrees/eager-stonebraker-c08ac7/src/machines-report.js +0 -278
  25. package/.claude/worktrees/eager-stonebraker-c08ac7/src/partnerReports/afriquia.js +0 -129
  26. package/.claude/worktrees/eager-stonebraker-c08ac7/src/partnerReports/dailyuse-report.js +0 -80
  27. package/.claude/worktrees/eager-stonebraker-c08ac7/src/partnerReports/delayedstart-report.js +0 -223
  28. package/.claude/worktrees/eager-stonebraker-c08ac7/src/partnerReports/driverranking-report.js +0 -328
  29. package/.claude/worktrees/eager-stonebraker-c08ac7/src/partnerReports/gpsjump-report.js +0 -61
  30. package/.claude/worktrees/eager-stonebraker-c08ac7/src/partnerReports/performance-report.js +0 -359
  31. package/.claude/worktrees/eager-stonebraker-c08ac7/src/refueling-report.js +0 -345
  32. package/.claude/worktrees/eager-stonebraker-c08ac7/src/reportStyle.js +0 -34
  33. package/.claude/worktrees/eager-stonebraker-c08ac7/src/sensor-report.js +0 -447
  34. package/.claude/worktrees/eager-stonebraker-c08ac7/src/speeding-report.js +0 -663
  35. package/.claude/worktrees/eager-stonebraker-c08ac7/src/stop-report.js +0 -242
  36. package/.claude/worktrees/eager-stonebraker-c08ac7/src/templates/ActivityReportTemplate.mjml +0 -51
  37. package/.claude/worktrees/eager-stonebraker-c08ac7/src/templates/EmptyReportTemplate.mjml +0 -25
  38. package/.claude/worktrees/eager-stonebraker-c08ac7/src/templates/EventsReportTemplate.mjml +0 -75
  39. package/.claude/worktrees/eager-stonebraker-c08ac7/src/templates/FuelDropReportTemplate.mjml +0 -60
  40. package/.claude/worktrees/eager-stonebraker-c08ac7/src/templates/IdleReportTemplate.mjml +0 -54
  41. package/.claude/worktrees/eager-stonebraker-c08ac7/src/templates/KmsReportGroupByDayTemplate.mjml +0 -43
  42. package/.claude/worktrees/eager-stonebraker-c08ac7/src/templates/KmsReportTemplate.mjml +0 -44
  43. package/.claude/worktrees/eager-stonebraker-c08ac7/src/templates/LocationReportTemplate.mjml +0 -37
  44. package/.claude/worktrees/eager-stonebraker-c08ac7/src/templates/RefuelingReportTemplate.mjml +0 -60
  45. package/.claude/worktrees/eager-stonebraker-c08ac7/src/templates/SensorReportTemplate.mjml +0 -23
  46. package/.claude/worktrees/eager-stonebraker-c08ac7/src/templates/SpeedingReportTemplate.mjml +0 -80
  47. package/.claude/worktrees/eager-stonebraker-c08ac7/src/templates/TripReportTemplate.mjml +0 -97
  48. package/.claude/worktrees/eager-stonebraker-c08ac7/src/templates/ZoneReportTemplate.mjml +0 -52
  49. package/.claude/worktrees/eager-stonebraker-c08ac7/src/templates/report.mjml +0 -96
  50. package/.claude/worktrees/eager-stonebraker-c08ac7/src/tests/activity.test.js +0 -99
  51. package/.claude/worktrees/eager-stonebraker-c08ac7/src/tests/carbone.test.js +0 -20
  52. package/.claude/worktrees/eager-stonebraker-c08ac7/src/tests/events.test.js +0 -30
  53. package/.claude/worktrees/eager-stonebraker-c08ac7/src/tests/fuel.test.js +0 -35
  54. package/.claude/worktrees/eager-stonebraker-c08ac7/src/tests/idle.test.js +0 -16
  55. package/.claude/worktrees/eager-stonebraker-c08ac7/src/tests/index.js +0 -24
  56. package/.claude/worktrees/eager-stonebraker-c08ac7/src/tests/index.test.js +0 -203
  57. package/.claude/worktrees/eager-stonebraker-c08ac7/src/tests/kms.test.js +0 -74
  58. package/.claude/worktrees/eager-stonebraker-c08ac7/src/tests/performance.test.js +0 -36
  59. package/.claude/worktrees/eager-stonebraker-c08ac7/src/tests/positions.test.js +0 -67
  60. package/.claude/worktrees/eager-stonebraker-c08ac7/src/tests/speeding.test.js +0 -120
  61. package/.claude/worktrees/eager-stonebraker-c08ac7/src/tests/translations.test.js +0 -21
  62. package/.claude/worktrees/eager-stonebraker-c08ac7/src/tests/trips.test.js +0 -140
  63. package/.claude/worktrees/eager-stonebraker-c08ac7/src/tests/zones.test.js +0 -162
  64. package/.claude/worktrees/eager-stonebraker-c08ac7/src/trip-report.js +0 -641
  65. package/.claude/worktrees/eager-stonebraker-c08ac7/src/util/device.js +0 -13
  66. package/.claude/worktrees/eager-stonebraker-c08ac7/src/util/driver.js +0 -90
  67. package/.claude/worktrees/eager-stonebraker-c08ac7/src/util/fuel.js +0 -102
  68. package/.claude/worktrees/eager-stonebraker-c08ac7/src/util/geofence.js +0 -52
  69. package/.claude/worktrees/eager-stonebraker-c08ac7/src/util/odoo.js +0 -18
  70. package/.claude/worktrees/eager-stonebraker-c08ac7/src/util/parallel.js +0 -64
  71. package/.claude/worktrees/eager-stonebraker-c08ac7/src/util/pdfDocument.js +0 -140
  72. package/.claude/worktrees/eager-stonebraker-c08ac7/src/util/route.js +0 -78
  73. package/.claude/worktrees/eager-stonebraker-c08ac7/src/util/timetable.js +0 -30
  74. package/.claude/worktrees/eager-stonebraker-c08ac7/src/util/traccar.js +0 -213
  75. package/.claude/worktrees/eager-stonebraker-c08ac7/src/util/traccar.worker.js +0 -49
  76. package/.claude/worktrees/eager-stonebraker-c08ac7/src/util/trips.js +0 -270
  77. package/.claude/worktrees/eager-stonebraker-c08ac7/src/util/utils.js +0 -358
  78. package/.claude/worktrees/eager-stonebraker-c08ac7/src/util/worker.js +0 -14
  79. package/.claude/worktrees/eager-stonebraker-c08ac7/src/util/xpert.js +0 -58
  80. package/.claude/worktrees/eager-stonebraker-c08ac7/src/word/index.js +0 -95
  81. package/.claude/worktrees/eager-stonebraker-c08ac7/src/zone-report.js +0 -904
@@ -1,904 +0,0 @@
1
- const automaticReports = require('./automaticReports')
2
- const {
3
- convertMS, convertToLocaleString, getTranslations, convertToFeature, convertPositionToFeature,
4
- calculateDistance,
5
- sortPositions, getDates, convertToLocaleDateString, convertToLocaleTimeString,
6
- isClientSide,
7
- getLanguage, convertFromUTC, convertFromLocal
8
- } = require('./util/utils')
9
- const jsPDF = require('jspdf')
10
- require('jspdf-autotable')
11
- const traccarHelper = require('./util/traccar')
12
- const { headerFromUser, addTable } = require('./util/pdfDocument')
13
- const { getStyle } = require('./reportStyle')
14
- const {
15
- getUserPartner
16
- } = require('fleetmap-partners')
17
- const { devicesToProcess } = require('./util/device')
18
- const {
19
- cleanPositions
20
- } = require('./util/route')
21
- const { checkGeofenceIn } = require('./util/geofence')
22
- const { parallel } = require('./util/parallel')
23
- const { getDataByDay } = require('./util/trips')
24
- const axios = require('axios').default
25
- const sliceSize = 1
26
- const deviceChunk = 5
27
- const fileName = 'ZoneReport'
28
-
29
- function simpleLimit (concurrency) {
30
- const queue = []
31
- let activeCount = 0
32
-
33
- const next = () => {
34
- if (queue.length === 0 || activeCount >= concurrency) return
35
- activeCount++
36
- const { fn, resolve, reject } = queue.shift()
37
- fn().then(resolve).catch(reject).finally(() => {
38
- activeCount--
39
- next()
40
- })
41
- }
42
-
43
- return fn => {
44
- return new Promise((resolve, reject) => {
45
- queue.push({ fn, resolve, reject })
46
- next()
47
- })
48
- }
49
- }
50
-
51
- exports.process = async (traccar, from, to, slice, deviceCount, devices, reportRangeDays, userData) => {
52
- const data = await traccarHelper.getAllInOne(
53
- traccar, from, to, slice, true, false, false, false,
54
- deviceCount, devices.length, sliceSize, reportRangeDays > 29 ? 1 : deviceChunk, undefined, false, true)
55
-
56
- const route = data.route
57
- const alerts = []
58
- if (isClientSide()) {
59
- alerts.push(...(await getInAndOutEvents(slice, cleanPositions(route), userData)))
60
- } else {
61
- alerts.push(...(await parallel('zone-report', 'getInAndOutEvents', slice, cleanPositions(route), userData)))
62
- }
63
- return await processDevices(from, to, slice, userData, { alerts, route, summary: data.summary })
64
- }
65
-
66
- async function createZoneReport (from, to, userData, traccar) {
67
- console.log('createZoneReport', from, to)
68
- const reportData = []
69
- const devices = devicesToProcess(userData)
70
- const allData = {
71
- devices: [],
72
- xpert: devices.filter(d => d.attributes.xpert).length > 0
73
- }
74
-
75
- if (isClientSide()) {
76
- const cookie = await axios.get('/pinmeapi/cookie/get', { withCredentials: true }).then(d => d.data)
77
- const url = `https://${process.env.SERVER_HOST}/pinmeapi/reports/zone-report`
78
- return axios.post(url, { from, to, userData, cookie }).then(d => d.data)
79
- }
80
- const types = ['geofenceEnter', 'geofenceExit']
81
-
82
- if (userData.byGroup) {
83
- for (const g of userData.groups) {
84
- const devices = userData.devices.filter(d => d.groupId === g.id)
85
- console.log(g.name + ' devices:' + devices.length)
86
- if (devices.length > 0) {
87
- const groupData = {
88
- devices: [],
89
- group: g,
90
- xpert: devices.filter(d => d.attributes.xpert).length > 0
91
- }
92
- const response = await traccar.reports.reportsEventsGet(from, to, null, [g.id], types)
93
- const data = response.data
94
-
95
- devices.sort((a, b) => (a.name > b.name) ? 1 : -1)
96
-
97
- if (data.length > 0) {
98
- groupData.devices = await processDevices(from, to, devices, userData, { alerts: data })
99
- reportData.push(groupData)
100
- }
101
- }
102
- }
103
- }
104
-
105
- const reportRangeDays = (new Date(to).getTime() - new Date(from).getTime()) / (1000 * 60 * 60 * 24)
106
- const sliced = automaticReports.sliceArray(devices, sliceSize)
107
-
108
- const url = 'https://rntba7aqifqx3iywrenb7pq6km0liwsr.lambda-url.us-east-1.on.aws/pinmeapi/process-report/zone-report-processing'
109
-
110
- const limit = simpleLimit(1000)
111
- const promises = sliced.map((slice, i) => limit(async () => {
112
- const now = new Date()
113
- return traccar.axios.post(url, {
114
- from,
115
- to,
116
- userData,
117
- slice,
118
- deviceCount: i,
119
- devices,
120
- reportRangeDays
121
- }, { timeout: 900000 })
122
- .catch(e => {
123
- console.error(url, (e.response && e.response.data) || e, 'try again')
124
- return traccar.axios.post(url, {
125
- from,
126
- to,
127
- userData,
128
- slice,
129
- deviceCount: i,
130
- devices,
131
- reportRangeDays
132
- }, { timeout: 900000 })
133
- }).then(response => {
134
- console.log('processed devices', slice.map(d => d.id), 'in', new Date() - now, 'ms')
135
- return response.data
136
- })
137
- })
138
- )
139
- const results = await Promise.all(promises)
140
- results.forEach(processed => {
141
- if (processed && processed.length) {
142
- allData.devices.push(...processed)
143
- }
144
- })
145
-
146
- reportData.push(allData)
147
-
148
- return reportData
149
- }
150
-
151
- async function processDevices (from, to, devices, userData, data) {
152
- const devicesResult = []
153
- console.log('processDevices', from, to, devices.length)
154
- for (const d of devices) {
155
- const alerts = data.alerts.filter(t => t && t.deviceId === d.id)
156
- const deviceRoute = data.route.filter(p => p.deviceId === d.id)
157
- let zoneInOutData = analyseAlerts(alerts, deviceRoute, userData, from, to, d).filter(d => !userData.onlyWithStop || d.stopped)
158
-
159
- if (userData.groupByDay || userData.zonesByColumn) {
160
- const dates = getDates(from, to, userData.user.attributes.timezone)
161
-
162
- const geofencesData = []
163
- for (const geofence of userData.geofences) {
164
- const filteredByGeofence = zoneInOutData.filter(d => d.geofenceId === geofence.id)
165
-
166
- const dataByDay = []
167
- for (const date of dates) {
168
- const startDateLocal = new Date(convertFromUTC(date, userData.user.attributes.timezone).toISOString().split('T')[0] + ' ' + userData.dayHours.startTime)
169
- const endDateLocal = new Date(convertFromUTC(date, userData.user.attributes.timezone).toISOString().split('T')[0] + ' ' + userData.dayHours.endTime)
170
- const startDate = isClientSide() ? startDateLocal : convertFromLocal(startDateLocal, userData.user.attributes.timezone)
171
- const endDate = isClientSide() ? endDateLocal : convertFromLocal(endDateLocal, userData.user.attributes.timezone)
172
-
173
- const { routeByDay, zoneInOutDayData } = await getDataByDay(d, date, { route: deviceRoute, zoneInOutData: filteredByGeofence }, userData)
174
-
175
- const firstIn = zoneInOutDayData.find(z => z.inTime && new Date(z.inTime.fixTime) > startDate)
176
- const lastOut = zoneInOutDayData.slice().reverse().find(z => z.outTime && new Date(z.outTime.fixTime) < endDate)
177
-
178
- const timeIn = calculateTimeIn(zoneInOutDayData, startDate, from, endDate, to)
179
- const distanceIn = calculateDistanceIn(zoneInOutDayData, deviceRoute, routeByDay, startDate, endDate, to, d)
180
- const distanceOut = calculateDistanceOut(zoneInOutDayData, routeByDay, startDate, endDate, 'distanceOut', d)
181
-
182
- dataByDay.push({
183
- date,
184
- firstIn: firstIn ? firstIn.inTime.fixTime : undefined,
185
- lastOut: lastOut ? lastOut.outTime.fixTime : undefined,
186
- distanceIn: distanceIn > 0 ? distanceIn : 0,
187
- distanceOut,
188
- totalInTime: timeIn,
189
- totalOutTime: (24 * 60 * 60 * 1000) - timeIn
190
- })
191
- }
192
-
193
- geofencesData.push({
194
- geofenceId: geofence.id,
195
- geofenceName: geofence.name,
196
- days: filterResult(dataByDay, userData)
197
- })
198
- }
199
-
200
- if (userData.zonesByColumn) {
201
- const groupByDay = []
202
- for (const date of dates) {
203
- const startDateLocal = new Date(convertFromUTC(date, userData.user.attributes.timezone).toISOString().split('T')[0] + ' ' + userData.dayHours.startTime)
204
- const endDateLocal = new Date(convertFromUTC(date, userData.user.attributes.timezone).toISOString().split('T')[0] + ' ' + userData.dayHours.endTime)
205
- const startDate = isClientSide() ? startDateLocal : convertFromLocal(startDateLocal, userData.user.attributes.timezone)
206
- const endDate = isClientSide() ? endDateLocal : convertFromLocal(endDateLocal, userData.user.attributes.timezone)
207
-
208
- const { routeByDay, zoneInOutDayData } = await getDataByDay(d, date, { route: deviceRoute, zoneInOutData }, userData)
209
-
210
- const distanceOut = calculateDistanceOut(zoneInOutDayData, routeByDay, startDate, endDate, 'distanceOutAny', d)
211
-
212
- const geofences = geofencesData.map(g => {
213
- const day = g.days.find(day => day.date === date)
214
- return {
215
- geofenceId: g.geofenceId,
216
- geofenceName: g.geofenceName,
217
- distanceIn: day && day.distanceIn
218
- }
219
- })
220
-
221
- if (distanceOut > 0.01 || geofences.filter(g => g.distanceIn > 0.01).length) {
222
- if (!userData.onlyWithKmsOut || distanceOut) {
223
- groupByDay.push({
224
- date,
225
- geofences,
226
- distanceOut
227
- })
228
- }
229
- }
230
- }
231
-
232
- if (groupByDay.length) {
233
- devicesResult.push({
234
- device: d,
235
- from,
236
- to,
237
- zonesByColumn: true,
238
- days: groupByDay
239
- })
240
- }
241
- } else {
242
- devicesResult.push({
243
- device: d,
244
- from,
245
- to,
246
- groupByDay: true,
247
- geofences: geofencesData
248
- })
249
- }
250
- } else {
251
- if (zoneInOutData.length > 0) {
252
- if (userData.tripsBetweenZones) {
253
- const tripsBetweenZones = []
254
- zoneInOutData = zoneInOutData.sort((a, b) => a.inTime && b.inTime && new Date(a.inTime.fixTime).getTime() - new Date(b.inTime.fixTime).getTime())
255
- let outGeofence
256
- let inGeofence
257
- for (let i = 0; i < zoneInOutData.length - 1; i++) {
258
- if (!outGeofence) {
259
- outGeofence = zoneInOutData[i]
260
- }
261
- inGeofence = zoneInOutData[i + 1]
262
-
263
- if (outGeofence.outTime &&
264
- inGeofence.inTime &&
265
- new Date(outGeofence.outTime.fixTime).getTime() < new Date(inGeofence.inTime.fixTime).getTime()) {
266
- tripsBetweenZones.push({
267
- outTime: outGeofence.outTime,
268
- outGeofenceName: outGeofence.geofenceName,
269
- inTime: inGeofence.inTime,
270
- inGeofenceName: inGeofence.geofenceName,
271
- distanceOut: outGeofence.distanceOutAny,
272
- totalOutTime: new Date(inGeofence.inTime.fixTime).getTime() - new Date(outGeofence.outTime.fixTime).getTime()
273
- })
274
- outGeofence = undefined
275
- } else {
276
- if (outGeofence.outTime &&
277
- inGeofence.outTime &&
278
- new Date(outGeofence.outTime.fixTime).getTime() <= new Date(inGeofence.outTime.fixTime).getTime()) {
279
- outGeofence = undefined
280
- }
281
- }
282
- }
283
- if (tripsBetweenZones.length > 0) {
284
- devicesResult.push({
285
- device: d,
286
- from,
287
- to,
288
- tripsBetweenZones: true,
289
- geofences: filterResult(tripsBetweenZones, userData)
290
- })
291
- }
292
- } else {
293
- devicesResult.push({
294
- device: d,
295
- from,
296
- to,
297
- geofences: filterResult(zoneInOutData, userData)
298
- })
299
- }
300
- }
301
- }
302
- }
303
-
304
- return devicesResult
305
- }
306
-
307
- function filterResult (result, userData) {
308
- result = userData.onlyWithKmsOut ? result.filter(a => a.distanceOut >= 0.5) : result
309
- return userData.onlyWithKmsIn ? result.filter(a => a.distanceIn === undefined || a.distanceIn >= 0.5) : result
310
- }
311
-
312
- function calculateTimeIn (zoneInOutDayData, fromByDay, from, toByDay, to) {
313
- let timeIn = zoneInOutDayData.length ? zoneInOutDayData.reduce((a, b) => a + (b.totalInTime || 0), 0) : 0
314
-
315
- if (zoneInOutDayData.length) {
316
- // Check if the first entry starts only on the day before
317
- if (!zoneInOutDayData[0].inTime || new Date(zoneInOutDayData[0].inTime.fixTime) < fromByDay) {
318
- const inTime = zoneInOutDayData[0].inTime ? new Date(zoneInOutDayData[0].inTime.fixTime) : from
319
- timeIn = timeIn - (fromByDay.getTime() - inTime.getTime())
320
- }
321
-
322
- // Check if the last entry ends only on the next day
323
- if (!zoneInOutDayData[zoneInOutDayData.length - 1].outTime || new Date(zoneInOutDayData[zoneInOutDayData.length - 1].outTime.fixTime) > toByDay) {
324
- const outTime = zoneInOutDayData[zoneInOutDayData.length - 1].outTime ? new Date(zoneInOutDayData[zoneInOutDayData.length - 1].outTime.fixTime) : to
325
- timeIn = timeIn - (outTime.getTime() - (toByDay.getTime() + 1000))
326
- }
327
- }
328
- return timeIn
329
- }
330
-
331
- function getNextIn (outDate, alerts, geofenceId, deviceRoute) {
332
- const next = alerts
333
- .filter(a => a.type === 'geofenceEnter' && (a.geofenceId === geofenceId || !geofenceId))
334
- .find(a => new Date(a.position.fixTime).getTime() > outDate)
335
- return (next && next.position && new Date(next.position.fixTime).getTime()) ||
336
- new Date(deviceRoute.slice(-1)[0].fixTime).getTime()
337
- }
338
-
339
- function getAnyNextIn (outDate, alerts, deviceRoute) {
340
- const next = alerts
341
- .filter(a => a.type === 'geofenceEnter')
342
- .find(a => new Date(a.position.fixTime).getTime() > outDate)
343
-
344
- if (next && !alerts.filter(a => a.type === 'geofenceExit' &&
345
- new Date(a.position.fixTime).getTime() > outDate &&
346
- new Date(a.position.fixTime).getTime() < new Date(next.position.fixTime).getTime()).length) {
347
- return (next && next.position && new Date(next.position.fixTime).getTime()) ||
348
- new Date(deviceRoute.slice(-1)[0].fixTime).getTime()
349
- }
350
- }
351
-
352
- function getAnyLastExit (inDate, alerts, deviceRoute) {
353
- const next = alerts
354
- .filter(a => a.type === 'geofenceExit')
355
- .find(a => new Date(a.position.fixTime).getTime() < inDate)
356
-
357
- if (next && !alerts.filter(a => a.type === 'geofenceEnter' &&
358
- new Date(a.position.fixTime).getTime() < inDate &&
359
- new Date(a.position.fixTime).getTime() > new Date(next.position.fixTime).getTime()).length) {
360
- return (next && next.position && new Date(next.position.fixTime).getTime()) ||
361
- new Date(deviceRoute.slice(-1)[0].fixTime).getTime()
362
- }
363
- }
364
-
365
- function calculateDistanceIn (zoneInOutDayData, deviceRoute, dayRoute, fromByDay, toByDay, to, device) {
366
- let distanceIn = zoneInOutDayData.length && dayRoute.length ? zoneInOutDayData.reduce((a, b) => a + (b.distanceIn || 0), 0) : 0
367
-
368
- if (zoneInOutDayData.length) {
369
- // Check if the first entry starts only on the day before
370
- if (!zoneInOutDayData[0].inTime || new Date(zoneInOutDayData[0].inTime.fixTime) < fromByDay) {
371
- const routeDayBefore = deviceRoute.filter(p => (!zoneInOutDayData[0].inTime || (new Date(p.fixTime) >= new Date(zoneInOutDayData[0].inTime.fixTime))) &&
372
- new Date(p.fixTime) < fromByDay)
373
- distanceIn = distanceIn - calculateDistance(routeDayBefore, device.attributes['report.ignoreOdometer'])
374
- }
375
-
376
- // Check if the last entry ends only on the next day
377
- if (!zoneInOutDayData[zoneInOutDayData.length - 1].outTime || new Date(zoneInOutDayData[zoneInOutDayData.length - 1].outTime.fixTime) > toByDay) {
378
- const outTime = zoneInOutDayData[zoneInOutDayData.length - 1].outTime ? new Date(zoneInOutDayData[zoneInOutDayData.length - 1].outTime.fixTime) : to
379
- const routeDayAfter = deviceRoute.filter(p => new Date(p.fixTime) < outTime && new Date(p.fixTime) > toByDay)
380
- distanceIn = distanceIn - calculateDistance(routeDayAfter, device.attributes['report.ignoreOdometer'])
381
- }
382
- }
383
-
384
- return distanceIn
385
- }
386
-
387
- function calculateDistanceOut (zoneInOutDayData, dayRoute, fromByDay, toByDay, field, device) {
388
- if (zoneInOutDayData.length) {
389
- zoneInOutDayData.sort((a, b) => (a.inTime ? new Date(a.inTime.fixTime).getTime() : 0) - (b.inTime ? new Date(b.inTime.fixTime).getTime() : 0))
390
- const dataInsideDay = zoneInOutDayData.filter(z => z.outTime && new Date(z.outTime.fixTime) < toByDay)
391
- let distanceOut = dataInsideDay.reduce((a, b) => a + (b[field] || 0), 0)
392
-
393
- if (zoneInOutDayData[0].inTime && new Date(zoneInOutDayData[0].inTime.fixTime) > fromByDay) {
394
- // Add distanceOut before the first entry
395
- const routeOut = dayRoute.filter(p =>
396
- new Date(p.fixTime).getTime() < new Date(zoneInOutDayData[0].inTime.fixTime).getTime() &&
397
- new Date(p.fixTime).getTime() >= fromByDay.getTime()
398
- )
399
- distanceOut = distanceOut + calculateDistance(routeOut, device.attributes['report.ignoreOdometer'])
400
- }
401
-
402
- zoneInOutDayData.sort((a, b) => (a.outTime ? new Date(a.outTime.fixTime).getTime() : 0) - (b.outTime ? new Date(b.outTime.fixTime).getTime() : 0))
403
- if (zoneInOutDayData[zoneInOutDayData.length - 1].outTime && new Date(zoneInOutDayData[zoneInOutDayData.length - 1].outTime.fixTime) < toByDay) {
404
- // Add distanceOut after last exit
405
- distanceOut = distanceOut - zoneInOutDayData[zoneInOutDayData.length - 1][field]
406
- const routeOut = dayRoute.filter(p =>
407
- new Date(p.fixTime) >= new Date(zoneInOutDayData[zoneInOutDayData.length - 1].outTime.fixTime).getTime() &&
408
- new Date(p.fixTime) < toByDay.getTime())
409
- distanceOut = distanceOut + calculateDistance(routeOut, device.attributes['report.ignoreOdometer'])
410
- }
411
-
412
- return distanceOut
413
- }
414
-
415
- return calculateDistance(dayRoute.filter(p => new Date(p.fixTime) >= fromByDay && new Date(p.fixTime) < toByDay), device.attributes['report.ignoreOdometer'])
416
- }
417
-
418
- function analyseAlerts (alerts, deviceRoute, userData, from, to, device) {
419
- const zoneInOutData = []
420
- let zoneInData = []
421
-
422
- alerts.forEach(a => {
423
- if (!a.position) {
424
- return
425
- }
426
- if (a.type === 'geofenceEnter') {
427
- const driver = userData.drivers.find(d => a.position.attributes && d.uniqueId === a.position.attributes.driverUniqueId)
428
- a.position.driverName = driver ? driver.name : ''
429
- if (zoneInData.length === 0) {
430
- const anyLastExist = getAnyLastExit(new Date(a.position.fixTime).getTime(), alerts, deviceRoute)
431
- a.anyLastExit = anyLastExist || from
432
- }
433
-
434
- zoneInData.push(a)
435
- } else if (a.type === 'geofenceExit') {
436
- const geofence = userData.geofences.find(g => g.id === a.geofenceId)
437
- if (!geofence) {
438
- return
439
- }
440
- const outDate = new Date(a.position.fixTime).getTime()
441
- const nextIn = getNextIn(outDate, alerts, a.geofenceId, deviceRoute)
442
- const routeAfterOut = deviceRoute.filter(p =>
443
- new Date(p.fixTime).getTime() >= outDate &&
444
- new Date(p.fixTime).getTime() < nextIn
445
- )
446
- const totalOutTime = nextIn - outDate
447
- const distanceOut = calculateDistance(routeAfterOut, device.attributes['report.ignoreOdometer'])
448
-
449
- let anyNextIn
450
- let distanceOutAny = 0
451
- if (zoneInData.length < 2) {
452
- anyNextIn = getAnyNextIn(outDate, alerts, deviceRoute) || to
453
- const routeAfterOutAndBeforeAnyNextIn = deviceRoute.filter(p =>
454
- new Date(p.fixTime).getTime() >= outDate &&
455
- new Date(p.fixTime).getTime() <= anyNextIn
456
- )
457
- distanceOutAny = Math.round(calculateDistance(routeAfterOutAndBeforeAnyNextIn, device.attributes['report.ignoreOdometer']))
458
- }
459
-
460
- const inData = zoneInData.find(z => z.geofenceId === a.geofenceId)
461
- if (inData) {
462
- const totalInTime = new Date(a.position.fixTime).getTime() - new Date(inData.position.fixTime).getTime()
463
- const inDate = new Date(inData.position.fixTime).getTime()
464
- const routeIn = deviceRoute.filter(p =>
465
- new Date(p.fixTime).getTime() >= inDate &&
466
- new Date(p.fixTime).getTime() < outDate
467
- )
468
- zoneInOutData.push({
469
- inTime: inData.position,
470
- outTime: a.position,
471
- totalInTime,
472
- totalOutTime,
473
- distanceIn: calculateDistance(routeIn, device.attributes['report.ignoreOdometer']),
474
- distanceOut,
475
- distanceOutAny,
476
- anyNextIn,
477
- anyLastExit: a.anyLastExit,
478
- geofenceName: geofence.name,
479
- geofenceId: geofence.id,
480
- stopped: routeIn.filter(p => !(p.attributes && p.attributes.ignition)).length > 0,
481
- driverName: inData.position.driverName
482
- })
483
- zoneInData = zoneInData.filter(z => z.geofenceId !== a.geofenceId)
484
- } else {
485
- const totalInTime = new Date(a.position.fixTime).getTime() - from.getTime()
486
- const routeIn = deviceRoute.filter(p =>
487
- new Date(p.fixTime).getTime() >= from.getTime() &&
488
- new Date(p.fixTime).getTime() < outDate
489
- )
490
- zoneInOutData.push({
491
- outTime: a.position,
492
- geofenceName: geofence.name,
493
- geofenceId: geofence.id,
494
- totalInTime,
495
- totalOutTime,
496
- distanceIn: calculateDistance(routeIn, device.attributes['report.ignoreOdometer']),
497
- driverName: '',
498
- distanceOut,
499
- distanceOutAny,
500
- anyNextIn,
501
- anyLastExit: a.anyLastExit
502
- })
503
- }
504
- }
505
- })
506
-
507
- for (const inData of zoneInData) {
508
- const geofence = userData.geofences.find(g => g.id === inData.geofenceId)
509
- if (geofence) {
510
- const totalInTime = to.getTime() - new Date(inData.position.fixTime).getTime()
511
- const routeIn = deviceRoute.filter(p =>
512
- new Date(p.fixTime).getTime() >= new Date(inData.position.fixTime).getTime() &&
513
- new Date(p.fixTime).getTime() < to.getTime()
514
- )
515
- zoneInOutData.push({
516
- inTime: inData.position,
517
- totalInTime,
518
- distanceIn: calculateDistance(routeIn, device.attributes['report.ignoreOdometer']),
519
- geofenceName: geofence.name,
520
- geofenceId: geofence.id,
521
- driverName: inData.position.driverName,
522
- anyLastExit: inData.anyLastExit
523
- })
524
- }
525
- }
526
-
527
- if (!zoneInOutData.length && userData.groupByDay) {
528
- const geofenceIn = alerts.find(a => a.type === 'geofenceEnter')
529
- zoneInOutData.push({
530
- distanceIn: geofenceIn ? calculateDistance(deviceRoute, device.attributes['report.ignoreOdometer']) : 0,
531
- distanceOut: !geofenceIn ? calculateDistance(deviceRoute, device.attributes['report.ignoreOdometer']) : 0,
532
- totalTime: to.getTime() - from.getTime()
533
- })
534
- }
535
-
536
- return zoneInOutData
537
- }
538
-
539
- async function getInAndOutEvents (devices, route, userData) {
540
- const events = []
541
- const geofencesFeatures = userData.geofences.filter(g => g.area).map(g => convertToFeature(g))
542
- devices.forEach(d => {
543
- const deviceRoute = route.filter(p => p.deviceId === d.id)
544
- const routePoints = deviceRoute.sort(sortPositions).map(p => convertPositionToFeature(p))
545
- console.log('getInAndOutEvents', d.id, deviceRoute.length, routePoints.length)
546
- routePoints.filter(p => p.properties.position.valid).forEach((p, i, array) => {
547
- p.inside = {}
548
- if (!i) {
549
- geofencesFeatures.forEach((g, id) => {
550
- if (checkGeofenceIn(p, g)) {
551
- p.inside[id] = true
552
- events.push(createEvent('geofenceEnter', d.id, p, g))
553
- }
554
- })
555
- return
556
- }
557
- const previous = array[i - 1]
558
- geofencesFeatures.forEach((g, id) => {
559
- if (previous.inside[id]) {
560
- if (!checkGeofenceIn(p, g)) {
561
- events.push(createEvent('geofenceExit', d.id, p, g))
562
- } else {
563
- p.inside[id] = true
564
- }
565
- } else {
566
- if (checkGeofenceIn(p, g)) {
567
- p.inside[id] = true
568
- events.push(createEvent('geofenceEnter', d.id, p, g))
569
- }
570
- }
571
- })
572
- })
573
- })
574
- return events
575
- }
576
-
577
- function createEvent (type, deviceId, p, g) {
578
- return {
579
- type,
580
- position: p.properties.position,
581
- geofenceId: g.properties.geofence.id,
582
- deviceId
583
- }
584
- }
585
-
586
- async function exportZoneReportToPDF (userData, reportData) {
587
- console.log('Export to PDF')
588
-
589
- const timezone = userData.user.attributes.timezone
590
- const translations = getTranslations(userData)
591
- const lang = getLanguage(userData)
592
-
593
- const headers = []
594
-
595
- if (userData.groupByDay) {
596
- headers.push(...[
597
- translations.report.date,
598
- translations.report.firstIn,
599
- translations.report.lastOut,
600
- translations.report.distanceIn || 'Kms Adentro',
601
- translations.report.timeIn,
602
- translations.report.distanceOut || 'Kms Afuera',
603
- translations.report.timeOut
604
- ])
605
- } else if (userData.zonesByColumn) {
606
- headers.push(translations.report.date)
607
- reportData.devices[0].days[0].geofences.forEach(g => headers.push(g.geofenceName))
608
- headers.push(translations.report.distanceOut || 'Kms Afuera')
609
- } else if (userData.tripsBetweenZones) {
610
- headers.push(...[
611
- translations.report.date_start,
612
- translations.report.geofenceOut || 'Zone OUT',
613
- translations.report.date_end,
614
- translations.report.geofenceIn || 'Zone IN',
615
- translations.report.duration,
616
- translations.report.distance
617
- ])
618
- } else {
619
- headers.push(...[
620
- translations.report.enter,
621
- translations.report.exit,
622
- translations.report.stopped,
623
- translations.report.duration,
624
- translations.report.distanceIn || 'Kms Adentro',
625
- translations.report.distanceOut || 'Kms Afuera',
626
- translations.report.geofence,
627
- translations.report.driver
628
- ])
629
- }
630
-
631
- if (reportData.devices) {
632
- // eslint-disable-next-line new-cap
633
- const doc = new jsPDF.jsPDF('l')
634
- await headerFromUser(doc, translations.report.titleZoneReport, userData.user)
635
- const style = getStyle(getUserPartner(userData.user))
636
- let space = 0
637
-
638
- reportData.devices.forEach((d, index) => {
639
- if (userData.groupByDay) {
640
- d.geofences.forEach((g, i) => {
641
- const data = []
642
- space = reportHeader(index === 0 && i === 0, doc, translations, d, lang, timezone, userData, g)
643
- g.days.forEach(a => {
644
- const temp = [
645
- convertToLocaleDateString(a.date, lang, timezone),
646
- a.firstIn ? convertToLocaleTimeString(a.firstIn, lang, timezone) : '-',
647
- a.lastOut ? convertToLocaleTimeString(a.lastOut, lang, timezone) : '-',
648
- a.distanceIn ? a.distanceIn.toLocaleString(lang, { maximumFractionDigits: 2 }) : 0,
649
- convertMS(a.totalInTime, false),
650
- a.distanceOut ? a.distanceOut.toLocaleString(lang, { maximumFractionDigits: 2 }) : 0,
651
- convertMS(a.totalOutTime, false)
652
- ]
653
- data.push(temp)
654
- })
655
- const footer = [
656
- 'Total:' + g.days.length,
657
- '', '',
658
- g.days.reduce((a, b) => a + (b.distanceIn ? b.distanceIn : 0), 0).toLocaleString(lang, { maximumFractionDigits: 2 }),
659
- '',
660
- g.days.reduce((a, b) => a + (b.distanceOut ? b.distanceOut : 0), 0).toLocaleString(lang, { maximumFractionDigits: 2 })
661
- ]
662
- addTable(doc, headers, data, footer, style, space + 40, {
663
- 0: { halign: 'right' },
664
- 1: { halign: 'right' },
665
- 2: { halign: 'right' },
666
- 3: { halign: 'right' },
667
- 4: { halign: 'right' },
668
- 5: { halign: 'right' },
669
- 6: { halign: 'right' }
670
- })
671
- })
672
- } else if (userData.zonesByColumn) {
673
- const data = []
674
- space = reportHeader(index === 0, doc, translations, d, lang, timezone, userData)
675
- d.days.forEach(day => {
676
- const temp = [convertToLocaleDateString(day.date, lang, timezone)]
677
- day.geofences.forEach(a => {
678
- temp.push(a.distanceIn ? a.distanceIn.toLocaleString(lang, { maximumFractionDigits: 2 }) : 0)
679
- })
680
- temp.push(day.distanceOut ? day.distanceOut.toLocaleString(lang, { maximumFractionDigits: 2 }) : 0)
681
- data.push(temp)
682
- })
683
- const footer = ['Total:' + d.days.length]
684
- reportData.devices[0].days[0].geofences.forEach(gColumn => {
685
- footer.push(d.days.reduce((a, b) => a + (b.geofences.find(g => g.geofenceId === gColumn.geofenceId).distanceIn), 0).toLocaleString(lang, { maximumFractionDigits: 2 }))
686
- })
687
- footer.push(d.days.reduce((a, b) => a + (b.distanceOut ? b.distanceOut : 0), 0).toLocaleString(lang, { maximumFractionDigits: 2 }))
688
-
689
- let columnIndex = 1
690
- const columnStyles = { }
691
- reportData.devices[0].days[0].geofences.forEach(() => {
692
- columnStyles[columnIndex] = { halign: 'right' }
693
- columnIndex++
694
- })
695
- columnStyles[columnIndex] = { halign: 'right' }
696
- addTable(doc, headers, data, footer, style, space + 40, columnStyles)
697
- } else {
698
- const data = []
699
- space = reportHeader(index === 0, doc, translations, d, lang, timezone, userData)
700
- if (userData.tripsBetweenZones) {
701
- d.geofences.forEach(a => {
702
- const temp = [
703
- geofenceExit(userData.user, a),
704
- a.outGeofenceName,
705
- geofenceEnter(userData.user, a),
706
- a.inGeofenceName,
707
- convertMS(a.totalOutTime, true),
708
- a.distanceOut ? a.distanceOut.toLocaleString(lang, { maximumFractionDigits: 2 }) : 0
709
- ]
710
- data.push(temp)
711
- })
712
- const footer = ['Total:' + d.geofences.length]
713
- addTable(doc, headers, data, footer, style, space + 35, {
714
- 4: { halign: 'right' },
715
- 5: { halign: 'right' }
716
- })
717
- } else {
718
- d.geofences.forEach(a => {
719
- const temp = [
720
- geofenceEnter(userData.user, a),
721
- geofenceExit(userData.user, a),
722
- a.stopped ? translations.report.yes : translations.report.no,
723
- convertMS(a.totalInTime, true),
724
- a.distanceIn ? a.distanceIn.toLocaleString(lang, { maximumFractionDigits: 2 }) : 0,
725
- a.distanceOut ? a.distanceOut.toLocaleString(lang, { maximumFractionDigits: 2 }) : 0,
726
- a.geofenceName,
727
- a.driverName
728
- ]
729
- data.push(temp)
730
- })
731
- const footer = ['Total:' + d.geofences.length]
732
- addTable(doc, headers, data, footer, style, space + 35, {
733
- 3: { halign: 'right' },
734
- 4: { halign: 'right' },
735
- 5: { halign: 'right' }
736
- })
737
- }
738
- }
739
- })
740
- return doc
741
- }
742
- }
743
-
744
- function exportZoneReportToExcel (userData, reportData) {
745
- console.log('Export to Excel')
746
-
747
- const translations = getTranslations(userData)
748
-
749
- const settings = {
750
- sheetName: translations.report.titleActivityReport, // The name of the sheet
751
- fileName // The name of the spreadsheet
752
- }
753
- const headers = []
754
- if (userData.groupByDay) {
755
- headers.push(...[
756
- { label: translations.report.geofence, value: 'zone' },
757
- { label: translations.report.vehicle, value: 'name' },
758
- { label: translations.report.date, value: 'date' },
759
- { label: translations.report.firstIn || 'First In', value: 'firstIn' },
760
- { label: translations.report.lastOut || 'Last Out', value: 'lastOut' },
761
- { label: translations.report.distanceIn || 'Kms Adentro', value: 'distanceIn', format: '0' },
762
- { label: translations.report.timeIn || 'Total In Time', value: 'totalInTime' },
763
- { label: translations.report.distanceOut || 'Kms Afuera', value: 'distanceOut', format: '0' },
764
- { label: translations.report.timeOut || 'Total Out Time', value: 'totalOutTime' }
765
- ])
766
- } else if (userData.zonesByColumn) {
767
- headers.push(...[
768
- { label: translations.report.vehicle, value: 'name' },
769
- { label: translations.report.date, value: 'date' }
770
- ])
771
- if (reportData.devices.length) {
772
- reportData.devices[0].days[0].geofences.forEach(g =>
773
- headers.push({ label: g.geofenceName, value: '' + g.geofenceId })
774
- )
775
- }
776
- headers.push({ label: translations.report.distanceOut || 'Kms Afuera', value: 'distanceOut', format: '0' })
777
- } else if (userData.tripsBetweenZones) {
778
- headers.push(...[
779
- { label: translations.report.vehicle, value: 'name' },
780
- { label: translations.report.date_start, value: 'exit' },
781
- { label: translations.report.geofenceOut || 'Zone OUT', value: 'zoneOut' },
782
- { label: translations.report.date_end, value: 'enter' },
783
- { label: translations.report.geofenceIn || 'Zone IN', value: 'zoneIn' },
784
- { label: translations.report.duration, value: 'totalOutTime' },
785
- { label: translations.report.distance, value: 'distanceOut', format: '0' }
786
- ])
787
- } else {
788
- headers.push(...[
789
- { label: translations.report.vehicle, value: 'name' },
790
- { label: translations.report.enter, value: 'enter' },
791
- { label: translations.report.exit, value: 'exit' },
792
- { label: translations.report.stopped, value: 'stopped' },
793
- { label: translations.report.duration, value: 'totalInTime' },
794
- { label: translations.report.distanceIn || 'Kms Adentro', value: 'distanceIn', format: '0' },
795
- { label: translations.report.distanceOut || 'Kms Afuera', value: 'distanceOut', format: '0' },
796
- { label: translations.report.geofence, value: 'zone' },
797
- { label: translations.report.driver, value: 'driver' }
798
- ])
799
- }
800
-
801
- let data = []
802
- if (reportData.devices) {
803
- reportData.devices.forEach(d => {
804
- if (userData.groupByDay) {
805
- d.geofences.forEach(g => {
806
- data = data.concat([{ zone: g.geofenceName }])
807
- data = data.concat(g.days.map(a => {
808
- return getColumns(d, a, userData, translations)
809
- }))
810
- data = data.concat([{}])
811
- })
812
- } else if (userData.zonesByColumn) {
813
- const lang = userData.user.attributes.lang
814
- d.days.forEach(day => {
815
- const row = {
816
- name: d.device.name,
817
- date: convertToLocaleDateString(day.date, lang, userData.user.attributes.timezone),
818
- distanceOut: day.distanceOut ? parseFloat(day.distanceOut.toFixed(2)) : 0
819
- }
820
- day.geofences.forEach(g => {
821
- row[g.geofenceId] = g.distanceIn ? parseFloat(g.distanceIn.toFixed(2)) : 0
822
- })
823
- data.push(row)
824
- })
825
- data = data.concat([{}])
826
- } else {
827
- data = data.concat(d.geofences.map(a => {
828
- return getColumns(d, a, userData, translations, true)
829
- }))
830
- data = data.concat([{}])
831
- }
832
- })
833
- return {
834
- headers,
835
- data,
836
- settings
837
- }
838
- }
839
- }
840
-
841
- function reportHeader (first, doc, translations, device, lang, timezone, userData, geofence) {
842
- const name = deviceName(device.device)
843
- const group = userData.groups.find(g => device.device.groupId === g.id)
844
-
845
- const space = first ? 8 : 0
846
- if (!first) {
847
- doc.addPage()
848
- }
849
-
850
- doc.setFontSize(13)
851
- doc.text(name, 20, space + 20)
852
- doc.setFontSize(11)
853
- doc.text(group ? translations.report.group + ': ' + group.name : '', 200, space + 20)
854
- doc.text(convertToLocaleString(device.from, lang, timezone) + ' - ' + convertToLocaleString(device.to, lang, timezone), 20, space + 25)
855
- if (userData.groupByDay) {
856
- doc.text(translations.report.geofence + ': ' + geofence.geofenceName, 20, space + 30)
857
- }
858
-
859
- return space
860
- }
861
-
862
- function getColumns (device, data, userData, translations, excel) {
863
- return {
864
- ...data,
865
- name: device.device.name,
866
- enter: geofenceEnter(userData.user, data),
867
- exit: geofenceExit(userData.user, data),
868
- stopped: data.stopped ? translations.report.yes : translations.report.no,
869
- date: convertToLocaleDateString(data.date, userData.user.attributes.lang, userData.user.attributes.timezone),
870
- firstIn: data.firstIn ? convertToLocaleTimeString(data.firstIn, userData.user.attributes.lang, userData.user.attributes.timezone) : '-',
871
- lastOut: data.lastOut ? convertToLocaleTimeString(data.lastOut, userData.user.attributes.lang, userData.user.attributes.timezone) : '-',
872
- distanceIn: excel
873
- ? data.distanceIn || 0
874
- : (data.distanceIn && data.distanceIn.toLocaleString(userData.user.attributes.lang, { maximumFractionDigits: 2 })) || 0,
875
- distanceOut: excel
876
- ? data.distanceOut || 0
877
- : (data.distanceOut && data.distanceOut.toLocaleString(userData.user.attributes.lang, { maximumFractionDigits: 2 })) || 0,
878
- totalInTime: convertMS(data.totalInTime, !userData.groupByDay),
879
- totalOutTime: convertMS(data.totalOutTime, !userData.groupByDay),
880
- zone: data.geofenceName,
881
- zoneOut: data.outGeofenceName,
882
- zoneIn: data.inGeofenceName,
883
- driver: data.driverName
884
- }
885
- }
886
-
887
- function deviceName (device) {
888
- return device.name + (device.attributes.license_plate ? ', ' + device.attributes.license_plate : '') + (device.model ? ', ' + device.model : '')
889
- }
890
-
891
- function geofenceEnter (user, row) {
892
- return row.inTime ? convertToLocaleString(row.inTime.fixTime, user.attributes.lang, user.attributes.timezone) : ''
893
- }
894
- function geofenceExit (user, row) {
895
- return row.outTime ? convertToLocaleString(row.outTime.fixTime, user.attributes.lang, user.attributes.timezone) : ''
896
- }
897
-
898
- exports.createZoneReport = createZoneReport
899
- exports.exportZoneReportToPDF = exportZoneReportToPDF
900
- exports.exportZoneReportToExcel = exportZoneReportToExcel
901
- exports.create = createZoneReport
902
- exports.exportToPDF = exportZoneReportToPDF
903
- exports.exportToExcel = exportZoneReportToExcel
904
- exports.getInAndOutEvents = getInAndOutEvents