fleetmap-reports 2.0.385 → 2.0.387
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.
package/package.json
CHANGED
package/src/activity-report.js
CHANGED
|
@@ -207,7 +207,8 @@ async function processDevices (from, to, devices, data, userData, traccarInstanc
|
|
|
207
207
|
endTime: tripsByDay.length && tripsByDay[tripsByDay.length - 1].endTime,
|
|
208
208
|
endAddress: tripsByDay.length && tripsByDay[tripsByDay.length - 1].endAddress,
|
|
209
209
|
startTimeIsOut: tripsByDay.some(t => t.startTimeIsOut),
|
|
210
|
-
endTimeIsOut: tripsByDay.some(t => t.endTimeIsOut)
|
|
210
|
+
endTimeIsOut: tripsByDay.some(t => t.endTimeIsOut),
|
|
211
|
+
activity: getDayActivity(tripsByDay, fromDay, toDay)
|
|
211
212
|
})
|
|
212
213
|
}
|
|
213
214
|
} else {
|
|
@@ -232,6 +233,7 @@ async function processDevices (from, to, devices, data, userData, traccarInstanc
|
|
|
232
233
|
summaryCurrentDay.endOdometer = summaryByDevice.endOdometer
|
|
233
234
|
summaryCurrentDay.startTimeIsOut = tripsByDay.some(t => t.startTimeIsOut)
|
|
234
235
|
summaryCurrentDay.endTimeIsOut = tripsByDay.some(t => t.endTimeIsOut)
|
|
236
|
+
summaryCurrentDay.activity = getDayActivity(tripsByDay, fromDay, toDay)
|
|
235
237
|
summary.push(summaryCurrentDay)
|
|
236
238
|
}
|
|
237
239
|
}
|
|
@@ -285,6 +287,17 @@ async function processDevices (from, to, devices, data, userData, traccarInstanc
|
|
|
285
287
|
return devicesResult
|
|
286
288
|
}
|
|
287
289
|
|
|
290
|
+
// Trips of one day as [{ start, end }] in hours since the start of that day (0-24),
|
|
291
|
+
// used to draw the daily activity timeline on screen and in the PDF.
|
|
292
|
+
function getDayActivity (tripsByDay, fromDay, toDay) {
|
|
293
|
+
const dayStart = new Date(fromDay).getTime()
|
|
294
|
+
const dayEnd = new Date(toDay).getTime()
|
|
295
|
+
const toHours = time => (Math.min(Math.max(new Date(time).getTime(), dayStart), dayEnd) - dayStart) / 3600000
|
|
296
|
+
return tripsByDay
|
|
297
|
+
.map(t => ({ start: toHours(t.startTime), end: toHours(t.endTime) }))
|
|
298
|
+
.filter(a => a.end > a.start)
|
|
299
|
+
}
|
|
300
|
+
|
|
288
301
|
function processDrivers (from, to, drivers, driversData, userData) {
|
|
289
302
|
const driversResult = []
|
|
290
303
|
drivers.forEach(d => {
|
|
@@ -508,6 +521,63 @@ function exportActivityReportToPDFVehicleGroupByDay (doc, translations, reportDa
|
|
|
508
521
|
|
|
509
522
|
const style = getStyle(getUserPartner(userData.user))
|
|
510
523
|
addTable(doc, headers, data, footValues, style, 40)
|
|
524
|
+
addActivityTimeline(doc, translations, d.summary, weekDays)
|
|
525
|
+
})
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
// Draws one bar per day (00h-24h) below the table: grey when stopped, green while driving.
|
|
529
|
+
function addActivityTimeline (doc, translations, summary, weekDays) {
|
|
530
|
+
if (!summary.some(s => s.activity)) {
|
|
531
|
+
return
|
|
532
|
+
}
|
|
533
|
+
const pageWidth = doc.internal.pageSize.getWidth()
|
|
534
|
+
const pageHeight = doc.internal.pageSize.getHeight()
|
|
535
|
+
const labelX = 14
|
|
536
|
+
const left = 50
|
|
537
|
+
const width = pageWidth - 14 - left
|
|
538
|
+
const rowHeight = 4
|
|
539
|
+
const rowGap = 2
|
|
540
|
+
const bottom = pageHeight - 15
|
|
541
|
+
|
|
542
|
+
const drawAxis = (y) => {
|
|
543
|
+
doc.setFontSize(7)
|
|
544
|
+
doc.setTextColor(80, 80, 80)
|
|
545
|
+
for (let h = 0; h <= 24; h += 2) {
|
|
546
|
+
doc.text(String(h).padStart(2, '0') + 'h', left + h / 24 * width, y, { align: 'center' })
|
|
547
|
+
}
|
|
548
|
+
return y + 2
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
let y = doc.lastAutoTable.finalY + 10
|
|
552
|
+
if (y + 20 > bottom) {
|
|
553
|
+
doc.addPage()
|
|
554
|
+
y = 20
|
|
555
|
+
}
|
|
556
|
+
doc.setFillColor(103, 194, 58)
|
|
557
|
+
doc.rect(labelX, y - 2.5, 3, 3, 'F')
|
|
558
|
+
doc.setFontSize(8)
|
|
559
|
+
doc.setTextColor(0, 0, 0)
|
|
560
|
+
doc.text(translations.vehicleTable.moving_vehicles, labelX + 4, y)
|
|
561
|
+
doc.setFillColor(220, 220, 220)
|
|
562
|
+
doc.rect(labelX + 30, y - 2.5, 3, 3, 'F')
|
|
563
|
+
doc.text(translations.vehicleTable.stopped_vehicles, labelX + 34, y)
|
|
564
|
+
y = drawAxis(y + 6)
|
|
565
|
+
|
|
566
|
+
summary.forEach(s => {
|
|
567
|
+
if (y + rowHeight > bottom) {
|
|
568
|
+
doc.addPage()
|
|
569
|
+
y = drawAxis(20)
|
|
570
|
+
}
|
|
571
|
+
doc.setFontSize(7)
|
|
572
|
+
doc.setTextColor(0, 0, 0)
|
|
573
|
+
doc.text(new Date(s.date).toLocaleDateString() + ' - ' + weekDays[new Date(s.date).getDay()], labelX, y + rowHeight - 0.8)
|
|
574
|
+
doc.setFillColor(220, 220, 220)
|
|
575
|
+
doc.rect(left, y, width, rowHeight, 'F')
|
|
576
|
+
doc.setFillColor(103, 194, 58)
|
|
577
|
+
for (const a of (s.activity || [])) {
|
|
578
|
+
doc.rect(left + a.start / 24 * width, y, Math.max((a.end - a.start) / 24 * width, 0.3), rowHeight, 'F')
|
|
579
|
+
}
|
|
580
|
+
y += rowHeight + rowGap
|
|
511
581
|
})
|
|
512
582
|
}
|
|
513
583
|
|
|
@@ -209,7 +209,7 @@ function calculateRPMSections (route, min, max) {
|
|
|
209
209
|
let newSection = true
|
|
210
210
|
route.forEach(p => {
|
|
211
211
|
const rpm = p.attributes.rpm
|
|
212
|
-
if ((
|
|
212
|
+
if ((min === undefined || rpm > min) && (!max || rpm < max)) {
|
|
213
213
|
if (newSection) {
|
|
214
214
|
sections.push([p])
|
|
215
215
|
} else {
|
package/src/rpm-report.js
CHANGED
|
@@ -10,7 +10,7 @@ const automaticReports = require('./automaticReports')
|
|
|
10
10
|
const { calculateRPMSections } = require('./partnerReports/performance-report')
|
|
11
11
|
|
|
12
12
|
const fileName = 'RPMReport'
|
|
13
|
-
const defaultThreshold =
|
|
13
|
+
const defaultThreshold = 0
|
|
14
14
|
|
|
15
15
|
async function createRPMReport (from, to, userData, traccarInstance) {
|
|
16
16
|
console.log('Create RPMReport', from, to)
|
|
@@ -23,6 +23,7 @@ async function createRPMReport (from, to, userData, traccarInstance) {
|
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
const dates = getDates(from, to, userData.user.attributes.timezone || getUserPartner(userData.user).timezone)
|
|
26
|
+
const threshold = getThreshold(userData)
|
|
26
27
|
|
|
27
28
|
let deviceCount = 0
|
|
28
29
|
const sliced = automaticReports.sliceArray(allDevices, 20)
|
|
@@ -31,7 +32,7 @@ async function createRPMReport (from, to, userData, traccarInstance) {
|
|
|
31
32
|
for (const d of devices) {
|
|
32
33
|
const deviceRoute = route.filter(p => p.deviceId === d.id && p.attributes.rpm !== undefined)
|
|
33
34
|
if (deviceRoute.length) {
|
|
34
|
-
allData.devices.push(processDevice(d, deviceRoute, dates))
|
|
35
|
+
allData.devices.push(processDevice(d, deviceRoute, dates, threshold))
|
|
35
36
|
}
|
|
36
37
|
}
|
|
37
38
|
deviceCount += devices.length
|
|
@@ -40,8 +41,10 @@ async function createRPMReport (from, to, userData, traccarInstance) {
|
|
|
40
41
|
return [allData]
|
|
41
42
|
}
|
|
42
43
|
|
|
43
|
-
|
|
44
|
-
|
|
44
|
+
// the report's own threshold (userData.rpmThreshold), independent of the device highRPM attribute used by other reports
|
|
45
|
+
function getThreshold (userData) {
|
|
46
|
+
const threshold = Number(userData.rpmThreshold)
|
|
47
|
+
return userData.rpmThreshold !== undefined && userData.rpmThreshold !== '' && Number.isFinite(threshold) ? threshold : defaultThreshold
|
|
45
48
|
}
|
|
46
49
|
|
|
47
50
|
function dayKey (date) {
|
|
@@ -49,8 +52,7 @@ function dayKey (date) {
|
|
|
49
52
|
}
|
|
50
53
|
|
|
51
54
|
// time between consecutive positions that are both above the threshold, attributed to the day of the first one
|
|
52
|
-
function processDevice (device, deviceRoute, dates) {
|
|
53
|
-
const threshold = getThreshold(device)
|
|
55
|
+
function processDevice (device, deviceRoute, dates, threshold = defaultThreshold) {
|
|
54
56
|
const days = dates.map(date => ({ date, time: 0 }))
|
|
55
57
|
calculateRPMSections(deviceRoute, threshold).forEach(section => {
|
|
56
58
|
for (let i = 1; i < section.length; i++) {
|
package/src/tests/rpm.test.js
CHANGED
|
@@ -17,17 +17,28 @@ describe('RPM_Report', function () {
|
|
|
17
17
|
position(new Date(2026, 9, 2, 9, 0), 2600),
|
|
18
18
|
position(new Date(2026, 9, 2, 9, 0, 30), 2600)
|
|
19
19
|
]
|
|
20
|
-
it('sums time above the
|
|
21
|
-
const result = processDevice({ id: 1, attributes: {} }, route, dates)
|
|
20
|
+
it('sums time above the threshold per day', () => {
|
|
21
|
+
const result = processDevice({ id: 1, attributes: {} }, route, dates, 2500)
|
|
22
22
|
assert.equal(result.threshold, 2500)
|
|
23
23
|
assert.equal(result.days[0].time, 3 * 60 * 1000)
|
|
24
24
|
assert.equal(result.days[1].time, 30 * 1000)
|
|
25
25
|
assert.equal(result.time, 3 * 60 * 1000 + 30 * 1000)
|
|
26
26
|
})
|
|
27
|
-
it('
|
|
28
|
-
const result = processDevice({ id: 1, attributes: { highRPM: 2650 } }, route, dates)
|
|
29
|
-
assert.equal(result.threshold,
|
|
30
|
-
assert.equal(result.days[0].time,
|
|
31
|
-
|
|
27
|
+
it('ignores the device highRPM attribute', () => {
|
|
28
|
+
const result = processDevice({ id: 1, attributes: { highRPM: 2650 } }, route, dates, 2500)
|
|
29
|
+
assert.equal(result.threshold, 2500)
|
|
30
|
+
assert.equal(result.days[0].time, 3 * 60 * 1000)
|
|
31
|
+
})
|
|
32
|
+
it('defaults to rpm > 0 (engine running time)', () => {
|
|
33
|
+
const idleRoute = [
|
|
34
|
+
position(new Date(2026, 9, 1, 8, 0), 0),
|
|
35
|
+
position(new Date(2026, 9, 1, 8, 10), 800),
|
|
36
|
+
position(new Date(2026, 9, 1, 8, 20), 2000),
|
|
37
|
+
position(new Date(2026, 9, 1, 8, 30), 0),
|
|
38
|
+
position(new Date(2026, 9, 1, 8, 40), 0)
|
|
39
|
+
]
|
|
40
|
+
const result = processDevice({ id: 1, attributes: {} }, idleRoute, dates)
|
|
41
|
+
assert.equal(result.threshold, 0)
|
|
42
|
+
assert.equal(result.days[0].time, 10 * 60 * 1000)
|
|
32
43
|
})
|
|
33
44
|
})
|