fleetmap-reports 2.0.169 → 2.0.171

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.
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fleetmap-reports",
3
- "version": "2.0.169",
3
+ "version": "2.0.171",
4
4
  "description": "",
5
5
  "main": "src/index.js",
6
6
  "scripts": {
@@ -1,7 +1,11 @@
1
1
  const traccarHelper = require('../util/traccar')
2
2
  const { isInsideTimetable } = require('../util/trips')
3
- const { convertFromUTCDate } = require('../util/utils')
3
+ const { convertFromUTCDate, getTranslations, convertMS, convertToLocaleTimeString, isClientSide } = require('../util/utils')
4
4
  const automaticReports = require('../automaticReports')
5
+ const jsPDF = require('jspdf')
6
+ const { getStyle } = require('../reportStyle')
7
+ const { getUserPartner } = require('fleetmap-partners')
8
+ const { addTable, headerFromUser } = require('../util/pdfDocument')
5
9
 
6
10
  async function createDailyUseReport (from, to, userData, traccar) {
7
11
  const reportData = []
@@ -77,4 +81,72 @@ function processDeviceData (allInOne, d, userData) {
77
81
  }
78
82
  }
79
83
 
84
+ async function exportDailyUseReportToPDF (userData, reportData) {
85
+ const translations = getTranslations(userData)
86
+ const timezone = userData.user.attributes.timezone
87
+ const lang = userData.user.attributes.lang || (isClientSide() && navigator.language)
88
+
89
+ const headers = [
90
+ translations.report.vehicle,
91
+ 'Immatriculation',
92
+ 'Groupe',
93
+ 'Matin départ',
94
+ 'Matin arrêt',
95
+ 'Matin temps',
96
+ 'Déjeuner',
97
+ 'Après-midi départ',
98
+ 'Après-midi arrêt',
99
+ 'Après-midi temps',
100
+ 'Total temps',
101
+ 'Total conduit',
102
+ 'Total arrêts',
103
+ 'Total kms'
104
+ ]
105
+
106
+ const data = []
107
+ reportData.forEach(d => {
108
+ const row = [
109
+ d.device,
110
+ d.licensePlate,
111
+ d.group,
112
+ convertToLocaleTimeString(d.morningStart, lang, timezone, userData.user),
113
+ convertToLocaleTimeString(d.morningEnd, lang, timezone, userData.user),
114
+ convertMS(d.morningTime),
115
+ convertMS(d.lunch),
116
+ convertToLocaleTimeString(d.afternoonStart, lang, timezone, userData.user),
117
+ convertToLocaleTimeString(d.afternoonEnd, lang, timezone, userData.user),
118
+ convertMS(d.afternoonTime),
119
+ convertMS(d.totalTime),
120
+ convertMS(d.totalDrivingTime),
121
+ d.totalStops,
122
+ (d.totalDistance / 100).toFixed(1)
123
+ ]
124
+ data.push(row)
125
+ })
126
+
127
+ const doc = new jsPDF.jsPDF('l')
128
+ await headerFromUser(doc, 'Rapport de Performance', userData.user)
129
+ const style = getStyle(getUserPartner(userData.user))
130
+
131
+ const footValues = []
132
+
133
+ doc.setFontSize(11)
134
+ doc.text(new Date(userData.from).toLocaleString() + ' - ' + new Date(userData.to).toLocaleString(), 20, 33)
135
+
136
+ style.headerFontSize = 8
137
+ style.bodyFontSize = 7
138
+ const columnStyles = {}
139
+ for (let i = 3; i < 14; i++) {
140
+ columnStyles[i] = { halign: 'right' }
141
+ }
142
+ addTable(doc, headers, data, footValues, style, 40, columnStyles)
143
+ return doc
144
+ }
145
+
146
+ function exportDailyUseReportToExcel (userData, reportData) {
147
+
148
+ }
149
+
80
150
  exports.createDailyUseReport = createDailyUseReport
151
+ exports.exportDailyUseReportToPDF = exportDailyUseReportToPDF
152
+ exports.exportDailyUseReportToExcel = exportDailyUseReportToExcel
@@ -0,0 +1,46 @@
1
+ async function createPassengerReport (from, to, userData, traccar) {
2
+ //const allDevices = await traccar.axios.get('/devices').then(d => d.data)
3
+ const devices = userData.devices
4
+ const drivers = await traccar.axios.get('/drivers').then(d => d.data)
5
+ const groups = await traccar.axios.get('/groups').then(d => d.data)
6
+ await Promise.all(groups.map(async g => {
7
+ const driversByGroup = await traccar.axios.get(`/drivers?groupId=${g.id}`).then(d => d.data)
8
+ driversByGroup.forEach(d => {
9
+ const _driver = drivers.find(a => a.id === d.id)
10
+ if (_driver) {
11
+ _driver.groupName = g.name
12
+ }
13
+ })
14
+ }))
15
+ const eventsUrl = `/reports/events?${devices.map(d => 'deviceId=' + d.id).join('&')
16
+ }&from=${from.toISOString()
17
+ }&to=${to.toISOString()
18
+ }`
19
+ const positionsUrl = `/reports/route?${devices.map(d => 'deviceId=' + d.id).join('&')
20
+ }&from=${from.toISOString()
21
+ }&to=${to.toISOString()
22
+ }`
23
+ const events = await traccar.axios.get(eventsUrl).then(d => d.data)
24
+ const positions = await traccar.axios.get(positionsUrl).then(d => d.data)
25
+ events.forEach(e => {
26
+ delete e.attributes
27
+ const driver = getDriver(e, positions, drivers)
28
+ const position = positions.find(p => p.id === e.positionId)
29
+ e.deviceName = devices.find(d => d.id === e.deviceId).name
30
+ e.groupName = driver.groupName || ''
31
+ e.fixtime = position && new Date(position.fixTime)
32
+ e.notes = driver && driver.attributes.notes
33
+ e.driverName = (driver && driver.name) || (position && position.attributes.driverUniqueId)
34
+ })
35
+ return events
36
+ }
37
+
38
+ function getDriver (event, positions, drivers) {
39
+ const p = positions.find(p => p.id === event.positionId)
40
+ if (!p) { return '' }
41
+ const d = drivers.find(d => d.uniqueId === p.attributes.driverUniqueId)
42
+ if (!d) { return '' }
43
+ return d
44
+ }
45
+
46
+ exports.createPassengerReport = createPassengerReport
@@ -1,7 +1,10 @@
1
1
  const automaticReports = require('./automaticReports')
2
2
  const traccarHelper = require('./util/traccar')
3
3
  const { devicesToProcess, deviceName } = require('./util/device')
4
- const { getTranslations, isClientSide, convertToLocaleString, convertMS } = require('./util/utils')
4
+ const {
5
+ getTranslations, isClientSide, convertToLocaleString, convertMS, getDates, convertToLocaleDateString,
6
+ convertToLocaleTimeString
7
+ } = require('./util/utils')
5
8
  const jsPDF = require('jspdf')
6
9
  const { headerFromUser, addTable } = require('./util/pdfDocument')
7
10
  const { getStyle } = require('./reportStyle')
@@ -44,12 +47,25 @@ function processDevices (from, to, devices, data, userData) {
44
47
 
45
48
  rows.sort((a, b) => new Date(a.startTime).getTime() - new Date(b.startTime).getTime())
46
49
 
47
- result.push({
48
- device: d,
49
- from,
50
- to,
51
- rows
52
- })
50
+ if (userData.groupByDay) {
51
+ const dates = getDates(from, to, userData.user.attributes.timezone)
52
+
53
+ const data = userData.groupBySensor ? groupByDayAndSensor(rows, dates) : groupByDay(rows, dates)
54
+
55
+ result.push({
56
+ device: d,
57
+ from,
58
+ to,
59
+ rows: data
60
+ })
61
+ } else {
62
+ result.push({
63
+ device: d,
64
+ from,
65
+ to,
66
+ rows
67
+ })
68
+ }
53
69
  }
54
70
 
55
71
  return result
@@ -74,6 +90,128 @@ async function create (from, to, userData, traccar) {
74
90
  return result
75
91
  }
76
92
 
93
+ function groupByDay (rows, dates) {
94
+ const groupedByDay = {}
95
+
96
+ dates.forEach(dateStr => {
97
+ const dayStart = new Date(dateStr)
98
+ dayStart.setHours(0, 0, 0, 0)
99
+ const dayEnd = new Date(dayStart.getTime() + 86400000 - 1) // 23:59:59
100
+
101
+ rows.forEach(row => {
102
+ if (row.value) {
103
+ const rowStart = new Date(row.startTime)
104
+ const rowEnd = new Date(row.endTime)
105
+
106
+ if (rowStart <= dayEnd && rowEnd >= dayStart) {
107
+ if (!groupedByDay[dateStr]) {
108
+ groupedByDay[dateStr] = {}
109
+ }
110
+
111
+ if (!groupedByDay[dateStr][row.name]) {
112
+ groupedByDay[dateStr][row.name] = {
113
+ firstActivation: null,
114
+ lastActivation: null,
115
+ totalDuration: 0
116
+ }
117
+ }
118
+
119
+ const periodStart = Math.max(rowStart.getTime(), dayStart.getTime())
120
+ const periodEnd = Math.min(rowEnd.getTime(), dayEnd.getTime())
121
+
122
+ const sensorData = groupedByDay[dateStr][row.name]
123
+
124
+ if (!sensorData.firstActivation || periodStart < sensorData.firstActivation) {
125
+ sensorData.firstActivation = periodStart
126
+ }
127
+
128
+ if (!sensorData.lastActivation || periodEnd > sensorData.lastActivation) {
129
+ sensorData.lastActivation = periodEnd
130
+ }
131
+
132
+ sensorData.totalDuration += periodEnd - periodStart
133
+ }
134
+ }
135
+ })
136
+ })
137
+
138
+ // 🖨️ Exibir os dados processados
139
+ Object.entries(groupedByDay).forEach(([date, sensors]) => {
140
+ Object.entries(sensors).forEach(([sensor, data]) => {
141
+ console.log(`Data: ${date}, Sensor: ${sensor}`)
142
+ console.log(` Primeira ativação: ${data.firstActivation ? new Date(data.firstActivation).toLocaleTimeString() : '00:00'}`)
143
+ console.log(` Última ativação: ${data.lastActivation ? new Date(data.lastActivation).toLocaleTimeString() : '23:59'}`)
144
+ console.log(` Duração total: ${(data.totalDuration / 1000 / 60).toFixed(2)} minutos`)
145
+ })
146
+ })
147
+
148
+ return Object.entries(groupedByDay)
149
+ }
150
+
151
+ function groupByDayAndSensor (rows, dates) {
152
+ const groupedData = {}
153
+
154
+ dates.forEach(dateStr => {
155
+ const dayStart = new Date(dateStr)
156
+ dayStart.setHours(0, 0, 0, 0)
157
+ const dayEnd = new Date(dayStart.getTime() + 86400000 - 1) // 23:59:59
158
+
159
+ if (!groupedData[dateStr]) {
160
+ groupedData[dateStr] = {
161
+ firstActivation: null,
162
+ lastActivation: null,
163
+ totalDuration: 0
164
+ }
165
+ }
166
+
167
+ const activePeriods = []
168
+
169
+ rows.forEach(row => {
170
+ if (row.value) {
171
+ const rowStart = new Date(row.startTime).getTime()
172
+ const rowEnd = new Date(row.endTime).getTime()
173
+
174
+ if (rowStart <= dayEnd.getTime() && rowEnd >= dayStart.getTime()) {
175
+ const periodStart = Math.max(rowStart, dayStart.getTime())
176
+ const periodEnd = Math.min(rowEnd, dayEnd.getTime())
177
+
178
+ activePeriods.push([periodStart, periodEnd]) // Store activation period
179
+ }
180
+ }
181
+ })
182
+
183
+ // Merge overlapping activation periods
184
+ activePeriods.sort((a, b) => a[0] - b[0]) // Sort by start time
185
+ const mergedPeriods = []
186
+ let currentPeriod = null
187
+
188
+ activePeriods.forEach(([start, end]) => {
189
+ if (!currentPeriod) {
190
+ currentPeriod = [start, end]
191
+ } else if (start <= currentPeriod[1]) {
192
+ // Extend the current period if overlapping
193
+ currentPeriod[1] = Math.max(currentPeriod[1], end)
194
+ } else {
195
+ // Store the finished period and start a new one
196
+ mergedPeriods.push(currentPeriod)
197
+ currentPeriod = [start, end]
198
+ }
199
+ })
200
+ if (currentPeriod) {
201
+ mergedPeriods.push(currentPeriod)
202
+ }
203
+
204
+ const totalDuration = mergedPeriods.reduce((sum, [start, end]) => sum + (end - start), 0)
205
+
206
+ if (mergedPeriods.length > 0) {
207
+ groupedData[dateStr].firstActivation = mergedPeriods[0][0]
208
+ groupedData[dateStr].lastActivation = mergedPeriods[mergedPeriods.length - 1][1]
209
+ groupedData[dateStr].totalDuration = totalDuration
210
+ }
211
+ })
212
+
213
+ return Object.entries(groupedData)
214
+ }
77
215
  async function exportToPDF (userData, reportData) {
78
216
  console.log('exportSensorReportToPDF')
79
217
 
@@ -82,14 +220,29 @@ async function exportToPDF (userData, reportData) {
82
220
  const lang = userData.user.attributes.lang || (isClientSide() && navigator.language)
83
221
  const reportDataRows = reportData.devices
84
222
 
85
- const headers = [
86
- 'Sensor',
87
- translations.report.start,
88
- translations.report.end,
89
- 'Estado',
90
- translations.report.duration,
91
- translations.report.endAddress
92
- ]
223
+ const headers = userData.groupByDay && userData.groupBySensor
224
+ ? [
225
+ translations.report.date,
226
+ translations.report.start,
227
+ translations.report.end,
228
+ translations.report.duration
229
+ ]
230
+ : (userData.groupByDay
231
+ ? [
232
+ translations.report.date,
233
+ 'Sensor',
234
+ translations.report.start,
235
+ translations.report.end,
236
+ translations.report.duration
237
+ ]
238
+ : [
239
+ 'Sensor',
240
+ translations.report.start,
241
+ translations.report.end,
242
+ 'Estado',
243
+ translations.report.duration,
244
+ translations.report.endAddress
245
+ ])
93
246
 
94
247
  if (reportDataRows) {
95
248
  // eslint-disable-next-line new-cap
@@ -114,25 +267,52 @@ async function exportToPDF (userData, reportData) {
114
267
  doc.text(group ? translations.report.group + ': ' + group.name : '', 200, space + 20)
115
268
  doc.text(convertToLocaleString(d.from, lang, timezone, userData.user) + ' - ' + convertToLocaleString(d.to, lang, timezone, userData.user), 20, space + 25)
116
269
 
117
- d.rows.forEach(row => {
118
- const temp = [
119
- row.name,
120
- convertToLocaleString(row.startTime, lang, timezone, userData.user),
121
- convertToLocaleString(row.endTime, lang, timezone, userData.user),
122
- row.valueDescription,
123
- convertMS(row.duration),
124
- row.endAddress
125
- ]
126
- data.push(temp)
127
- })
270
+ if (userData.groupByDay && userData.groupBySensor) {
271
+ d.rows.forEach(day => {
272
+ const temp = [
273
+ convertToLocaleDateString(day[0], lang, timezone, userData.user),
274
+ day[1].firstActivation ? convertToLocaleTimeString(day[1].firstActivation, lang, timezone, userData.user) : '-',
275
+ day[1].lastActivation ? convertToLocaleTimeString(day[1].lastActivation, lang, timezone, userData.user) : '-',
276
+ convertMS(day[1].totalDuration)
277
+ ]
278
+ data.push(temp)
279
+ })
280
+ } else if (userData.groupByDay) {
281
+ d.rows.forEach(day => {
282
+ const sensors = Object.entries(day[1])
283
+ sensors.forEach(sensor => {
284
+ const temp = [
285
+ convertToLocaleDateString(day[0], lang, timezone, userData.user),
286
+ sensor[0],
287
+ sensor[1].firstActivation ? convertToLocaleTimeString(sensor[1].firstActivation, lang, timezone, userData.user) : '-',
288
+ sensor[1].lastActivation ? convertToLocaleTimeString(sensor[1].lastActivation, lang, timezone, userData.user) : '-',
289
+ convertMS(sensor[1].totalDuration)
290
+ ]
291
+ data.push(temp)
292
+ })
293
+ })
294
+ } else {
295
+ d.rows.forEach(row => {
296
+ const temp = [
297
+ row.name,
298
+ convertToLocaleString(row.startTime, lang, timezone, userData.user),
299
+ convertToLocaleString(row.endTime, lang, timezone, userData.user),
300
+ row.valueDescription,
301
+ convertMS(row.duration),
302
+ row.endAddress
303
+ ]
304
+ data.push(temp)
305
+ })
306
+ }
128
307
 
129
- const footValues = []
308
+ const totalDuration = (userData.groupByDay && userData.groupBySensor) ? getTotalDuration(d.rows) : 0
309
+
310
+ const footValues = (userData.groupByDay && userData.groupBySensor) ? ['', '', '', totalDuration] : []
130
311
 
131
312
  const style = getStyle(getUserPartner(userData.user))
132
313
 
133
- addTable(doc, headers, data, footValues, style, 40, {
134
- 4: { halign: 'right' }
135
- })
314
+ addTable(doc, headers, data, footValues, style, 40,
315
+ (userData.groupByDay && userData.groupBySensor ? { 3: { halign: 'right' } } : { 4: { halign: 'right' } }))
136
316
 
137
317
  index++
138
318
  }
@@ -141,6 +321,10 @@ async function exportToPDF (userData, reportData) {
141
321
  }
142
322
  }
143
323
 
324
+ function getTotalDuration (rows) {
325
+ return convertMS(rows.reduce((a, b) => a + b[1].totalDuration, 0), false)
326
+ }
327
+
144
328
  function exportToExcel (userData, reportData) {}
145
329
 
146
330
  exports.exportToPDF = exportToPDF
@@ -0,0 +1,19 @@
1
+ const { getReports } = require('./index')
2
+ const assert = require('assert')
3
+ const { createDailyUseReport } = require('../partnerReports/dailyuse-report')
4
+
5
+ describe('dailyuse', function () {
6
+ // eslint-disable-next-line no-undef
7
+ it('dailyuse report', async () => {
8
+ const report = await getReports()
9
+ const userData = await report.getUserData()
10
+
11
+ const data = await createDailyUseReport(
12
+ new Date(Date.UTC(2023, 10, 1, 0, 0, 0, 0)),
13
+ new Date(Date.UTC(2023, 10, 6, 23, 59, 59, 0)),
14
+ userData,
15
+ report.traccar)
16
+ console.log(data)
17
+ assert.equal(data[0].consumption, 19.799999999999997)
18
+ }, 8000000)
19
+ })
@@ -0,0 +1,23 @@
1
+ const { getReports } = require('./index')
2
+ const assert = require('assert')
3
+ const { convertMS } = require('../util/utils')
4
+ const { createGPSJumpReport } = require('../partnerReports/gpsjump-report')
5
+
6
+ // eslint-disable-next-line no-undef
7
+ describe('gpsjump', function () {
8
+ // eslint-disable-next-line no-undef
9
+ it('gpsjump report', async () => {
10
+ const report = await getReports()
11
+ const userData = await report.getUserData()
12
+ userData.devices = userData.devices.filter(d => d.id === 22326)
13
+ const data = await createGPSJumpReport(
14
+ new Date(Date.UTC(2023, 6, 1, 0, 0, 0, 0)),
15
+ new Date(Date.UTC(2023, 6, 17, 23, 59, 59, 0)),
16
+ userData,
17
+ report.traccar)
18
+ console.log(data)
19
+ assert.equal(data[0].consumption, 19.799999999999997)
20
+ assert.equal(data[0].distance, 326.7657)
21
+ assert.equal(convertMS(data[1].time * 1000, false), '08:19')
22
+ }, 8000000)
23
+ })
@@ -65,10 +65,11 @@ describe('Kms_Reports', function () {
65
65
  it('KmsReport by timezone marrocos', async () => {
66
66
  const reports = await getReports(process.env.email, process.env.password)
67
67
  const userData = await reports.getUserData()
68
+ userData.groupByDay = true
68
69
  userData.devices = userData.devices.filter(d => d.id === 93497)
69
70
  const data = await reports.kmsReport(convertFromLocal(new Date(2024, 2, 18, 0, 0, 0), userData.user.attributes.timezone), convertFromLocal(new Date(2024, 2, 18, 23, 59, 59), userData.user.attributes.timezone),
70
71
  userData)
71
72
  const device = data[0].devices.find(d => d.device.id === 93497)
72
- assert.equal(device, 1)
73
+ assert.equal(device.days.length, 1)
73
74
  }, 3000000)
74
75
  })
@@ -159,4 +159,21 @@ describe('zones', function () {
159
159
  console.log(first)
160
160
  assert.equal(first.days[6].distanceOut, 867.0554430738234)
161
161
  }, 4000000)
162
+
163
+ it('works with casais zones in columns 7', async () => {
164
+ const report = await getReports()
165
+ const userData = await report.getUserData()
166
+ userData.zonesByColumn = true
167
+ userData.devices = userData.devices.filter(d => d.id === 81166)
168
+ userData.geofences = userData.geofences.filter(g => g.name === 'baliza 1 - raio verde ' ||
169
+ g.name === 'baliza 2 - raio amarelo')
170
+ userData.onlyWithKmsOut = false
171
+ const result = await report.zoneReport(
172
+ new Date(Date.UTC(2023, 9, 9, 0, 0, 0, 0)),
173
+ new Date(Date.UTC(2023, 9, 31, 23, 59, 59, 0)),
174
+ userData)
175
+ const first = result[0].devices[0]
176
+ console.log(first)
177
+ assert.equal(first.days[17].distanceOut, 720.7985320478997)
178
+ }, 4000000)
162
179
  })