@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
|
@@ -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
|
|
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.
|
|
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
package/package.json
CHANGED
|
@@ -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
|
+
};
|