fleetmap-reports 2.0.358 → 2.0.360

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 (82) hide show
  1. package/package.json +1 -1
  2. package/src/partnerReports/driverranking-report.js +39 -7
  3. package/src/util/utils.js +3 -0
  4. package/.claude/worktrees/eager-stonebraker-c08ac7/.eslintrc.js +0 -15
  5. package/.claude/worktrees/eager-stonebraker-c08ac7/lang/enGB.js +0 -309
  6. package/.claude/worktrees/eager-stonebraker-c08ac7/lang/esCL.js +0 -297
  7. package/.claude/worktrees/eager-stonebraker-c08ac7/lang/frFR.json +0 -324
  8. package/.claude/worktrees/eager-stonebraker-c08ac7/lang/index.js +0 -35
  9. package/.claude/worktrees/eager-stonebraker-c08ac7/lang/ptBR.js +0 -301
  10. package/.claude/worktrees/eager-stonebraker-c08ac7/lang/ptPT.js +0 -310
  11. package/.claude/worktrees/eager-stonebraker-c08ac7/package-lock.json +0 -11944
  12. package/.claude/worktrees/eager-stonebraker-c08ac7/package.json +0 -45
  13. package/.claude/worktrees/eager-stonebraker-c08ac7/src/activity-report.js +0 -822
  14. package/.claude/worktrees/eager-stonebraker-c08ac7/src/automaticReports.js +0 -34
  15. package/.claude/worktrees/eager-stonebraker-c08ac7/src/carbone/index.js +0 -13
  16. package/.claude/worktrees/eager-stonebraker-c08ac7/src/custom/vistawasteActivity-report.js +0 -544
  17. package/.claude/worktrees/eager-stonebraker-c08ac7/src/events-report.js +0 -360
  18. package/.claude/worktrees/eager-stonebraker-c08ac7/src/fuel-consumption-report.js +0 -296
  19. package/.claude/worktrees/eager-stonebraker-c08ac7/src/fuel-drop-report.js +0 -128
  20. package/.claude/worktrees/eager-stonebraker-c08ac7/src/here.js +0 -172
  21. package/.claude/worktrees/eager-stonebraker-c08ac7/src/idle-report.js +0 -403
  22. package/.claude/worktrees/eager-stonebraker-c08ac7/src/index.js +0 -244
  23. package/.claude/worktrees/eager-stonebraker-c08ac7/src/kms-report.js +0 -549
  24. package/.claude/worktrees/eager-stonebraker-c08ac7/src/location-report.js +0 -496
  25. package/.claude/worktrees/eager-stonebraker-c08ac7/src/machines-report.js +0 -278
  26. package/.claude/worktrees/eager-stonebraker-c08ac7/src/partnerReports/afriquia.js +0 -129
  27. package/.claude/worktrees/eager-stonebraker-c08ac7/src/partnerReports/dailyuse-report.js +0 -80
  28. package/.claude/worktrees/eager-stonebraker-c08ac7/src/partnerReports/delayedstart-report.js +0 -223
  29. package/.claude/worktrees/eager-stonebraker-c08ac7/src/partnerReports/driverranking-report.js +0 -328
  30. package/.claude/worktrees/eager-stonebraker-c08ac7/src/partnerReports/gpsjump-report.js +0 -61
  31. package/.claude/worktrees/eager-stonebraker-c08ac7/src/partnerReports/performance-report.js +0 -359
  32. package/.claude/worktrees/eager-stonebraker-c08ac7/src/refueling-report.js +0 -345
  33. package/.claude/worktrees/eager-stonebraker-c08ac7/src/reportStyle.js +0 -34
  34. package/.claude/worktrees/eager-stonebraker-c08ac7/src/sensor-report.js +0 -447
  35. package/.claude/worktrees/eager-stonebraker-c08ac7/src/speeding-report.js +0 -663
  36. package/.claude/worktrees/eager-stonebraker-c08ac7/src/stop-report.js +0 -242
  37. package/.claude/worktrees/eager-stonebraker-c08ac7/src/templates/ActivityReportTemplate.mjml +0 -51
  38. package/.claude/worktrees/eager-stonebraker-c08ac7/src/templates/EmptyReportTemplate.mjml +0 -25
  39. package/.claude/worktrees/eager-stonebraker-c08ac7/src/templates/EventsReportTemplate.mjml +0 -75
  40. package/.claude/worktrees/eager-stonebraker-c08ac7/src/templates/FuelDropReportTemplate.mjml +0 -60
  41. package/.claude/worktrees/eager-stonebraker-c08ac7/src/templates/IdleReportTemplate.mjml +0 -54
  42. package/.claude/worktrees/eager-stonebraker-c08ac7/src/templates/KmsReportGroupByDayTemplate.mjml +0 -43
  43. package/.claude/worktrees/eager-stonebraker-c08ac7/src/templates/KmsReportTemplate.mjml +0 -44
  44. package/.claude/worktrees/eager-stonebraker-c08ac7/src/templates/LocationReportTemplate.mjml +0 -37
  45. package/.claude/worktrees/eager-stonebraker-c08ac7/src/templates/RefuelingReportTemplate.mjml +0 -60
  46. package/.claude/worktrees/eager-stonebraker-c08ac7/src/templates/SensorReportTemplate.mjml +0 -23
  47. package/.claude/worktrees/eager-stonebraker-c08ac7/src/templates/SpeedingReportTemplate.mjml +0 -80
  48. package/.claude/worktrees/eager-stonebraker-c08ac7/src/templates/TripReportTemplate.mjml +0 -97
  49. package/.claude/worktrees/eager-stonebraker-c08ac7/src/templates/ZoneReportTemplate.mjml +0 -52
  50. package/.claude/worktrees/eager-stonebraker-c08ac7/src/templates/report.mjml +0 -96
  51. package/.claude/worktrees/eager-stonebraker-c08ac7/src/tests/activity.test.js +0 -99
  52. package/.claude/worktrees/eager-stonebraker-c08ac7/src/tests/carbone.test.js +0 -20
  53. package/.claude/worktrees/eager-stonebraker-c08ac7/src/tests/events.test.js +0 -30
  54. package/.claude/worktrees/eager-stonebraker-c08ac7/src/tests/fuel.test.js +0 -35
  55. package/.claude/worktrees/eager-stonebraker-c08ac7/src/tests/idle.test.js +0 -16
  56. package/.claude/worktrees/eager-stonebraker-c08ac7/src/tests/index.js +0 -24
  57. package/.claude/worktrees/eager-stonebraker-c08ac7/src/tests/index.test.js +0 -203
  58. package/.claude/worktrees/eager-stonebraker-c08ac7/src/tests/kms.test.js +0 -74
  59. package/.claude/worktrees/eager-stonebraker-c08ac7/src/tests/performance.test.js +0 -36
  60. package/.claude/worktrees/eager-stonebraker-c08ac7/src/tests/positions.test.js +0 -67
  61. package/.claude/worktrees/eager-stonebraker-c08ac7/src/tests/speeding.test.js +0 -120
  62. package/.claude/worktrees/eager-stonebraker-c08ac7/src/tests/translations.test.js +0 -21
  63. package/.claude/worktrees/eager-stonebraker-c08ac7/src/tests/trips.test.js +0 -140
  64. package/.claude/worktrees/eager-stonebraker-c08ac7/src/tests/zones.test.js +0 -162
  65. package/.claude/worktrees/eager-stonebraker-c08ac7/src/trip-report.js +0 -641
  66. package/.claude/worktrees/eager-stonebraker-c08ac7/src/util/device.js +0 -13
  67. package/.claude/worktrees/eager-stonebraker-c08ac7/src/util/driver.js +0 -90
  68. package/.claude/worktrees/eager-stonebraker-c08ac7/src/util/fuel.js +0 -102
  69. package/.claude/worktrees/eager-stonebraker-c08ac7/src/util/geofence.js +0 -52
  70. package/.claude/worktrees/eager-stonebraker-c08ac7/src/util/odoo.js +0 -18
  71. package/.claude/worktrees/eager-stonebraker-c08ac7/src/util/parallel.js +0 -64
  72. package/.claude/worktrees/eager-stonebraker-c08ac7/src/util/pdfDocument.js +0 -140
  73. package/.claude/worktrees/eager-stonebraker-c08ac7/src/util/route.js +0 -78
  74. package/.claude/worktrees/eager-stonebraker-c08ac7/src/util/timetable.js +0 -30
  75. package/.claude/worktrees/eager-stonebraker-c08ac7/src/util/traccar.js +0 -213
  76. package/.claude/worktrees/eager-stonebraker-c08ac7/src/util/traccar.worker.js +0 -49
  77. package/.claude/worktrees/eager-stonebraker-c08ac7/src/util/trips.js +0 -270
  78. package/.claude/worktrees/eager-stonebraker-c08ac7/src/util/utils.js +0 -358
  79. package/.claude/worktrees/eager-stonebraker-c08ac7/src/util/worker.js +0 -14
  80. package/.claude/worktrees/eager-stonebraker-c08ac7/src/util/xpert.js +0 -58
  81. package/.claude/worktrees/eager-stonebraker-c08ac7/src/word/index.js +0 -95
  82. 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