@fleetbase/fleetops-engine 0.6.16 → 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_11_170800_add_company_index_to_routes_table.php +21 -0
- 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 +19 -1
- package/server/src/Http/Controllers/Internal/v1/OrderController.php +31 -8
- 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 +27 -50
- 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/Waypoint.php +2 -0
- package/server/src/Models/WorkOrder.php +532 -0
- package/server/src/Support/Utils.php +5 -0
- package/server/src/Traits/HasTrackingNumber.php +64 -10
- package/server/src/Traits/Maintainable.php +307 -0
- package/server/src/Traits/PayloadAccessors.php +126 -0
|
@@ -0,0 +1,548 @@
|
|
|
1
|
+
<?php
|
|
2
|
+
|
|
3
|
+
namespace Fleetbase\FleetOps\Models;
|
|
4
|
+
|
|
5
|
+
use Fleetbase\Casts\Json;
|
|
6
|
+
use Fleetbase\FleetOps\Casts\Point;
|
|
7
|
+
use Fleetbase\Models\Category;
|
|
8
|
+
use Fleetbase\Models\File;
|
|
9
|
+
use Fleetbase\Models\Model;
|
|
10
|
+
use Fleetbase\Models\User;
|
|
11
|
+
use Fleetbase\Traits\HasApiModelBehavior;
|
|
12
|
+
use Fleetbase\Traits\HasCustomFields;
|
|
13
|
+
use Fleetbase\Traits\HasMetaAttributes;
|
|
14
|
+
use Fleetbase\Traits\HasPublicId;
|
|
15
|
+
use Fleetbase\Traits\HasUuid;
|
|
16
|
+
use Fleetbase\Traits\Searchable;
|
|
17
|
+
use Fleetbase\Traits\TracksApiCredential;
|
|
18
|
+
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
|
19
|
+
use Illuminate\Database\Eloquent\Relations\HasMany;
|
|
20
|
+
use Illuminate\Database\Eloquent\Relations\MorphMany;
|
|
21
|
+
use Illuminate\Database\Eloquent\Relations\MorphTo;
|
|
22
|
+
use Spatie\Activitylog\LogOptions;
|
|
23
|
+
use Spatie\Activitylog\Traits\LogsActivity;
|
|
24
|
+
use Spatie\Sluggable\HasSlug;
|
|
25
|
+
use Spatie\Sluggable\SlugOptions;
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Class Asset.
|
|
29
|
+
*
|
|
30
|
+
* Represents a physical asset in the fleet (truck, trailer, container, drone, forklift, etc.).
|
|
31
|
+
* Assets are the primary operational units that can be tracked, maintained, and managed.
|
|
32
|
+
*/
|
|
33
|
+
class Asset extends Model
|
|
34
|
+
{
|
|
35
|
+
use HasUuid;
|
|
36
|
+
use HasPublicId;
|
|
37
|
+
use TracksApiCredential;
|
|
38
|
+
use HasApiModelBehavior;
|
|
39
|
+
use HasSlug;
|
|
40
|
+
use LogsActivity;
|
|
41
|
+
use HasMetaAttributes;
|
|
42
|
+
use Searchable;
|
|
43
|
+
use HasCustomFields;
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* The database table used by the model.
|
|
47
|
+
*
|
|
48
|
+
* @var string
|
|
49
|
+
*/
|
|
50
|
+
protected $table = 'assets';
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* The type of public Id to generate.
|
|
54
|
+
*
|
|
55
|
+
* @var string
|
|
56
|
+
*/
|
|
57
|
+
protected $publicIdType = 'asset';
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* The attributes that can be queried.
|
|
61
|
+
*
|
|
62
|
+
* @var array
|
|
63
|
+
*/
|
|
64
|
+
protected $searchableColumns = ['name', 'code', 'vin', 'plate_number', 'make', 'model', 'serial_number', 'public_id'];
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* The attributes that can be used for filtering.
|
|
68
|
+
*
|
|
69
|
+
* @var array
|
|
70
|
+
*/
|
|
71
|
+
protected $filterParams = ['category_uuid', 'vendor_uuid', 'type', 'status', 'make', 'model', 'year'];
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* The attributes that are spatial columns.
|
|
75
|
+
*
|
|
76
|
+
* @var array
|
|
77
|
+
*/
|
|
78
|
+
protected $spatialFields = ['location'];
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* The attributes that are mass assignable.
|
|
82
|
+
*
|
|
83
|
+
* @var array
|
|
84
|
+
*/
|
|
85
|
+
protected $fillable = [
|
|
86
|
+
'company_uuid',
|
|
87
|
+
'category_uuid',
|
|
88
|
+
'vendor_uuid',
|
|
89
|
+
'warranty_uuid',
|
|
90
|
+
'telematic_uuid',
|
|
91
|
+
'assigned_to_uuid',
|
|
92
|
+
'assigned_to_type',
|
|
93
|
+
'operator_uuid',
|
|
94
|
+
'operator_type',
|
|
95
|
+
'current_place_uuid',
|
|
96
|
+
'photo_uuid',
|
|
97
|
+
'name',
|
|
98
|
+
'code',
|
|
99
|
+
'type',
|
|
100
|
+
'location',
|
|
101
|
+
'speed',
|
|
102
|
+
'heading',
|
|
103
|
+
'altitude',
|
|
104
|
+
'status',
|
|
105
|
+
'vin',
|
|
106
|
+
'plate_number',
|
|
107
|
+
'make',
|
|
108
|
+
'model',
|
|
109
|
+
'year',
|
|
110
|
+
'color',
|
|
111
|
+
'serial_number',
|
|
112
|
+
'odometer',
|
|
113
|
+
'engine_hours',
|
|
114
|
+
'gvw',
|
|
115
|
+
'capacity',
|
|
116
|
+
'specs',
|
|
117
|
+
'attributes',
|
|
118
|
+
'slug',
|
|
119
|
+
];
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Dynamic attributes that are appended to object.
|
|
123
|
+
*
|
|
124
|
+
* @var array
|
|
125
|
+
*/
|
|
126
|
+
protected $appends = [
|
|
127
|
+
'category_name',
|
|
128
|
+
'vendor_name',
|
|
129
|
+
'warranty_name',
|
|
130
|
+
'current_location',
|
|
131
|
+
'photo_url',
|
|
132
|
+
'display_name',
|
|
133
|
+
'is_online',
|
|
134
|
+
'last_maintenance',
|
|
135
|
+
'next_maintenance_due',
|
|
136
|
+
];
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* The attributes excluded from the model's JSON form.
|
|
140
|
+
*
|
|
141
|
+
* @var array
|
|
142
|
+
*/
|
|
143
|
+
protected $hidden = ['category', 'vendor', 'warranty', 'telematic', 'currentPlace', 'photo'];
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* The attributes that should be cast to native types.
|
|
147
|
+
*
|
|
148
|
+
* @var array
|
|
149
|
+
*/
|
|
150
|
+
protected $casts = [
|
|
151
|
+
'year' => 'integer',
|
|
152
|
+
'odometer' => 'integer',
|
|
153
|
+
'engine_hours' => 'integer',
|
|
154
|
+
'gvw' => 'decimal:2',
|
|
155
|
+
'capacity' => Json::class,
|
|
156
|
+
'specs' => Json::class,
|
|
157
|
+
'attributes' => Json::class,
|
|
158
|
+
'location' => Point::class,
|
|
159
|
+
];
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Properties which activity needs to be logged.
|
|
163
|
+
*
|
|
164
|
+
* @var array
|
|
165
|
+
*/
|
|
166
|
+
protected static $logAttributes = '*';
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Do not log empty changed.
|
|
170
|
+
*
|
|
171
|
+
* @var bool
|
|
172
|
+
*/
|
|
173
|
+
protected static $submitEmptyLogs = false;
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* The name of the subject to log.
|
|
177
|
+
*
|
|
178
|
+
* @var string
|
|
179
|
+
*/
|
|
180
|
+
protected static $logName = 'asset';
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Get the options for generating the slug.
|
|
184
|
+
*/
|
|
185
|
+
public function getSlugOptions(): SlugOptions
|
|
186
|
+
{
|
|
187
|
+
return SlugOptions::create()
|
|
188
|
+
->generateSlugsFrom(['name', 'code'])
|
|
189
|
+
->saveSlugsTo('slug');
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Get the activity log options for the model.
|
|
194
|
+
*/
|
|
195
|
+
public function getActivitylogOptions(): LogOptions
|
|
196
|
+
{
|
|
197
|
+
return LogOptions::defaults()->logAll();
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
public function assignedTo(): MorphTo
|
|
201
|
+
{
|
|
202
|
+
return $this->morphTo();
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
public function operator(): MorphTo
|
|
206
|
+
{
|
|
207
|
+
return $this->morphTo();
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
public function category(): BelongsTo
|
|
211
|
+
{
|
|
212
|
+
return $this->belongsTo(Category::class, 'category_uuid', 'uuid');
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
public function vendor(): BelongsTo
|
|
216
|
+
{
|
|
217
|
+
return $this->belongsTo(Vendor::class, 'vendor_uuid', 'uuid');
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
public function warranty(): BelongsTo
|
|
221
|
+
{
|
|
222
|
+
return $this->belongsTo(Warranty::class, 'warranty_uuid', 'uuid');
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
public function telematic(): BelongsTo
|
|
226
|
+
{
|
|
227
|
+
return $this->belongsTo(Telematic::class, 'telematic_uuid', 'uuid');
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
public function currentPlace(): BelongsTo
|
|
231
|
+
{
|
|
232
|
+
return $this->belongsTo(Place::class, 'current_place_uuid', 'uuid');
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
public function photo(): BelongsTo
|
|
236
|
+
{
|
|
237
|
+
return $this->belongsTo(File::class, 'photo_uuid', 'uuid');
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
public function createdBy(): BelongsTo
|
|
241
|
+
{
|
|
242
|
+
return $this->belongsTo(User::class, 'created_by_uuid', 'uuid');
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
public function updatedBy(): BelongsTo
|
|
246
|
+
{
|
|
247
|
+
return $this->belongsTo(User::class, 'updated_by_uuid', 'uuid');
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
public function devices(): HasMany
|
|
251
|
+
{
|
|
252
|
+
return $this->hasMany(Device::class, 'attachable_uuid');
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
public function equipments(): HasMany
|
|
256
|
+
{
|
|
257
|
+
return $this->hasMany(Equipment::class, 'equipable_uuid', 'uuid')
|
|
258
|
+
->where('equipable_type', static::class);
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
public function maintenances(): HasMany
|
|
262
|
+
{
|
|
263
|
+
return $this->hasMany(Maintenance::class, 'maintainable_uuid', 'uuid')
|
|
264
|
+
->where('maintainable_type', static::class);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
public function sensors(): HasMany
|
|
268
|
+
{
|
|
269
|
+
return $this->hasMany(Sensor::class, 'sensorable_uuid', 'uuid')
|
|
270
|
+
->where('sensorable_type', static::class);
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
public function parts(): MorphMany
|
|
274
|
+
{
|
|
275
|
+
return $this->morphMany(Part::class, 'asset');
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
public function positions(): HasMany
|
|
279
|
+
{
|
|
280
|
+
return $this->hasMany(Position::class, 'subject_uuid');
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
/**
|
|
284
|
+
* Get the category name.
|
|
285
|
+
*/
|
|
286
|
+
public function getCategoryNameAttribute(): ?string
|
|
287
|
+
{
|
|
288
|
+
return $this->category?->name;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
/**
|
|
292
|
+
* Get the vendor name.
|
|
293
|
+
*/
|
|
294
|
+
public function getVendorNameAttribute(): ?string
|
|
295
|
+
{
|
|
296
|
+
return $this->vendor?->name;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
/**
|
|
300
|
+
* Get the warranty name.
|
|
301
|
+
*/
|
|
302
|
+
public function getWarrantyNameAttribute(): ?string
|
|
303
|
+
{
|
|
304
|
+
return $this->warranty?->name;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
/**
|
|
308
|
+
* Get the current location information.
|
|
309
|
+
*/
|
|
310
|
+
public function getCurrentLocationAttribute(): ?array
|
|
311
|
+
{
|
|
312
|
+
if ($this->currentPlace) {
|
|
313
|
+
return [
|
|
314
|
+
'name' => $this->currentPlace->name,
|
|
315
|
+
'address' => $this->currentPlace->address,
|
|
316
|
+
'latitude' => $this->currentPlace->latitude,
|
|
317
|
+
'longitude' => $this->currentPlace->longitude,
|
|
318
|
+
];
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
// Try to get location from telematic
|
|
322
|
+
if ($this->telematic && $this->telematic->last_location) {
|
|
323
|
+
return $this->telematic->last_location;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
return null;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
/**
|
|
330
|
+
* Get the photo URL.
|
|
331
|
+
*/
|
|
332
|
+
public function getPhotoUrlAttribute(): ?string
|
|
333
|
+
{
|
|
334
|
+
return $this->photo?->url;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
/**
|
|
338
|
+
* Get the display name for the asset.
|
|
339
|
+
*/
|
|
340
|
+
public function getDisplayNameAttribute(): string
|
|
341
|
+
{
|
|
342
|
+
if ($this->name) {
|
|
343
|
+
return $this->name;
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
$parts = array_filter([
|
|
347
|
+
$this->make,
|
|
348
|
+
$this->model,
|
|
349
|
+
$this->year,
|
|
350
|
+
$this->code,
|
|
351
|
+
]);
|
|
352
|
+
|
|
353
|
+
return implode(' ', $parts) ?: 'Asset #' . $this->public_id;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
/**
|
|
357
|
+
* Check if the asset is currently online (via telematic).
|
|
358
|
+
*/
|
|
359
|
+
public function getIsOnlineAttribute(): bool
|
|
360
|
+
{
|
|
361
|
+
return $this->telematic?->is_online ?? false;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
/**
|
|
365
|
+
* Get the last maintenance record.
|
|
366
|
+
*/
|
|
367
|
+
public function getLastMaintenanceAttribute(): ?Maintenance
|
|
368
|
+
{
|
|
369
|
+
return $this->maintenances()
|
|
370
|
+
->where('status', 'completed')
|
|
371
|
+
->orderBy('completed_at', 'desc')
|
|
372
|
+
->first();
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
/**
|
|
376
|
+
* Get the next maintenance due date.
|
|
377
|
+
*/
|
|
378
|
+
public function getNextMaintenanceDueAttribute(): ?string
|
|
379
|
+
{
|
|
380
|
+
$nextMaintenance = $this->maintenances()
|
|
381
|
+
->where('status', 'scheduled')
|
|
382
|
+
->orderBy('scheduled_at', 'asc')
|
|
383
|
+
->first();
|
|
384
|
+
|
|
385
|
+
return $nextMaintenance?->scheduled_at?->toDateString();
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
/**
|
|
389
|
+
* Scope to get assets by type.
|
|
390
|
+
*
|
|
391
|
+
* @param \Illuminate\Database\Eloquent\Builder $query
|
|
392
|
+
*
|
|
393
|
+
* @return \Illuminate\Database\Eloquent\Builder
|
|
394
|
+
*/
|
|
395
|
+
public function scopeByType($query, string $type)
|
|
396
|
+
{
|
|
397
|
+
return $query->where('type', $type);
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
/**
|
|
401
|
+
* Scope to get active assets.
|
|
402
|
+
*
|
|
403
|
+
* @param \Illuminate\Database\Eloquent\Builder $query
|
|
404
|
+
*
|
|
405
|
+
* @return \Illuminate\Database\Eloquent\Builder
|
|
406
|
+
*/
|
|
407
|
+
public function scopeActive($query)
|
|
408
|
+
{
|
|
409
|
+
return $query->where('status', 'active');
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
/**
|
|
413
|
+
* Scope to get assets with telematics.
|
|
414
|
+
*
|
|
415
|
+
* @param \Illuminate\Database\Eloquent\Builder $query
|
|
416
|
+
*
|
|
417
|
+
* @return \Illuminate\Database\Eloquent\Builder
|
|
418
|
+
*/
|
|
419
|
+
public function scopeWithTelematics($query)
|
|
420
|
+
{
|
|
421
|
+
return $query->whereNotNull('telematic_uuid');
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
/**
|
|
425
|
+
* Scope to get assets that are online.
|
|
426
|
+
*
|
|
427
|
+
* @param \Illuminate\Database\Eloquent\Builder $query
|
|
428
|
+
*
|
|
429
|
+
* @return \Illuminate\Database\Eloquent\Builder
|
|
430
|
+
*/
|
|
431
|
+
public function scopeOnline($query)
|
|
432
|
+
{
|
|
433
|
+
return $query->whereHas('telematic', function ($q) {
|
|
434
|
+
$q->online();
|
|
435
|
+
});
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
/**
|
|
439
|
+
* Update the asset's odometer reading.
|
|
440
|
+
*/
|
|
441
|
+
public function updateOdometer(int $reading, ?string $source = null): bool
|
|
442
|
+
{
|
|
443
|
+
if ($reading > $this->odometer) {
|
|
444
|
+
$updated = $this->update(['odometer' => $reading]);
|
|
445
|
+
|
|
446
|
+
if ($updated) {
|
|
447
|
+
activity('odometer_update')
|
|
448
|
+
->performedOn($this)
|
|
449
|
+
->withProperties([
|
|
450
|
+
'old_reading' => $this->getOriginal('odometer'),
|
|
451
|
+
'new_reading' => $reading,
|
|
452
|
+
'source' => $source ?? 'manual',
|
|
453
|
+
])
|
|
454
|
+
->log('Odometer updated');
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
return $updated;
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
return false;
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
/**
|
|
464
|
+
* Update the asset's engine hours.
|
|
465
|
+
*/
|
|
466
|
+
public function updateEngineHours(int $hours, ?string $source = null): bool
|
|
467
|
+
{
|
|
468
|
+
if ($hours > $this->engine_hours) {
|
|
469
|
+
$updated = $this->update(['engine_hours' => $hours]);
|
|
470
|
+
|
|
471
|
+
if ($updated) {
|
|
472
|
+
activity('engine_hours_update')
|
|
473
|
+
->performedOn($this)
|
|
474
|
+
->withProperties([
|
|
475
|
+
'old_hours' => $this->getOriginal('engine_hours'),
|
|
476
|
+
'new_hours' => $hours,
|
|
477
|
+
'source' => $source ?? 'manual',
|
|
478
|
+
])
|
|
479
|
+
->log('Engine hours updated');
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
return $updated;
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
return false;
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
/**
|
|
489
|
+
* Check if the asset needs maintenance.
|
|
490
|
+
*/
|
|
491
|
+
public function needsMaintenance(): bool
|
|
492
|
+
{
|
|
493
|
+
// Check if there's overdue maintenance
|
|
494
|
+
$overdueMaintenance = $this->maintenances()
|
|
495
|
+
->where('status', 'scheduled')
|
|
496
|
+
->where('scheduled_at', '<', now())
|
|
497
|
+
->exists();
|
|
498
|
+
|
|
499
|
+
if ($overdueMaintenance) {
|
|
500
|
+
return true;
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
// Check maintenance intervals based on odometer or engine hours
|
|
504
|
+
$specs = $this->specs ?? [];
|
|
505
|
+
$maintenanceInterval = $specs['maintenance_interval'] ?? null;
|
|
506
|
+
|
|
507
|
+
if ($maintenanceInterval && $this->last_maintenance) {
|
|
508
|
+
$milesSinceLastMaintenance = $this->odometer - $this->last_maintenance->odometer;
|
|
509
|
+
|
|
510
|
+
return $milesSinceLastMaintenance >= $maintenanceInterval;
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
return false;
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
/**
|
|
517
|
+
* Get the asset's utilization rate.
|
|
518
|
+
*/
|
|
519
|
+
public function getUtilizationRate(int $days = 30): float
|
|
520
|
+
{
|
|
521
|
+
// This would calculate based on actual usage data
|
|
522
|
+
// For now, return a placeholder calculation
|
|
523
|
+
$totalHours = $days * 24;
|
|
524
|
+
$usedHours = $this->engine_hours ?? 0;
|
|
525
|
+
|
|
526
|
+
return min(($usedHours / $totalHours) * 100, 100);
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
/**
|
|
530
|
+
* Schedule maintenance for the asset.
|
|
531
|
+
*/
|
|
532
|
+
public function scheduleMaintenance(string $type, \DateTime $scheduledAt, array $details = []): Maintenance
|
|
533
|
+
{
|
|
534
|
+
return Maintenance::create([
|
|
535
|
+
'company_uuid' => $this->company_uuid,
|
|
536
|
+
'maintainable_type' => static::class,
|
|
537
|
+
'maintainable_uuid' => $this->uuid,
|
|
538
|
+
'type' => $type,
|
|
539
|
+
'status' => 'scheduled',
|
|
540
|
+
'scheduled_at' => $scheduledAt,
|
|
541
|
+
'odometer' => $this->odometer,
|
|
542
|
+
'engine_hours' => $this->engine_hours,
|
|
543
|
+
'summary' => $details['summary'] ?? null,
|
|
544
|
+
'notes' => $details['notes'] ?? null,
|
|
545
|
+
'created_by_uuid' => auth()->id(),
|
|
546
|
+
]);
|
|
547
|
+
}
|
|
548
|
+
}
|
|
@@ -10,6 +10,7 @@ use Fleetbase\Models\Model;
|
|
|
10
10
|
use Fleetbase\Models\User;
|
|
11
11
|
use Fleetbase\Notifications\UserInvited;
|
|
12
12
|
use Fleetbase\Traits\HasApiModelBehavior;
|
|
13
|
+
use Fleetbase\Traits\HasCustomFields;
|
|
13
14
|
use Fleetbase\Traits\HasInternalId;
|
|
14
15
|
use Fleetbase\Traits\HasMetaAttributes;
|
|
15
16
|
use Fleetbase\Traits\HasPublicId;
|
|
@@ -42,6 +43,7 @@ class Contact extends Model
|
|
|
42
43
|
use LogsActivity;
|
|
43
44
|
use CausesActivity;
|
|
44
45
|
use Notifiable;
|
|
46
|
+
use HasCustomFields;
|
|
45
47
|
|
|
46
48
|
/**
|
|
47
49
|
* The database table used by the model.
|