@fleetbase/fleetops-engine 0.6.17 → 0.6.18

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) 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_28_054920_create_warranties_table.php +56 -0
  7. package/server/migrations/2025_08_28_054921_create_telematics_table.php +60 -0
  8. package/server/migrations/2025_08_28_054922_create_assets_table.php +94 -0
  9. package/server/migrations/2025_08_28_054925_create_devices_table.php +190 -0
  10. package/server/migrations/2025_08_28_054926_create_device_events_table.php +99 -0
  11. package/server/migrations/2025_08_28_054926_create_sensors_table.php +62 -0
  12. package/server/migrations/2025_08_28_054927_create_parts_table.php +73 -0
  13. package/server/migrations/2025_08_28_054929_create_equipments_table.php +58 -0
  14. package/server/migrations/2025_08_28_054930_create_work_orders_table.php +85 -0
  15. package/server/migrations/2025_08_28_054931_create_maintenances_table.php +79 -0
  16. package/server/migrations/2025_08_28_082002_update_vehicles_table_telematics.php +60 -0
  17. package/server/src/Http/Controllers/Api/v1/OrderController.php +6 -1
  18. package/server/src/Http/Resources/v1/Order.php +111 -60
  19. package/server/src/Models/Asset.php +548 -0
  20. package/server/src/Models/Contact.php +2 -0
  21. package/server/src/Models/Device.php +435 -0
  22. package/server/src/Models/DeviceEvent.php +501 -0
  23. package/server/src/Models/Driver.php +2 -0
  24. package/server/src/Models/Entity.php +2 -0
  25. package/server/src/Models/Equipment.php +483 -0
  26. package/server/src/Models/Fleet.php +2 -0
  27. package/server/src/Models/FuelReport.php +2 -0
  28. package/server/src/Models/Issue.php +2 -0
  29. package/server/src/Models/Maintenance.php +549 -0
  30. package/server/src/Models/Order.php +32 -112
  31. package/server/src/Models/OrderConfig.php +8 -0
  32. package/server/src/Models/Part.php +502 -0
  33. package/server/src/Models/Payload.php +101 -20
  34. package/server/src/Models/Place.php +10 -4
  35. package/server/src/Models/Sensor.php +510 -0
  36. package/server/src/Models/ServiceArea.php +1 -1
  37. package/server/src/Models/Telematic.php +336 -0
  38. package/server/src/Models/Vehicle.php +45 -1
  39. package/server/src/Models/VehicleDevice.php +1 -1
  40. package/server/src/Models/Vendor.php +2 -0
  41. package/server/src/Models/Warranty.php +413 -0
  42. package/server/src/Models/WorkOrder.php +532 -0
  43. package/server/src/Support/Utils.php +5 -0
  44. package/server/src/Traits/Maintainable.php +307 -0
@@ -0,0 +1,62 @@
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('sensors', 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();
20
+ $table->string('slug')->nullable()->index();
21
+ $table->string('sensor_type')->index(); // temp, humidity, door, fuel, pressure, vibration, cargo-weight, etc.
22
+ $table->string('unit')->nullable(); // C, F, %, psi, kg, etc.
23
+ $table->float('min_threshold')->nullable();
24
+ $table->float('max_threshold')->nullable();
25
+ $table->boolean('threshold_inclusive')->default(true);
26
+ $table->string('type')->nullable()->index();
27
+
28
+ $table->timestamp('last_reading_at')->nullable()->index();
29
+ $table->string('last_value')->nullable(); // use string to store raw value
30
+ $table->json('calibration')->nullable(); // { offset, slope, notes }
31
+ $table->unsignedInteger('report_frequency_sec')->nullable();
32
+
33
+ // A sensor can belong to a device, asset, or any other entity
34
+ $table->string('sensorable_type')->nullable();
35
+ $table->uuid('sensorable_uuid')->nullable();
36
+ $table->index(['sensorable_type', 'sensorable_uuid']);
37
+
38
+ // Optionally coupled to a device (if it streams via a device)
39
+ $table->foreignUuid('device_uuid')->nullable()->constrained('devices', 'uuid')->nullOnDelete();
40
+ $table->foreignUuid('warranty_uuid')->nullable()->constrained('warranties', 'uuid')->nullOnDelete();
41
+
42
+ $table->json('meta')->nullable();
43
+ $table->foreignUuid('created_by_uuid')->nullable()->constrained('users', 'uuid')->nullOnDelete();
44
+ $table->foreignUuid('updated_by_uuid')->nullable()->constrained('users', 'uuid')->nullOnDelete();
45
+
46
+ $table->softDeletes();
47
+ $table->timestamps();
48
+
49
+ $table->index(['company_uuid', 'sensor_type']);
50
+ });
51
+ }
52
+
53
+ /**
54
+ * Reverse the migrations.
55
+ */
56
+ public function down(): void
57
+ {
58
+ Schema::disableForeignKeyConstraints();
59
+ Schema::dropIfExists('sensors');
60
+ Schema::enableForeignKeyConstraints();
61
+ }
62
+ };
@@ -0,0 +1,73 @@
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('parts', 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
+ $table->string('sku')->nullable()->index();
21
+ $table->string('name')->index();
22
+ $table->string('manufacturer')->nullable()->index();
23
+ $table->string('model')->nullable();
24
+ $table->string('serial_number')->nullable()->index();
25
+ $table->string('barcode')->nullable()->index();
26
+ $table->string('type')->nullable()->index();
27
+ $table->string('slug')->nullable()->index();
28
+
29
+ $table->text('description')->nullable();
30
+ $table->foreignUuid('vendor_uuid')->nullable()->constrained('vendors', 'uuid')->nullOnDelete();
31
+
32
+ $table->integer('quantity_on_hand')->default(0);
33
+ $table->decimal('unit_cost', 12, 2)->nullable();
34
+ $table->decimal('msrp', 12, 2)->nullable();
35
+ $table->integer('reorder_point')->default(5);
36
+ $table->integer('reorder_quantity')->default(10);
37
+ $table->integer('max_stock_level')->nullable();
38
+ $table->integer('reserved_quantity')->default(0);
39
+
40
+ $table->integer('allocated_quantity')->default(0); // -- Allocated but not used
41
+ $table->string('inventory_method')->default('fifo'); // -- fifo, lifo, weighted_average
42
+
43
+ // Optional default target (e.g., a part kept for a specific asset or vehicle)
44
+ $table->string('asset_type')->nullable();
45
+ $table->uuid('asset_uuid')->nullable();
46
+ $table->index(['asset_type', 'asset_uuid']);
47
+
48
+ $table->foreignUuid('warranty_uuid')->nullable()->constrained('warranties', 'uuid')->nullOnDelete();
49
+
50
+ $table->json('specs')->nullable();
51
+ $table->json('meta')->nullable();
52
+
53
+ $table->foreignUuid('created_by_uuid')->nullable()->constrained('users', 'uuid')->nullOnDelete();
54
+ $table->foreignUuid('updated_by_uuid')->nullable()->constrained('users', 'uuid')->nullOnDelete();
55
+
56
+ $table->timestamp('last_ordered_at')->nullable();
57
+ $table->softDeletes();
58
+ $table->timestamps();
59
+
60
+ $table->unique(['company_uuid', 'sku']);
61
+ });
62
+ }
63
+
64
+ /**
65
+ * Reverse the migrations.
66
+ */
67
+ public function down(): void
68
+ {
69
+ Schema::disableForeignKeyConstraints();
70
+ Schema::dropIfExists('parts');
71
+ Schema::enableForeignKeyConstraints();
72
+ }
73
+ };
@@ -0,0 +1,58 @@
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('equipments', 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
+ $table->string('name')->index();
21
+ $table->string('code')->nullable()->index();
22
+ $table->string('type')->nullable()->index(); // ppe, tool, accessory, fridge unit, etc.
23
+ $table->string('status')->default('active')->index();
24
+ $table->string('slug')->nullable()->index();
25
+ $table->string('serial_number')->nullable()->index();
26
+ $table->string('manufacturer')->nullable();
27
+ $table->string('model')->nullable();
28
+
29
+ // Can be assigned to an asset, device, driver, or facility
30
+ $table->string('equipable_type')->nullable();
31
+ $table->uuid('equipable_uuid')->nullable();
32
+ $table->index(['equipable_type', 'equipable_uuid']);
33
+
34
+ $table->date('purchased_at')->nullable();
35
+ $table->integer('purchase_price')->nullable();
36
+ $table->string('currency')->nullable();
37
+
38
+ $table->foreignUuid('warranty_uuid')->nullable()->constrained('warranties', 'uuid')->nullOnDelete();
39
+
40
+ $table->json('meta')->nullable();
41
+ $table->foreignUuid('created_by_uuid')->nullable()->constrained('users', 'uuid')->nullOnDelete();
42
+ $table->foreignUuid('updated_by_uuid')->nullable()->constrained('users', 'uuid')->nullOnDelete();
43
+
44
+ $table->softDeletes();
45
+ $table->timestamps();
46
+ });
47
+ }
48
+
49
+ /**
50
+ * Reverse the migrations.
51
+ */
52
+ public function down(): void
53
+ {
54
+ Schema::disableForeignKeyConstraints();
55
+ Schema::dropIfExists('equipments');
56
+ Schema::enableForeignKeyConstraints();
57
+ }
58
+ };
@@ -0,0 +1,85 @@
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('work_orders', 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
+ $table->string('code')->nullable()->index(); // external WO number
21
+ $table->string('subject')->index();
22
+ $table->string('status')->default('open')->index(); // open, in_progress, blocked, done, canceled
23
+ $table->string('priority')->nullable()->index(); // low, normal, high, critical
24
+
25
+ // What the WO is for asset, equipment, device, place, etc.
26
+ $table->string('target_type')->nullable();
27
+ $table->uuid('target_uuid')->nullable();
28
+ $table->index(['target_type', 'target_uuid']);
29
+
30
+ // Who it's assigned to (contact/technician/team/user/vendor)
31
+ $table->string('assignee_type')->nullable();
32
+ $table->uuid('assignee_uuid')->nullable();
33
+ $table->index(['assignee_type', 'assignee_uuid']);
34
+
35
+ $table->timestamp('opened_at')->nullable()->index();
36
+ $table->timestamp('due_at')->nullable()->index();
37
+ $table->timestamp('closed_at')->nullable()->index();
38
+
39
+ // -- Resource Planning
40
+ $table->json('required_skills')->nullable(); // -- Skills needed for work
41
+ $table->json('required_certifications')->nullable(); // -- Certs needed
42
+ $table->integer('estimated_duration_hours')->nullable();
43
+ $table->integer('actual_duration_hours')->nullable();
44
+ $table->json('resource_requirements')->nullable(); // -- Tools, parts, etc.
45
+
46
+ // -- Scheduling & Dependencies
47
+ $table->timestamp('earliest_start_date')->nullable();
48
+ $table->timestamp('latest_completion_date')->nullable();
49
+ $table->string('scheduling_priority')->default('normal'); // -- low, normal, high, urgent
50
+ $table->boolean('can_be_split')->default(false); // -- Can work be divided
51
+
52
+ // -- Cost Management
53
+ $table->integer('estimated_cost')->nullable();
54
+ $table->integer('approved_budget')->nullable();
55
+ $table->integer('actual_cost')->nullable();
56
+ $table->string('currency')->nullable();
57
+ $table->json('cost_breakdown')->nullable(); // -- Labor, parts, overhead
58
+ $table->string('cost_center')->nullable()->index();
59
+ $table->string('budget_code')->nullable()->index();
60
+
61
+ $table->text('instructions')->nullable();
62
+ $table->text('completion_notes')->nullable();
63
+ $table->json('checklist')->nullable(); // [{title, required, done_at}, ...]
64
+ $table->json('meta')->nullable();
65
+
66
+ $table->foreignUuid('created_by_uuid')->nullable()->constrained('users', 'uuid')->nullOnDelete();
67
+ $table->foreignUuid('updated_by_uuid')->nullable()->constrained('users', 'uuid')->nullOnDelete();
68
+
69
+ $table->softDeletes();
70
+ $table->timestamps();
71
+
72
+ $table->index(['company_uuid', 'status', 'priority']);
73
+ });
74
+ }
75
+
76
+ /**
77
+ * Reverse the migrations.
78
+ */
79
+ public function down(): void
80
+ {
81
+ Schema::disableForeignKeyConstraints();
82
+ Schema::dropIfExists('work_orders');
83
+ Schema::enableForeignKeyConstraints();
84
+ }
85
+ };
@@ -0,0 +1,79 @@
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('maintenances', 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
+ // Target of maintenance (usually an asset; could be equipment)
21
+ $table->string('maintainable_type')->nullable();
22
+ $table->uuid('maintainable_uuid')->nullable();
23
+ $table->index(['maintainable_type', 'maintainable_uuid']);
24
+
25
+ $table->foreignUuid('work_order_uuid')->nullable()->constrained('work_orders', 'uuid')->nullOnDelete();
26
+
27
+ $table->string('type')->nullable()->index(); // scheduled, unscheduled, inspection, corrective
28
+ $table->string('status')->default('open')->index(); // open, in_progress, done, canceled
29
+ $table->string('priority')->nullable()->index(); // low, normal, high, critical
30
+
31
+ $table->timestamp('scheduled_at')->nullable()->index();
32
+ $table->timestamp('started_at')->nullable()->index();
33
+ $table->timestamp('completed_at')->nullable()->index();
34
+
35
+ $table->unsignedBigInteger('odometer')->nullable();
36
+ $table->unsignedBigInteger('engine_hours')->nullable();
37
+
38
+ // Downtime
39
+ $table->integer('estimated_downtime_hours')->nullable();
40
+ $table->timestamp('downtime_start')->nullable();
41
+ $table->timestamp('downtime_end')->nullable();
42
+
43
+ // Vendor or internal performer
44
+ $table->string('performed_by_type')->nullable();
45
+ $table->uuid('performed_by_uuid')->nullable();
46
+ $table->index(['performed_by_type', 'performed_by_uuid']);
47
+
48
+ $table->text('summary')->nullable();
49
+ $table->text('notes')->nullable();
50
+ $table->json('line_items')->nullable(); // [{part_uuid, qty, unit_cost}, ...]
51
+ $table->integer('labor_cost')->nullable();
52
+ $table->integer('parts_cost')->nullable();
53
+ $table->integer('tax')->nullable();
54
+ $table->integer('total_cost')->nullable();
55
+ $table->string('currency')->nullable();
56
+
57
+ $table->json('attachments')->nullable();
58
+ $table->json('meta')->nullable();
59
+
60
+ $table->foreignUuid('created_by_uuid')->nullable()->constrained('users', 'uuid')->nullOnDelete();
61
+ $table->foreignUuid('updated_by_uuid')->nullable()->constrained('users', 'uuid')->nullOnDelete();
62
+
63
+ $table->softDeletes();
64
+ $table->timestamps();
65
+
66
+ $table->index(['company_uuid', 'status', 'type']);
67
+ });
68
+ }
69
+
70
+ /**
71
+ * Reverse the migrations.
72
+ */
73
+ public function down(): void
74
+ {
75
+ Schema::disableForeignKeyConstraints();
76
+ Schema::dropIfExists('maintenances');
77
+ Schema::enableForeignKeyConstraints();
78
+ }
79
+ };
@@ -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::table('vehicles', function (Blueprint $table) {
14
+ $table->foreignUuid('telematic_uuid')->after('company_uuid')->nullable()->constrained('telematics', 'uuid')->nullOnDelete();
15
+ $table->foreignUuid('warranty_uuid')->after('telematic_uuid')->nullable()->constrained('warranties', 'uuid')->nullOnDelete();
16
+ $table->foreignUuid('category_uuid')->after('telematic_uuid')->nullable()->constrained('categories', 'uuid')->nullOnDelete();
17
+
18
+ // Identification
19
+ $table->string('serial_number')->nullable()->after('plate_number');
20
+ $table->string('call_sign')->nullable()->after('plate_number');
21
+
22
+ // Financial Tracking
23
+ $table->integer('acquisition_cost')->nullable()->after('vin_data');
24
+ $table->integer('current_value')->nullable()->after('vin_data');
25
+ $table->integer('depreciation_rate')->nullable()->after('vin_data'); // -- Annual percentage
26
+ $table->integer('insurance_value')->nullable()->after('vin_data');
27
+ $table->string('currency')->nullable()->after('vin_data');
28
+ $table->string('financing_status')->nullable()->index()->after('vin_data'); // -- owned, leased, financed
29
+ $table->timestamp('lease_expires_at')->nullable()->index()->after('slug');
30
+ $table->timestamp('purchased_at')->nullable()->index()->after('slug');
31
+ });
32
+ }
33
+
34
+ /**
35
+ * Reverse the migrations.
36
+ */
37
+ public function down(): void
38
+ {
39
+ Schema::disableForeignKeyConstraints();
40
+ Schema::table('vehicles', function (Blueprint $table) {
41
+ $table->dropForeign(['telematic_uuid']);
42
+ $table->dropForeign(['warranty_uuid']);
43
+ $table->dropForeign(['category_uuid']);
44
+ $table->dropColumn('telematic_uuid');
45
+ $table->dropColumn('warranty_uuid');
46
+ $table->dropColumn('category_uuid');
47
+ $table->dropColumn('serial_number');
48
+ $table->dropColumn('call_sign');
49
+ $table->dropColumn('acquisition_cost');
50
+ $table->dropColumn('current_value');
51
+ $table->dropColumn('depreciation_rate');
52
+ $table->dropColumn('insurance_value');
53
+ $table->dropColumn('currency');
54
+ $table->dropColumn('financing_status');
55
+ $table->dropColumn('lease_expires_at');
56
+ $table->dropColumn('purchased_at');
57
+ });
58
+ Schema::enableForeignKeyConstraints();
59
+ }
60
+ };
@@ -36,6 +36,7 @@ use Illuminate\Http\Request;
36
36
  use Illuminate\Http\UploadedFile;
37
37
  use Illuminate\Support\Arr;
38
38
  use Illuminate\Support\Carbon;
39
+ use Illuminate\Support\Facades\Log;
39
40
  use Illuminate\Support\Facades\Storage;
40
41
  use Illuminate\Support\Str;
41
42
  use Illuminate\Validation\ValidationException;
@@ -910,7 +911,11 @@ class OrderController extends Controller
910
911
  $order->save();
911
912
 
912
913
  // trigger start event
913
- event(new OrderStarted($order));
914
+ try {
915
+ event(new OrderStarted($order));
916
+ } catch (\Exception $e) {
917
+ Log::debug('Unable to complete order started event', ['error' => $e->getMessage(), 'order' => $order]);
918
+ }
914
919
 
915
920
  // set order as drivers current order
916
921
  $driver->current_job_uuid = $order->uuid;
@@ -15,68 +15,119 @@ class Order extends FleetbaseResource
15
15
  * Transform the resource into an array.
16
16
  *
17
17
  * @param \Illuminate\Http\Request $request
18
- *
19
- * @return array
20
18
  */
21
- public function toArray($request)
19
+ public function toArray($request): array
22
20
  {
23
- return [
24
- 'id' => $this->when(Http::isInternalRequest(), $this->id, $this->public_id),
25
- 'uuid' => $this->when(Http::isInternalRequest(), $this->uuid),
26
- 'public_id' => $this->when(Http::isInternalRequest(), $this->public_id),
27
- 'internal_id' => $this->internal_id,
28
- 'company_uuid' => $this->when(Http::isInternalRequest(), $this->company_uuid),
29
- 'transaction_uuid' => $this->when(Http::isInternalRequest(), $this->transaction_uuid),
30
- 'customer_uuid' => $this->when(Http::isInternalRequest(), $this->customer_uuid),
31
- 'customer_type' => $this->when(Http::isInternalRequest(), $this->customer_type),
32
- 'facilitator_uuid' => $this->when(Http::isInternalRequest(), $this->facilitator_uuid),
33
- 'facilitator_type' => $this->when(Http::isInternalRequest(), $this->facilitator_type),
34
- 'payload_uuid' => $this->when(Http::isInternalRequest(), $this->payload_uuid),
35
- 'route_uuid' => $this->when(Http::isInternalRequest(), $this->route_uuid),
36
- 'purchase_rate_uuid' => $this->when(Http::isInternalRequest(), $this->purchase_rate_uuid),
37
- 'tracking_number_uuid' => $this->when(Http::isInternalRequest(), $this->tracking_number_uuid),
38
- 'driver_assigned_uuid' => $this->when(Http::isInternalRequest(), $this->driver_assigned_uuid),
39
- 'vehicle_assigned_uuid' => $this->when(Http::isInternalRequest(), $this->vehicle_assigned_uuid),
40
- 'service_quote_uuid' => $this->when(Http::isInternalRequest(), $this->service_quote_uuid),
41
- 'has_driver_assigned' => $this->when(Http::isInternalRequest(), $this->has_driver_assigned),
42
- 'is_scheduled' => $this->when(Http::isInternalRequest(), $this->is_scheduled),
43
- 'order_config_uuid' => $this->when(Http::isInternalRequest(), $this->order_config_uuid),
44
- 'order_config' => $this->when(Http::isInternalRequest(), $this->whenLoaded('orderConfig', fn () => $this->orderConfig), data_get($this->orderConfig, 'public_id')),
45
- 'custom_field_values' => $this->when(Http::isInternalRequest(), $this->customFieldValues),
46
- 'customer' => $this->setCustomerType(Resolve::resourceForMorph($this->customer_type, $this->customer_uuid)),
47
- 'payload' => new Payload($this->payload),
48
- 'facilitator' => $this->setFacilitatorType(Resolve::resourceForMorph($this->facilitator_type, $this->facilitator_uuid)),
49
- 'driver_assigned' => new Driver($this->driverAssigned()->without(['jobs', 'currentJob'])->first()),
50
- 'vehicle_assigned' => new Vehicle($this->vehicleAssigned()->without(['fleets', 'vendor'])->first()),
51
- 'tracking_number' => new TrackingNumber($this->trackingNumber),
52
- 'tracking_statuses' => $this->whenLoaded('trackingStatuses', fn () => TrackingStatus::collection($this->trackingStatuses)),
53
- 'tracking' => $this->when(Http::isInternalRequest(), $this->trackingNumber ? $this->trackingNumber->tracking_number : null),
54
- 'barcode' => $this->when(Http::isInternalRequest(), $this->trackingNumber ? $this->trackingNumber->barcode : null),
55
- 'qr_code' => $this->when(Http::isInternalRequest(), $this->trackingNumber ? $this->trackingNumber->qr_code : null),
56
- 'comments' => $this->when(Http::isInternalRequest(), Comment::collection($this->comments)),
57
- 'files' => $this->when(Http::isInternalRequest(), $this->files, File::collection($this->files)),
58
- 'purchase_rate' => new PurchaseRate($this->purchaseRate),
59
- 'notes' => $this->notes,
60
- ...$this->getCustomFieldValues(),
61
- 'custom_fields' => $this->when(Http::isPublicRequest(), fn () => $this->getCustomFieldKeys()),
62
- 'type' => $this->type,
63
- 'status' => $this->status,
64
- 'pod_method' => $this->pod_method,
65
- 'pod_required' => (bool) data_get($this, 'pod_required', false),
66
- 'dispatched' => (bool) data_get($this, 'dispatched', false),
67
- 'adhoc' => (bool) data_get($this, 'adhoc', false),
68
- 'adhoc_distance' => (int) $this->getAdhocDistance(),
69
- 'distance' => (int) $this->distance,
70
- 'time' => (int) $this->time,
71
- 'tracker_data' => $this->when($request->has('with_tracker_data') || !empty($this->resource->tracker_data), fn () => $this->resource->tracker_data ?? $this->resource->tracker()->toArray()),
72
- 'eta' => $this->when($request->has('with_eta') || !empty($this->resource->eta), fn () => $this->resource->eta ?? $this->resource->tracker()->eta()),
73
- 'meta' => data_get($this, 'meta', Utils::createObject()),
74
- 'dispatched_at' => $this->dispatched_at,
75
- 'started_at' => $this->started_at,
76
- 'scheduled_at' => $this->scheduled_at,
77
- 'updated_at' => $this->updated_at,
78
- 'created_at' => $this->created_at,
79
- ];
21
+ $isInternal = Http::isInternalRequest();
22
+ $isPublic = Http::isPublicRequest();
23
+
24
+ // Precompute expensive bits safely
25
+ $orderConfigPublicId = data_get($this->orderConfig, 'public_id');
26
+
27
+ // Driver / vehicle relations (prevent eager noise)
28
+ $driverAssignedModel = $this->driverAssigned()->without(['jobs', 'currentJob'])->first();
29
+ $vehicleAssignedModel = $this->vehicleAssigned()->without(['fleets', 'vendor'])->first();
30
+
31
+ // Tracker helpers (avoid calling ->tracker() twice)
32
+ $withTrackerData = $request->has('with_tracker_data') || !empty($this->resource->tracker_data);
33
+ $withEta = $request->has('with_eta') || !empty($this->resource->eta);
34
+ $tracker = ($withTrackerData || $withEta) ? $this->resource->tracker() : null;
35
+
36
+ return $this->withCustomFields([
37
+ // IDs
38
+ 'id' => $this->when($isInternal, $this->id, $this->public_id),
39
+ 'uuid' => $this->when($isInternal, $this->uuid),
40
+ 'public_id' => $this->when($isInternal, $this->public_id),
41
+ 'internal_id' => $this->internal_id,
42
+
43
+ // FKs (internal only)
44
+ 'company_uuid' => $this->when($isInternal, $this->company_uuid),
45
+ 'transaction_uuid' => $this->when($isInternal, $this->transaction_uuid),
46
+ 'customer_uuid' => $this->when($isInternal, $this->customer_uuid),
47
+ 'customer_type' => $this->when($isInternal, $this->customer_type),
48
+ 'facilitator_uuid' => $this->when($isInternal, $this->facilitator_uuid),
49
+ 'facilitator_type' => $this->when($isInternal, $this->facilitator_type),
50
+ 'payload_uuid' => $this->when($isInternal, $this->payload_uuid),
51
+ 'route_uuid' => $this->when($isInternal, $this->route_uuid),
52
+ 'purchase_rate_uuid' => $this->when($isInternal, $this->purchase_rate_uuid),
53
+ 'tracking_number_uuid' => $this->when($isInternal, $this->tracking_number_uuid),
54
+ 'driver_assigned_uuid' => $this->when($isInternal, $this->driver_assigned_uuid),
55
+ 'vehicle_assigned_uuid'=> $this->when($isInternal, $this->vehicle_assigned_uuid),
56
+ 'service_quote_uuid' => $this->when($isInternal, $this->service_quote_uuid),
57
+ 'has_driver_assigned' => $this->when($isInternal, $this->has_driver_assigned),
58
+ 'is_scheduled' => $this->when($isInternal, $this->is_scheduled),
59
+
60
+ // Order config: internal gets the relation; public gets the public_id (or null)
61
+ 'order_config_uuid' => $this->when($isInternal, $this->order_config_uuid),
62
+ 'order_config' => $this->when(
63
+ $isInternal,
64
+ $this->whenLoaded('orderConfig', function () {
65
+ return $this->orderConfig;
66
+ }),
67
+ $orderConfigPublicId
68
+ ),
69
+
70
+ // Custom field values (internal only) the relation collection is fine for internal,
71
+ // public callers will not see this key at all
72
+ 'custom_field_values' => $this->when($isInternal, $this->customFieldValues),
73
+
74
+ // Morph resources
75
+ 'customer' => $this->setCustomerType(Resolve::resourceForMorph($this->customer_type, $this->customer_uuid)),
76
+ 'payload' => new Payload($this->payload),
77
+ 'facilitator' => $this->setFacilitatorType(Resolve::resourceForMorph($this->facilitator_type, $this->facilitator_uuid)),
78
+
79
+ // Assigned entities
80
+ 'driver_assigned' => new Driver($driverAssignedModel),
81
+ 'vehicle_assigned' => new Vehicle($vehicleAssignedModel),
82
+
83
+ // Tracking
84
+ 'tracking_number' => new TrackingNumber($this->trackingNumber),
85
+ 'tracking_statuses' => $this->whenLoaded('trackingStatuses', function () {
86
+ return TrackingStatus::collection($this->trackingStatuses);
87
+ }),
88
+ 'tracking' => $this->when($isInternal, $this->trackingNumber ? $this->trackingNumber->tracking_number : null),
89
+ 'barcode' => $this->when($isInternal, $this->trackingNumber ? $this->trackingNumber->barcode : null),
90
+ 'qr_code' => $this->when($isInternal, $this->trackingNumber ? $this->trackingNumber->qr_code : null),
91
+
92
+ // Comments & files (internal gets raw; public gets resource collections)
93
+ 'comments' => $this->when($isInternal, Comment::collection($this->comments)),
94
+ 'files' => $this->when($isInternal, $this->files, File::collection($this->files)),
95
+
96
+ // Pricing / notes
97
+ 'purchase_rate' => new PurchaseRate($this->purchaseRate),
98
+ 'notes' => $this->notes,
99
+
100
+ // Expose only keys list publicly
101
+ 'custom_fields' => $this->when($isPublic, function () {
102
+ return $this->getCustomFieldKeys();
103
+ }),
104
+
105
+ // Basic attrs
106
+ 'type' => $this->type,
107
+ 'status' => $this->status,
108
+ 'pod_method' => $this->pod_method,
109
+ 'pod_required' => (bool) data_get($this, 'pod_required', false),
110
+ 'dispatched' => (bool) data_get($this, 'dispatched', false),
111
+ 'adhoc' => (bool) data_get($this, 'adhoc', false),
112
+ 'adhoc_distance' => (int) $this->getAdhocDistance(),
113
+ 'distance' => (int) $this->distance,
114
+ 'time' => (int) $this->time,
115
+
116
+ // Tracker derived data (computed only when requested/present)
117
+ 'tracker_data' => $this->when($withTrackerData, function () use ($tracker) {
118
+ return $this->resource->tracker_data ?? ($tracker ? $tracker->toArray() : null);
119
+ }),
120
+ 'eta' => $this->when($withEta, function () use ($tracker) {
121
+ return $this->resource->eta ?? ($tracker ? $tracker->eta() : null);
122
+ }),
123
+
124
+ 'meta' => data_get($this, 'meta', Utils::createObject()),
125
+ 'dispatched_at' => $this->dispatched_at,
126
+ 'started_at' => $this->started_at,
127
+ 'scheduled_at' => $this->scheduled_at,
128
+ 'updated_at' => $this->updated_at,
129
+ 'created_at' => $this->created_at,
130
+ ]);
80
131
  }
81
132
 
82
133
  /**