@fleetbase/fleetops-engine 0.6.20 → 0.6.22

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 (162) hide show
  1. package/addon/components/custom-entity/form.hbs +14 -14
  2. package/addon/components/device/card.hbs +1 -0
  3. package/addon/components/device/card.js +3 -0
  4. package/addon/components/device/details.hbs +92 -43
  5. package/addon/components/device/form.hbs +108 -60
  6. package/addon/components/device/form.js +36 -8
  7. package/addon/components/device/manager.hbs +29 -0
  8. package/addon/components/device/manager.js +95 -0
  9. package/addon/components/device/panel-header.hbs +32 -0
  10. package/addon/components/device/panel-header.js +3 -0
  11. package/addon/components/device/pill.hbs +16 -0
  12. package/addon/components/device/pill.js +3 -0
  13. package/addon/components/driver/details.hbs +4 -0
  14. package/addon/components/driver/details.js +19 -1
  15. package/addon/components/driver/form.hbs +14 -3
  16. package/addon/components/driver/form.js +49 -47
  17. package/addon/components/driver/pill.hbs +17 -0
  18. package/addon/components/driver/pill.js +3 -0
  19. package/addon/components/entity/form.hbs +7 -5
  20. package/addon/components/layout/fleet-ops-sidebar.js +12 -12
  21. package/addon/components/map/drawer/device-event-listing.hbs +64 -0
  22. package/addon/components/map/drawer/device-event-listing.js +181 -0
  23. package/addon/components/map/drawer/position-listing.hbs +100 -0
  24. package/addon/components/map/drawer/position-listing.js +455 -0
  25. package/addon/components/map/drawer.js +2 -0
  26. package/addon/components/map/leaflet-live-map.hbs +7 -2
  27. package/addon/components/modals/attach-device.hbs +18 -0
  28. package/addon/components/modals/attach-device.js +3 -0
  29. package/addon/components/order/details/detail.hbs +2 -54
  30. package/addon/components/order/details/detail.js +1 -0
  31. package/addon/components/order/details/payload.hbs +6 -4
  32. package/addon/components/order/details/payload.js +2 -0
  33. package/addon/components/order/pill.hbs +34 -0
  34. package/addon/components/order/pill.js +3 -0
  35. package/addon/components/order-config-manager/custom-fields.js +1 -1
  36. package/addon/components/positions-replay.hbs +339 -0
  37. package/addon/components/positions-replay.js +409 -0
  38. package/addon/components/sensor/details.hbs +64 -38
  39. package/addon/components/sensor/form.hbs +112 -63
  40. package/addon/components/sensor/form.js +36 -24
  41. package/addon/components/sensor/panel-header.hbs +32 -0
  42. package/addon/components/sensor/panel-header.js +3 -0
  43. package/addon/components/telematic/details.hbs +40 -16
  44. package/addon/components/telematic/form.hbs +63 -64
  45. package/addon/components/telematic/form.js +73 -4
  46. package/addon/components/vehicle/card.hbs +2 -2
  47. package/addon/components/vehicle/details.hbs +4 -0
  48. package/addon/components/vehicle/details.js +19 -1
  49. package/addon/components/vehicle/form.hbs +4 -0
  50. package/addon/components/vehicle/pill.hbs +34 -0
  51. package/addon/components/vehicle/pill.js +3 -0
  52. package/addon/controllers/analytics/reports/index/edit.js +1 -1
  53. package/addon/controllers/connectivity/devices/index/details.js +22 -1
  54. package/addon/controllers/connectivity/devices/index/edit.js +66 -1
  55. package/addon/controllers/connectivity/devices/index.js +51 -9
  56. package/addon/controllers/connectivity/events/index.js +65 -16
  57. package/addon/controllers/connectivity/sensors/index/details.js +22 -1
  58. package/addon/controllers/connectivity/sensors/index/edit.js +66 -1
  59. package/addon/controllers/connectivity/sensors/index.js +66 -6
  60. package/addon/controllers/connectivity/telematics/index/details.js +22 -1
  61. package/addon/controllers/connectivity/telematics/index/edit.js +66 -1
  62. package/addon/controllers/connectivity/telematics/index.js +20 -11
  63. package/addon/controllers/management/fleets/index/details.js +26 -21
  64. package/addon/controllers/management/fleets/index/edit.js +9 -6
  65. package/addon/controllers/management/vehicles/index/details.js +26 -13
  66. package/addon/controllers/settings/custom-fields.js +6 -0
  67. package/addon/helpers/get-fleet-ops-option-label.js +11 -0
  68. package/addon/routes/connectivity/devices/index/details.js +27 -1
  69. package/addon/routes/connectivity/devices/index/edit.js +27 -1
  70. package/addon/routes/connectivity/sensors/index/details.js +27 -1
  71. package/addon/routes/connectivity/sensors/index/edit.js +27 -1
  72. package/addon/routes/connectivity/telematics/index/details.js +27 -1
  73. package/addon/routes/connectivity/telematics/index/edit.js +27 -1
  74. package/addon/routes/management/drivers/index/details/positions.js +3 -0
  75. package/addon/routes/management/vehicles/index/details/positions.js +3 -0
  76. package/addon/routes.js +4 -0
  77. package/addon/services/movement-tracker.js +81 -30
  78. package/addon/services/position-playback.js +486 -0
  79. package/addon/services/resource-metadata.js +46 -0
  80. package/addon/styles/fleetops-engine.css +157 -0
  81. package/addon/templates/connectivity/devices/index/details/index.hbs +2 -2
  82. package/addon/templates/connectivity/devices/index/details.hbs +15 -2
  83. package/addon/templates/connectivity/devices/index/edit.hbs +1 -1
  84. package/addon/templates/connectivity/events/index.hbs +1 -1
  85. package/addon/templates/connectivity/sensors/index/details/index.hbs +2 -2
  86. package/addon/templates/connectivity/sensors/index/details.hbs +15 -2
  87. package/addon/templates/connectivity/sensors/index/edit.hbs +1 -1
  88. package/addon/templates/connectivity/telematics/index/details/index.hbs +2 -2
  89. package/addon/templates/connectivity/telematics/index/details.hbs +14 -2
  90. package/addon/templates/connectivity/telematics/index/edit.hbs +1 -1
  91. package/addon/templates/management/drivers/index/details/positions.hbs +2 -0
  92. package/addon/templates/management/vehicles/index/details/devices.hbs +1 -2
  93. package/addon/templates/management/vehicles/index/details/positions.hbs +1 -0
  94. package/addon/utils/fleet-ops-options.js +95 -0
  95. package/app/components/device/card.js +1 -0
  96. package/app/components/device/manager.js +1 -0
  97. package/app/components/device/panel-header.js +1 -0
  98. package/app/components/device/pill.js +1 -0
  99. package/app/components/driver/pill.js +1 -0
  100. package/app/components/map/drawer/device-event-listing.js +1 -0
  101. package/app/components/map/drawer/position-listing.js +1 -0
  102. package/app/components/modals/attach-device.js +1 -0
  103. package/app/components/order/pill.js +1 -0
  104. package/app/components/positions-replay.js +1 -0
  105. package/app/components/sensor/panel-header.js +1 -0
  106. package/app/components/vehicle/pill.js +1 -0
  107. package/app/helpers/get-fleet-ops-option-label.js +1 -0
  108. package/app/routes/management/drivers/index/details/positions.js +1 -0
  109. package/app/routes/management/vehicles/index/details/positions.js +1 -0
  110. package/app/services/position-playback.js +1 -0
  111. package/app/services/resource-metadata.js +1 -0
  112. package/app/templates/management/drivers/index/details/positions.js +1 -0
  113. package/app/templates/management/vehicles/index/details/positions.js +1 -0
  114. package/composer.json +1 -1
  115. package/extension.json +1 -1
  116. package/package.json +4 -4
  117. package/server/config/telematics.php +111 -0
  118. package/server/migrations/2025_10_27_000001_add_telematics_integration_fields.php +70 -0
  119. package/server/migrations/2025_10_27_171322_fix_device_column_names.php +107 -0
  120. package/server/migrations/2025_10_27_203023_add_company_uuid_to_device_events_table.php +28 -0
  121. package/server/src/Console/Commands/ReplayVehicleLocations.php +225 -0
  122. package/server/src/Contracts/TelematicProviderDescriptor.php +72 -0
  123. package/server/src/Contracts/TelematicProviderInterface.php +119 -0
  124. package/server/src/Exceptions/TelematicProviderException.php +14 -0
  125. package/server/src/Exceptions/TelematicRateLimitExceededException.php +12 -0
  126. package/server/src/Http/Controllers/Api/v1/DriverController.php +24 -14
  127. package/server/src/Http/Controllers/Api/v1/VehicleController.php +27 -7
  128. package/server/src/Http/Controllers/Internal/v1/DeviceController.php +22 -0
  129. package/server/src/Http/Controllers/Internal/v1/PositionController.php +240 -0
  130. package/server/src/Http/Controllers/Internal/v1/SensorController.php +11 -0
  131. package/server/src/Http/Controllers/Internal/v1/TelematicController.php +141 -0
  132. package/server/src/Http/Controllers/TelematicWebhookController.php +169 -0
  133. package/server/src/Http/Filter/DeviceEventFilter.php +68 -0
  134. package/server/src/Http/Filter/PositionFilter.php +35 -0
  135. package/server/src/Http/Resources/v1/Position.php +44 -0
  136. package/server/src/Jobs/ReplayPositions.php +64 -0
  137. package/server/src/Jobs/SendPositionReplay.php +65 -0
  138. package/server/src/Jobs/SyncTelematicDevicesJob.php +106 -0
  139. package/server/src/Jobs/TestTelematicConnectionJob.php +102 -0
  140. package/server/src/Models/Asset.php +10 -8
  141. package/server/src/Models/Device.php +79 -12
  142. package/server/src/Models/DeviceEvent.php +33 -3
  143. package/server/src/Models/Driver.php +28 -1
  144. package/server/src/Models/Maintenance.php +15 -12
  145. package/server/src/Models/Part.php +2 -0
  146. package/server/src/Models/Payload.php +0 -1
  147. package/server/src/Models/Place.php +4 -1
  148. package/server/src/Models/Position.php +27 -17
  149. package/server/src/Models/Sensor.php +78 -13
  150. package/server/src/Models/Telematic.php +116 -6
  151. package/server/src/Models/TrackingNumber.php +3 -1
  152. package/server/src/Models/Vehicle.php +8 -11
  153. package/server/src/Models/WorkOrder.php +8 -5
  154. package/server/src/Providers/FleetOpsServiceProvider.php +2 -0
  155. package/server/src/Support/Telematics/Providers/AbstractProvider.php +151 -0
  156. package/server/src/Support/Telematics/Providers/FlespiProvider.php +182 -0
  157. package/server/src/Support/Telematics/Providers/GeotabProvider.php +181 -0
  158. package/server/src/Support/Telematics/Providers/SamsaraProvider.php +177 -0
  159. package/server/src/Support/Telematics/TelematicProviderRegistry.php +147 -0
  160. package/server/src/Support/Telematics/TelematicService.php +223 -0
  161. package/server/src/Support/Utils.php +1 -1
  162. package/server/src/routes.php +24 -1
@@ -3,6 +3,11 @@
3
3
  namespace Fleetbase\FleetOps\Models;
4
4
 
5
5
  use Fleetbase\Casts\Json;
6
+ use Fleetbase\Casts\PolymorphicType;
7
+ use Fleetbase\FleetOps\Casts\Point;
8
+ use Fleetbase\LaravelMysqlSpatial\Eloquent\SpatialTrait;
9
+ use Fleetbase\LaravelMysqlSpatial\Types\Point as SpatialPoint;
10
+ use Fleetbase\Models\File;
6
11
  use Fleetbase\Models\Model;
7
12
  use Fleetbase\Models\User;
8
13
  use Fleetbase\Traits\HasApiModelBehavior;
@@ -15,6 +20,8 @@ use Fleetbase\Traits\TracksApiCredential;
15
20
  use Illuminate\Database\Eloquent\Relations\BelongsTo;
16
21
  use Illuminate\Database\Eloquent\Relations\HasMany;
17
22
  use Illuminate\Database\Eloquent\Relations\MorphTo;
23
+ use Illuminate\Support\Arr;
24
+ use Illuminate\Support\Str;
18
25
  use Spatie\Activitylog\LogOptions;
19
26
  use Spatie\Activitylog\Traits\LogsActivity;
20
27
  use Spatie\Sluggable\HasSlug;
@@ -37,6 +44,7 @@ class Device extends Model
37
44
  use HasMetaAttributes;
38
45
  use Searchable;
39
46
  use HasCustomFields;
47
+ use SpatialTrait;
40
48
 
41
49
  /**
42
50
  * The database table used by the model.
@@ -57,7 +65,7 @@ class Device extends Model
57
65
  *
58
66
  * @var array
59
67
  */
60
- protected $searchableColumns = ['name', 'model', 'serial_number', 'public_id'];
68
+ protected $searchableColumns = ['name', 'model', 'serial_number', 'manufacturer', 'public_id'];
61
69
 
62
70
  /**
63
71
  * The attributes that can be used for filtering.
@@ -74,15 +82,23 @@ class Device extends Model
74
82
  protected $fillable = [
75
83
  'company_uuid',
76
84
  'telematic_uuid',
85
+ 'attachable_uuid',
86
+ 'attachable_type',
77
87
  'warranty_uuid',
78
- 'device_type',
88
+ 'photo_uuid',
89
+ 'type',
79
90
  'device_id',
80
- 'device_provider',
81
- 'device_name',
82
- 'device_model',
83
- 'device_location',
91
+ 'internal_id',
92
+ 'imei',
93
+ 'imsi',
94
+ 'firmware_version',
95
+ 'provider',
96
+ 'name',
97
+ 'model',
98
+ 'location',
84
99
  'manufacturer',
85
100
  'serial_number',
101
+ 'last_position',
86
102
  'installation_date',
87
103
  'last_maintenance_date',
88
104
  'meta',
@@ -94,8 +110,6 @@ class Device extends Model
94
110
  'notes',
95
111
  'status',
96
112
  'last_online_at',
97
- 'attachable_type',
98
- 'attachable_uuid',
99
113
  'slug',
100
114
  ];
101
115
 
@@ -110,6 +124,7 @@ class Device extends Model
110
124
  'is_online',
111
125
  'attached_to_name',
112
126
  'connection_status',
127
+ 'photo_url',
113
128
  ];
114
129
 
115
130
  /**
@@ -117,7 +132,14 @@ class Device extends Model
117
132
  *
118
133
  * @var array
119
134
  */
120
- protected $hidden = ['warranty', 'telematic', 'attachable'];
135
+ protected $hidden = [];
136
+
137
+ /**
138
+ * The attributes that are spatial columns.
139
+ *
140
+ * @var array
141
+ */
142
+ protected $spatialFields = ['last_position'];
121
143
 
122
144
  /**
123
145
  * The attributes that should be cast to native types.
@@ -125,9 +147,14 @@ class Device extends Model
125
147
  * @var array
126
148
  */
127
149
  protected $casts = [
128
- 'last_online_at' => 'datetime',
129
- 'meta' => Json::class,
130
- 'options' => Json::class,
150
+ 'installation_date' => 'date',
151
+ 'last_maintenance_date' => 'date',
152
+ 'last_online_at' => 'datetime',
153
+ 'last_position' => Point::class,
154
+ 'meta' => Json::class,
155
+ 'options' => Json::class,
156
+ 'data' => Json::class,
157
+ 'attachable_type' => PolymorphicType::class,
131
158
  ];
132
159
 
133
160
  /**
@@ -204,6 +231,21 @@ class Device extends Model
204
231
  return $this->hasMany(Sensor::class, 'device_uuid', 'uuid');
205
232
  }
206
233
 
234
+ public function photo(): BelongsTo
235
+ {
236
+ return $this->belongsTo(File::class);
237
+ }
238
+
239
+ /**
240
+ * Get photo URL attribute.
241
+ *
242
+ * @return string
243
+ */
244
+ public function getPhotoUrlAttribute()
245
+ {
246
+ return data_get($this, 'photo.url', 'https://flb-assets.s3.ap-southeast-1.amazonaws.com/static/image-file-icon.png');
247
+ }
248
+
207
249
  /**
208
250
  * Get the warranty name.
209
251
  */
@@ -432,4 +474,29 @@ class Device extends Model
432
474
  ->limit($limit)
433
475
  ->get();
434
476
  }
477
+
478
+ /**
479
+ * Creates a new position for the vehicle.
480
+ */
481
+ public function createPosition(array $attributes = [], Model|string|null $destination = null): ?Position
482
+ {
483
+ if (!isset($attributes['coordinates']) && isset($attributes['location'])) {
484
+ $attributes['coordinates'] = $attributes['location'];
485
+ }
486
+
487
+ if (!isset($attributes['coordinates']) && isset($attributes['latitude']) && isset($attributes['longitude'])) {
488
+ $attributes['coordinates'] = new SpatialPoint($attributes['latitude'], $attributes['longitude']);
489
+ }
490
+
491
+ // handle destination if set
492
+ $destinationUuid = Str::isUuid($destination) ? $destination : data_get($destination, 'uuid');
493
+
494
+ return Position::create([
495
+ ...Arr::only($attributes, ['coordinates', 'heading', 'bearing', 'speed', 'altitude', 'order_uuid']),
496
+ 'subject_uuid' => $this->uuid,
497
+ 'subject_type' => $this->getMorphClass(),
498
+ 'company_uuid' => $this->company_uuid,
499
+ 'destination_uuid' => $destinationUuid,
500
+ ]);
501
+ }
435
502
  }
@@ -4,6 +4,7 @@ namespace Fleetbase\FleetOps\Models;
4
4
 
5
5
  use Fleetbase\Casts\Json;
6
6
  use Fleetbase\Models\Alert;
7
+ use Fleetbase\Models\Company;
7
8
  use Fleetbase\Models\Model;
8
9
  use Fleetbase\Models\User;
9
10
  use Fleetbase\Traits\HasApiModelBehavior;
@@ -67,6 +68,7 @@ class DeviceEvent extends Model
67
68
  * @var array
68
69
  */
69
70
  protected $fillable = [
71
+ 'company_uuid',
70
72
  'device_uuid',
71
73
  'payload',
72
74
  'meta',
@@ -81,6 +83,7 @@ class DeviceEvent extends Model
81
83
  'code',
82
84
  'reason',
83
85
  'comment',
86
+ 'resolved_at',
84
87
  'slug',
85
88
  ];
86
89
 
@@ -109,9 +112,9 @@ class DeviceEvent extends Model
109
112
  * @var array
110
113
  */
111
114
  protected $casts = [
112
- 'data' => Json::class,
113
- 'occurred_at' => 'datetime',
114
- 'processed_at' => 'datetime',
115
+ 'payload' => Json::class,
116
+ 'meta' => Json::class,
117
+ 'resolved_at' => 'datetime',
115
118
  ];
116
119
 
117
120
  /**
@@ -143,6 +146,11 @@ class DeviceEvent extends Model
143
146
  return LogOptions::defaults()->logAll();
144
147
  }
145
148
 
149
+ public function company(): BelongsTo
150
+ {
151
+ return $this->belongsTo(Company::class, 'company_uuid', 'uuid');
152
+ }
153
+
146
154
  public function device(): BelongsTo
147
155
  {
148
156
  return $this->belongsTo(Device::class, 'device_uuid', 'uuid');
@@ -498,4 +506,26 @@ class DeviceEvent extends Model
498
506
  'created_at' => $this->created_at?->toISOString(),
499
507
  ];
500
508
  }
509
+
510
+ /**
511
+ * Create a Position for this event’s device attachable using position data.
512
+ *
513
+ * This delegates to the shared DeviceEventHelper utility.
514
+ */
515
+ public function createPosition(array $positionData = []): ?Position
516
+ {
517
+ if (empty($positionData)) {
518
+ return null;
519
+ }
520
+
521
+ // Ensure device and attachable are loaded
522
+ $this->loadMissing('device.attachable');
523
+ $attachable = $this->device?->attachable;
524
+
525
+ if ($attachable && method_exists($attachable, 'createPosition')) {
526
+ return $attachable->createPosition($positionData);
527
+ }
528
+
529
+ return null;
530
+ }
501
531
  }
@@ -8,6 +8,7 @@ use Fleetbase\FleetOps\Scopes\DriverScope;
8
8
  use Fleetbase\FleetOps\Support\Utils;
9
9
  use Fleetbase\FleetOps\Support\Utils as FleetOpsUtils;
10
10
  use Fleetbase\LaravelMysqlSpatial\Eloquent\SpatialTrait;
11
+ use Fleetbase\LaravelMysqlSpatial\Types\Point as SpatialPoint;
11
12
  use Fleetbase\Models\File;
12
13
  use Fleetbase\Models\Model;
13
14
  use Fleetbase\Models\User;
@@ -24,6 +25,7 @@ use Illuminate\Database\Eloquent\Relations\BelongsTo;
24
25
  use Illuminate\Database\Eloquent\Relations\HasMany;
25
26
  use Illuminate\Database\Eloquent\Relations\HasManyThrough;
26
27
  use Illuminate\Notifications\Notifiable;
28
+ use Illuminate\Support\Arr;
27
29
  use Illuminate\Support\Collection;
28
30
  use Illuminate\Support\Facades\DB;
29
31
  use Illuminate\Support\Str;
@@ -645,7 +647,7 @@ class Driver extends Model
645
647
  $positionData = [
646
648
  'company_uuid' => session('company', $this->company_uuid),
647
649
  'subject_uuid' => $this->uuid,
648
- 'subject_type' => get_class($this),
650
+ 'subject_type' => $this->getMorphClass(),
649
651
  'coordinates' => $this->location,
650
652
  'altitude' => $this->altitude,
651
653
  'heading' => $this->heading,
@@ -661,6 +663,31 @@ class Driver extends Model
661
663
  return ($isFirstPosition || $isPast50Meters) ? Position::create($positionData) : null;
662
664
  }
663
665
 
666
+ /**
667
+ * Creates a new position for the vehicle.
668
+ */
669
+ public function createPosition(array $attributes = [], Model|string|null $destination = null): ?Position
670
+ {
671
+ if (!isset($attributes['coordinates']) && isset($attributes['location'])) {
672
+ $attributes['coordinates'] = $attributes['location'];
673
+ }
674
+
675
+ if (!isset($attributes['coordinates']) && isset($attributes['latitude']) && isset($attributes['longitude'])) {
676
+ $attributes['coordinates'] = new SpatialPoint($attributes['latitude'], $attributes['longitude']);
677
+ }
678
+
679
+ // handle destination if set
680
+ $destinationUuid = Str::isUuid($destination) ? $destination : data_get($destination, 'uuid');
681
+
682
+ return Position::create([
683
+ ...Arr::only($attributes, ['coordinates', 'heading', 'bearing', 'speed', 'altitude', 'order_uuid']),
684
+ 'subject_uuid' => $this->uuid,
685
+ 'subject_type' => $this->getMorphClass(),
686
+ 'company_uuid' => $this->company_uuid,
687
+ 'destination_uuid' => $destinationUuid,
688
+ ]);
689
+ }
690
+
664
691
  /**
665
692
  * Get the user relationship from the driver.
666
693
  */
@@ -3,6 +3,7 @@
3
3
  namespace Fleetbase\FleetOps\Models;
4
4
 
5
5
  use Fleetbase\Casts\Json;
6
+ use Fleetbase\Casts\PolymorphicType;
6
7
  use Fleetbase\Models\Model;
7
8
  use Fleetbase\Models\User;
8
9
  use Fleetbase\Traits\HasApiModelBehavior;
@@ -122,18 +123,20 @@ class Maintenance extends Model
122
123
  * @var array
123
124
  */
124
125
  protected $casts = [
125
- 'scheduled_at' => 'datetime',
126
- 'started_at' => 'datetime',
127
- 'completed_at' => 'datetime',
128
- 'odometer' => 'integer',
129
- 'engine_hours' => 'integer',
130
- 'labor_cost' => 'decimal:2',
131
- 'parts_cost' => 'decimal:2',
132
- 'tax' => 'decimal:2',
133
- 'total_cost' => 'decimal:2',
134
- 'line_items' => Json::class,
135
- 'attachments' => Json::class,
136
- 'meta' => Json::class,
126
+ 'scheduled_at' => 'datetime',
127
+ 'started_at' => 'datetime',
128
+ 'completed_at' => 'datetime',
129
+ 'odometer' => 'integer',
130
+ 'engine_hours' => 'integer',
131
+ 'labor_cost' => 'decimal:2',
132
+ 'parts_cost' => 'decimal:2',
133
+ 'tax' => 'decimal:2',
134
+ 'total_cost' => 'decimal:2',
135
+ 'line_items' => Json::class,
136
+ 'attachments' => Json::class,
137
+ 'meta' => Json::class,
138
+ 'maintainable_type' => PolymorphicType::class,
139
+ 'performed_by_type' => PolymorphicType::class,
137
140
  ];
138
141
 
139
142
  /**
@@ -3,6 +3,7 @@
3
3
  namespace Fleetbase\FleetOps\Models;
4
4
 
5
5
  use Fleetbase\Casts\Json;
6
+ use Fleetbase\Casts\PolymorphicType;
6
7
  use Fleetbase\FleetOps\Traits\Maintainable;
7
8
  use Fleetbase\Models\Alert;
8
9
  use Fleetbase\Models\File;
@@ -129,6 +130,7 @@ class Part extends Model
129
130
  'msrp' => 'decimal:2',
130
131
  'specs' => Json::class,
131
132
  'meta' => Json::class,
133
+ 'asset_type' => PolymorphicType::class,
132
134
  ];
133
135
 
134
136
  /**
@@ -388,7 +388,6 @@ class Payload extends Model
388
388
  } else {
389
389
  $place = Place::createFromMixed($attributes);
390
390
 
391
-
392
391
  // Store temp search UUID for traceability if present and different
393
392
  if ($place instanceof Place && isset($attributes['uuid']) && $place->uuid !== $attributes['uuid']) {
394
393
  $place->updateMeta('search_uuid', $attributes['uuid']);
@@ -366,6 +366,7 @@ class Place extends Model
366
366
  if ($saveInstance) {
367
367
  $place->save();
368
368
  }
369
+
369
370
  return $place;
370
371
  }
371
372
 
@@ -554,8 +555,10 @@ class Place extends Model
554
555
  }
555
556
 
556
557
  // If has $attributes['address']
558
+ $address = $place['address'];
557
559
  if (!empty($place['address'])) {
558
- return static::createFromGeocodingLookup($place['address'], $saveInstance);
560
+ // return static::createFromGeocodingLookup($place['address'], $saveInstance);
561
+ return static::create(array_merge($place, static::getValuesFromGeocodingLookup($address)));
559
562
  }
560
563
 
561
564
  // Perform google lookup to fill address
@@ -2,11 +2,14 @@
2
2
 
3
3
  namespace Fleetbase\FleetOps\Models;
4
4
 
5
+ use Fleetbase\Casts\PolymorphicType;
5
6
  use Fleetbase\LaravelMysqlSpatial\Eloquent\SpatialTrait;
6
7
  use Fleetbase\Models\Model;
7
8
  use Fleetbase\Traits\HasApiModelBehavior;
8
9
  use Fleetbase\Traits\HasUuid;
9
10
  use Fleetbase\Traits\TracksApiCredential;
11
+ use Illuminate\Database\Eloquent\Relations\BelongsTo;
12
+ use Illuminate\Database\Eloquent\Relations\MorphTo;
10
13
 
11
14
  class Position extends Model
12
15
  {
@@ -48,7 +51,16 @@ class Position extends Model
48
51
  *
49
52
  * @var array
50
53
  */
51
- protected $appends = [];
54
+ protected $appends = ['latitude', 'longitude'];
55
+
56
+ /**
57
+ * The attributes that should be cast to native types.
58
+ *
59
+ * @var array
60
+ */
61
+ protected $casts = [
62
+ 'subject_type' => PolymorphicType::class,
63
+ ];
52
64
 
53
65
  /**
54
66
  * Get filter parameters for this model.
@@ -85,35 +97,33 @@ class Position extends Model
85
97
  */
86
98
  protected static $logName = 'position';
87
99
 
88
- /**
89
- * @return \Illuminate\Database\Eloquent\Relations\BelongsTo
90
- */
91
- public function company()
100
+ public function company(): BelongsTo
92
101
  {
93
102
  return $this->belongsTo(\Fleetbase\Models\Company::class);
94
103
  }
95
104
 
96
- /**
97
- * @return \Illuminate\Database\Eloquent\Relations\BelongsTo
98
- */
99
- public function order()
105
+ public function order(): BelongsTo
100
106
  {
101
107
  return $this->belongsTo(Order::class);
102
108
  }
103
109
 
104
- /**
105
- * @return \Illuminate\Database\Eloquent\Relations\BelongsTo
106
- */
107
- public function destination()
110
+ public function destination(): BelongsTo
108
111
  {
109
112
  return $this->belongsTo(Place::class);
110
113
  }
111
114
 
112
- /**
113
- * @return \Illuminate\Database\Eloquent\Relations\MorphTo
114
- */
115
- public function subject()
115
+ public function subject(): MorphTo
116
116
  {
117
117
  return $this->morphTo(__FUNCTION__, 'subject_type', 'subject_uuid')->withoutGlobalScopes();
118
118
  }
119
+
120
+ public function getLongitudeAttribute(): float
121
+ {
122
+ return $this->coordinates?->getLng() ?? 0;
123
+ }
124
+
125
+ public function getLatitudeAttribute(): float
126
+ {
127
+ return $this->coordinates?->getLat() ?? 0;
128
+ }
119
129
  }
@@ -3,7 +3,11 @@
3
3
  namespace Fleetbase\FleetOps\Models;
4
4
 
5
5
  use Fleetbase\Casts\Json;
6
+ use Fleetbase\FleetOps\Casts\Point;
7
+ use Fleetbase\LaravelMysqlSpatial\Eloquent\SpatialTrait;
8
+ use Fleetbase\LaravelMysqlSpatial\Types\Point as SpatialPoint;
6
9
  use Fleetbase\Models\Alert;
10
+ use Fleetbase\Models\File;
7
11
  use Fleetbase\Models\Model;
8
12
  use Fleetbase\Models\User;
9
13
  use Fleetbase\Traits\HasApiModelBehavior;
@@ -16,6 +20,8 @@ use Fleetbase\Traits\TracksApiCredential;
16
20
  use Illuminate\Database\Eloquent\Relations\BelongsTo;
17
21
  use Illuminate\Database\Eloquent\Relations\HasMany;
18
22
  use Illuminate\Database\Eloquent\Relations\MorphTo;
23
+ use Illuminate\Support\Arr;
24
+ use Illuminate\Support\Str;
19
25
  use Spatie\Activitylog\LogOptions;
20
26
  use Spatie\Activitylog\Traits\LogsActivity;
21
27
  use Spatie\Sluggable\HasSlug;
@@ -39,6 +45,7 @@ class Sensor extends Model
39
45
  use HasMetaAttributes;
40
46
  use Searchable;
41
47
  use HasCustomFields;
48
+ use SpatialTrait;
42
49
 
43
50
  /**
44
51
  * The database table used by the model.
@@ -77,8 +84,16 @@ class Sensor extends Model
77
84
  'company_uuid',
78
85
  'device_uuid',
79
86
  'warranty_uuid',
87
+ 'telematic_uuid',
88
+ 'photo_uuid',
80
89
  'name',
81
- 'sensor_type',
90
+ 'type',
91
+ 'internal_id',
92
+ 'imei',
93
+ 'imsi',
94
+ 'firmware_version',
95
+ 'serial_number',
96
+ 'last_position',
82
97
  'unit',
83
98
  'min_threshold',
84
99
  'max_threshold',
@@ -100,12 +115,9 @@ class Sensor extends Model
100
115
  * @var array
101
116
  */
102
117
  protected $appends = [
103
- 'device_name',
104
- 'warranty_name',
105
- 'attached_to_name',
106
118
  'is_active',
107
119
  'threshold_status',
108
- 'last_reading_formatted',
120
+ 'photo_url',
109
121
  ];
110
122
 
111
123
  /**
@@ -113,7 +125,14 @@ class Sensor extends Model
113
125
  *
114
126
  * @var array
115
127
  */
116
- protected $hidden = ['device', 'warranty', 'sensorable'];
128
+ protected $hidden = [];
129
+
130
+ /**
131
+ * The attributes that are spatial columns.
132
+ *
133
+ * @var array
134
+ */
135
+ protected $spatialFields = ['last_position'];
117
136
 
118
137
  /**
119
138
  * The attributes that should be cast to native types.
@@ -121,13 +140,14 @@ class Sensor extends Model
121
140
  * @var array
122
141
  */
123
142
  protected $casts = [
124
- 'min_threshold' => 'float',
125
- 'max_threshold' => 'float',
126
- 'threshold_inclusive' => 'boolean',
127
- 'last_reading_at' => 'datetime',
128
- 'report_frequency_sec' => 'integer',
129
- 'calibration' => Json::class,
130
- 'meta' => Json::class,
143
+ 'min_threshold' => 'float',
144
+ 'max_threshold' => 'float',
145
+ 'threshold_inclusive' => 'boolean',
146
+ 'last_reading_at' => 'datetime',
147
+ 'report_frequency_sec' => 'integer',
148
+ 'last_position' => Point::class,
149
+ 'calibration' => Json::class,
150
+ 'meta' => Json::class,
131
151
  ];
132
152
 
133
153
  /**
@@ -169,6 +189,11 @@ class Sensor extends Model
169
189
  return LogOptions::defaults()->logAll();
170
190
  }
171
191
 
192
+ public function telematic(): BelongsTo
193
+ {
194
+ return $this->belongsTo(Telematic::class, 'telematic_uuid', 'uuid');
195
+ }
196
+
172
197
  public function device(): BelongsTo
173
198
  {
174
199
  return $this->belongsTo(Device::class, 'device_uuid', 'uuid');
@@ -194,12 +219,27 @@ class Sensor extends Model
194
219
  return $this->morphTo();
195
220
  }
196
221
 
222
+ public function photo(): BelongsTo
223
+ {
224
+ return $this->belongsTo(File::class);
225
+ }
226
+
197
227
  public function alerts(): HasMany
198
228
  {
199
229
  return $this->hasMany(Alert::class, 'subject_uuid', 'uuid')
200
230
  ->where('subject_type', static::class);
201
231
  }
202
232
 
233
+ /**
234
+ * Get photo URL attribute.
235
+ *
236
+ * @return string
237
+ */
238
+ public function getPhotoUrlAttribute()
239
+ {
240
+ return data_get($this, 'photo.url', 'https://flb-assets.s3.ap-southeast-1.amazonaws.com/static/image-file-icon.png');
241
+ }
242
+
203
243
  /**
204
244
  * Get the device name.
205
245
  */
@@ -507,4 +547,29 @@ class Sensor extends Model
507
547
  ],
508
548
  ];
509
549
  }
550
+
551
+ /**
552
+ * Creates a new position for the vehicle.
553
+ */
554
+ public function createPosition(array $attributes = [], Model|string|null $destination = null): ?Position
555
+ {
556
+ if (!isset($attributes['coordinates']) && isset($attributes['location'])) {
557
+ $attributes['coordinates'] = $attributes['location'];
558
+ }
559
+
560
+ if (!isset($attributes['coordinates']) && isset($attributes['latitude']) && isset($attributes['longitude'])) {
561
+ $attributes['coordinates'] = new SpatialPoint($attributes['latitude'], $attributes['longitude']);
562
+ }
563
+
564
+ // handle destination if set
565
+ $destinationUuid = Str::isUuid($destination) ? $destination : data_get($destination, 'uuid');
566
+
567
+ return Position::create([
568
+ ...Arr::only($attributes, ['coordinates', 'heading', 'bearing', 'speed', 'altitude', 'order_uuid']),
569
+ 'subject_uuid' => $this->uuid,
570
+ 'subject_type' => $this->getMorphClass(),
571
+ 'company_uuid' => $this->company_uuid,
572
+ 'destination_uuid' => $destinationUuid,
573
+ ]);
574
+ }
510
575
  }