fleetmap-reports 2.0.357 → 2.0.358

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 (87) hide show
  1. package/.claude/worktrees/eager-stonebraker-c08ac7/.eslintrc.js +15 -0
  2. package/.claude/worktrees/eager-stonebraker-c08ac7/lang/enGB.js +309 -0
  3. package/.claude/worktrees/eager-stonebraker-c08ac7/lang/esCL.js +297 -0
  4. package/.claude/worktrees/eager-stonebraker-c08ac7/lang/frFR.json +324 -0
  5. package/.claude/worktrees/eager-stonebraker-c08ac7/lang/index.js +35 -0
  6. package/.claude/worktrees/eager-stonebraker-c08ac7/lang/ptBR.js +301 -0
  7. package/.claude/worktrees/eager-stonebraker-c08ac7/lang/ptPT.js +310 -0
  8. package/.claude/worktrees/eager-stonebraker-c08ac7/package-lock.json +11944 -0
  9. package/.claude/worktrees/eager-stonebraker-c08ac7/package.json +45 -0
  10. package/.claude/worktrees/eager-stonebraker-c08ac7/src/activity-report.js +822 -0
  11. package/.claude/worktrees/eager-stonebraker-c08ac7/src/automaticReports.js +34 -0
  12. package/.claude/worktrees/eager-stonebraker-c08ac7/src/carbone/index.js +13 -0
  13. package/.claude/worktrees/eager-stonebraker-c08ac7/src/custom/vistawasteActivity-report.js +544 -0
  14. package/.claude/worktrees/eager-stonebraker-c08ac7/src/events-report.js +360 -0
  15. package/.claude/worktrees/eager-stonebraker-c08ac7/src/fuel-consumption-report.js +296 -0
  16. package/.claude/worktrees/eager-stonebraker-c08ac7/src/fuel-drop-report.js +128 -0
  17. package/.claude/worktrees/eager-stonebraker-c08ac7/src/here.js +172 -0
  18. package/.claude/worktrees/eager-stonebraker-c08ac7/src/idle-report.js +403 -0
  19. package/.claude/worktrees/eager-stonebraker-c08ac7/src/index.js +244 -0
  20. package/.claude/worktrees/eager-stonebraker-c08ac7/src/kms-report.js +549 -0
  21. package/.claude/worktrees/eager-stonebraker-c08ac7/src/location-report.js +496 -0
  22. package/.claude/worktrees/eager-stonebraker-c08ac7/src/machines-report.js +278 -0
  23. package/.claude/worktrees/eager-stonebraker-c08ac7/src/partnerReports/afriquia.js +129 -0
  24. package/.claude/worktrees/eager-stonebraker-c08ac7/src/partnerReports/dailyuse-report.js +80 -0
  25. package/.claude/worktrees/eager-stonebraker-c08ac7/src/partnerReports/delayedstart-report.js +223 -0
  26. package/.claude/worktrees/eager-stonebraker-c08ac7/src/partnerReports/driverranking-report.js +328 -0
  27. package/.claude/worktrees/eager-stonebraker-c08ac7/src/partnerReports/gpsjump-report.js +61 -0
  28. package/.claude/worktrees/eager-stonebraker-c08ac7/src/partnerReports/performance-report.js +359 -0
  29. package/.claude/worktrees/eager-stonebraker-c08ac7/src/refueling-report.js +345 -0
  30. package/.claude/worktrees/eager-stonebraker-c08ac7/src/reportStyle.js +34 -0
  31. package/.claude/worktrees/eager-stonebraker-c08ac7/src/sensor-report.js +447 -0
  32. package/.claude/worktrees/eager-stonebraker-c08ac7/src/speeding-report.js +663 -0
  33. package/.claude/worktrees/eager-stonebraker-c08ac7/src/stop-report.js +242 -0
  34. package/.claude/worktrees/eager-stonebraker-c08ac7/src/templates/ActivityReportTemplate.mjml +51 -0
  35. package/.claude/worktrees/eager-stonebraker-c08ac7/src/templates/EmptyReportTemplate.mjml +25 -0
  36. package/.claude/worktrees/eager-stonebraker-c08ac7/src/templates/EventsReportTemplate.mjml +75 -0
  37. package/.claude/worktrees/eager-stonebraker-c08ac7/src/templates/FuelDropReportTemplate.mjml +60 -0
  38. package/.claude/worktrees/eager-stonebraker-c08ac7/src/templates/IdleReportTemplate.mjml +54 -0
  39. package/.claude/worktrees/eager-stonebraker-c08ac7/src/templates/KmsReportGroupByDayTemplate.mjml +43 -0
  40. package/.claude/worktrees/eager-stonebraker-c08ac7/src/templates/KmsReportTemplate.mjml +44 -0
  41. package/.claude/worktrees/eager-stonebraker-c08ac7/src/templates/LocationReportTemplate.mjml +37 -0
  42. package/.claude/worktrees/eager-stonebraker-c08ac7/src/templates/RefuelingReportTemplate.mjml +60 -0
  43. package/.claude/worktrees/eager-stonebraker-c08ac7/src/templates/SensorReportTemplate.mjml +23 -0
  44. package/.claude/worktrees/eager-stonebraker-c08ac7/src/templates/SpeedingReportTemplate.mjml +80 -0
  45. package/.claude/worktrees/eager-stonebraker-c08ac7/src/templates/TripReportTemplate.mjml +97 -0
  46. package/.claude/worktrees/eager-stonebraker-c08ac7/src/templates/ZoneReportTemplate.mjml +52 -0
  47. package/.claude/worktrees/eager-stonebraker-c08ac7/src/templates/report.mjml +96 -0
  48. package/.claude/worktrees/eager-stonebraker-c08ac7/src/tests/activity.test.js +99 -0
  49. package/.claude/worktrees/eager-stonebraker-c08ac7/src/tests/carbone.test.js +20 -0
  50. package/.claude/worktrees/eager-stonebraker-c08ac7/src/tests/events.test.js +30 -0
  51. package/.claude/worktrees/eager-stonebraker-c08ac7/src/tests/fuel.test.js +35 -0
  52. package/.claude/worktrees/eager-stonebraker-c08ac7/src/tests/idle.test.js +16 -0
  53. package/.claude/worktrees/eager-stonebraker-c08ac7/src/tests/index.js +24 -0
  54. package/.claude/worktrees/eager-stonebraker-c08ac7/src/tests/index.test.js +203 -0
  55. package/.claude/worktrees/eager-stonebraker-c08ac7/src/tests/kms.test.js +74 -0
  56. package/.claude/worktrees/eager-stonebraker-c08ac7/src/tests/performance.test.js +36 -0
  57. package/.claude/worktrees/eager-stonebraker-c08ac7/src/tests/positions.test.js +67 -0
  58. package/.claude/worktrees/eager-stonebraker-c08ac7/src/tests/speeding.test.js +120 -0
  59. package/.claude/worktrees/eager-stonebraker-c08ac7/src/tests/translations.test.js +21 -0
  60. package/.claude/worktrees/eager-stonebraker-c08ac7/src/tests/trips.test.js +140 -0
  61. package/.claude/worktrees/eager-stonebraker-c08ac7/src/tests/zones.test.js +162 -0
  62. package/.claude/worktrees/eager-stonebraker-c08ac7/src/trip-report.js +641 -0
  63. package/.claude/worktrees/eager-stonebraker-c08ac7/src/util/device.js +13 -0
  64. package/.claude/worktrees/eager-stonebraker-c08ac7/src/util/driver.js +90 -0
  65. package/.claude/worktrees/eager-stonebraker-c08ac7/src/util/fuel.js +102 -0
  66. package/.claude/worktrees/eager-stonebraker-c08ac7/src/util/geofence.js +52 -0
  67. package/.claude/worktrees/eager-stonebraker-c08ac7/src/util/odoo.js +18 -0
  68. package/.claude/worktrees/eager-stonebraker-c08ac7/src/util/parallel.js +64 -0
  69. package/.claude/worktrees/eager-stonebraker-c08ac7/src/util/pdfDocument.js +140 -0
  70. package/.claude/worktrees/eager-stonebraker-c08ac7/src/util/route.js +78 -0
  71. package/.claude/worktrees/eager-stonebraker-c08ac7/src/util/timetable.js +30 -0
  72. package/.claude/worktrees/eager-stonebraker-c08ac7/src/util/traccar.js +213 -0
  73. package/.claude/worktrees/eager-stonebraker-c08ac7/src/util/traccar.worker.js +49 -0
  74. package/.claude/worktrees/eager-stonebraker-c08ac7/src/util/trips.js +270 -0
  75. package/.claude/worktrees/eager-stonebraker-c08ac7/src/util/utils.js +358 -0
  76. package/.claude/worktrees/eager-stonebraker-c08ac7/src/util/worker.js +14 -0
  77. package/.claude/worktrees/eager-stonebraker-c08ac7/src/util/xpert.js +58 -0
  78. package/.claude/worktrees/eager-stonebraker-c08ac7/src/word/index.js +95 -0
  79. package/.claude/worktrees/eager-stonebraker-c08ac7/src/zone-report.js +904 -0
  80. package/lang/enGB.js +18 -0
  81. package/lang/esCL.js +18 -0
  82. package/lang/frFR.json +7 -1
  83. package/lang/ptBR.js +18 -0
  84. package/lang/ptPT.js +18 -0
  85. package/package.json +1 -1
  86. package/src/partnerReports/driverranking-report.js +149 -49
  87. package/src/util/pdfDocument.js +2 -1
package/lang/enGB.js CHANGED
@@ -193,7 +193,25 @@ module.exports = {
193
193
  titleIdleReport: 'Idle Report',
194
194
  titleMachinesReport: 'Machines Report',
195
195
  titleStopReport: 'Stop Report',
196
+ titleDriverRankingReport: 'Driver Ranking Report',
196
197
  titleSensorReport: 'Sensor Report',
198
+ rank: 'Rank',
199
+ grade: 'Grade',
200
+ safetyScore: 'Safety',
201
+ efficiencyScore: 'Efficiency',
202
+ complianceScore: 'Compliance',
203
+ score: 'Overall Score',
204
+ avgFuelConsumption: 'Avg. L/100km',
205
+ highEngineRPM: 'High RPM',
206
+ hardBraking: 'Hard Braking',
207
+ hardAcceleration: 'Hard Acceleration',
208
+ hardCornering: 'Hard Cornering',
209
+ overspeedAlarm: 'Speeding',
210
+ geofenceAlarm: 'Seatbelt',
211
+ continuesDrivingAlarm: 'Continuous Driving',
212
+ reverseAlarm: 'Reverse Gear',
213
+ otherAlarm: 'Other',
214
+ detailedMetrics: 'Detailed metrics',
197
215
  from: 'From',
198
216
  to: 'to',
199
217
  headerStartAddress: 'Start address',
package/lang/esCL.js CHANGED
@@ -183,7 +183,25 @@ module.exports = {
183
183
  titleIdleReport: 'Informe de Ralenti',
184
184
  titleMachinesReport: 'Informe de Maquinas',
185
185
  titleStopReport: 'Informe de Paradas',
186
+ titleDriverRankingReport: 'Informe de Ranking de Conductores',
186
187
  titleSensorReport: 'Informe de Sensores',
188
+ rank: 'Posición',
189
+ grade: 'Calificación',
190
+ safetyScore: 'Seguridad',
191
+ efficiencyScore: 'Eficiencia',
192
+ complianceScore: 'Cumplimiento',
193
+ score: 'Puntuación General',
194
+ avgFuelConsumption: 'Consumo Prom. L/100km',
195
+ highEngineRPM: 'RPM Alto',
196
+ hardBraking: 'Frenado Brusco',
197
+ hardAcceleration: 'Aceleración Brusca',
198
+ hardCornering: 'Giro Brusco',
199
+ overspeedAlarm: 'Exceso de Velocidad',
200
+ geofenceAlarm: 'Cinturón de Seguridad',
201
+ continuesDrivingAlarm: 'Conducción Continua',
202
+ reverseAlarm: 'Marcha Atrás',
203
+ otherAlarm: 'Otros',
204
+ detailedMetrics: 'Métricas detalladas',
187
205
  from: 'De',
188
206
  to: 'a',
189
207
  headerStartAddress: 'Lugar de início',
package/lang/frFR.json CHANGED
@@ -297,7 +297,13 @@
297
297
  "geofenceAlarm": "Ceinture",
298
298
  "continuesDrivingAlarm": "Cond. Continue",
299
299
  "reverseAlarm": "Marche Arriere",
300
- "otherAlarm": "Other"
300
+ "otherAlarm": "Other",
301
+ "rank": "Rang",
302
+ "grade": "Note",
303
+ "safetyScore": "Sécurité",
304
+ "efficiencyScore": "Efficacité",
305
+ "complianceScore": "Conformité",
306
+ "detailedMetrics": "Métriques détaillées"
301
307
  },
302
308
  "layout": {
303
309
  "deviceOnline": "Appareil en ligne",
package/lang/ptBR.js CHANGED
@@ -185,7 +185,25 @@ module.exports = {
185
185
  titleIdleReport: 'Relatórios de Ralenti',
186
186
  titleMachinesReport: 'Relatório de Máquinas',
187
187
  titleStopReport: 'Relatório de Paragens',
188
+ titleDriverRankingReport: 'Relatório de Ranking de Motoristas',
188
189
  titleSensorReport: 'Relatório de Sensores',
190
+ rank: 'Posição',
191
+ grade: 'Nota',
192
+ safetyScore: 'Segurança',
193
+ efficiencyScore: 'Eficiência',
194
+ complianceScore: 'Conformidade',
195
+ score: 'Pontuação Geral',
196
+ avgFuelConsumption: 'Consumo Médio L/100km',
197
+ highEngineRPM: 'RPM Alta',
198
+ hardBraking: 'Frenagem Brusca',
199
+ hardAcceleration: 'Aceleração Brusca',
200
+ hardCornering: 'Curva Brusca',
201
+ overspeedAlarm: 'Excesso de Velocidade',
202
+ geofenceAlarm: 'Cinto de Segurança',
203
+ continuesDrivingAlarm: 'Condução Contínua',
204
+ reverseAlarm: 'Marcha à Ré',
205
+ otherAlarm: 'Outros',
206
+ detailedMetrics: 'Métricas detalhadas',
189
207
  from: 'De',
190
208
  to: 'a',
191
209
  headerStartAddress: 'Local de início',
package/lang/ptPT.js CHANGED
@@ -191,7 +191,25 @@ module.exports = {
191
191
  titleIdleReport: 'Relatórios de Ralenti',
192
192
  titleMachinesReport: 'Relatório de Máquinas',
193
193
  titleStopReport: 'Relatório de Paragens',
194
+ titleDriverRankingReport: 'Relatório de Ranking de Condutores',
194
195
  titleSensorReport: 'Relatório de Sensores',
196
+ rank: 'Posição',
197
+ grade: 'Nota',
198
+ safetyScore: 'Segurança',
199
+ efficiencyScore: 'Eficiência',
200
+ complianceScore: 'Conformidade',
201
+ score: 'Pontuação Geral',
202
+ avgFuelConsumption: 'Consumo Médio L/100km',
203
+ highEngineRPM: 'RPM Alto',
204
+ hardBraking: 'Travagem Brusca',
205
+ hardAcceleration: 'Aceleração Brusca',
206
+ hardCornering: 'Curva Brusca',
207
+ overspeedAlarm: 'Excesso de Velocidade',
208
+ geofenceAlarm: 'Cinto de Segurança',
209
+ continuesDrivingAlarm: 'Condução Contínua',
210
+ reverseAlarm: 'Marcha-atrás',
211
+ otherAlarm: 'Outros',
212
+ detailedMetrics: 'Métricas detalhadas',
195
213
  from: 'De',
196
214
  to: 'a',
197
215
  headerStartAddress: 'Local de início',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fleetmap-reports",
3
- "version": "2.0.357",
3
+ "version": "2.0.358",
4
4
  "description": "",
5
5
  "main": "src/index.js",
6
6
  "scripts": {
@@ -24,6 +24,39 @@ const continuesDrivingCoefficient = 0.1
24
24
  const reverseCoefficient = 0.1
25
25
  const otherCoefficient = 0.1
26
26
 
27
+ const grades = [
28
+ { min: 90, grade: 'A' },
29
+ { min: 75, grade: 'B' },
30
+ { min: 60, grade: 'C' },
31
+ { min: 40, grade: 'D' },
32
+ { min: -Infinity, grade: 'F' }
33
+ ]
34
+
35
+ // Converts a "the lower the better" metric into a 0-100 score (100 = met target, 0 = twice the target or worse)
36
+ function goodnessScore (actual, target) {
37
+ if (!target || target <= 0) {
38
+ return 100
39
+ }
40
+ return Math.max(0, Math.min(100, 100 * (1 - (actual / target))))
41
+ }
42
+
43
+ // Same as goodnessScore, but for metrics where deviating from the target in either direction is bad (e.g. distance)
44
+ function deviationScore (actual, target) {
45
+ if (!target || target <= 0) {
46
+ return 100
47
+ }
48
+ return Math.max(0, Math.min(100, 100 * (1 - (Math.abs(actual - target) / target))))
49
+ }
50
+
51
+ function weightedAverage (metrics) {
52
+ const totalWeight = metrics.reduce((sum, m) => sum + m.weight, 0)
53
+ return metrics.reduce((sum, m) => sum + (m.value * m.weight), 0) / totalWeight
54
+ }
55
+
56
+ function toGrade (score) {
57
+ return grades.find(g => score >= g.min).grade
58
+ }
59
+
27
60
  async function create (from, to, userData, traccar) {
28
61
  const reportData = {
29
62
  drivers: []
@@ -74,24 +107,30 @@ async function create (from, to, userData, traccar) {
74
107
 
75
108
  const allDriversData = (Array.from(driversData.values())).filter(d => d.distance > 0)
76
109
  allDriversData.forEach(d => {
77
- const distanceScore = (Math.abs(d.distance - userData.distanceTarget) / userData.distanceTarget) * distanceCoefficient
78
- const rpmScore = ((d.highEngineRPM / 60000) / userData.rpmTarget) * rpmCoefficient
79
- const hardBrakingScore = (d.hardBraking / userData.hardBrakingTarget) * hardBrakingCoefficient
80
- const hardAccelerationScore = (d.hardAcceleration / userData.hardAccelerationTarget) * hardAccelerationCoefficient
81
- const hardCorneringScore = (d.hardCornering / userData.hardCorneringTarget) * hardCorneringCoefficient
82
- const overspeedScore = (d.overspeed / userData.overspeedTarget) * overspeedCoefficient
83
- const consumptionScore = (getCanAvgConsumption(d.distance, d.spentFuel).byKms / userData.fuelConsumptionTarget) * consumptionCoefficient
84
- const geofenceAlarmScore = (d.geofenceAlarm / userData.geofenceTarget) * geofenceCoefficient
85
- const continuesDrivingAlarmScore = (d.continuesDrivingAlarm / userData.continuesDrivingTarget) * continuesDrivingCoefficient
86
- const reverseAlarmScore = (d.reverseAlarm / userData.reverseTarget) * reverseCoefficient
87
- const otherAlarmScore = (d.otherAlarm / userData.otherTarget) * otherCoefficient
88
-
89
110
  d.avgFuelConsumption = getCanAvgConsumption(d.distance, d.spentFuel).byKms
90
- d.score = distanceScore + rpmScore + hardBrakingScore + hardAccelerationScore +
91
- hardCorneringScore + consumptionScore + overspeedScore + geofenceAlarmScore +
92
- continuesDrivingAlarmScore + reverseAlarmScore + otherAlarmScore
111
+
112
+ d.safetyScore = weightedAverage([
113
+ { value: goodnessScore(d.hardBraking, userData.hardBrakingTarget), weight: hardBrakingCoefficient },
114
+ { value: goodnessScore(d.hardAcceleration, userData.hardAccelerationTarget), weight: hardAccelerationCoefficient },
115
+ { value: goodnessScore(d.hardCornering, userData.hardCorneringTarget), weight: hardCorneringCoefficient },
116
+ { value: goodnessScore(d.overspeed, userData.overspeedTarget), weight: overspeedCoefficient },
117
+ { value: goodnessScore(d.reverseAlarm, userData.reverseTarget), weight: reverseCoefficient }
118
+ ])
119
+ d.efficiencyScore = weightedAverage([
120
+ { value: deviationScore(d.distance, userData.distanceTarget), weight: distanceCoefficient },
121
+ { value: goodnessScore(d.avgFuelConsumption, userData.fuelConsumptionTarget), weight: consumptionCoefficient },
122
+ { value: goodnessScore(d.highEngineRPM / 60000, userData.rpmTarget), weight: rpmCoefficient }
123
+ ])
124
+ d.complianceScore = weightedAverage([
125
+ { value: goodnessScore(d.geofenceAlarm, userData.geofenceTarget), weight: geofenceCoefficient },
126
+ { value: goodnessScore(d.continuesDrivingAlarm, userData.continuesDrivingTarget), weight: continuesDrivingCoefficient },
127
+ { value: goodnessScore(d.otherAlarm, userData.otherTarget), weight: otherCoefficient }
128
+ ])
129
+ d.overallScore = (d.safetyScore + d.efficiencyScore + d.complianceScore) / 3
130
+ d.grade = toGrade(d.overallScore)
93
131
  })
94
- allDriversData.sort((a, b) => (a.score > b.score) ? 1 : -1)
132
+ allDriversData.sort((a, b) => b.overallScore - a.overallScore)
133
+ allDriversData.forEach((d, index) => { d.rank = index + 1 })
95
134
 
96
135
  reportData.drivers.push(...allDriversData)
97
136
 
@@ -207,7 +246,13 @@ async function exportToExcel (userData, reportData) {
207
246
  }
208
247
 
209
248
  const headers = [
249
+ { label: translations.report.rank, value: 'rank' },
210
250
  { label: translations.report.driver, value: 'name' },
251
+ { label: translations.report.grade, value: 'grade' },
252
+ { label: translations.report.score, value: 'overallScore' },
253
+ { label: translations.report.safetyScore, value: 'safetyScore' },
254
+ { label: translations.report.efficiencyScore, value: 'efficiencyScore' },
255
+ { label: translations.report.complianceScore, value: 'complianceScore' },
211
256
  { label: translations.report.distance, value: 'distance' },
212
257
  { label: translations.report.avgFuelConsumption, value: 'avgFuelConsumption' },
213
258
  { label: translations.report.highEngineRPM, value: 'highEngineRPM' },
@@ -218,16 +263,21 @@ async function exportToExcel (userData, reportData) {
218
263
  { label: translations.report.geofenceAlarm, value: 'geofenceAlarm' },
219
264
  { label: translations.report.continuesDrivingAlarm, value: 'continuesDrivingAlarm' },
220
265
  { label: translations.report.reverseAlarm, value: 'reverseAlarm' },
221
- { label: translations.report.otherAlarm, value: 'otherAlarm' },
222
- { label: translations.report.score, value: 'score' }
266
+ { label: translations.report.otherAlarm, value: 'otherAlarm' }
223
267
  ]
224
268
 
225
269
  let data = []
226
270
  if (reportDataRows) {
227
271
  reportDataRows.forEach(row => {
228
272
  data = data.concat({
273
+ rank: row.rank,
229
274
  name: row.name,
230
- distance: Number(row.distance.toFixed(1)),
275
+ grade: row.grade,
276
+ overallScore: Math.round(row.overallScore),
277
+ safetyScore: Math.round(row.safetyScore),
278
+ efficiencyScore: Math.round(row.efficiencyScore),
279
+ complianceScore: Math.round(row.complianceScore),
280
+ distance: Number((row.distance / 1000).toFixed(1)),
231
281
  avgFuelConsumption: Number(row.avgFuelConsumption.toFixed(2)),
232
282
  highEngineRPM: convertMS(row.highEngineRPM, false),
233
283
  hardBraking: row.hardBraking,
@@ -237,11 +287,9 @@ async function exportToExcel (userData, reportData) {
237
287
  geofenceAlarm: row.geofenceAlarm,
238
288
  continuesDrivingAlarm: row.continuesDrivingAlarm,
239
289
  reverseAlarm: row.reverseAlarm,
240
- otherAlarm: row.otherAlarm,
241
- score: Number(row.score.toFixed(2))
290
+ otherAlarm: row.otherAlarm
242
291
  })
243
292
  })
244
- console.log(data)
245
293
  return {
246
294
  headers,
247
295
  data,
@@ -250,15 +298,33 @@ async function exportToExcel (userData, reportData) {
250
298
  }
251
299
  }
252
300
 
301
+ const gradeColors = {
302
+ A: { fill: [200, 236, 204], text: [30, 110, 40] },
303
+ B: { fill: [223, 240, 196], text: [66, 110, 30] },
304
+ C: { fill: [255, 236, 179], text: [153, 101, 0] },
305
+ D: { fill: [255, 205, 178], text: [166, 74, 0] },
306
+ F: { fill: [255, 205, 210], text: [153, 0, 20] }
307
+ }
308
+
253
309
  async function exportToPDF (userData, reportData) {
254
- console.log('exportDelayedStartReportToPDF')
310
+ console.log('exportDriverRankingReportToPDF')
255
311
 
256
312
  const timezone = userData.user.attributes.timezone
257
313
  const translations = getTranslations(userData)
258
314
  const lang = userData.user.attributes.lang || (isClientSide() && navigator.language)
259
315
  const reportDataRows = reportData.drivers
260
316
 
261
- const headers = [
317
+ const scoreboardHeaders = [
318
+ translations.report.rank,
319
+ translations.report.driver,
320
+ translations.report.grade,
321
+ translations.report.score,
322
+ translations.report.safetyScore,
323
+ translations.report.efficiencyScore,
324
+ translations.report.complianceScore
325
+ ]
326
+
327
+ const detailHeaders = [
262
328
  translations.report.driver,
263
329
  translations.report.distance,
264
330
  translations.report.avgFuelConsumption,
@@ -270,8 +336,7 @@ async function exportToPDF (userData, reportData) {
270
336
  translations.report.geofenceAlarm,
271
337
  translations.report.continuesDrivingAlarm,
272
338
  translations.report.reverseAlarm,
273
- translations.report.otherAlarm,
274
- translations.report.score
339
+ translations.report.otherAlarm
275
340
  ]
276
341
 
277
342
  if (reportDataRows) {
@@ -282,30 +347,66 @@ async function exportToPDF (userData, reportData) {
282
347
  doc.setFontSize(13)
283
348
  doc.text(convertToLocaleString(reportData.from, lang, timezone, userData.user) + ' - ' + convertToLocaleString(reportData.to, lang, timezone, userData.user), 20, 25)
284
349
 
285
- const data = []
286
- reportDataRows.forEach(function (row) {
287
- const temp = [
288
- row.name,
289
- row.distance.toFixed(1),
290
- row.avgFuelConsumption.toFixed(2),
291
- convertMS(row.highEngineRPM, false),
292
- row.hardBraking,
293
- row.hardAcceleration,
294
- row.hardCornering,
295
- row.overspeed,
296
- row.geofenceAlarm,
297
- row.continuesDrivingAlarm,
298
- row.reverseAlarm,
299
- row.otherAlarm,
300
- row.score.toFixed(2)
301
- ]
302
- data.push(temp)
303
- })
304
- const footValues = []
350
+ const scoreboardData = reportDataRows.map(row => [
351
+ row.rank,
352
+ row.name,
353
+ row.grade,
354
+ Math.round(row.overallScore),
355
+ Math.round(row.safetyScore),
356
+ Math.round(row.efficiencyScore),
357
+ Math.round(row.complianceScore)
358
+ ])
305
359
 
306
360
  const style = getStyle(getUserPartner(userData.user))
307
361
 
308
- addTable(doc, headers, data, footValues, style, 40, {
362
+ addTable(doc, scoreboardHeaders, scoreboardData, [], style, 40, {
363
+ 0: { halign: 'center' },
364
+ 2: { halign: 'center' },
365
+ 3: { halign: 'right' },
366
+ 4: { halign: 'right' },
367
+ 5: { halign: 'right' },
368
+ 6: { halign: 'right' }
369
+ }, (cellData) => {
370
+ if (cellData.section !== 'body') {
371
+ return
372
+ }
373
+ const row = reportDataRows[cellData.row.index]
374
+ if (!row) {
375
+ return
376
+ }
377
+ if (cellData.column.index === 0) {
378
+ cellData.cell.styles.fontStyle = row.rank <= 3 ? 'bold' : 'normal'
379
+ }
380
+ if (cellData.column.index === 2 || cellData.column.index === 3) {
381
+ const colors = gradeColors[row.grade]
382
+ cellData.cell.styles.fillColor = colors.fill
383
+ cellData.cell.styles.textColor = colors.text
384
+ cellData.cell.styles.fontStyle = 'bold'
385
+ }
386
+ })
387
+
388
+ const detailData = reportDataRows.map(row => [
389
+ row.name,
390
+ (row.distance / 1000).toFixed(1),
391
+ row.avgFuelConsumption.toFixed(2),
392
+ convertMS(row.highEngineRPM, false),
393
+ row.hardBraking,
394
+ row.hardAcceleration,
395
+ row.hardCornering,
396
+ row.overspeed,
397
+ row.geofenceAlarm,
398
+ row.continuesDrivingAlarm,
399
+ row.reverseAlarm,
400
+ row.otherAlarm
401
+ ])
402
+
403
+ const detailStartY = doc.lastAutoTable.finalY + 12
404
+ doc.setFontSize(10)
405
+ doc.setFont('helvetica', 'bold')
406
+ doc.text(translations.report.detailedMetrics, 15, detailStartY - 4)
407
+ doc.setFont('helvetica', 'normal')
408
+
409
+ addTable(doc, detailHeaders, detailData, [], { ...style, bodyFontSize: 7, headerFontSize: 8 }, detailStartY, {
309
410
  1: { halign: 'right' },
310
411
  2: { halign: 'right' },
311
412
  3: { halign: 'right' },
@@ -316,8 +417,7 @@ async function exportToPDF (userData, reportData) {
316
417
  8: { halign: 'right' },
317
418
  9: { halign: 'right' },
318
419
  10: { halign: 'right' },
319
- 11: { halign: 'right' },
320
- 12: { halign: 'right' }
420
+ 11: { halign: 'right' }
321
421
  })
322
422
  return doc
323
423
  }
@@ -61,7 +61,7 @@ async function headerFromUser (doc, title, user, orientation = 'l') {
61
61
  return header(doc, title, partner && partner.host, getStyle(partner), orientation)
62
62
  }
63
63
 
64
- function addTable (doc, headers, data, footValues, style, startY, columnStyles = {}) {
64
+ function addTable (doc, headers, data, footValues, style, startY, columnStyles = {}, didParseCell) {
65
65
  doc.autoTable({
66
66
  head: [headers],
67
67
  body: data,
@@ -97,6 +97,7 @@ function addTable (doc, headers, data, footValues, style, startY, columnStyles =
97
97
  })
98
98
  })
99
99
  },
100
+ ...(didParseCell ? { didParseCell } : {}),
100
101
  columnStyles,
101
102
  startY
102
103
  })