@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.
Files changed (49) hide show
  1. package/addon/components/layout/fleet-ops-sidebar.hbs +25 -0
  2. package/addon/templates/virtual.hbs +3 -3
  3. package/composer.json +3 -2
  4. package/extension.json +1 -1
  5. package/package.json +1 -1
  6. package/server/migrations/2025_08_11_170800_add_company_index_to_routes_table.php +21 -0
  7. package/server/migrations/2025_08_28_054920_create_warranties_table.php +56 -0
  8. package/server/migrations/2025_08_28_054921_create_telematics_table.php +60 -0
  9. package/server/migrations/2025_08_28_054922_create_assets_table.php +94 -0
  10. package/server/migrations/2025_08_28_054925_create_devices_table.php +190 -0
  11. package/server/migrations/2025_08_28_054926_create_device_events_table.php +99 -0
  12. package/server/migrations/2025_08_28_054926_create_sensors_table.php +62 -0
  13. package/server/migrations/2025_08_28_054927_create_parts_table.php +73 -0
  14. package/server/migrations/2025_08_28_054929_create_equipments_table.php +58 -0
  15. package/server/migrations/2025_08_28_054930_create_work_orders_table.php +85 -0
  16. package/server/migrations/2025_08_28_054931_create_maintenances_table.php +79 -0
  17. package/server/migrations/2025_08_28_082002_update_vehicles_table_telematics.php +60 -0
  18. package/server/src/Http/Controllers/Api/v1/OrderController.php +19 -1
  19. package/server/src/Http/Controllers/Internal/v1/OrderController.php +31 -8
  20. package/server/src/Http/Resources/v1/Order.php +111 -60
  21. package/server/src/Models/Asset.php +548 -0
  22. package/server/src/Models/Contact.php +2 -0
  23. package/server/src/Models/Device.php +435 -0
  24. package/server/src/Models/DeviceEvent.php +501 -0
  25. package/server/src/Models/Driver.php +2 -0
  26. package/server/src/Models/Entity.php +27 -50
  27. package/server/src/Models/Equipment.php +483 -0
  28. package/server/src/Models/Fleet.php +2 -0
  29. package/server/src/Models/FuelReport.php +2 -0
  30. package/server/src/Models/Issue.php +2 -0
  31. package/server/src/Models/Maintenance.php +549 -0
  32. package/server/src/Models/Order.php +32 -112
  33. package/server/src/Models/OrderConfig.php +8 -0
  34. package/server/src/Models/Part.php +502 -0
  35. package/server/src/Models/Payload.php +101 -20
  36. package/server/src/Models/Place.php +10 -4
  37. package/server/src/Models/Sensor.php +510 -0
  38. package/server/src/Models/ServiceArea.php +1 -1
  39. package/server/src/Models/Telematic.php +336 -0
  40. package/server/src/Models/Vehicle.php +45 -1
  41. package/server/src/Models/VehicleDevice.php +1 -1
  42. package/server/src/Models/Vendor.php +2 -0
  43. package/server/src/Models/Warranty.php +413 -0
  44. package/server/src/Models/Waypoint.php +2 -0
  45. package/server/src/Models/WorkOrder.php +532 -0
  46. package/server/src/Support/Utils.php +5 -0
  47. package/server/src/Traits/HasTrackingNumber.php +64 -10
  48. package/server/src/Traits/Maintainable.php +307 -0
  49. package/server/src/Traits/PayloadAccessors.php +126 -0
@@ -21,6 +21,11 @@
21
21
  @onClick={{fn this.universe.transitionMenuItem (concat this.routePrefix "virtual") menuItem}}
22
22
  @menuItem={{menuItem}}
23
23
  @icon={{menuItem.icon}}
24
+ @iconComponent={{menuItem.iconComponent}}
25
+ @iconComponentOptions={{menuItem.iconComponentOptions}}
26
+ @iconSize={{menuItem.iconSize}}
27
+ @iconPrefix={{menuItem.iconPrefix}}
28
+ @iconClass={{menuItem.iconClass}}
24
29
  @rightSideComponent={{menuItem.rightSideComponent}}
25
30
  @rightSideComponentContext={{menuItem.rightSideComponentContext}}
26
31
  @rightSideStatus={{menuItem.rightSideStatus}}
@@ -68,6 +73,11 @@
68
73
  onClick=menuItem.onClick
69
74
  route=(concat this.routePrefix menuItem.route)
70
75
  icon=menuItem.icon
76
+ iconComponent=menuItem.iconComponent
77
+ iconComponentOptions=menuItem.iconComponentOptions
78
+ iconSize=menuItem.iconSize
79
+ iconPrefix=menuItem.iconPrefix
80
+ iconClass=menuItem.iconClass
71
81
  rightSideComponent=menuItem.rightSideComponent
72
82
  rightSideComponentContext=menuItem.rightSideComponentContext
73
83
  rightSideStatus=menuItem.rightSideStatus
@@ -94,6 +104,11 @@
94
104
  @onClick={{menuItem.onClick}}
95
105
  @route={{concat this.routePrefix menuItem.route}}
96
106
  @icon={{menuItem.icon}}
107
+ @iconComponent={{menuItem.iconComponent}}
108
+ @iconComponentOptions={{menuItem.iconComponentOptions}}
109
+ @iconSize={{menuItem.iconSize}}
110
+ @iconPrefix={{menuItem.iconPrefix}}
111
+ @iconClass={{menuItem.iconClass}}
97
112
  @rightSideComponent={{menuItem.rightSideComponent}}
98
113
  @rightSideComponentContext={{menuItem.rightSideComponentContext}}
99
114
  @rightSideStatus={{menuItem.rightSideStatus}}
@@ -126,6 +141,11 @@
126
141
  @onClick={{fn this.universe.transitionMenuItem (concat this.routePrefix "virtual") menuItem}}
127
142
  @menuItem={{menuItem}}
128
143
  @icon={{menuItem.icon}}
144
+ @iconComponent={{menuItem.iconComponent}}
145
+ @iconComponentOptions={{menuItem.iconComponentOptions}}
146
+ @iconSize={{menuItem.iconSize}}
147
+ @iconPrefix={{menuItem.iconPrefix}}
148
+ @iconClass={{menuItem.iconClass}}
129
149
  @rightSideComponent={{menuItem.rightSideComponent}}
130
150
  @rightSideComponentContext={{menuItem.rightSideComponentContext}}
131
151
  @rightSideStatus={{menuItem.rightSideStatus}}
@@ -172,6 +192,11 @@
172
192
  @onClick={{fn this.universe.transitionMenuItem (concat this.routePrefix "virtual") menuItem}}
173
193
  @menuItem={{menuItem}}
174
194
  @icon={{menuItem.icon}}
195
+ @iconComponent={{menuItem.iconComponent}}
196
+ @iconComponentOptions={{menuItem.iconComponentOptions}}
197
+ @iconSize={{menuItem.iconSize}}
198
+ @iconPrefix={{menuItem.iconPrefix}}
199
+ @iconClass={{menuItem.iconClass}}
175
200
  @rightSideComponent={{menuItem.rightSideComponent}}
176
201
  @rightSideComponentContext={{menuItem.rightSideComponentContext}}
177
202
  @rightSideStatus={{menuItem.rightSideStatus}}
@@ -1,8 +1,8 @@
1
1
  <Layout::Section::Header @title={{@model.title}} />
2
2
 
3
- <Layout::Section::Body class="overflow-y-scroll h-full">
4
- <div class="container mx-auto h-screen">
5
- <div class="max-w-3xl my-10 mx-auto space-y-">
3
+ <Layout::Section::Body id="fleetops-virtual-body" class="overflow-y-scroll h-full">
4
+ <div id="fleetops-virtual-container" class="container mx-auto h-screen">
5
+ <div id="fleetops-virtual-wrapper" class="max-w-3xl my-10 mx-auto">
6
6
  {{component @model.component params=@model.componentParams}}
7
7
  </div>
8
8
  </div>
package/composer.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fleetbase/fleetops-api",
3
- "version": "0.6.16",
3
+ "version": "0.6.18",
4
4
  "description": "Fleet & Transport Management Extension for Fleetbase",
5
5
  "keywords": [
6
6
  "fleetbase-extension",
@@ -22,15 +22,16 @@
22
22
  ],
23
23
  "require": {
24
24
  "php": "^8.0",
25
- "fleetbase/core-api": "*",
26
25
  "barryvdh/laravel-dompdf": "^2.0",
27
26
  "brick/geo": "0.7.2",
28
27
  "cknow/laravel-money": "^7.1",
28
+ "fleetbase/core-api": "*",
29
29
  "geocoder-php/google-maps-places-provider": "^1.4",
30
30
  "giggsey/libphonenumber-for-php": "^8.13",
31
31
  "league/geotools": "^1.1.0",
32
32
  "milon/barcode": "^10.0",
33
33
  "php-http/guzzle7-adapter": "^1.0",
34
+ "php-units-of-measure/php-units-of-measure": "^2.2",
34
35
  "psr/http-factory-implementation": "*",
35
36
  "toin0u/geocoder-laravel": "^4.4",
36
37
  "webit/eval-math": "^1.0"
package/extension.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "Fleet-Ops",
3
- "version": "0.6.16",
3
+ "version": "0.6.18",
4
4
  "description": "Fleet & Transport Management Extension for Fleetbase",
5
5
  "repository": "https://github.com/fleetbase/fleetops",
6
6
  "license": "AGPL-3.0-or-later",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fleetbase/fleetops-engine",
3
- "version": "0.6.16",
3
+ "version": "0.6.18",
4
4
  "description": "Fleet & Transport Management Extension for Fleetbase",
5
5
  "fleetbase": {
6
6
  "route": "fleet-ops"
@@ -0,0 +1,21 @@
1
+ <?php
2
+
3
+ use Illuminate\Database\Migrations\Migration;
4
+ use Illuminate\Database\Schema\Blueprint;
5
+ use Illuminate\Support\Facades\Schema;
6
+
7
+ return new class extends Migration {
8
+ public function up(): void
9
+ {
10
+ Schema::table('routes', function (Blueprint $table) {
11
+ $table->index(['company_uuid', 'id'], 'idx_routes_company_id');
12
+ });
13
+ }
14
+
15
+ public function down(): void
16
+ {
17
+ Schema::table('routes', function (Blueprint $table) {
18
+ $table->dropIndex('idx_routes_company_id');
19
+ });
20
+ }
21
+ };
@@ -0,0 +1,56 @@
1
+ <?php
2
+
3
+ use Illuminate\Database\Migrations\Migration;
4
+ use Illuminate\Database\Schema\Blueprint;
5
+ use Illuminate\Support\Facades\Schema;
6
+
7
+ return new class extends Migration {
8
+ /**
9
+ * Run the migrations.
10
+ */
11
+ public function up(): void
12
+ {
13
+ Schema::create('warranties', function (Blueprint $table) {
14
+ $table->increments('id');
15
+ $table->uuid('uuid')->index();
16
+ $table->string('_key')->nullable()->index();
17
+ $table->foreignUuid('company_uuid')->constrained('companies', 'uuid')->cascadeOnDelete();
18
+ $table->foreignUuid('vendor_uuid')->nullable()->constrained('vendors', 'uuid')->nullOnDelete();
19
+ $table->foreignUuid('category_uuid')->nullable()->constrained('categories', 'uuid')->nullOnDelete();
20
+ $table->foreignUuid('created_by_uuid')->nullable()->constrained('users', 'uuid')->nullOnDelete();
21
+ $table->foreignUuid('updated_by_uuid')->nullable()->constrained('users', 'uuid')->nullOnDelete();
22
+
23
+ // Covered item
24
+ $table->string('subject_type')->nullable();
25
+ $table->uuid('subject_uuid')->nullable();
26
+ $table->index(['subject_type', 'subject_uuid']);
27
+
28
+ $table->string('provider')->nullable()->index(); // manufacturer or third-party
29
+ $table->string('policy_number')->nullable()->index();
30
+ $table->string('type')->nullable()->index();
31
+
32
+ $table->date('start_date')->nullable()->index();
33
+ $table->date('end_date')->nullable()->index();
34
+
35
+ $table->json('coverage')->nullable(); // { parts: true, labor: false, roadside: true, limits: ... }
36
+ $table->json('terms')->nullable(); // text/structured
37
+ $table->text('policy')->nullable(); // text/structured
38
+ $table->json('meta')->nullable();
39
+
40
+ $table->softDeletes();
41
+ $table->timestamps();
42
+
43
+ $table->index(['company_uuid', 'end_date']);
44
+ });
45
+ }
46
+
47
+ /**
48
+ * Reverse the migrations.
49
+ */
50
+ public function down(): void
51
+ {
52
+ Schema::disableForeignKeyConstraints();
53
+ Schema::dropIfExists('warranties');
54
+ Schema::enableForeignKeyConstraints();
55
+ }
56
+ };
@@ -0,0 +1,60 @@
1
+ <?php
2
+
3
+ use Illuminate\Database\Migrations\Migration;
4
+ use Illuminate\Database\Schema\Blueprint;
5
+ use Illuminate\Support\Facades\Schema;
6
+
7
+ return new class extends Migration {
8
+ /**
9
+ * Run the migrations.
10
+ */
11
+ public function up(): void
12
+ {
13
+ Schema::create('telematics', function (Blueprint $table) {
14
+ $table->increments('id');
15
+ $table->uuid('uuid')->index();
16
+ $table->string('_key')->nullable()->index();
17
+ $table->foreignUuid('company_uuid')->constrained('companies', 'uuid')->cascadeOnDelete();
18
+
19
+ $table->string('name')->nullable()->index(); // optional label
20
+ $table->string('provider')->nullable()->index(); // samsara, geotab, custom, etc.
21
+ $table->string('model')->nullable();
22
+ $table->string('serial_number')->nullable()->index();
23
+ $table->string('firmware_version')->nullable();
24
+ $table->string('status')->default('active')->index(); // active, inactive
25
+
26
+ // Connectivity identifiers
27
+ $table->string('imei')->nullable()->index();
28
+ $table->string('iccid')->nullable()->index();
29
+ $table->string('imsi')->nullable()->index();
30
+ $table->string('msisdn')->nullable()->index();
31
+ $table->string('type')->nullable()->index();
32
+
33
+ // Last heartbeat / metrics
34
+ $table->json('last_metrics')->nullable(); // { lat, lng, speed, heading, temp, ... }
35
+
36
+ $table->json('config')->nullable();
37
+ $table->json('meta')->nullable();
38
+
39
+ $table->foreignUuid('created_by_uuid')->nullable()->constrained('users', 'uuid')->nullOnDelete();
40
+ $table->foreignUuid('updated_by_uuid')->nullable()->constrained('users', 'uuid')->nullOnDelete();
41
+ $table->foreignUuid('warranty_uuid')->nullable()->constrained('warranties', 'uuid')->nullOnDelete();
42
+
43
+ $table->timestamp('last_seen_at')->nullable()->index();
44
+ $table->softDeletes();
45
+ $table->timestamps();
46
+
47
+ $table->index(['company_uuid', 'provider']);
48
+ });
49
+ }
50
+
51
+ /**
52
+ * Reverse the migrations.
53
+ */
54
+ public function down(): void
55
+ {
56
+ Schema::disableForeignKeyConstraints();
57
+ Schema::dropIfExists('telematics');
58
+ Schema::enableForeignKeyConstraints();
59
+ }
60
+ };
@@ -0,0 +1,94 @@
1
+ <?php
2
+
3
+ use Illuminate\Database\Migrations\Migration;
4
+ use Illuminate\Database\Schema\Blueprint;
5
+ use Illuminate\Support\Facades\Schema;
6
+
7
+ return new class extends Migration {
8
+ /**
9
+ * Run the migrations.
10
+ */
11
+ public function up(): void
12
+ {
13
+ Schema::create('assets', function (Blueprint $table) {
14
+ $table->increments('id');
15
+ $table->uuid('uuid')->index();
16
+ $table->string('_key')->nullable()->index();
17
+ $table->foreignUuid('company_uuid')->constrained('companies', 'uuid')->cascadeOnDelete();
18
+ $table->foreignUuid('category_uuid')->nullable()->constrained('categories', 'uuid')->nullOnDelete();
19
+
20
+ // Asset operator - can be vendor, contact, driver, user
21
+ $table->string('operator_type')->nullable();
22
+ $table->uuid('operator_uuid')->nullable();
23
+ $table->index(['operator_type', 'operator_uuid']);
24
+
25
+ // Asset assigned to - can be vendor, contact, driver, user
26
+ $table->string('assigned_to_type')->nullable();
27
+ $table->uuid('assigned_to_uuid')->nullable();
28
+ $table->index(['assigned_to_type', 'assigned_to_uuid']);
29
+
30
+ $table->string('name')->index();
31
+ $table->string('code')->nullable()->index(); // human code / fleet tag
32
+ $table->string('type')->nullable()->index(); // vehicle, trailer, container, drone, etc.
33
+ $table->string('status')->default('active')->index(); // active, inactive, retired, maintenance
34
+ $table->point('location')->nullable();
35
+ $table->string('speed')->nullable();
36
+ $table->string('heading')->nullable();
37
+ $table->string('altitude')->nullable();
38
+
39
+ // Financial Tracking
40
+ $table->integer('acquisition_cost')->nullable();
41
+ $table->integer('current_value')->nullable();
42
+ $table->integer('depreciation_rate')->nullable(); // -- Annual percentage
43
+ $table->integer('insurance_value')->nullable();
44
+ $table->string('currency')->nullable();
45
+ $table->string('financing_status')->nullable(); // -- owned, leased, financed
46
+ $table->date('lease_expires_at')->nullable()->index();
47
+
48
+ // Identity / registration
49
+ $table->string('vin')->nullable()->index();
50
+ $table->string('plate_number')->nullable()->index();
51
+ $table->string('make')->nullable();
52
+ $table->string('model')->nullable();
53
+ $table->unsignedSmallInteger('year')->nullable();
54
+ $table->string('color')->nullable();
55
+ $table->string('serial_number')->nullable()->index();
56
+ $table->string('call_sign')->nullable()->index();
57
+ $table->string('slug')->nullable()->index();
58
+
59
+ // Attachments & relations
60
+ $table->foreignUuid('vendor_uuid')->nullable()->constrained('vendors', 'uuid')->nullOnDelete();
61
+ $table->foreignUuid('current_place_uuid')->nullable()->constrained('places', 'uuid')->nullOnDelete();
62
+ $table->foreignUuid('telematic_uuid')->nullable()->constrained('telematics', 'uuid')->nullOnDelete();
63
+
64
+ // Operational attributes
65
+ $table->unsignedBigInteger('odometer')->nullable(); // in meters or units you prefer
66
+ $table->unsignedBigInteger('engine_hours')->nullable();
67
+ $table->decimal('gvw', 10, 2)->nullable(); // gross vehicle weight
68
+ $table->json('capacity')->nullable(); // e.g. { volume_l:..., payload_kg:... }
69
+ $table->json('specs')->nullable(); // arbitrary spec map
70
+ $table->json('attributes')->nullable(); // freeform tags/flags
71
+
72
+ // Ownership, auditing
73
+ $table->foreignUuid('created_by_uuid')->nullable()->constrained('users', 'uuid')->nullOnDelete();
74
+ $table->foreignUuid('updated_by_uuid')->nullable()->constrained('users', 'uuid')->nullOnDelete();
75
+ $table->foreignUuid('warranty_uuid')->nullable()->constrained('warranties', 'uuid')->nullOnDelete();
76
+
77
+ $table->softDeletes();
78
+ $table->timestamps();
79
+
80
+ $table->index(['company_uuid', 'status']);
81
+ $table->unique(['company_uuid', 'code']); // ensure human code unique per company
82
+ });
83
+ }
84
+
85
+ /**
86
+ * Reverse the migrations.
87
+ */
88
+ public function down(): void
89
+ {
90
+ Schema::disableForeignKeyConstraints();
91
+ Schema::dropIfExists('assets');
92
+ Schema::enableForeignKeyConstraints();
93
+ }
94
+ };
@@ -0,0 +1,190 @@
1
+ <?php
2
+
3
+ use Illuminate\Database\Migrations\Migration;
4
+ use Illuminate\Database\Schema\Blueprint;
5
+ use Illuminate\Support\Facades\DB;
6
+ use Illuminate\Support\Facades\Schema;
7
+
8
+ return new class extends Migration {
9
+ public function up(): void
10
+ {
11
+ // Rename legacy table if it exists
12
+ if (Schema::hasTable('vehicle_devices') && !Schema::hasTable('devices')) {
13
+ Schema::rename('vehicle_devices', 'devices');
14
+ }
15
+
16
+ // Bail out if the destination table still doesn't exist for some reason
17
+ if (!Schema::hasTable('devices')) {
18
+ return;
19
+ }
20
+
21
+ // Add/align new columns (guard each add with hasColumn)
22
+ Schema::table('devices', function (Blueprint $table) {
23
+ // warranty_uuid
24
+ if (!Schema::hasColumn('devices', 'warranty_uuid')) {
25
+ $table->foreignUuid('warranty_uuid')->after('uuid')->nullable()->constrained('warranties', 'uuid')->nullOnDelete();
26
+ }
27
+
28
+ // telematic_uuid (1:1 to telematics.uuid)
29
+ if (!Schema::hasColumn('devices', 'telematic_uuid')) {
30
+ $table->foreignUuid('telematic_uuid')->after('uuid')->nullable()->constrained('telematics', 'uuid')->nullOnDelete();
31
+ }
32
+
33
+ // lifecycle / telemetry
34
+ if (!Schema::hasColumn('devices', 'last_online_at')) {
35
+ $table->timestamp('last_online_at')->after('notes')->nullable()->index();
36
+ }
37
+
38
+ // polymorphic attachment: attachable_type + attachable_uuid (UUID)
39
+ if (!Schema::hasColumn('devices', 'attachable_type') && !Schema::hasColumn('devices', 'attachable_uuid')) {
40
+ $table->string('attachable_type')->after('telematic_uuid')->nullable();
41
+ $table->uuid('attachable_uuid')->after('telematic_uuid')->nullable();
42
+ $table->index(['attachable_type', 'attachable_uuid']);
43
+ }
44
+
45
+ // meta/options blobs (order doesn’t matter; avoid AFTER to keep this migration portable)
46
+ if (!Schema::hasColumn('devices', 'meta')) {
47
+ $table->json('meta')->nullable();
48
+ }
49
+
50
+ if (!Schema::hasColumn('devices', 'options')) {
51
+ $table->json('options')->nullable()->after('meta');
52
+ }
53
+
54
+ if (!Schema::hasColumn('devices', 'slug')) {
55
+ $table->string('slug')->nullable()->index()->after('status');
56
+ }
57
+
58
+ if (!Schema::hasColumn('devices', '_key')) {
59
+ $table->string('_key')->nullable()->index()->after('uuid');
60
+ }
61
+ });
62
+
63
+ // Data migration: vehicle_uuid → attachable (Vehicle)
64
+ if (Schema::hasColumn('devices', 'vehicle_uuid')) {
65
+ // Move values (MySQL/MariaDB & PostgreSQL compatible form)
66
+ // - For MySQL: backslashes in class name must be doubled in SQL string literal
67
+ $vehicleModel = 'Fleetbase\\\\FleetOps\\\\Models\\\\Vehicle';
68
+
69
+ // Use raw SQL to assign attachable_id from vehicle_uuid in one pass
70
+ // MySQL / MariaDB
71
+ if ($this->isMySql()) {
72
+ DB::statement("
73
+ UPDATE `devices`
74
+ SET `attachable_type` = '{$vehicleModel}',
75
+ `attachable_uuid` = `vehicle_uuid`
76
+ WHERE `vehicle_uuid` IS NOT NULL
77
+ ");
78
+ } else {
79
+ // PostgreSQL / others (no backticks)
80
+ $vehicleModelPg = 'Fleetbase\\FleetOps\\Models\\Vehicle';
81
+ DB::statement("
82
+ UPDATE devices
83
+ SET attachable_type = '{$vehicleModelPg}',
84
+ attachable_uuid = vehicle_uuid
85
+ WHERE vehicle_uuid IS NOT NULL
86
+ ");
87
+ }
88
+
89
+ // Drop the old column now that data is migrated
90
+ Schema::table('devices', function (Blueprint $table) {
91
+ if (Schema::hasColumn('devices', 'vehicle_uuid')) {
92
+ $table->dropForeign('vehicle_devices_vehicle_uuid_foreign');
93
+ $table->dropColumn('vehicle_uuid');
94
+ }
95
+ });
96
+ }
97
+ }
98
+
99
+ public function down(): void
100
+ {
101
+ // If devices table doesn't exist, nothing to do
102
+ if (!Schema::hasTable('devices')) {
103
+ return;
104
+ }
105
+
106
+ // Re-introduce vehicle_uuid to reverse the migration
107
+ Schema::table('devices', function (Blueprint $table) {
108
+ if (!Schema::hasColumn('devices', 'vehicle_uuid')) {
109
+ $table->uuid('vehicle_uuid')->after('uuid')->nullable();
110
+ }
111
+ });
112
+
113
+ // Move back attachable → vehicle_uuid where it was a Vehicle
114
+ $vehicleModelSqlMy = 'Fleetbase\\\\FleetOps\\\\Models\\\\Vehicle';
115
+ $vehicleModelSqlPg = 'Fleetbase\\FleetOps\\Models\\Vehicle';
116
+
117
+ if ($this->isMySql()) {
118
+ DB::statement("
119
+ UPDATE `devices`
120
+ SET `vehicle_uuid` = `attachable_uuid`
121
+ WHERE `attachable_type` = '{$vehicleModelSqlMy}'
122
+ ");
123
+ } else {
124
+ DB::statement("
125
+ UPDATE devices
126
+ SET vehicle_uuid = attachable_uuid
127
+ WHERE attachable_type = '{$vehicleModelSqlPg}'
128
+ ");
129
+ }
130
+
131
+ // Drop new columns added in up()
132
+ Schema::table('devices', function (Blueprint $table) {
133
+ // Drop FKs before columns if present
134
+ if (Schema::hasColumn('devices', 'telematic_uuid')) {
135
+ // fallback: FK name is unknown; attempt generic drop then column
136
+ try {
137
+ $table->dropForeign(['telematic_uuid']);
138
+ } catch (Throwable $e) {
139
+ }
140
+ $table->dropColumn('telematic_uuid');
141
+ }
142
+
143
+ // Drop FKs before columns if present
144
+ if (Schema::hasColumn('devices', 'warranty_uuid')) {
145
+ // fallback: FK name is unknown; attempt generic drop then column
146
+ try {
147
+ $table->dropForeign(['warranty_uuid']);
148
+ } catch (Throwable $e) {
149
+ }
150
+ $table->dropColumn('warranty_uuid');
151
+ }
152
+
153
+ if (Schema::hasColumn('devices', 'last_online_at')) {
154
+ $table->dropColumn('last_online_at');
155
+ }
156
+
157
+ if (Schema::hasColumn('devices', 'attachable_type') || Schema::hasColumn('devices', 'attachable_uuid')) {
158
+ $table->dropColumn('attachable_uuid');
159
+ $table->dropColumn('attachable_type');
160
+ }
161
+
162
+ if (Schema::hasColumn('devices', 'options')) {
163
+ $table->dropColumn('options');
164
+ }
165
+ if (Schema::hasColumn('devices', 'meta')) {
166
+ $table->dropColumn('meta');
167
+ }
168
+ });
169
+
170
+ // Rename devices back to vehicle_devices if that table does not already exist
171
+ if (!Schema::hasTable('vehicle_devices') && Schema::hasTable('devices')) {
172
+ Schema::rename('devices', 'vehicle_devices');
173
+ Schema::table('vehicle_devices', function (Blueprint $table) {
174
+ if (Schema::hasColumn('vehicle_devices', 'vehicle_uuid')) {
175
+ $table->foreign(['vehicle_uuid'])->references('uuid')->on('vehicles')->cascadeOnDelete();
176
+ }
177
+ });
178
+ Schema::table('vehicle_device_events', function (Blueprint $table) {
179
+ if (Schema::hasColumn('vehicle_device_events', 'vehicle_device_uuid')) {
180
+ $table->foreign(['vehicle_device_uuid'])->references('uuid')->on('vehicle_devices')->cascadeOnDelete();
181
+ }
182
+ });
183
+ }
184
+ }
185
+
186
+ private function isMySql(): bool
187
+ {
188
+ return in_array(DB::getDriverName(), ['mysql', 'mariadb']);
189
+ }
190
+ };
@@ -0,0 +1,99 @@
1
+ <?php
2
+
3
+ use Illuminate\Database\Migrations\Migration;
4
+ use Illuminate\Database\Schema\Blueprint;
5
+ use Illuminate\Support\Facades\DB;
6
+ use Illuminate\Support\Facades\Schema;
7
+
8
+ return new class extends Migration {
9
+ public function up(): void
10
+ {
11
+ // Rename legacy table if needed
12
+ if (Schema::hasTable('vehicle_device_events') && !Schema::hasTable('device_events')) {
13
+ Schema::rename('vehicle_device_events', 'device_events');
14
+ }
15
+
16
+ if (!Schema::hasTable('device_events')) {
17
+ // Nothing to do if the table doesn't exist
18
+ return;
19
+ }
20
+
21
+ // Add device_uuid FK (many events per device; do NOT make it unique)
22
+ Schema::table('device_events', function (Blueprint $table) {
23
+ if (!Schema::hasColumn('device_events', 'device_uuid')) {
24
+ $table->foreignUuid('device_uuid')->after('uuid')->nullable()->constrained('devices', 'uuid')->cascadeOnDelete();
25
+ }
26
+
27
+ if (!Schema::hasColumn('device_events', 'event_type')) {
28
+ $table->string('event_type')->after('ident')->nullable();
29
+ }
30
+
31
+ if (!Schema::hasColumn('device_events', 'severity')) {
32
+ $table->string('severity')->after('ident')->nullable();
33
+ }
34
+
35
+ if (!Schema::hasColumn('device_events', '_key')) {
36
+ $table->string('_key')->after('uuid')->nullable()->index();
37
+ }
38
+ });
39
+
40
+ // Migrate data: vehicle_device_uuid -> device_uuid (if legacy column exists)
41
+ if (Schema::hasColumn('device_events', 'vehicle_device_uuid')) {
42
+ // Copy values in one pass
43
+ DB::table('device_events')
44
+ ->whereNotNull('vehicle_device_uuid')
45
+ ->update([
46
+ 'device_uuid' => DB::raw('vehicle_device_uuid'),
47
+ ]);
48
+
49
+ // Drop legacy column after successful copy
50
+ Schema::table('device_events', function (Blueprint $table) {
51
+ if (Schema::hasColumn('device_events', 'vehicle_device_uuid')) {
52
+ $table->dropForeign('vehicle_device_events_vehicle_device_uuid_foreign');
53
+ $table->dropColumn('vehicle_device_uuid');
54
+ }
55
+ });
56
+ }
57
+ }
58
+
59
+ public function down(): void
60
+ {
61
+ if (!Schema::hasTable('device_events')) {
62
+ return;
63
+ }
64
+
65
+ // 1) Re-introduce legacy column
66
+ Schema::table('device_events', function (Blueprint $table) {
67
+ if (!Schema::hasColumn('device_events', 'vehicle_device_uuid')) {
68
+ $table->uuid('vehicle_device_uuid')->nullable();
69
+ }
70
+ });
71
+
72
+ // 2) Move data back where present
73
+ if (Schema::hasColumn('device_events', 'device_uuid')) {
74
+ DB::table('device_events')
75
+ ->whereNotNull('device_uuid')
76
+ ->update([
77
+ 'vehicle_device_uuid' => DB::raw('device_uuid'),
78
+ ]);
79
+ }
80
+
81
+ // 3) Drop new FK/column
82
+ Schema::table('device_events', function (Blueprint $table) {
83
+ if (Schema::hasColumn('device_events', 'device_uuid')) {
84
+ // Drop FK safely when possible
85
+ // fallback: FK name is unknown; attempt generic drop then column
86
+ try {
87
+ $table->dropForeign(['device_uuid']);
88
+ } catch (Throwable $e) {
89
+ }
90
+ $table->dropColumn('device_uuid');
91
+ }
92
+ });
93
+
94
+ // 4) Rename table back if the old name doesn't exist
95
+ if (!Schema::hasTable('vehicle_device_events') && Schema::hasTable('device_events')) {
96
+ Schema::rename('device_events', 'vehicle_device_events');
97
+ }
98
+ }
99
+ };