@fleetbase/fleetops-engine 0.6.35 → 0.6.37

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 (73) hide show
  1. package/addon/components/avatar-manager.hbs +86 -0
  2. package/addon/components/avatar-manager.js +165 -0
  3. package/addon/components/customer/create-order-form.js +5 -0
  4. package/addon/components/layout/fleet-ops-sidebar.js +8 -0
  5. package/addon/components/order-progress-card.js +1 -1
  6. package/addon/controllers/analytics/reports/index/edit.js +2 -0
  7. package/addon/controllers/analytics/reports/index/new.js +2 -0
  8. package/addon/controllers/connectivity/devices/index/edit.js +2 -0
  9. package/addon/controllers/connectivity/devices/index/new.js +2 -0
  10. package/addon/controllers/connectivity/sensors/index/edit.js +2 -0
  11. package/addon/controllers/connectivity/sensors/index/new.js +2 -0
  12. package/addon/controllers/connectivity/telematics/index/edit.js +2 -0
  13. package/addon/controllers/connectivity/telematics/index/new.js +2 -0
  14. package/addon/controllers/management/contacts/customers/edit.js +2 -0
  15. package/addon/controllers/management/contacts/customers/new.js +2 -0
  16. package/addon/controllers/management/contacts/index/edit.js +2 -0
  17. package/addon/controllers/management/contacts/index/new.js +2 -0
  18. package/addon/controllers/management/drivers/index/edit.js +2 -0
  19. package/addon/controllers/management/drivers/index/new.js +2 -0
  20. package/addon/controllers/management/fleets/index/edit.js +2 -0
  21. package/addon/controllers/management/fleets/index/new.js +2 -0
  22. package/addon/controllers/management/fuel-reports/index/edit.js +2 -0
  23. package/addon/controllers/management/fuel-reports/index/new.js +2 -0
  24. package/addon/controllers/management/issues/index/edit.js +2 -0
  25. package/addon/controllers/management/issues/index/new.js +2 -0
  26. package/addon/controllers/management/places/index/edit.js +2 -0
  27. package/addon/controllers/management/places/index/new.js +2 -0
  28. package/addon/controllers/management/vehicles/index/edit.js +2 -0
  29. package/addon/controllers/management/vehicles/index/new.js +2 -0
  30. package/addon/controllers/management/vendors/index/edit.js +2 -0
  31. package/addon/controllers/management/vendors/index/new.js +2 -0
  32. package/addon/controllers/operations/orders/index/new.js +2 -0
  33. package/addon/controllers/operations/routes/index/new.js +1 -0
  34. package/addon/controllers/operations/service-rates/index/edit.js +2 -0
  35. package/addon/controllers/operations/service-rates/index/new.js +2 -0
  36. package/addon/controllers/settings/avatars.js +3 -0
  37. package/addon/extension.js +0 -5
  38. package/addon/routes/settings/avatars.js +3 -0
  39. package/addon/routes.js +1 -0
  40. package/addon/templates/operations/orders/index/new.hbs +1 -0
  41. package/addon/templates/settings/avatars.hbs +10 -0
  42. package/app/components/avatar-manager.js +1 -0
  43. package/app/controllers/settings/avatars.js +1 -0
  44. package/app/routes/settings/avatars.js +1 -0
  45. package/app/templates/settings/avatars.js +1 -0
  46. package/composer.json +1 -1
  47. package/extension.json +1 -1
  48. package/package.json +3 -3
  49. package/server/config/fleetops.php +1 -1
  50. package/server/src/Auth/Schemas/FleetOps.php +4 -0
  51. package/server/src/Expansions/UserFilterExpansion.php +31 -0
  52. package/server/src/Http/Controllers/Internal/v1/ContactController.php +5 -2
  53. package/server/src/Http/Controllers/Internal/v1/DriverController.php +35 -2
  54. package/server/src/Http/Controllers/Internal/v1/FleetController.php +5 -2
  55. package/server/src/Http/Controllers/Internal/v1/FuelReportController.php +5 -2
  56. package/server/src/Http/Controllers/Internal/v1/IssueController.php +5 -2
  57. package/server/src/Http/Controllers/Internal/v1/PlaceController.php +5 -2
  58. package/server/src/Http/Controllers/Internal/v1/VehicleController.php +8 -3
  59. package/server/src/Http/Controllers/Internal/v1/VendorController.php +5 -2
  60. package/server/src/Http/Requests/Internal/CreateDriverRequest.php +2 -1
  61. package/server/src/Imports/ContactImport.php +6 -0
  62. package/server/src/Imports/DriverImport.php +6 -0
  63. package/server/src/Imports/FleetImport.php +6 -0
  64. package/server/src/Imports/FuelReportImport.php +6 -0
  65. package/server/src/Imports/IssueImport.php +6 -0
  66. package/server/src/Imports/PlaceImport.php +6 -0
  67. package/server/src/Imports/VehicleImport.php +6 -0
  68. package/server/src/Imports/VendorImport.php +6 -0
  69. package/server/src/Models/Vehicle.php +9 -2
  70. package/server/src/Rules/ResolvableVehicle.php +90 -0
  71. package/server/src/Support/Reporting/FleetOpsReportSchema.php +129 -1
  72. package/translations/en-us.yaml +2 -0
  73. package/DRIVER_SCHEDULING.md +0 -186
@@ -9,6 +9,11 @@ use Maatwebsite\Excel\Concerns\WithHeadingRow;
9
9
 
10
10
  class PlaceImport implements ToCollection, WithHeadingRow
11
11
  {
12
+ /**
13
+ * Counter for successfully imported rows.
14
+ */
15
+ public int $imported = 0;
16
+
12
17
  /**
13
18
  * @return Collection
14
19
  */
@@ -20,6 +25,7 @@ class PlaceImport implements ToCollection, WithHeadingRow
20
25
  }
21
26
 
22
27
  Place::createFromImport($row, true);
28
+ $this->imported++;
23
29
  }
24
30
  }
25
31
  }
@@ -9,6 +9,11 @@ use Maatwebsite\Excel\Concerns\WithHeadingRow;
9
9
 
10
10
  class VehicleImport implements ToCollection, WithHeadingRow
11
11
  {
12
+ /**
13
+ * Counter for successfully imported rows.
14
+ */
15
+ public int $imported = 0;
16
+
12
17
  /**
13
18
  * @return Collection
14
19
  */
@@ -20,6 +25,7 @@ class VehicleImport implements ToCollection, WithHeadingRow
20
25
  }
21
26
 
22
27
  Vehicle::createFromImport($row, true);
28
+ $this->imported++;
23
29
  }
24
30
  }
25
31
  }
@@ -9,6 +9,11 @@ use Maatwebsite\Excel\Concerns\WithHeadingRow;
9
9
 
10
10
  class VendorImport implements ToCollection, WithHeadingRow
11
11
  {
12
+ /**
13
+ * Counter for successfully imported rows.
14
+ */
15
+ public int $imported = 0;
16
+
12
17
  /**
13
18
  * @return Collection
14
19
  */
@@ -20,6 +25,7 @@ class VendorImport implements ToCollection, WithHeadingRow
20
25
  }
21
26
 
22
27
  Vendor::createFromImport($row, true);
28
+ $this->imported++;
23
29
  }
24
30
  }
25
31
  }
@@ -507,7 +507,7 @@ class Vehicle extends Model
507
507
  *
508
508
  * @return \Illuminate\Support\Collection
509
509
  */
510
- public static function getAvatarOptions()
510
+ public static function getAvatarOptions(?callable $customQueryCallback = null)
511
511
  {
512
512
  $options = [
513
513
  '2_door_truck.svg',
@@ -540,8 +540,15 @@ class Vehicle extends Model
540
540
  'taxi.svg',
541
541
  ];
542
542
 
543
+ // Query for custom avatars
544
+ $customAvatarsQuery = File::where('type', 'vehicle-avatar');
545
+ if (is_callable($customQueryCallback)) {
546
+ $customQueryCallback($customAvatarsQuery);
547
+ }
548
+ $customAvatars = $customAvatarsQuery->get();
549
+
543
550
  // Get custom avatars
544
- $customAvatars = collect(File::where('type', 'vehicle-avatar')->get()->mapWithKeys(
551
+ $customAvatars = collect($customAvatars->mapWithKeys(
545
552
  function ($file) {
546
553
  $key = str_replace(['.svg', '.png'], '', 'Custom: ' . $file->original_filename);
547
554
 
@@ -0,0 +1,90 @@
1
+ <?php
2
+
3
+ namespace Fleetbase\FleetOps\Rules;
4
+
5
+ use Fleetbase\FleetOps\Models\Vehicle;
6
+ use Illuminate\Contracts\Validation\Rule;
7
+ use Illuminate\Support\Str;
8
+
9
+ class ResolvableVehicle implements Rule
10
+ {
11
+ /**
12
+ * The resolved vehicle instance, if found.
13
+ */
14
+ protected ?Vehicle $resolved = null;
15
+
16
+ /**
17
+ * Determine if the validation rule passes.
18
+ *
19
+ * Accepts:
20
+ * - A public_id string (e.g. "vehicle_abc123")
21
+ * - A UUID string (e.g. "550e8400-e29b-41d4-a716-446655440000")
22
+ * - An array/object containing an "id", "public_id", or "uuid" key
23
+ *
24
+ * @param string $attribute
25
+ *
26
+ * @return bool
27
+ */
28
+ public function passes($attribute, $value)
29
+ {
30
+ $identifier = $this->extractIdentifier($value);
31
+
32
+ if (empty($identifier)) {
33
+ return true; // nullable — let the nullable rule handle empty values
34
+ }
35
+
36
+ if (Str::isUuid($identifier)) {
37
+ $this->resolved = Vehicle::where('uuid', $identifier)->first();
38
+ } else {
39
+ $this->resolved = Vehicle::where('public_id', $identifier)->first();
40
+ }
41
+
42
+ return $this->resolved !== null;
43
+ }
44
+
45
+ /**
46
+ * Get the validation error message.
47
+ *
48
+ * @return string
49
+ */
50
+ public function message()
51
+ {
52
+ return 'The :attribute must be a valid vehicle public ID, UUID, or vehicle object.';
53
+ }
54
+
55
+ /**
56
+ * Extract a string identifier from the given value.
57
+ *
58
+ * Handles a plain string, an associative array, or a stdClass object.
59
+ */
60
+ protected function extractIdentifier($value): ?string
61
+ {
62
+ if (is_string($value)) {
63
+ return $value;
64
+ }
65
+
66
+ if (is_array($value)) {
67
+ return data_get($value, 'id')
68
+ ?? data_get($value, 'public_id')
69
+ ?? data_get($value, 'uuid')
70
+ ?? null;
71
+ }
72
+
73
+ if (is_object($value)) {
74
+ return data_get($value, 'id')
75
+ ?? data_get($value, 'public_id')
76
+ ?? data_get($value, 'uuid')
77
+ ?? null;
78
+ }
79
+
80
+ return null;
81
+ }
82
+
83
+ /**
84
+ * Get the resolved Vehicle model instance after validation passes.
85
+ */
86
+ public function getResolved(): ?Vehicle
87
+ {
88
+ return $this->resolved;
89
+ }
90
+ }
@@ -47,7 +47,7 @@ class FleetOpsReportSchema implements ReportSchema
47
47
  ->description('Delivery and service orders')
48
48
  ->category('Operations')
49
49
  ->extension('fleet-ops')
50
- ->excludeColumns(['uuid', 'deleted_at', 'meta']) // Hide foreign keys and system columns
50
+ ->excludeColumns(['uuid', 'deleted_at']) // Hide foreign keys and system columns
51
51
  ->maxRows(50000)
52
52
  ->cacheTtl(3600)
53
53
  ->columns([
@@ -157,6 +157,12 @@ class FleetOpsReportSchema implements ReportSchema
157
157
  ->description('When the order was last updated')
158
158
  ->filterable()
159
159
  ->sortable(),
160
+
161
+ Column::make('meta', 'json')
162
+ ->label('Metadata')
163
+ ->description('Order metadata and custom fields')
164
+ ->searchable()
165
+ ->filterable(),
160
166
  ])
161
167
  ->computedColumns([
162
168
  Column::count('total_orders', 'id')
@@ -178,6 +184,18 @@ class FleetOpsReportSchema implements ReportSchema
178
184
  Column::avg('average_time', 'time')
179
185
  ->label('Average Time')
180
186
  ->description('Average duration per order'),
187
+
188
+ Column::sum('total_transaction_amount', 'amount')
189
+ ->label('Total Transaction Amount')
190
+ ->description('Sum of all transaction amounts'),
191
+
192
+ Column::avg('average_transaction_amount', 'amount')
193
+ ->label('Average Transaction Amount')
194
+ ->description('Average transaction amount per order'),
195
+
196
+ Column::count('orders_with_transactions', 'transaction_uuid')
197
+ ->label('Orders with Transactions')
198
+ ->description('Count of orders that have transactions'),
181
199
  ])
182
200
  ->relationships([
183
201
  // Auto-join relationships for seamless access
@@ -272,6 +290,116 @@ class FleetOpsReportSchema implements ReportSchema
272
290
  Column::make('phone', 'string')->label('Phone'),
273
291
  Column::make('type', 'string')->label('Type'),
274
292
  ]),
293
+
294
+ Relationship::hasAutoJoin('transaction', 'transactions')
295
+ ->label('Transaction')
296
+ ->localKey('transaction_uuid')
297
+ ->foreignKey('uuid')
298
+ ->columns([
299
+ Column::make('public_id', 'string')
300
+ ->label('Transaction ID')
301
+ ->description('Public transaction identifier')
302
+ ->searchable()
303
+ ->filterable()
304
+ ->sortable(),
305
+
306
+ Column::make('gateway_transaction_id', 'string')
307
+ ->label('Gateway Transaction ID')
308
+ ->description('Transaction ID from payment gateway')
309
+ ->searchable()
310
+ ->filterable()
311
+ ->sortable(),
312
+
313
+ Column::make('gateway', 'string')
314
+ ->label('Payment Gateway')
315
+ ->description('Payment gateway used for transaction')
316
+ ->filterable()
317
+ ->aggregatable(),
318
+
319
+ Column::make('amount', 'integer')
320
+ ->label('Amount')
321
+ ->description('Transaction amount (in cents)')
322
+ ->aggregatable()
323
+ ->sortable()
324
+ ->transformer(function ($value) {
325
+ // Convert cents to dollars with 2 decimal places
326
+ return number_format($value / 100, 2);
327
+ }),
328
+
329
+ Column::make('currency', 'string')
330
+ ->label('Currency')
331
+ ->description('Transaction currency code')
332
+ ->filterable()
333
+ ->aggregatable(),
334
+
335
+ Column::make('description', 'string')
336
+ ->label('Description')
337
+ ->description('Transaction description')
338
+ ->searchable()
339
+ ->filterable(),
340
+
341
+ Column::make('type', 'string')
342
+ ->label('Transaction Type')
343
+ ->description('Type of transaction')
344
+ ->filterable()
345
+ ->aggregatable(),
346
+
347
+ Column::make('status', 'string')
348
+ ->label('Transaction Status')
349
+ ->description('Current transaction status')
350
+ ->filterable()
351
+ ->aggregatable()
352
+ ->transformer(function ($value) {
353
+ $labels = [
354
+ 'pending' => 'Pending',
355
+ 'completed' => 'Completed',
356
+ 'failed' => 'Failed',
357
+ 'refunded' => 'Refunded',
358
+ 'canceled' => 'Canceled',
359
+ ];
360
+
361
+ return $labels[$value] ?? ucfirst($value);
362
+ }),
363
+
364
+ Column::make('created_at', 'datetime')
365
+ ->label('Transaction Date')
366
+ ->description('When the transaction was created')
367
+ ->filterable()
368
+ ->sortable()
369
+ ->aggregatable(),
370
+ ])
371
+ ->with([
372
+ // Nested relationship for transaction items
373
+ Relationship::hasAutoJoin('items', 'transaction_items')
374
+ ->label('Transaction Items')
375
+ ->localKey('uuid')
376
+ ->foreignKey('transaction_uuid')
377
+ ->columns([
378
+ Column::make('amount', 'string')
379
+ ->label('Item Amount')
380
+ ->description('Line item amount')
381
+ ->aggregatable()
382
+ ->sortable(),
383
+
384
+ Column::make('currency', 'string')
385
+ ->label('Item Currency')
386
+ ->description('Line item currency code')
387
+ ->filterable(),
388
+
389
+ Column::make('details', 'string')
390
+ ->label('Item Details')
391
+ ->description('Detailed description of the line item')
392
+ ->searchable()
393
+ ->filterable(),
394
+
395
+ Column::make('code', 'string')
396
+ ->label('Item Code')
397
+ ->description('Item or SKU code')
398
+ ->searchable()
399
+ ->filterable()
400
+ ->sortable(),
401
+ ]),
402
+ ]),
275
403
  ]);
276
404
  }
277
405
 
@@ -77,6 +77,7 @@ menu:
77
77
  routing: Routing
78
78
  custom-fields: Custom Fields
79
79
  order-board: Order Board
80
+ avatars: Avatars
80
81
 
81
82
  resource:
82
83
  asset: Asset
@@ -1680,6 +1681,7 @@ route-list:
1680
1681
  more-waypoints: more waypoints
1681
1682
 
1682
1683
  settings:
1684
+ avatar-management: Avatar Management
1683
1685
  custom-fields: Custom Fields
1684
1686
  notifications:
1685
1687
  fleet-ops-notification-settings: Fleet-Ops Notification Settings
@@ -1,186 +0,0 @@
1
- # FleetOps Driver Scheduling Module
2
-
3
- This module implements driver scheduling with Hours of Service (HOS) compliance for FleetOps.
4
-
5
- ## Features
6
-
7
- - **Fleet-Wide Scheduler**: Manage all drivers' schedules in a single calendar view
8
- - **Driver Schedule Management**: Create, edit, and manage driver shifts
9
- - **HOS Compliance**: Real-time validation against FMCSA regulations
10
- - **Calendar View**: Visual schedule management with drag-and-drop
11
- - **Resource Timeline**: View multiple drivers' schedules simultaneously
12
- - **Individual Driver Schedules**: Dedicated schedule view on driver detail pages
13
- - **Availability Management**: Track driver availability and time-off requests
14
- - **Conflict Detection**: Automatic detection of scheduling conflicts
15
- - **Dual View Modes**: Toggle between order scheduling and driver scheduling
16
-
17
- ## Components
18
-
19
- ### Operations/Scheduler (Fleet-Wide View)
20
-
21
- The main scheduler view at `operations/scheduler` now supports both order scheduling and driver scheduling.
22
-
23
- **Location**: `addon/controllers/operations/scheduler/index.js`
24
-
25
- **Features**:
26
- - **View Mode Toggle**: Switch between "Orders" and "Driver Schedules" modes
27
- - **Resource Timeline**: In driver mode, shows all drivers as resources with their shifts
28
- - **Drag-and-Drop**: Drag shifts between drivers or reschedule by dragging
29
- - **Add Shift**: Quick action button to create new driver shifts
30
- - **Real-Time Updates**: Calendar updates automatically when shifts are modified
31
- - **Status Colors**: Visual indicators for shift status (pending, confirmed, etc.)
32
-
33
- **View Modes**:
34
- 1. **Orders Mode** (default):
35
- - Month calendar view
36
- - Drag unscheduled orders to calendar
37
- - Manage order scheduling
38
-
39
- 2. **Driver Schedules Mode**:
40
- - Resource timeline view (week view with drivers as resources)
41
- - View all drivers' shifts simultaneously
42
- - Drag shifts between drivers
43
- - Reschedule shifts by dragging
44
- - Click shifts to view/edit details
45
-
46
- **Usage**:
47
- Navigate to Operations → Scheduler, then toggle between "Orders" and "Driver Schedules" using the header buttons.
48
-
49
- ### Driver::Schedule
50
-
51
- Displays and manages a driver's schedule from their detail page.
52
-
53
- **Location**: `addon/components/driver/schedule.js`
54
-
55
- **Features**:
56
- - HOS compliance dashboard with visual indicators
57
- - Weekly calendar view of driver shifts
58
- - Upcoming shifts list (next 5 shifts)
59
- - Availability and time-off management
60
- - Quick actions for adding shifts and requesting time off
61
-
62
- **Usage**:
63
- ```handlebars
64
- <Driver::Schedule @driver={{@model}} />
65
- ```
66
-
67
- **Integration**: Add a "Schedule" tab to the driver detail page.
68
-
69
- ## Backend Components
70
-
71
- ### HOSConstraint
72
-
73
- Validates schedule items against FMCSA Hours of Service regulations.
74
-
75
- **Location**: `server/src/Constraints/HOSConstraint.php`
76
-
77
- **Regulations Enforced**:
78
- 1. **11-Hour Driving Limit**: Maximum 11 hours driving after 10 consecutive hours off duty
79
- 2. **14-Hour Duty Window**: Cannot drive beyond 14th hour after coming on duty
80
- 3. **60/70-Hour Weekly Limit**: Cannot drive after 60/70 hours in 7/8 consecutive days
81
- 4. **30-Minute Break**: Required after 8 cumulative hours of driving
82
-
83
- **Violation Severity**:
84
- - `critical`: Immediate compliance issue, shift cannot be scheduled
85
- - `warning`: Approaching limit, requires attention
86
-
87
- **Registration**:
88
- ```php
89
- // In FleetOps ServiceProvider boot() method
90
- $constraintService = app(\Fleetbase\Services\Scheduling\ConstraintService::class);
91
- $constraintService->register('driver', \Fleetbase\FleetOps\Constraints\HOSConstraint::class);
92
- ```
93
-
94
- ## HOS Compliance Dashboard
95
-
96
- The HOS dashboard displays:
97
- - **Daily Driving Hours**: X/11 hours with circular progress indicator
98
- - **Weekly Hours**: X/70 hours with circular progress indicator
99
- - **Compliance Status**: Badge indicating compliance level
100
- - Green: Compliant
101
- - Yellow: Approaching Limit
102
- - Red: At Limit
103
-
104
- ## API Endpoints
105
-
106
- FleetOps extends the core scheduling endpoints with driver-specific functionality:
107
-
108
- - `GET /drivers/{id}/hos-status` - Get HOS compliance status for a driver
109
- - `GET /drivers/{id}/schedule` - Get schedule items for a driver
110
- - `POST /drivers/{id}/schedule` - Create a new shift for a driver
111
- - `PUT /schedule-items/{id}` - Update a shift (with HOS validation)
112
- - `DELETE /schedule-items/{id}` - Delete a shift
113
-
114
- ## Workflow
115
-
116
- ### Creating a Driver Shift
117
-
118
- 1. User clicks "Add Shift" button
119
- 2. Modal opens with shift form
120
- 3. User selects date, time, vehicle, and other details
121
- 4. System validates against HOS constraints
122
- 5. If violations found, display warnings/errors
123
- 6. If valid, create schedule item
124
- 7. Update driver schedule view and HOS dashboard
125
-
126
- ### HOS Validation
127
-
128
- 1. When a schedule item is created/updated
129
- 2. `HOSConstraint::validate()` is called
130
- 3. Checks all four HOS regulations
131
- 4. Returns violations array if any
132
- 5. Frontend displays violations to user
133
- 6. User can adjust shift or override (with proper permissions)
134
-
135
- ## Integration with Core Scheduling
136
-
137
- FleetOps uses the core scheduling module with driver-specific customizations:
138
-
139
- **Core Components Used**:
140
- - `Schedule` model (subject_type: 'fleet', subject_uuid: fleet.id)
141
- - `ScheduleItem` model (assignee_type: 'driver', assignee_uuid: driver.id)
142
- - `ScheduleAvailability` model (subject_type: 'driver')
143
- - `ScheduleConstraint` model (subject_type: 'driver')
144
-
145
- **FleetOps Extensions**:
146
- - `HOSConstraint` for compliance validation
147
- - `Driver::Schedule` component for driver-specific UI
148
- - HOS status API endpoint
149
- - Driver-specific scheduling logic
150
-
151
- ## Future Enhancements
152
-
153
- - **Automatic Schedule Generation**: AI-powered schedule optimization
154
- - **ELD Integration**: Sync with Electronic Logging Devices
155
- - **Predictive HOS**: Forecast HOS availability for future dates
156
- - **Mobile App**: Driver-facing mobile app for schedule viewing
157
- - **Notifications**: Real-time alerts for schedule changes and HOS warnings
158
- - **Reporting**: HOS compliance reports and analytics
159
-
160
- ## Testing
161
-
162
- ### HOS Constraint Tests
163
-
164
- Test cases should cover:
165
- - 11-hour driving limit enforcement
166
- - 14-hour duty window enforcement
167
- - 60/70-hour weekly limit enforcement
168
- - 30-minute break requirement
169
- - Edge cases (consecutive shifts, split shifts, etc.)
170
-
171
- ### Integration Tests
172
-
173
- - Create driver shift via API
174
- - Validate HOS constraints are enforced
175
- - Update shift and verify re-validation
176
- - Delete shift and verify HOS recalculation
177
- - Test driver schedule view rendering
178
-
179
- ## Compliance Notes
180
-
181
- This implementation follows FMCSA Hours of Service regulations as of 2025. Regulations may vary by:
182
- - Jurisdiction (US Federal, state-specific, Canada, etc.)
183
- - Vehicle type (property-carrying vs. passenger-carrying)
184
- - Industry (short-haul vs. long-haul)
185
-
186
- **Important**: This is a software implementation and should not be the sole method of HOS compliance. Proper driver training, ELD integration, and regular audits are essential for full compliance.