@fleetbase/fleetops-engine 0.6.17 → 0.6.18
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/addon/components/layout/fleet-ops-sidebar.hbs +25 -0
- package/addon/templates/virtual.hbs +3 -3
- package/composer.json +3 -2
- package/extension.json +1 -1
- package/package.json +1 -1
- package/server/migrations/2025_08_28_054920_create_warranties_table.php +56 -0
- package/server/migrations/2025_08_28_054921_create_telematics_table.php +60 -0
- package/server/migrations/2025_08_28_054922_create_assets_table.php +94 -0
- package/server/migrations/2025_08_28_054925_create_devices_table.php +190 -0
- package/server/migrations/2025_08_28_054926_create_device_events_table.php +99 -0
- package/server/migrations/2025_08_28_054926_create_sensors_table.php +62 -0
- package/server/migrations/2025_08_28_054927_create_parts_table.php +73 -0
- package/server/migrations/2025_08_28_054929_create_equipments_table.php +58 -0
- package/server/migrations/2025_08_28_054930_create_work_orders_table.php +85 -0
- package/server/migrations/2025_08_28_054931_create_maintenances_table.php +79 -0
- package/server/migrations/2025_08_28_082002_update_vehicles_table_telematics.php +60 -0
- package/server/src/Http/Controllers/Api/v1/OrderController.php +6 -1
- package/server/src/Http/Resources/v1/Order.php +111 -60
- package/server/src/Models/Asset.php +548 -0
- package/server/src/Models/Contact.php +2 -0
- package/server/src/Models/Device.php +435 -0
- package/server/src/Models/DeviceEvent.php +501 -0
- package/server/src/Models/Driver.php +2 -0
- package/server/src/Models/Entity.php +2 -0
- package/server/src/Models/Equipment.php +483 -0
- package/server/src/Models/Fleet.php +2 -0
- package/server/src/Models/FuelReport.php +2 -0
- package/server/src/Models/Issue.php +2 -0
- package/server/src/Models/Maintenance.php +549 -0
- package/server/src/Models/Order.php +32 -112
- package/server/src/Models/OrderConfig.php +8 -0
- package/server/src/Models/Part.php +502 -0
- package/server/src/Models/Payload.php +101 -20
- package/server/src/Models/Place.php +10 -4
- package/server/src/Models/Sensor.php +510 -0
- package/server/src/Models/ServiceArea.php +1 -1
- package/server/src/Models/Telematic.php +336 -0
- package/server/src/Models/Vehicle.php +45 -1
- package/server/src/Models/VehicleDevice.php +1 -1
- package/server/src/Models/Vendor.php +2 -0
- package/server/src/Models/Warranty.php +413 -0
- package/server/src/Models/WorkOrder.php +532 -0
- package/server/src/Support/Utils.php +5 -0
- package/server/src/Traits/Maintainable.php +307 -0
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
<?php
|
|
2
|
+
|
|
3
|
+
namespace Fleetbase\FleetOps\Traits;
|
|
4
|
+
|
|
5
|
+
use Fleetbase\FleetOps\Models\Maintenance;
|
|
6
|
+
use Illuminate\Database\Eloquent\Relations\MorphMany;
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Trait Maintainable.
|
|
10
|
+
*
|
|
11
|
+
* Provides maintenance-related functionality for models that can be maintained.
|
|
12
|
+
* This trait can be used by Asset, Equipment, and other maintainable entities.
|
|
13
|
+
*/
|
|
14
|
+
trait Maintainable
|
|
15
|
+
{
|
|
16
|
+
/**
|
|
17
|
+
* Get all maintenances for this maintainable entity.
|
|
18
|
+
*/
|
|
19
|
+
public function maintenances(): MorphMany
|
|
20
|
+
{
|
|
21
|
+
return $this->morphMany(Maintenance::class, 'maintainable');
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Get scheduled maintenances.
|
|
26
|
+
*/
|
|
27
|
+
public function scheduledMaintenances(): MorphMany
|
|
28
|
+
{
|
|
29
|
+
return $this->maintenances()->where('status', 'scheduled');
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Get completed maintenances.
|
|
34
|
+
*/
|
|
35
|
+
public function completedMaintenances(): MorphMany
|
|
36
|
+
{
|
|
37
|
+
return $this->maintenances()->where('status', 'completed');
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Get overdue maintenances.
|
|
42
|
+
*/
|
|
43
|
+
public function overdueMaintenances(): MorphMany
|
|
44
|
+
{
|
|
45
|
+
return $this->maintenances()
|
|
46
|
+
->where('status', 'scheduled')
|
|
47
|
+
->where('scheduled_at', '<', now());
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Get the last completed maintenance.
|
|
52
|
+
*/
|
|
53
|
+
public function getLastMaintenanceAttribute(): ?Maintenance
|
|
54
|
+
{
|
|
55
|
+
return $this->completedMaintenances()
|
|
56
|
+
->orderBy('completed_at', 'desc')
|
|
57
|
+
->first();
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Get the next scheduled maintenance.
|
|
62
|
+
*/
|
|
63
|
+
public function getNextMaintenanceAttribute(): ?Maintenance
|
|
64
|
+
{
|
|
65
|
+
return $this->scheduledMaintenances()
|
|
66
|
+
->orderBy('scheduled_at', 'asc')
|
|
67
|
+
->first();
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Check if the entity needs maintenance.
|
|
72
|
+
*/
|
|
73
|
+
public function needsMaintenance(): bool
|
|
74
|
+
{
|
|
75
|
+
// Check if there's overdue maintenance
|
|
76
|
+
if ($this->overdueMaintenances()->exists()) {
|
|
77
|
+
return true;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Check maintenance intervals based on usage metrics
|
|
81
|
+
return $this->checkMaintenanceIntervals();
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Check maintenance intervals based on usage metrics.
|
|
86
|
+
*/
|
|
87
|
+
protected function checkMaintenanceIntervals(): bool
|
|
88
|
+
{
|
|
89
|
+
$lastMaintenance = $this->last_maintenance;
|
|
90
|
+
|
|
91
|
+
if (!$lastMaintenance) {
|
|
92
|
+
// If no maintenance history, check against purchase/creation date
|
|
93
|
+
$baseDate = $this->purchased_at ?? $this->created_at;
|
|
94
|
+
|
|
95
|
+
return $this->checkIntervalsSinceDate($baseDate);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Check intervals since last maintenance
|
|
99
|
+
return $this->checkIntervalsSinceDate($lastMaintenance->completed_at);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Check maintenance intervals since a specific date.
|
|
104
|
+
*
|
|
105
|
+
* @param \Carbon\Carbon $date
|
|
106
|
+
*/
|
|
107
|
+
protected function checkIntervalsSinceDate($date): bool
|
|
108
|
+
{
|
|
109
|
+
$specs = $this->specs ?? [];
|
|
110
|
+
$meta = $this->meta ?? [];
|
|
111
|
+
|
|
112
|
+
// Check time-based intervals
|
|
113
|
+
$maintenanceIntervalDays = $specs['maintenance_interval_days'] ?? $meta['maintenance_interval_days'] ?? null;
|
|
114
|
+
if ($maintenanceIntervalDays && $date->diffInDays(now()) >= $maintenanceIntervalDays) {
|
|
115
|
+
return true;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Check odometer-based intervals (for assets with odometer)
|
|
119
|
+
if (isset($this->odometer) && isset($specs['maintenance_interval_miles'])) {
|
|
120
|
+
$lastOdometer = $this->last_maintenance?->odometer ?? 0;
|
|
121
|
+
$milesSinceLastMaintenance = $this->odometer - $lastOdometer;
|
|
122
|
+
if ($milesSinceLastMaintenance >= $specs['maintenance_interval_miles']) {
|
|
123
|
+
return true;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// Check engine hours-based intervals (for assets with engine hours)
|
|
128
|
+
if (isset($this->engine_hours) && isset($specs['maintenance_interval_hours'])) {
|
|
129
|
+
$lastEngineHours = $this->last_maintenance?->engine_hours ?? 0;
|
|
130
|
+
$hoursSinceLastMaintenance = $this->engine_hours - $lastEngineHours;
|
|
131
|
+
if ($hoursSinceLastMaintenance >= $specs['maintenance_interval_hours']) {
|
|
132
|
+
return true;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
return false;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Schedule maintenance for the entity.
|
|
141
|
+
*/
|
|
142
|
+
public function scheduleMaintenance(string $type, \DateTime $scheduledAt, array $details = []): Maintenance
|
|
143
|
+
{
|
|
144
|
+
return Maintenance::create([
|
|
145
|
+
'company_uuid' => $this->company_uuid,
|
|
146
|
+
'maintainable_type' => static::class,
|
|
147
|
+
'maintainable_uuid' => $this->uuid,
|
|
148
|
+
'type' => $type,
|
|
149
|
+
'status' => 'scheduled',
|
|
150
|
+
'scheduled_at' => $scheduledAt,
|
|
151
|
+
'odometer' => $this->odometer ?? null,
|
|
152
|
+
'engine_hours' => $this->engine_hours ?? null,
|
|
153
|
+
'summary' => $details['summary'] ?? null,
|
|
154
|
+
'notes' => $details['notes'] ?? null,
|
|
155
|
+
'priority' => $details['priority'] ?? 'medium',
|
|
156
|
+
'created_by_uuid' => auth()->id(),
|
|
157
|
+
]);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Get maintenance cost for a specific period.
|
|
162
|
+
*/
|
|
163
|
+
public function getMaintenanceCost(int $days = 365): float
|
|
164
|
+
{
|
|
165
|
+
$startDate = now()->subDays($days);
|
|
166
|
+
|
|
167
|
+
return $this->completedMaintenances()
|
|
168
|
+
->where('completed_at', '>=', $startDate)
|
|
169
|
+
->sum('total_cost') ?? 0;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Get maintenance frequency (maintenances per year).
|
|
174
|
+
*/
|
|
175
|
+
public function getMaintenanceFrequency(int $days = 365): float
|
|
176
|
+
{
|
|
177
|
+
$startDate = now()->subDays($days);
|
|
178
|
+
$maintenanceCount = $this->completedMaintenances()
|
|
179
|
+
->where('completed_at', '>=', $startDate)
|
|
180
|
+
->count();
|
|
181
|
+
|
|
182
|
+
return ($maintenanceCount / $days) * 365;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* Get average maintenance duration in hours.
|
|
187
|
+
*/
|
|
188
|
+
public function getAverageMaintenanceDuration(int $days = 365): ?float
|
|
189
|
+
{
|
|
190
|
+
$startDate = now()->subDays($days);
|
|
191
|
+
$maintenances = $this->completedMaintenances()
|
|
192
|
+
->where('completed_at', '>=', $startDate)
|
|
193
|
+
->whereNotNull('started_at')
|
|
194
|
+
->whereNotNull('completed_at')
|
|
195
|
+
->get();
|
|
196
|
+
|
|
197
|
+
if ($maintenances->isEmpty()) {
|
|
198
|
+
return null;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
$totalHours = $maintenances->sum(function ($maintenance) {
|
|
202
|
+
return $maintenance->started_at->diffInHours($maintenance->completed_at);
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
return $totalHours / $maintenances->count();
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* Get maintenance efficiency rating.
|
|
210
|
+
*/
|
|
211
|
+
public function getMaintenanceEfficiency(int $days = 365): ?float
|
|
212
|
+
{
|
|
213
|
+
$startDate = now()->subDays($days);
|
|
214
|
+
$maintenances = $this->completedMaintenances()
|
|
215
|
+
->where('completed_at', '>=', $startDate)
|
|
216
|
+
->get();
|
|
217
|
+
|
|
218
|
+
if ($maintenances->isEmpty()) {
|
|
219
|
+
return null;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
$onTimeCount = $maintenances->filter(function ($maintenance) {
|
|
223
|
+
return $maintenance->wasCompletedOnTime();
|
|
224
|
+
})->count();
|
|
225
|
+
|
|
226
|
+
return ($onTimeCount / $maintenances->count()) * 100;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* Get upcoming maintenance due dates.
|
|
231
|
+
*
|
|
232
|
+
* @return \Illuminate\Database\Eloquent\Collection
|
|
233
|
+
*/
|
|
234
|
+
public function getUpcomingMaintenance(int $days = 30)
|
|
235
|
+
{
|
|
236
|
+
$endDate = now()->addDays($days);
|
|
237
|
+
|
|
238
|
+
return $this->scheduledMaintenances()
|
|
239
|
+
->where('scheduled_at', '<=', $endDate)
|
|
240
|
+
->orderBy('scheduled_at', 'asc')
|
|
241
|
+
->get();
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* Create preventive maintenance schedule.
|
|
246
|
+
*/
|
|
247
|
+
public function createPreventiveMaintenanceSchedule(array $intervals = []): array
|
|
248
|
+
{
|
|
249
|
+
$schedule = [];
|
|
250
|
+
$specs = $this->specs ?? [];
|
|
251
|
+
$defaultIntervals = $specs['maintenance_intervals'] ?? [];
|
|
252
|
+
|
|
253
|
+
$intervals = array_merge($defaultIntervals, $intervals);
|
|
254
|
+
|
|
255
|
+
foreach ($intervals as $type => $interval) {
|
|
256
|
+
$lastMaintenance = $this->completedMaintenances()
|
|
257
|
+
->where('type', $type)
|
|
258
|
+
->orderBy('completed_at', 'desc')
|
|
259
|
+
->first();
|
|
260
|
+
|
|
261
|
+
$baseDate = $lastMaintenance?->completed_at ?? $this->created_at;
|
|
262
|
+
$nextDue = $baseDate->copy()->addDays($interval['days'] ?? 365);
|
|
263
|
+
|
|
264
|
+
if ($nextDue->isFuture()) {
|
|
265
|
+
$schedule[] = [
|
|
266
|
+
'type' => $type,
|
|
267
|
+
'due_date' => $nextDue,
|
|
268
|
+
'interval_days' => $interval['days'] ?? 365,
|
|
269
|
+
'priority' => $interval['priority'] ?? 'medium',
|
|
270
|
+
'description' => $interval['description'] ?? "Scheduled {$type} maintenance",
|
|
271
|
+
];
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
return $schedule;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
/**
|
|
279
|
+
* Get maintenance history summary.
|
|
280
|
+
*/
|
|
281
|
+
public function getMaintenanceHistorySummary(int $days = 365): array
|
|
282
|
+
{
|
|
283
|
+
$startDate = now()->subDays($days);
|
|
284
|
+
$maintenances = $this->maintenances()
|
|
285
|
+
->where('created_at', '>=', $startDate)
|
|
286
|
+
->get();
|
|
287
|
+
|
|
288
|
+
$completed = $maintenances->where('status', 'completed');
|
|
289
|
+
$scheduled = $maintenances->where('status', 'scheduled');
|
|
290
|
+
$overdue = $scheduled->filter(function ($maintenance) {
|
|
291
|
+
return $maintenance->scheduled_at->isPast();
|
|
292
|
+
});
|
|
293
|
+
|
|
294
|
+
return [
|
|
295
|
+
'total_maintenances' => $maintenances->count(),
|
|
296
|
+
'completed_count' => $completed->count(),
|
|
297
|
+
'scheduled_count' => $scheduled->count(),
|
|
298
|
+
'overdue_count' => $overdue->count(),
|
|
299
|
+
'total_cost' => $completed->sum('total_cost'),
|
|
300
|
+
'average_cost' => $completed->avg('total_cost'),
|
|
301
|
+
'total_downtime_hours' => $completed->sum('duration_hours'),
|
|
302
|
+
'average_duration_hours' => $completed->avg('duration_hours'),
|
|
303
|
+
'on_time_percentage' => $this->getMaintenanceEfficiency($days),
|
|
304
|
+
'most_common_type' => $completed->groupBy('type')->sortByDesc->count()->keys()->first(),
|
|
305
|
+
];
|
|
306
|
+
}
|
|
307
|
+
}
|