fleetmap-reports 1.0.377 → 1.0.378
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/lang/enGB.js +1 -0
- package/lang/esCL.js +1 -0
- package/lang/ptBR.js +1 -0
- package/lang/ptPT.js +1 -0
- package/package.json +1 -1
- package/src/fuelconsumption-report.js +49 -34
- package/src/index.test.js +29 -0
- package/src/refueling-report.js +58 -42
- package/src/tests/index.js +1 -1
- package/src/util/odoo.js +14 -0
package/lang/enGB.js
CHANGED
|
@@ -178,6 +178,7 @@ module.exports = {
|
|
|
178
178
|
titleSpeedingReport: 'Overspeed Report',
|
|
179
179
|
titleZoneReport: 'Zone Report',
|
|
180
180
|
titleRefuelReport: 'Refueling Report',
|
|
181
|
+
titleFuelConsumptionReport: 'Fuel Consumption Report',
|
|
181
182
|
titleFuelDropReport: 'Fuel Drop Report',
|
|
182
183
|
titleEventsReport: 'Events Report',
|
|
183
184
|
titleActivityReport: 'Activity Report',
|
package/lang/esCL.js
CHANGED
|
@@ -172,6 +172,7 @@ module.exports = {
|
|
|
172
172
|
titleSpeedingReport: 'Informe de Excesos de Velocidad',
|
|
173
173
|
titleZoneReport: 'Informe de Zonas',
|
|
174
174
|
titleRefuelingReport: 'Informe de Cargas',
|
|
175
|
+
titleFuelConsumptionReport: 'Informe de Consumos',
|
|
175
176
|
titleFuelDropReport: 'Fuel Drop Report',
|
|
176
177
|
titleEventsReport: 'Informe de Eventos',
|
|
177
178
|
titleActivityReport: 'Activity Report',
|
package/lang/ptBR.js
CHANGED
|
@@ -168,6 +168,7 @@ module.exports = {
|
|
|
168
168
|
titleSpeedingReport: 'Relatório de Excessos de Velocidade',
|
|
169
169
|
titleZoneReport: 'Relatório de Zonas',
|
|
170
170
|
titleRefuelingReport: 'Relatório de Abastecimentos',
|
|
171
|
+
titleFuelConsumptionReport: 'Relatório de Consumos',
|
|
171
172
|
titleFuelDropReport: 'Relatório de Perdas de Combustível',
|
|
172
173
|
titleEventsReport: 'Relatório de Eventos',
|
|
173
174
|
titleActivityReport: 'Relatório de Actividade',
|
package/lang/ptPT.js
CHANGED
|
@@ -174,6 +174,7 @@ module.exports = {
|
|
|
174
174
|
titleSpeedingReport: 'Relatório de Excessos de Velocidade',
|
|
175
175
|
titleZoneReport: 'Relatório de Zonas',
|
|
176
176
|
titleRefuelingReport: 'Relatório de Abastecimentos',
|
|
177
|
+
titleFuelConsumptionReport: 'Relatório de Consumos',
|
|
177
178
|
titleFuelDropReport: 'Relatório de Perdas de Combustível',
|
|
178
179
|
titleEventsReport: 'Relatório de Eventos',
|
|
179
180
|
titleActivityReport: 'Relatório de Actividade',
|
package/package.json
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
|
-
const automaticReports = require("./automaticReports")
|
|
2
|
-
const refuelingReport = require("./refueling-report")
|
|
1
|
+
const automaticReports = require("./automaticReports")
|
|
2
|
+
const refuelingReport = require("./refueling-report")
|
|
3
|
+
const traccarHelper = require("./util/traccar")
|
|
4
|
+
const odoo = require("./util/odoo")
|
|
5
|
+
|
|
6
|
+
|
|
3
7
|
|
|
4
8
|
async function createFuelConsumptionReport(from, to, userData, traccar) {
|
|
5
9
|
console.log('Create FuelConsumption Report')
|
|
@@ -13,66 +17,61 @@ async function createFuelConsumptionReport(from, to, userData, traccar) {
|
|
|
13
17
|
}
|
|
14
18
|
|
|
15
19
|
userData.devices.sort((a, b) => (a.name > b.name) ? 1 : -1)
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
const
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
20
|
+
|
|
21
|
+
const allInOne = await traccarHelper.getAllInOne(traccar, from, to, userData.devices, true, true, false, false)
|
|
22
|
+
const tripsData = allInOne.trips
|
|
23
|
+
const routeData = allInOne.route
|
|
24
|
+
|
|
25
|
+
const fuelServicesData = []
|
|
26
|
+
if(userData.withOdooServices) {
|
|
27
|
+
fuelServicesData.push(...(await odoo.getOdooFuelServices(traccar, from, to)))
|
|
24
28
|
}
|
|
25
|
-
console.log('Trips:'+data.length)
|
|
26
29
|
|
|
27
|
-
if(
|
|
30
|
+
if(tripsData.length === 0) {
|
|
28
31
|
return reportData
|
|
29
32
|
}
|
|
30
33
|
|
|
31
34
|
allData.totalDevices = 0
|
|
32
35
|
|
|
33
36
|
for (const d of userData.devices) {
|
|
34
|
-
const trips =
|
|
37
|
+
const trips = tripsData.filter(t => t.deviceId===d.id)
|
|
38
|
+
const route = routeData.filter(r => r.deviceId===d.id)
|
|
39
|
+
const deviceFuelServices = fuelServicesData.filter(f => d.attributes.odooId === f.vehicle_id[0])
|
|
35
40
|
|
|
36
41
|
if(trips.length > 0) {
|
|
37
|
-
const
|
|
38
|
-
const
|
|
39
|
-
|
|
40
|
-
const refuelingPositions = refuelingReport.calculateRefuelingPositions(d, positions)
|
|
41
|
-
|
|
42
|
-
const groupedRefuelings = refuelingPositions.reduce(
|
|
43
|
-
(entryMap, e) => entryMap.set(e.position.fixTime.substring(0, 10), [...entryMap.get(e.position.fixTime.substring(0, 10))||[], e]),
|
|
42
|
+
const refuelingPositions = await refuelingReport.calculateRefuelingPositions(userData, d, {route: route, trips: trips, fuelServices: deviceFuelServices})
|
|
43
|
+
const groupedRefuelingsByDay = refuelingPositions.reduce(
|
|
44
|
+
(entryMap, e) => entryMap.set(e.date.substring(0, 10), [...entryMap.get(e.date.substring(0, 10))||[], e]),
|
|
44
45
|
new Map()
|
|
45
46
|
)
|
|
46
47
|
|
|
47
48
|
trips.forEach(t => t.startDate = t.startTime.substring(0, 10))
|
|
48
|
-
const
|
|
49
|
+
const groupedTripsByDay = trips.reduce(
|
|
49
50
|
(entryMap, e) => entryMap.set(e.startDate, [...entryMap.get(e.startDate)||[], e]),
|
|
50
51
|
new Map()
|
|
51
52
|
)
|
|
52
53
|
|
|
53
54
|
const days = []
|
|
54
|
-
const keys = Array.from(
|
|
55
|
+
const keys = Array.from(groupedTripsByDay.keys())
|
|
56
|
+
const odooAvgConsumption = userData.withOdooServices ? calculateAvgConsumption(refuelingPositions, trips) : 0
|
|
55
57
|
|
|
56
|
-
keys.forEach(
|
|
57
|
-
const dayTrips =
|
|
58
|
-
const dayRefueling =
|
|
58
|
+
keys.forEach(day => {
|
|
59
|
+
const dayTrips = groupedTripsByDay.get(day)
|
|
60
|
+
const dayRefueling = groupedRefuelingsByDay.get(day)
|
|
59
61
|
const distance = dayTrips.reduce((a, b) => a + b.distance, 0)
|
|
60
|
-
|
|
61
|
-
automaticReports.calculateXpertSpentFuel(key, positions)
|
|
62
|
-
:
|
|
63
|
-
automaticReports.calculateSpentFuel(dayTrips.reduce((a, b) => a + b.spentFuel, 0), d)
|
|
62
|
+
const spentFuel = calculateConsumption(userData, d, day, odooAvgConsumption, { trips: dayTrips, route: route })
|
|
64
63
|
|
|
65
|
-
const
|
|
66
|
-
date:
|
|
64
|
+
const dataRow = {
|
|
65
|
+
date: day,
|
|
67
66
|
distance: distance,
|
|
68
67
|
spentFuel: spentFuel,
|
|
69
|
-
avgConsumption: distance > 0 && spentFuel > 0 ? Math.round(spentFuel * 100 / (distance / 1000)) : 0,
|
|
68
|
+
avgConsumption: userData.withOdooServices ? odooAvgConsumption : distance > 0 && spentFuel > 0 ? Math.round(spentFuel * 100 / (distance / 1000)) : 0,
|
|
70
69
|
endOdometer: dayTrips.slice(-1)[0].endOdometer,
|
|
71
70
|
duration: dayTrips.reduce((a, b) => a + b.duration, 0),
|
|
72
71
|
refueling: (dayRefueling && dayRefueling.length > 0) ? dayRefueling.reduce((a, b) => a + b.diff, 0) : 0,
|
|
73
72
|
}
|
|
74
73
|
|
|
75
|
-
days.push(
|
|
74
|
+
days.push(dataRow)
|
|
76
75
|
})
|
|
77
76
|
|
|
78
77
|
if (days.length > 0) {
|
|
@@ -90,4 +89,20 @@ async function createFuelConsumptionReport(from, to, userData, traccar) {
|
|
|
90
89
|
return reportData
|
|
91
90
|
}
|
|
92
91
|
|
|
93
|
-
|
|
92
|
+
function calculateAvgConsumption(refuelingPositions, trips) {
|
|
93
|
+
const odooTotalfuel = refuelingPositions.reduce((a, b) => a + b.diff, 0)
|
|
94
|
+
const totalKms = trips.reduce((a, b) => a + b.distance, 0)
|
|
95
|
+
return Math.round(odooTotalfuel * 100 / (totalKms / 1000))
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function calculateConsumption(userData, d, day, avgConsumption, data) {
|
|
99
|
+
if (d.attributes.xpert) {
|
|
100
|
+
return automaticReports.calculateXpertSpentFuel(day, data.route)
|
|
101
|
+
}
|
|
102
|
+
if (userData.withOdooServices) {
|
|
103
|
+
return Math.round((avgConsumption * (data.trips.reduce((a, b) => a + b.distance, 0)/1000)) / 100)
|
|
104
|
+
}
|
|
105
|
+
return automaticReports.calculateSpentFuel(data.trips.reduce((a, b) => a + b.spentFuel, 0), d)
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
exports.createFuelConsumptionReport = createFuelConsumptionReport
|
package/src/index.test.js
CHANGED
|
@@ -295,4 +295,33 @@ describe('Test_Reports', function() {
|
|
|
295
295
|
assert.equal(device.refuelings.length, 4)
|
|
296
296
|
|
|
297
297
|
}, 30000)
|
|
298
|
+
it('FuelConsumption Report', async () => {
|
|
299
|
+
const report = await getReports()
|
|
300
|
+
const userData = await report.getUserData()
|
|
301
|
+
userData.devices = userData.devices.filter(d => d.id === 22326)
|
|
302
|
+
const data = await report.fuelConsumptionReport(new Date(2022, 0, 1, 0, 0, 0, 0),
|
|
303
|
+
new Date(2022, 0, 10, 23, 59, 59, 0),
|
|
304
|
+
userData)
|
|
305
|
+
|
|
306
|
+
assert.equal(data.length, 1)
|
|
307
|
+
const device = data[0].devices.find(d => d.device.id===22326)
|
|
308
|
+
console.log(device.days)
|
|
309
|
+
assert.equal(device.days.length, 8)
|
|
310
|
+
assert.equal(device.days[0].spentFuel, 2)
|
|
311
|
+
|
|
312
|
+
}, 30000)
|
|
313
|
+
it('FuelConsumption Odoo Report', async () => {
|
|
314
|
+
const report = await getReports()
|
|
315
|
+
const userData = await report.getUserData()
|
|
316
|
+
userData.withOdooServices = true
|
|
317
|
+
userData.devices = userData.devices.filter(d => d.id === 25508)
|
|
318
|
+
const data = await report.fuelConsumptionReport(new Date(2022, 1, 1, 0, 0, 0, 0),
|
|
319
|
+
new Date(2022, 1, 28, 23, 59, 59, 0),
|
|
320
|
+
userData)
|
|
321
|
+
|
|
322
|
+
assert.equal(data.length, 1)
|
|
323
|
+
const device = data[0].devices.find(d => d.device.id===25508)
|
|
324
|
+
assert.equal(device.days.length, 25)
|
|
325
|
+
|
|
326
|
+
}, 30000)
|
|
298
327
|
})
|
package/src/refueling-report.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
const helpers = require("turf")
|
|
2
|
-
const automaticReports = require("./automaticReports")
|
|
1
|
+
const helpers = require("turf")
|
|
2
|
+
const automaticReports = require("./automaticReports")
|
|
3
3
|
|
|
4
4
|
const positionsToCheck = 15
|
|
5
5
|
|
|
@@ -40,7 +40,7 @@ async function createRefuelingReport(from, to, userData, traccar) {
|
|
|
40
40
|
const positions = data.filter(t => t.deviceId===d.id)
|
|
41
41
|
|
|
42
42
|
if(positions.length > 0) {
|
|
43
|
-
const refuelingPositions = this.calculateRefuelingPositions(d, positions)
|
|
43
|
+
const refuelingPositions = await this.calculateRefuelingPositions(userData, d, {route: positions})
|
|
44
44
|
|
|
45
45
|
if (userData.drivers.length > 0) {
|
|
46
46
|
refuelingPositions.forEach(p => {
|
|
@@ -106,53 +106,69 @@ function coordsDistance(lon1, lat1, lon2, lat2) {
|
|
|
106
106
|
return (helpers.distance(from, to) * 1000)
|
|
107
107
|
}
|
|
108
108
|
|
|
109
|
-
function calculateRefuelingPositions(d,
|
|
109
|
+
async function calculateRefuelingPositions(userData, d, data) {
|
|
110
110
|
const refuelingPositions = []
|
|
111
|
-
let refuelingActivated = false
|
|
112
|
-
let currentValue = null
|
|
113
|
-
let positionsChecked = 0
|
|
114
|
-
let lastRefuelingIndex = 0
|
|
115
|
-
positions.forEach(function(element, index, array){
|
|
116
|
-
if(!element.attributes.ignition && refuelingActivated && !currentValue){
|
|
117
|
-
//Ignition is off and Refueling is Active and there is no current value
|
|
118
|
-
|
|
119
|
-
//Get 5 position before the ignition off to calculate the current value of the fuel tank level
|
|
120
|
-
//lastRefuelingIndex is used to avoid re-analyzing positions prior to a refueling
|
|
121
|
-
const before = (index > 5 ? array.slice((index-6 < lastRefuelingIndex ? lastRefuelingIndex : index-6), index-1) : array.slice(0, index-1)).filter(b => b.attributes.ignition && b.attributes.fuel)
|
|
122
|
-
currentValue = Math.round(before.reduce((a, b) => a + b.attributes.fuel, 0) / before.length )
|
|
123
|
-
|
|
124
|
-
positionsChecked = 0
|
|
125
|
-
}
|
|
126
111
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
const
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
const
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
positionsChecked = positionsToCheck+1 // to reset values
|
|
112
|
+
if(userData.withOdooServices && d.attributes.odooId) {
|
|
113
|
+
console.log(data.fuelServices)
|
|
114
|
+
data.fuelServices.forEach(s => {
|
|
115
|
+
const serviceDate = new Date(s.date)
|
|
116
|
+
serviceDate.setHours(23, 59, 59, 59)
|
|
117
|
+
|
|
118
|
+
const routeBeforeService = data.route.filter(r => new Date(r.fixTime).getTime() < serviceDate.getTime())
|
|
119
|
+
|
|
120
|
+
const quantity = s.notes.split(':')[1].replace(',', '.') || 0
|
|
121
|
+
|
|
122
|
+
if (routeBeforeService.length) {
|
|
123
|
+
refuelingPositions.push({date: s.date, diff: Number.parseFloat(quantity), cost: s.amount})
|
|
140
124
|
}
|
|
125
|
+
})
|
|
126
|
+
} else {
|
|
127
|
+
let refuelingActivated = false
|
|
128
|
+
let currentValue = null
|
|
129
|
+
let positionsChecked = 0
|
|
130
|
+
let lastRefuelingIndex = 0
|
|
131
|
+
data.route.forEach(function (element, index, array) {
|
|
132
|
+
if (!element.attributes.ignition && refuelingActivated && !currentValue) {
|
|
133
|
+
//Ignition is off and Refueling is Active and there is no current value
|
|
134
|
+
|
|
135
|
+
//Get 5 position before the ignition off to calculate the current value of the fuel tank level
|
|
136
|
+
//lastRefuelingIndex is used to avoid re-analyzing positions prior to a refueling
|
|
137
|
+
const before = (index > 5 ? array.slice((index - 6 < lastRefuelingIndex ? lastRefuelingIndex : index - 6), index - 1) : array.slice(0, index - 1)).filter(b => b.attributes.ignition && b.attributes.fuel)
|
|
138
|
+
currentValue = Math.round(before.reduce((a, b) => a + b.attributes.fuel, 0) / before.length)
|
|
141
139
|
|
|
142
|
-
if(positionsChecked > positionsToCheck) {
|
|
143
|
-
refuelingActivated = false
|
|
144
|
-
currentValue = null
|
|
145
140
|
positionsChecked = 0
|
|
146
|
-
} else {
|
|
147
|
-
positionsChecked++
|
|
148
141
|
}
|
|
149
|
-
}
|
|
150
142
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
143
|
+
if (element.attributes.ignition && refuelingActivated && currentValue) {
|
|
144
|
+
//Ignition is on and Refueling is Active and there is a current value
|
|
145
|
+
//Calculate the value of the fuel tank level after turning the ignition back on
|
|
146
|
+
const after = (array.length > index + 5 ? array.slice(index, index + 1) : array.slice(index)).filter(b => b.attributes.ignition && b.attributes.fuel)
|
|
147
|
+
const newValue = Math.round(after.reduce((a, b) => a + b.attributes.fuel, 0) / after.length)
|
|
148
|
+
|
|
149
|
+
const diff = automaticReports.calculateFuelDiff(currentValue, newValue, d)
|
|
150
|
+
if (diff > 20) {
|
|
151
|
+
//New refueling detected
|
|
152
|
+
const value = Math.round(diff * d.attributes.fuel_tank_capacity / 100)
|
|
153
|
+
refuelingPositions.push({position: element, date: element.fixTime, diff: value})
|
|
154
|
+
lastRefuelingIndex = index
|
|
155
|
+
positionsChecked = positionsToCheck + 1 // to reset values
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
if (positionsChecked > positionsToCheck) {
|
|
159
|
+
refuelingActivated = false
|
|
160
|
+
currentValue = null
|
|
161
|
+
positionsChecked = 0
|
|
162
|
+
} else {
|
|
163
|
+
positionsChecked++
|
|
164
|
+
}
|
|
165
|
+
}
|
|
155
166
|
|
|
167
|
+
if (element.attributes.ignition && !refuelingActivated) {
|
|
168
|
+
refuelingActivated = true
|
|
169
|
+
}
|
|
170
|
+
})
|
|
171
|
+
}
|
|
156
172
|
return refuelingPositions
|
|
157
173
|
}
|
|
158
174
|
|
package/src/tests/index.js
CHANGED
|
@@ -16,7 +16,7 @@ axiosCookieJarSupport(axios)
|
|
|
16
16
|
|
|
17
17
|
const getReports = async() => {
|
|
18
18
|
await new SessionApi(traccarConfig, null, axios).sessionPost(process.env.email, process.env.password)
|
|
19
|
-
return new Index(traccarConfig, axios)
|
|
19
|
+
return new Index(traccarConfig, axios, cookieJar)
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
exports.getReports = getReports
|
package/src/util/odoo.js
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
|
|
2
|
+
async function getOdooFuelServices(traccar, from, to) {
|
|
3
|
+
const url = `/odoo/reports/refuelingServices?startDate=${from.toDateString()}&endDate=${to.toDateString()}`
|
|
4
|
+
|
|
5
|
+
const {data} = await traccar.axios.get(url,{
|
|
6
|
+
jar: traccar.cookieJar,
|
|
7
|
+
withCredentials: true })
|
|
8
|
+
|
|
9
|
+
console.log(data)
|
|
10
|
+
|
|
11
|
+
return data
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
exports.getOdooFuelServices = getOdooFuelServices
|