fleetmap-reports 2.0.375 → 2.0.377

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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fleetmap-reports",
3
- "version": "2.0.375",
3
+ "version": "2.0.377",
4
4
  "description": "",
5
5
  "main": "src/index.js",
6
6
  "scripts": {
@@ -12,16 +12,6 @@ const { calculateConsumption, calculateTotalFuelFromSensor } = require('./util/f
12
12
  const { getSummary } = require('./util/traccar')
13
13
  const { calculatePartialTrip } = require('./util/trips')
14
14
 
15
- // --- Timezone-consistent day bucketing (fuel-consumption report only) --------
16
- // The shared getDates / getSummaryByDay / getDataByDay helpers resolve calendar
17
- // day boundaries from the JS engine's timezone when they run in the browser, but
18
- // from the user's configured timezone when they run on the server. So the same
19
- // report computed client-side and server-side attributes midnight-straddling
20
- // trips (and their distance / fuel / duration) to different days. The helpers
21
- // below always key off the user's timezone, matching the server, so both
22
- // execution paths produce identical daily figures. They are local on purpose -
23
- // changing the shared helpers would move every other report's numbers.
24
-
25
15
  function getReportTimezone (userData) {
26
16
  const tz = userData.user.attributes.timezone || getUserPartner(userData.user).timezone
27
17
  try {
@@ -84,24 +74,23 @@ function getDayWindow (date, tz) {
84
74
  return [zonedToUTC(year, month, day, 0, 0, 0, tz), zonedToUTC(year, month, day, 23, 59, 59, tz)]
85
75
  }
86
76
 
87
- // Local, timezone-consistent equivalent of getSummaryByDay for this report.
88
- async function getReportSummaryByDay (dates, userData, traccar, devices, tz, from, to) {
77
+ async function getReportSummaryByDay (dates, userData, traccar, devices, tz, reportFrom, reportTo, firstDate, lastDate) {
89
78
  const days = []
90
- for (const [index, date] of dates.entries()) {
79
+ for (const date of dates) {
91
80
  const { year, month, day } = zonedParts(date, tz)
92
81
 
93
82
  let startDate = zonedToUTC(year, month, day, ...parseHMS(userData.dayHours.startTime), tz)
94
- if (index === 0) {
95
- const fromParts = zonedParts(from, tz)
96
- const fromClamp = zonedToUTC(year, month, day, fromParts.hour, fromParts.minute, fromParts.second, tz)
97
- startDate = startDate.getTime() > fromClamp.getTime() ? startDate : fromClamp
83
+ if (date.getTime() === firstDate.getTime()) {
84
+ const p = zonedParts(reportFrom, tz)
85
+ const clamp = zonedToUTC(year, month, day, p.hour, p.minute, p.second, tz)
86
+ if (clamp.getTime() > startDate.getTime()) startDate = clamp
98
87
  }
99
88
 
100
89
  let endDate = zonedToUTC(year, month, day, ...parseHMS(userData.dayHours.endTime), tz)
101
- if (index === dates.length - 1) {
102
- const toParts = zonedParts(to, tz)
103
- const toClamp = zonedToUTC(year, month, day, toParts.hour, toParts.minute, toParts.second, tz)
104
- endDate = endDate.getTime() < toClamp.getTime() ? endDate : toClamp
90
+ if (date.getTime() === lastDate.getTime()) {
91
+ const p = zonedParts(reportTo, tz)
92
+ const clamp = zonedToUTC(year, month, day, p.hour, p.minute, p.second, tz)
93
+ if (clamp.getTime() < endDate.getTime()) endDate = clamp
105
94
  }
106
95
 
107
96
  const summary = await getSummary(traccar, startDate, endDate, devices)
@@ -154,22 +143,19 @@ async function createFuelConsumptionReport (from, to, userData, traccar) {
154
143
 
155
144
  const daysByDevice = new Map()
156
145
 
157
- // Process the period in chunks of whole calendar days (computed once up front, then grouped) so
158
- // we never hold more than ~a week of raw GPS positions for the whole device set in memory at
159
- // once - a single busy device can have tens of thousands of positions over a month. Chunking the
160
- // already-computed dates list, rather than re-deriving dates per raw millisecond sub-range, avoids
161
- // any risk of a boundary day being counted twice (or dropped) due to timezone rounding.
162
146
  const timezone = getReportTimezone(userData)
163
147
  const allDates = getReportDates(from, to, timezone)
164
148
  const dateGroups = automaticReports.sliceArray(allDates, 7)
149
+ const firstDate = allDates[0]
150
+ const lastDate = allDates[allDates.length - 1]
165
151
 
166
152
  for (let chunkIndex = 0; chunkIndex < dateGroups.length; chunkIndex++) {
167
153
  const dates = dateGroups[chunkIndex]
168
- const chunkFrom = chunkIndex === 0 ? from : dates[0]
169
- const chunkTo = chunkIndex === dateGroups.length - 1 ? to : dateGroups[chunkIndex + 1][0]
154
+ const chunkFrom = chunkIndex === 0 ? from : getDayWindow(dates[0], timezone)[0]
155
+ const chunkTo = chunkIndex === dateGroups.length - 1 ? to : getDayWindow(dates[dates.length - 1], timezone)[1]
170
156
 
171
157
  const allInOne = await traccarHelper.getAllInOne(traccar, chunkFrom, chunkTo, devices, true, true, true, false)
172
- const summary = await getReportSummaryByDay(dates, userData, traccar, devices, timezone, chunkFrom, chunkTo)
158
+ const summary = await getReportSummaryByDay(dates, userData, traccar, devices, timezone, from, to, firstDate, lastDate)
173
159
 
174
160
  const fuelServicesData = []
175
161
  if (devices.some(d => d.attributes.odooId)) {
package/src/util/trips.js CHANGED
@@ -133,6 +133,9 @@ function isInsideTimetable (t, userData) {
133
133
  function updateTrip (device, t, route) {
134
134
  route = route.filter(p => p && p.attributes.ignition)
135
135
  if (!route.length) { return }
136
+ const originalDistance = t.distance
137
+ const originalDuration = t.duration
138
+ const originalSpentFuel = t.spentFuel
136
139
  const distance = calculateDistance(route, device ? device.attributes['report.ignoreOdometer'] : true) * 1000
137
140
  t.distance = distance
138
141
  t.duration = new Date(route[route.length - 1].fixTime).getTime() - new Date(route[0].fixTime).getTime()
@@ -140,6 +143,13 @@ function updateTrip (device, t, route) {
140
143
  t.averageSpeed = distance > 0 ? ((route.reduce((a, b) => a + b.speed, 0)) / route.length) : 0
141
144
  t.startOdometer = device && !device.attributes['report.ignoreOdometer'] && route[0].attributes.odometer ? route[0].attributes.odometer : route[0].attributes.totalDistance
142
145
  t.endOdometer = device && !device.attributes['report.ignoreOdometer'] && route[route.length - 1].attributes.odometer ? route[route.length - 1].attributes.odometer : route[route.length - 1].attributes.totalDistance
146
+ if (originalSpentFuel > 0) {
147
+ // A trip clipped at a report day boundary keeps only part of its route, so its
148
+ // share of the trip's total fuel must be prorated too, otherwise the full trip's
149
+ // fuel gets counted on both days it was split across.
150
+ const ratio = originalDistance > 0 ? distance / originalDistance : (originalDuration > 0 ? t.duration / originalDuration : 1)
151
+ t.spentFuel = originalSpentFuel * Math.min(Math.max(ratio, 0), 1)
152
+ }
143
153
  }
144
154
 
145
155
  function getUncompletedTrip (device, deviceRoute, deviceTrips) {