@fleetbase/fleetops-engine 0.6.55 → 0.6.57

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 (161) hide show
  1. package/addon/components/admin/map-settings.hbs +1 -1
  2. package/addon/components/ai/create-order-preview.hbs +297 -0
  3. package/addon/components/ai/create-order-preview.js +362 -0
  4. package/addon/components/ai/route-preview-map.hbs +79 -0
  5. package/addon/components/ai/route-preview-map.js +527 -0
  6. package/addon/components/device/details.js +1 -1
  7. package/addon/components/device-event/details.hbs +192 -0
  8. package/addon/components/device-event/details.js +151 -0
  9. package/addon/components/driver/schedule.js +1 -1
  10. package/addon/components/fleet/details.hbs +21 -8
  11. package/addon/components/fleet/details.js +55 -1
  12. package/addon/components/fleet/hierarchy-tree.hbs +137 -0
  13. package/addon/components/fleet/hierarchy-tree.js +528 -0
  14. package/addon/components/layout/fleet-ops-sidebar.js +15 -9
  15. package/addon/components/map/drawer/device-event-listing.js +1 -0
  16. package/addon/components/map/drawer/driver-listing.js +1 -0
  17. package/addon/components/map/drawer/place-listing.js +1 -0
  18. package/addon/components/map/drawer/vehicle-listing.js +1 -0
  19. package/addon/components/order/details.hbs +34 -1
  20. package/addon/components/order/form/details.hbs +4 -0
  21. package/addon/components/order/form.hbs +9 -1
  22. package/addon/components/telematic/details.hbs +1 -1
  23. package/addon/components/telematic/form.hbs +2 -2
  24. package/addon/components/telematic/settings.hbs +1 -1
  25. package/addon/components/vehicle/card.hbs +14 -2
  26. package/addon/components/vehicle/form.hbs +13 -1
  27. package/addon/controllers/connectivity/devices/index/details/events.js +1 -1
  28. package/addon/controllers/connectivity/devices/index/details/vehicle.js +2 -2
  29. package/addon/controllers/connectivity/events/details.js +68 -1
  30. package/addon/controllers/connectivity/sensors/index.js +4 -0
  31. package/addon/controllers/connectivity/telematics/details/events.js +1 -1
  32. package/addon/controllers/connectivity/telematics/details/sensors.js +4 -0
  33. package/addon/controllers/settings/map.js +19 -0
  34. package/addon/extension.js +14 -0
  35. package/addon/instance-initializers/register-vroom-allocation.js +1 -1
  36. package/addon/routes/connectivity/events/details.js +26 -1
  37. package/addon/routes/management/fleets/index/details.js +1 -1
  38. package/addon/routes/management/vehicles/index/details.js +3 -1
  39. package/addon/services/map-adapter/google.js +66 -1
  40. package/addon/services/map-adapter/leaflet.js +1 -1
  41. package/addon/services/map-adapter-interface.js +1 -1
  42. package/addon/services/map-manager.js +16 -1
  43. package/addon/services/map-settings.js +15 -0
  44. package/addon/styles/fleetops-engine.css +487 -1
  45. package/addon/templates/connectivity/devices/index/details/vehicle.hbs +2 -1
  46. package/addon/templates/connectivity/events/details.hbs +40 -1
  47. package/addon/templates/settings/map.hbs +38 -9
  48. package/addon/utils/leaflet-plugin-loader.js +5 -5
  49. package/addon/utils/map-drawer-dropdown-position.js +11 -13
  50. package/addon/utils/route-colors.js +1 -1
  51. package/app/components/ai/create-order-preview.js +1 -0
  52. package/app/components/ai/route-preview-map.js +1 -0
  53. package/app/components/device-event/details.hbs +192 -0
  54. package/app/components/device-event/details.js +1 -0
  55. package/app/components/fleet/hierarchy-tree.js +1 -0
  56. package/app/services/route-engine.js +1 -0
  57. package/composer.json +1 -1
  58. package/extension.json +1 -1
  59. package/package.json +6 -4
  60. package/scripts/normalize-rgba-alpha.js +90 -0
  61. package/server/migrations/2026_06_23_000001_delete_bad_afaqy_telematics_sensors.php +34 -0
  62. package/server/src/Exports/ContactExport.php +12 -5
  63. package/server/src/Exports/DeviceExport.php +92 -0
  64. package/server/src/Exports/DriverExport.php +28 -3
  65. package/server/src/Exports/EquipmentExport.php +94 -0
  66. package/server/src/Exports/FleetExport.php +22 -7
  67. package/server/src/Exports/FuelReportExport.php +16 -1
  68. package/server/src/Exports/IssueExport.php +25 -3
  69. package/server/src/Exports/MaintenanceExport.php +106 -0
  70. package/server/src/Exports/MaintenanceScheduleExport.php +99 -0
  71. package/server/src/Exports/OrderExport.php +24 -5
  72. package/server/src/Exports/PartExport.php +99 -0
  73. package/server/src/Exports/PlaceExport.php +33 -7
  74. package/server/src/Exports/SensorExport.php +94 -0
  75. package/server/src/Exports/ServiceAreaExport.php +31 -7
  76. package/server/src/Exports/ServiceRateExport.php +41 -1
  77. package/server/src/Exports/TelematicExport.php +90 -0
  78. package/server/src/Exports/VehicleExport.php +274 -8
  79. package/server/src/Exports/VendorExport.php +15 -2
  80. package/server/src/Exports/WorkOrderExport.php +92 -0
  81. package/server/src/Http/Controllers/Api/v1/Concerns/ResolvesFleetOpsApiResources.php +176 -0
  82. package/server/src/Http/Controllers/Api/v1/DeviceController.php +225 -0
  83. package/server/src/Http/Controllers/Api/v1/EquipmentController.php +114 -0
  84. package/server/src/Http/Controllers/Api/v1/FuelTransactionController.php +185 -0
  85. package/server/src/Http/Controllers/Api/v1/PartController.php +120 -0
  86. package/server/src/Http/Controllers/Api/v1/SensorController.php +132 -0
  87. package/server/src/Http/Controllers/Api/v1/WorkOrderController.php +161 -0
  88. package/server/src/Http/Controllers/Internal/v1/DeviceController.php +18 -0
  89. package/server/src/Http/Controllers/Internal/v1/EquipmentController.php +17 -0
  90. package/server/src/Http/Controllers/Internal/v1/MaintenanceController.php +17 -0
  91. package/server/src/Http/Controllers/Internal/v1/MaintenanceScheduleController.php +17 -0
  92. package/server/src/Http/Controllers/Internal/v1/PartController.php +17 -0
  93. package/server/src/Http/Controllers/Internal/v1/SensorController.php +18 -0
  94. package/server/src/Http/Controllers/Internal/v1/SettingController.php +29 -5
  95. package/server/src/Http/Controllers/Internal/v1/TelematicController.php +18 -0
  96. package/server/src/Http/Controllers/Internal/v1/WorkOrderController.php +17 -0
  97. package/server/src/Http/Filter/DeviceFilter.php +33 -10
  98. package/server/src/Http/Filter/EquipmentFilter.php +23 -0
  99. package/server/src/Http/Filter/FuelProviderTransactionFilter.php +57 -2
  100. package/server/src/Http/Filter/PartFilter.php +47 -0
  101. package/server/src/Http/Filter/SensorFilter.php +34 -6
  102. package/server/src/Http/Filter/WorkOrderFilter.php +23 -0
  103. package/server/src/Http/Requests/CreateDeviceRequest.php +51 -0
  104. package/server/src/Http/Requests/CreateEquipmentRequest.php +35 -0
  105. package/server/src/Http/Requests/CreateFuelTransactionRequest.php +50 -0
  106. package/server/src/Http/Requests/CreatePartRequest.php +40 -0
  107. package/server/src/Http/Requests/CreateSensorRequest.php +47 -0
  108. package/server/src/Http/Requests/CreateWorkOrderRequest.php +42 -0
  109. package/server/src/Http/Requests/UpdateDeviceRequest.php +7 -0
  110. package/server/src/Http/Requests/UpdateEquipmentRequest.php +7 -0
  111. package/server/src/Http/Requests/UpdateFuelTransactionRequest.php +7 -0
  112. package/server/src/Http/Requests/UpdatePartRequest.php +7 -0
  113. package/server/src/Http/Requests/UpdateSensorRequest.php +7 -0
  114. package/server/src/Http/Requests/UpdateWorkOrderRequest.php +7 -0
  115. package/server/src/Http/Resources/v1/Device.php +72 -0
  116. package/server/src/Http/Resources/v1/Equipment.php +57 -0
  117. package/server/src/Http/Resources/v1/Fleet.php +8 -2
  118. package/server/src/Http/Resources/v1/FuelTransaction.php +69 -0
  119. package/server/src/Http/Resources/v1/Part.php +64 -0
  120. package/server/src/Http/Resources/v1/Sensor.php +68 -0
  121. package/server/src/Jobs/SyncTelematicDevicesJob.php +194 -38
  122. package/server/src/Models/Driver.php +6 -0
  123. package/server/src/Models/Order.php +20 -7
  124. package/server/src/Models/Place.php +11 -3
  125. package/server/src/Observers/PurchaseRateObserver.php +2 -1
  126. package/server/src/Providers/FleetOpsServiceProvider.php +26 -0
  127. package/server/src/Support/Ai/Capabilities/AbstractFleetOpsAICapability.php +88 -0
  128. package/server/src/Support/Ai/Capabilities/AssetStatusCapability.php +108 -0
  129. package/server/src/Support/Ai/Capabilities/ConsoleNavigationCapability.php +55 -0
  130. package/server/src/Support/Ai/Capabilities/CreateOrderPreviewCapability.php +508 -0
  131. package/server/src/Support/Ai/Capabilities/DocsHelpCapability.php +49 -0
  132. package/server/src/Support/Ai/Capabilities/ImportOrdersPreviewCapability.php +66 -0
  133. package/server/src/Support/Ai/Capabilities/OperationalQueryCapability.php +319 -0
  134. package/server/src/Support/Ai/Capabilities/OptimizeOrderRouteCapability.php +240 -0
  135. package/server/src/Support/Ai/Capabilities/OrderInsightsCapability.php +95 -0
  136. package/server/src/Support/Ai/Capabilities/SearchResourcesCapability.php +195 -0
  137. package/server/src/Support/Ai/FleetOpsAiQueryResources.php +150 -0
  138. package/server/src/Support/Analytics/RevenueTrend.php +3 -8
  139. package/server/src/Support/Metrics/ActiveRevenueQuery.php +121 -0
  140. package/server/src/Support/Metrics/AvgOrderValueMetric.php +1 -7
  141. package/server/src/Support/Metrics/EarningsMetric.php +1 -10
  142. package/server/src/Support/Telematics/Providers/AfaqyProvider.php +104 -5
  143. package/server/src/Support/Telematics/Providers/SafeeProvider.php +550 -54
  144. package/server/src/Support/Telematics/TelematicService.php +28 -9
  145. package/server/src/Support/Utils.php +52 -0
  146. package/server/src/routes.php +67 -14
  147. package/server/tests/AiCreateOrderPreviewCapabilityTest.php +131 -0
  148. package/server/tests/AiOperationalQueryCapabilityTest.php +75 -0
  149. package/server/tests/ExportCoverageTest.php +79 -0
  150. package/server/tests/FleetHierarchyResourceTest.php +12 -0
  151. package/server/tests/MetricsRegistryTest.php +45 -5
  152. package/server/tests/OrderPayloadPlaceResolutionTest.php +53 -0
  153. package/server/tests/PointResolutionTest.php +63 -0
  154. package/server/tests/PublicFleetOpsRestEndpointsTest.php +191 -0
  155. package/server/tests/TelematicsHardeningTest.php +636 -1
  156. package/addon/components/table/cell/dropdown/action-item.hbs +0 -31
  157. package/addon/components/table/cell/dropdown/action-item.js +0 -62
  158. package/addon/components/table/cell/dropdown.hbs +0 -28
  159. package/addon/components/table/cell/dropdown.js +0 -91
  160. package/app/components/table/cell/dropdown/action-item.js +0 -1
  161. package/app/components/table/cell/dropdown.js +0 -1
@@ -0,0 +1,225 @@
1
+ <?php
2
+
3
+ namespace Fleetbase\FleetOps\Http\Controllers\Api\v1;
4
+
5
+ use Fleetbase\FleetOps\Http\Controllers\Api\v1\Concerns\ResolvesFleetOpsApiResources;
6
+ use Fleetbase\FleetOps\Http\Requests\CreateDeviceRequest;
7
+ use Fleetbase\FleetOps\Http\Requests\UpdateDeviceRequest;
8
+ use Fleetbase\FleetOps\Http\Resources\v1\DeletedResource;
9
+ use Fleetbase\FleetOps\Http\Resources\v1\Device as DeviceResource;
10
+ use Fleetbase\FleetOps\Models\Device;
11
+ use Fleetbase\FleetOps\Models\Telematic;
12
+ use Fleetbase\FleetOps\Models\Vehicle;
13
+ use Fleetbase\FleetOps\Models\Warranty;
14
+ use Fleetbase\FleetOps\Support\Utils;
15
+ use Fleetbase\Http\Controllers\Controller;
16
+ use Fleetbase\LaravelMysqlSpatial\Types\Point;
17
+ use Fleetbase\Models\File;
18
+ use Illuminate\Http\JsonResponse;
19
+ use Illuminate\Http\Request;
20
+ use Illuminate\Support\Facades\Log;
21
+
22
+ class DeviceController extends Controller
23
+ {
24
+ use ResolvesFleetOpsApiResources;
25
+
26
+ public function create(CreateDeviceRequest $request)
27
+ {
28
+ $this->rejectUuidIdentifiers($request);
29
+
30
+ $input = $this->input($request);
31
+ $input['company_uuid'] = session('company');
32
+
33
+ $device = Device::create($input)->load(['telematic', 'warranty', 'attachable', 'photo'])->loadCount('sensors');
34
+
35
+ return new DeviceResource($device);
36
+ }
37
+
38
+ public function update(string $id, UpdateDeviceRequest $request)
39
+ {
40
+ $this->rejectUuidIdentifiers($request);
41
+
42
+ try {
43
+ $device = $this->resolveModel(Device::class, $id);
44
+ } catch (\Illuminate\Database\Eloquent\ModelNotFoundException $exception) {
45
+ return response()->json(['error' => 'Device resource not found.'], 404);
46
+ }
47
+
48
+ $device->update($this->input($request));
49
+
50
+ return new DeviceResource($device->refresh()->load(['telematic', 'warranty', 'attachable', 'photo'])->loadCount('sensors'));
51
+ }
52
+
53
+ public function query(Request $request)
54
+ {
55
+ $this->rejectUuidIdentifiers($request);
56
+
57
+ $results = Device::queryWithRequest($request, function (&$query) {
58
+ $query->with(['telematic', 'warranty', 'attachable', 'photo'])->withCount('sensors');
59
+ });
60
+
61
+ return DeviceResource::collection($results);
62
+ }
63
+
64
+ public function find(string $id)
65
+ {
66
+ try {
67
+ $device = $this->resolveModel(Device::class, $id)->load(['telematic', 'warranty', 'attachable', 'photo']);
68
+ $device->loadCount('sensors');
69
+ } catch (\Illuminate\Database\Eloquent\ModelNotFoundException $exception) {
70
+ return response()->json(['error' => 'Device resource not found.'], 404);
71
+ }
72
+
73
+ return new DeviceResource($device);
74
+ }
75
+
76
+ public function delete(string $id)
77
+ {
78
+ try {
79
+ $device = $this->resolveModel(Device::class, $id);
80
+ } catch (\Illuminate\Database\Eloquent\ModelNotFoundException $exception) {
81
+ return response()->json(['error' => 'Device resource not found.'], 404);
82
+ }
83
+
84
+ $device->delete();
85
+
86
+ return new DeletedResource($device);
87
+ }
88
+
89
+ public function attach(Request $request, string $id): JsonResponse
90
+ {
91
+ $this->rejectUuidIdentifiers($request);
92
+
93
+ $request->validate([
94
+ 'vehicle' => 'required_without:attachable|string|nullable',
95
+ ]);
96
+
97
+ $deviceId = $id;
98
+ $vehicleId = $request->input('vehicle') ?? $request->input('attachable');
99
+
100
+ try {
101
+ $device = $this->resolveModel(Device::class, $deviceId);
102
+ $vehicle = $this->resolveModel(Vehicle::class, $vehicleId);
103
+ } catch (\Illuminate\Database\Eloquent\ModelNotFoundException $exception) {
104
+ $this->logDeviceAttachmentLookupFailure('attach', $deviceId, $vehicleId);
105
+
106
+ return response()->json(['error' => 'Device or vehicle resource not found.'], 404);
107
+ }
108
+
109
+ try {
110
+ $device->attachTo($vehicle);
111
+ $device->load(['telematic', 'warranty', 'attachable', 'photo'])->loadCount('sensors');
112
+ } catch (\Throwable $e) {
113
+ $this->logDeviceAttachmentFailure('attach', $device, $vehicle, $e);
114
+
115
+ return response()->json(['error' => 'Unable to attach device to vehicle.'], 500);
116
+ }
117
+
118
+ return response()->json([
119
+ 'status' => 'ok',
120
+ 'device' => new DeviceResource($device),
121
+ ]);
122
+ }
123
+
124
+ public function detach(string $id): JsonResponse
125
+ {
126
+ try {
127
+ $device = $this->resolveModel(Device::class, $id);
128
+ } catch (\Illuminate\Database\Eloquent\ModelNotFoundException $exception) {
129
+ $this->logDeviceAttachmentLookupFailure('detach', $id, null);
130
+
131
+ return response()->json(['error' => 'Device resource not found.'], 404);
132
+ }
133
+
134
+ try {
135
+ $device->detach();
136
+ $device->load(['telematic', 'warranty', 'attachable', 'photo'])->loadCount('sensors');
137
+ } catch (\Throwable $e) {
138
+ $this->logDeviceAttachmentFailure('detach', $device, null, $e);
139
+
140
+ return response()->json(['error' => 'Unable to detach device from vehicle.'], 500);
141
+ }
142
+
143
+ return response()->json([
144
+ 'status' => 'ok',
145
+ 'device' => new DeviceResource($device),
146
+ ]);
147
+ }
148
+
149
+ protected function input(Request $request): array
150
+ {
151
+ $input = $request->only([
152
+ 'type',
153
+ 'device_id',
154
+ 'internal_id',
155
+ 'imei',
156
+ 'imsi',
157
+ 'firmware_version',
158
+ 'provider',
159
+ 'name',
160
+ 'model',
161
+ 'location',
162
+ 'manufacturer',
163
+ 'serial_number',
164
+ 'installation_date',
165
+ 'last_maintenance_date',
166
+ 'meta',
167
+ 'data',
168
+ 'options',
169
+ 'online',
170
+ 'status',
171
+ 'data_frequency',
172
+ 'notes',
173
+ 'last_online_at',
174
+ ]);
175
+
176
+ if ($request->filled(['latitude', 'longitude'])) {
177
+ $input['last_position'] = new Point($request->input('latitude'), $request->input('longitude'));
178
+ } elseif ($request->filled('last_position')) {
179
+ $input['last_position'] = Utils::getPointFromCoordinates($request->input('last_position'));
180
+ }
181
+
182
+ $this->applyPublicIdRelation($input, 'telematic', 'telematic_uuid', Telematic::class, $request);
183
+ $this->applyPublicIdRelation($input, 'warranty', 'warranty_uuid', Warranty::class, $request);
184
+ $this->applyPublicIdRelation($input, 'photo', 'photo_uuid', File::class, $request);
185
+
186
+ if ($request->exists('attachable')) {
187
+ if (blank($request->input('attachable'))) {
188
+ $input['attachable_type'] = null;
189
+ $input['attachable_uuid'] = null;
190
+ } else {
191
+ [$type, $uuid] = $this->resolveMorph($request->input('attachable_type'), $request->input('attachable'));
192
+ $input['attachable_type'] = $type;
193
+ $input['attachable_uuid'] = $uuid;
194
+ }
195
+ }
196
+
197
+ return $input;
198
+ }
199
+
200
+ protected function logDeviceAttachmentLookupFailure(string $action, string $deviceId, ?string $vehicleId): void
201
+ {
202
+ Log::warning('Public API device attachment lookup failed', [
203
+ 'action' => $action,
204
+ 'device_id' => $deviceId,
205
+ 'vehicle_id' => $vehicleId,
206
+ 'company_uuid' => session('company'),
207
+ 'request_id' => request()->headers->get('X-Request-ID') ?? request()->headers->get('X-Correlation-ID'),
208
+ ]);
209
+ }
210
+
211
+ protected function logDeviceAttachmentFailure(string $action, Device $device, ?Vehicle $vehicle, \Throwable $exception): void
212
+ {
213
+ Log::error('Public API device attachment failed', [
214
+ 'action' => $action,
215
+ 'device_uuid' => $device->uuid,
216
+ 'device_id' => $device->public_id,
217
+ 'vehicle_uuid' => $vehicle?->uuid,
218
+ 'vehicle_id' => $vehicle?->public_id,
219
+ 'company_uuid' => session('company'),
220
+ 'request_id' => request()->headers->get('X-Request-ID') ?? request()->headers->get('X-Correlation-ID'),
221
+ 'exception_class' => get_class($exception),
222
+ 'exception' => $exception->getMessage(),
223
+ ]);
224
+ }
225
+ }
@@ -0,0 +1,114 @@
1
+ <?php
2
+
3
+ namespace Fleetbase\FleetOps\Http\Controllers\Api\v1;
4
+
5
+ use Fleetbase\FleetOps\Http\Controllers\Api\v1\Concerns\ResolvesFleetOpsApiResources;
6
+ use Fleetbase\FleetOps\Http\Requests\CreateEquipmentRequest;
7
+ use Fleetbase\FleetOps\Http\Requests\UpdateEquipmentRequest;
8
+ use Fleetbase\FleetOps\Http\Resources\v1\DeletedResource;
9
+ use Fleetbase\FleetOps\Http\Resources\v1\Equipment as EquipmentResource;
10
+ use Fleetbase\FleetOps\Models\Equipment;
11
+ use Fleetbase\FleetOps\Models\Warranty;
12
+ use Fleetbase\Http\Controllers\Controller;
13
+ use Fleetbase\Models\File;
14
+ use Illuminate\Http\Request;
15
+
16
+ class EquipmentController extends Controller
17
+ {
18
+ use ResolvesFleetOpsApiResources;
19
+
20
+ public function create(CreateEquipmentRequest $request)
21
+ {
22
+ $this->rejectUuidIdentifiers($request);
23
+
24
+ $input = $this->input($request);
25
+ $input['company_uuid'] = session('company');
26
+
27
+ $equipment = Equipment::create($input)->load(['warranty', 'photo', 'equipable']);
28
+
29
+ return new EquipmentResource($equipment);
30
+ }
31
+
32
+ public function update(string $id, UpdateEquipmentRequest $request)
33
+ {
34
+ $this->rejectUuidIdentifiers($request);
35
+
36
+ try {
37
+ $equipment = $this->resolveModel(Equipment::class, $id);
38
+ } catch (\Illuminate\Database\Eloquent\ModelNotFoundException $exception) {
39
+ return response()->json(['error' => 'Equipment resource not found.'], 404);
40
+ }
41
+
42
+ $equipment->update($this->input($request));
43
+
44
+ return new EquipmentResource($equipment->refresh()->load(['warranty', 'photo', 'equipable']));
45
+ }
46
+
47
+ public function query(Request $request)
48
+ {
49
+ $this->rejectUuidIdentifiers($request);
50
+
51
+ $results = Equipment::queryWithRequest($request, function (&$query) {
52
+ $query->with(['warranty', 'photo', 'equipable']);
53
+ });
54
+
55
+ return EquipmentResource::collection($results);
56
+ }
57
+
58
+ public function find(string $id)
59
+ {
60
+ try {
61
+ $equipment = $this->resolveModel(Equipment::class, $id)->load(['warranty', 'photo', 'equipable']);
62
+ } catch (\Illuminate\Database\Eloquent\ModelNotFoundException $exception) {
63
+ return response()->json(['error' => 'Equipment resource not found.'], 404);
64
+ }
65
+
66
+ return new EquipmentResource($equipment);
67
+ }
68
+
69
+ public function delete(string $id)
70
+ {
71
+ try {
72
+ $equipment = $this->resolveModel(Equipment::class, $id);
73
+ } catch (\Illuminate\Database\Eloquent\ModelNotFoundException $exception) {
74
+ return response()->json(['error' => 'Equipment resource not found.'], 404);
75
+ }
76
+
77
+ $equipment->delete();
78
+
79
+ return new DeletedResource($equipment);
80
+ }
81
+
82
+ protected function input(Request $request): array
83
+ {
84
+ $input = $request->only([
85
+ 'name',
86
+ 'code',
87
+ 'type',
88
+ 'status',
89
+ 'serial_number',
90
+ 'manufacturer',
91
+ 'model',
92
+ 'purchased_at',
93
+ 'purchase_price',
94
+ 'currency',
95
+ 'meta',
96
+ ]);
97
+
98
+ $this->applyPublicIdRelation($input, 'warranty', 'warranty_uuid', Warranty::class, $request);
99
+ $this->applyPublicIdRelation($input, 'photo', 'photo_uuid', File::class, $request);
100
+
101
+ if ($request->exists('equipable')) {
102
+ if (blank($request->input('equipable'))) {
103
+ $input['equipable_type'] = null;
104
+ $input['equipable_uuid'] = null;
105
+ } else {
106
+ [$type, $uuid] = $this->resolveMorph($request->input('equipable_type'), $request->input('equipable'));
107
+ $input['equipable_type'] = $type;
108
+ $input['equipable_uuid'] = $uuid;
109
+ }
110
+ }
111
+
112
+ return $input;
113
+ }
114
+ }
@@ -0,0 +1,185 @@
1
+ <?php
2
+
3
+ namespace Fleetbase\FleetOps\Http\Controllers\Api\v1;
4
+
5
+ use Fleetbase\FleetOps\Http\Controllers\Api\v1\Concerns\ResolvesFleetOpsApiResources;
6
+ use Fleetbase\FleetOps\Http\Requests\CreateFuelTransactionRequest;
7
+ use Fleetbase\FleetOps\Http\Requests\UpdateFuelTransactionRequest;
8
+ use Fleetbase\FleetOps\Http\Resources\v1\DeletedResource;
9
+ use Fleetbase\FleetOps\Http\Resources\v1\FuelTransaction as FuelTransactionResource;
10
+ use Fleetbase\FleetOps\Models\Driver;
11
+ use Fleetbase\FleetOps\Models\FuelProviderConnection;
12
+ use Fleetbase\FleetOps\Models\FuelProviderTransaction;
13
+ use Fleetbase\FleetOps\Models\FuelReport;
14
+ use Fleetbase\FleetOps\Models\Order;
15
+ use Fleetbase\FleetOps\Models\Vehicle;
16
+ use Fleetbase\FleetOps\Support\FuelProviders\FuelProviderService;
17
+ use Fleetbase\Http\Controllers\Controller;
18
+ use Illuminate\Http\JsonResponse;
19
+ use Illuminate\Http\Request;
20
+
21
+ class FuelTransactionController extends Controller
22
+ {
23
+ use ResolvesFleetOpsApiResources;
24
+
25
+ public function __construct(protected FuelProviderService $fuelProviderService)
26
+ {
27
+ }
28
+
29
+ public function create(CreateFuelTransactionRequest $request)
30
+ {
31
+ $this->rejectUuidIdentifiers($request);
32
+
33
+ $input = $this->input($request);
34
+ $input['company_uuid'] = session('company');
35
+
36
+ $transaction = FuelProviderTransaction::create($input)->load(['connection', 'vehicle', 'driver', 'order', 'fuelReport']);
37
+
38
+ return new FuelTransactionResource($transaction);
39
+ }
40
+
41
+ public function update(string $id, UpdateFuelTransactionRequest $request)
42
+ {
43
+ $this->rejectUuidIdentifiers($request);
44
+
45
+ try {
46
+ $transaction = $this->findTransaction($id);
47
+ } catch (\Illuminate\Database\Eloquent\ModelNotFoundException $exception) {
48
+ return response()->json(['error' => 'FuelTransaction resource not found.'], 404);
49
+ }
50
+
51
+ $transaction->update($this->input($request));
52
+
53
+ return new FuelTransactionResource($transaction->refresh()->load(['connection', 'vehicle', 'driver', 'order', 'fuelReport']));
54
+ }
55
+
56
+ public function query(Request $request)
57
+ {
58
+ $this->rejectUuidIdentifiers($request);
59
+
60
+ $results = FuelProviderTransaction::queryWithRequest($request, function (&$query) {
61
+ $query->with(['connection', 'vehicle', 'driver', 'order', 'fuelReport']);
62
+ });
63
+
64
+ return FuelTransactionResource::collection($results);
65
+ }
66
+
67
+ public function find(string $id)
68
+ {
69
+ try {
70
+ $transaction = $this->findTransaction($id)->load(['connection', 'vehicle', 'driver', 'order', 'fuelReport']);
71
+ } catch (\Illuminate\Database\Eloquent\ModelNotFoundException $exception) {
72
+ return response()->json(['error' => 'FuelTransaction resource not found.'], 404);
73
+ }
74
+
75
+ return new FuelTransactionResource($transaction);
76
+ }
77
+
78
+ public function delete(string $id)
79
+ {
80
+ try {
81
+ $transaction = $this->findTransaction($id);
82
+ } catch (\Illuminate\Database\Eloquent\ModelNotFoundException $exception) {
83
+ return response()->json(['error' => 'FuelTransaction resource not found.'], 404);
84
+ }
85
+
86
+ $transaction->delete();
87
+
88
+ return new DeletedResource($transaction);
89
+ }
90
+
91
+ public function matchVehicle(Request $request, string $id): JsonResponse
92
+ {
93
+ $this->rejectUuidIdentifiers($request);
94
+
95
+ $request->validate(['vehicle' => 'required|string']);
96
+ $transaction = $this->findTransaction($id);
97
+ $vehicle = $this->resolveModel(Vehicle::class, $request->input('vehicle'));
98
+
99
+ return response()->json([
100
+ 'status' => 'ok',
101
+ 'transaction' => new FuelTransactionResource($this->fuelProviderService->matchVehicle($transaction, $vehicle)),
102
+ ]);
103
+ }
104
+
105
+ public function matchOrder(Request $request, string $id): JsonResponse
106
+ {
107
+ $this->rejectUuidIdentifiers($request);
108
+
109
+ $request->validate(['order' => 'required|string']);
110
+ $transaction = $this->findTransaction($id);
111
+ $order = $this->resolveModel(Order::class, $request->input('order'));
112
+
113
+ return response()->json([
114
+ 'status' => 'ok',
115
+ 'transaction' => new FuelTransactionResource($this->fuelProviderService->matchOrder($transaction, $order)),
116
+ ]);
117
+ }
118
+
119
+ public function reprocess(string $id): JsonResponse
120
+ {
121
+ $transaction = $this->findTransaction($id);
122
+
123
+ return response()->json([
124
+ 'status' => 'ok',
125
+ 'transaction' => new FuelTransactionResource($this->fuelProviderService->reprocessTransaction($transaction)),
126
+ ]);
127
+ }
128
+
129
+ public function review(Request $request, string $id): JsonResponse
130
+ {
131
+ $this->rejectUuidIdentifiers($request);
132
+
133
+ $request->validate(['status' => 'required|string|in:reviewed,ignored']);
134
+ $transaction = $this->findTransaction($id);
135
+
136
+ return response()->json([
137
+ 'status' => 'ok',
138
+ 'transaction' => new FuelTransactionResource($this->fuelProviderService->reviewTransaction($transaction, $request->input('status'))),
139
+ ]);
140
+ }
141
+
142
+ protected function findTransaction(string $id): FuelProviderTransaction
143
+ {
144
+ return $this->resolveModel(FuelProviderTransaction::class, $id);
145
+ }
146
+
147
+ protected function input(Request $request): array
148
+ {
149
+ $input = $request->only([
150
+ 'provider',
151
+ 'provider_transaction_id',
152
+ 'provider_vehicle_id',
153
+ 'vehicle_card_id',
154
+ 'internal_number',
155
+ 'structure_number',
156
+ 'plate_number',
157
+ 'vin',
158
+ 'serial_number',
159
+ 'call_sign',
160
+ 'trip_number',
161
+ 'station_name',
162
+ 'station_latitude',
163
+ 'station_longitude',
164
+ 'transaction_at',
165
+ 'volume',
166
+ 'metric_unit',
167
+ 'amount',
168
+ 'currency',
169
+ 'odometer',
170
+ 'sync_status',
171
+ 'matched_at',
172
+ 'normalized_payload',
173
+ 'raw_payload',
174
+ 'meta',
175
+ ]);
176
+
177
+ $this->applyPublicIdRelation($input, 'connection', 'fuel_provider_connection_uuid', FuelProviderConnection::class, $request);
178
+ $this->applyPublicIdRelation($input, 'fuel_report', 'fuel_report_uuid', FuelReport::class, $request);
179
+ $this->applyPublicIdRelation($input, 'vehicle', 'vehicle_uuid', Vehicle::class, $request);
180
+ $this->applyPublicIdRelation($input, 'driver', 'driver_uuid', Driver::class, $request);
181
+ $this->applyPublicIdRelation($input, 'order', 'order_uuid', Order::class, $request);
182
+
183
+ return $input;
184
+ }
185
+ }
@@ -0,0 +1,120 @@
1
+ <?php
2
+
3
+ namespace Fleetbase\FleetOps\Http\Controllers\Api\v1;
4
+
5
+ use Fleetbase\FleetOps\Http\Controllers\Api\v1\Concerns\ResolvesFleetOpsApiResources;
6
+ use Fleetbase\FleetOps\Http\Requests\CreatePartRequest;
7
+ use Fleetbase\FleetOps\Http\Requests\UpdatePartRequest;
8
+ use Fleetbase\FleetOps\Http\Resources\v1\DeletedResource;
9
+ use Fleetbase\FleetOps\Http\Resources\v1\Part as PartResource;
10
+ use Fleetbase\FleetOps\Models\Part;
11
+ use Fleetbase\FleetOps\Models\Vendor;
12
+ use Fleetbase\FleetOps\Models\Warranty;
13
+ use Fleetbase\Http\Controllers\Controller;
14
+ use Fleetbase\Models\File;
15
+ use Illuminate\Http\Request;
16
+
17
+ class PartController extends Controller
18
+ {
19
+ use ResolvesFleetOpsApiResources;
20
+
21
+ public function create(CreatePartRequest $request)
22
+ {
23
+ $this->rejectUuidIdentifiers($request);
24
+
25
+ $input = $this->input($request);
26
+ $input['company_uuid'] = session('company');
27
+
28
+ $part = Part::create($input)->load(['vendor', 'warranty', 'photo', 'asset']);
29
+
30
+ return new PartResource($part);
31
+ }
32
+
33
+ public function update(string $id, UpdatePartRequest $request)
34
+ {
35
+ $this->rejectUuidIdentifiers($request);
36
+
37
+ try {
38
+ $part = $this->resolveModel(Part::class, $id);
39
+ } catch (\Illuminate\Database\Eloquent\ModelNotFoundException $exception) {
40
+ return response()->json(['error' => 'Part resource not found.'], 404);
41
+ }
42
+
43
+ $part->update($this->input($request));
44
+
45
+ return new PartResource($part->refresh()->load(['vendor', 'warranty', 'photo', 'asset']));
46
+ }
47
+
48
+ public function query(Request $request)
49
+ {
50
+ $this->rejectUuidIdentifiers($request);
51
+
52
+ $results = Part::queryWithRequest($request, function (&$query) {
53
+ $query->with(['vendor', 'warranty', 'photo', 'asset']);
54
+ });
55
+
56
+ return PartResource::collection($results);
57
+ }
58
+
59
+ public function find(string $id)
60
+ {
61
+ try {
62
+ $part = $this->resolveModel(Part::class, $id)->load(['vendor', 'warranty', 'photo', 'asset']);
63
+ } catch (\Illuminate\Database\Eloquent\ModelNotFoundException $exception) {
64
+ return response()->json(['error' => 'Part resource not found.'], 404);
65
+ }
66
+
67
+ return new PartResource($part);
68
+ }
69
+
70
+ public function delete(string $id)
71
+ {
72
+ try {
73
+ $part = $this->resolveModel(Part::class, $id);
74
+ } catch (\Illuminate\Database\Eloquent\ModelNotFoundException $exception) {
75
+ return response()->json(['error' => 'Part resource not found.'], 404);
76
+ }
77
+
78
+ $part->delete();
79
+
80
+ return new DeletedResource($part);
81
+ }
82
+
83
+ protected function input(Request $request): array
84
+ {
85
+ $input = $request->only([
86
+ 'sku',
87
+ 'name',
88
+ 'manufacturer',
89
+ 'model',
90
+ 'serial_number',
91
+ 'barcode',
92
+ 'description',
93
+ 'quantity_on_hand',
94
+ 'unit_cost',
95
+ 'msrp',
96
+ 'currency',
97
+ 'type',
98
+ 'status',
99
+ 'specs',
100
+ 'meta',
101
+ ]);
102
+
103
+ $this->applyPublicIdRelation($input, 'vendor', 'vendor_uuid', Vendor::class, $request);
104
+ $this->applyPublicIdRelation($input, 'warranty', 'warranty_uuid', Warranty::class, $request);
105
+ $this->applyPublicIdRelation($input, 'photo', 'photo_uuid', File::class, $request);
106
+
107
+ if ($request->exists('asset')) {
108
+ if (blank($request->input('asset'))) {
109
+ $input['asset_type'] = null;
110
+ $input['asset_uuid'] = null;
111
+ } else {
112
+ [$type, $uuid] = $this->resolveMorph($request->input('asset_type'), $request->input('asset'));
113
+ $input['asset_type'] = $type;
114
+ $input['asset_uuid'] = $uuid;
115
+ }
116
+ }
117
+
118
+ return $input;
119
+ }
120
+ }