@fleetbase/fleetops-engine 0.6.31 → 0.6.33
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/customer/orders.js +1 -1
- package/addon/services/leaflet-routing-control.js +1 -1
- package/addon/services/movement-tracker.js +2 -1
- package/addon/services/route-optimization.js +1 -1
- package/composer.json +1 -1
- package/extension.json +1 -1
- package/package.json +4 -4
- package/server/src/Console/Commands/TestEmail.php +98 -0
- package/server/src/Http/Controllers/Api/v1/DriverController.php +3 -0
- package/server/src/Http/Controllers/Api/v1/VehicleController.php +3 -8
- package/server/src/Http/Controllers/Internal/v1/DriverController.php +31 -1
- package/server/src/Http/Controllers/Internal/v1/LiveController.php +2 -1
- package/server/src/Http/Controllers/Internal/v1/OrderController.php +0 -32
- package/server/src/Http/Controllers/Internal/v1/PlaceController.php +0 -33
- package/server/src/Http/Controllers/Internal/v1/VendorController.php +0 -31
- package/server/src/Http/Filter/OrderFilter.php +1 -1
- package/server/src/Http/Requests/CreateServiceRateRequest.php +2 -2
- package/server/src/Http/Requests/Internal/CreateDriverRequest.php +69 -5
- package/server/src/Http/Resources/v1/Order.php +1 -0
- package/server/src/Models/Driver.php +2 -2
- package/server/src/Models/Place.php +22 -0
- package/server/src/Observers/OrderObserver.php +50 -0
- package/server/src/Providers/FleetOpsServiceProvider.php +1 -0
|
@@ -42,7 +42,7 @@ export default class CustomerOrdersComponent extends Component {
|
|
|
42
42
|
|
|
43
43
|
get modalsManager() {
|
|
44
44
|
const owner = getOwner(this);
|
|
45
|
-
const application =
|
|
45
|
+
const application = this.universe.getApplicationInstance();
|
|
46
46
|
const modalsManager = application ? application.lookup('service:modals-manager') : owner.lookup('service:modals-manager');
|
|
47
47
|
return modalsManager;
|
|
48
48
|
}
|
|
@@ -67,7 +67,7 @@ export default class LeafletRoutingControlService extends Service {
|
|
|
67
67
|
|
|
68
68
|
#initializeRegistry() {
|
|
69
69
|
const registry = 'registry:routing-controls';
|
|
70
|
-
const application =
|
|
70
|
+
const application = this.universe.getApplicationInstance();
|
|
71
71
|
if (!application.hasRegistration(registry)) {
|
|
72
72
|
application.register(registry, new RoutingControlRegistry(), { instantiate: false });
|
|
73
73
|
}
|
|
@@ -122,6 +122,7 @@ export class EventBuffer {
|
|
|
122
122
|
|
|
123
123
|
export default class MovementTrackerService extends Service {
|
|
124
124
|
@service socket;
|
|
125
|
+
@service universe;
|
|
125
126
|
@tracked channels = [];
|
|
126
127
|
@tracked buffers = new Map();
|
|
127
128
|
|
|
@@ -131,7 +132,7 @@ export default class MovementTrackerService extends Service {
|
|
|
131
132
|
}
|
|
132
133
|
|
|
133
134
|
#getOwner(owner = null) {
|
|
134
|
-
return owner ??
|
|
135
|
+
return owner ?? this.universe.getApplicationInstance() ?? getOwner(this);
|
|
135
136
|
}
|
|
136
137
|
|
|
137
138
|
#getBuffer(key, model, opts = {}) {
|
|
@@ -57,7 +57,7 @@ export default class RouteOptimizationService extends Service {
|
|
|
57
57
|
|
|
58
58
|
#initializeRegistry() {
|
|
59
59
|
const registry = 'registry:route-optimization-engines';
|
|
60
|
-
const application =
|
|
60
|
+
const application = this.universe.getApplicationInstance();
|
|
61
61
|
if (!application.hasRegistration(registry)) {
|
|
62
62
|
application.register(registry, new RouteOptimizationRegistry(), { instantiate: false });
|
|
63
63
|
}
|
package/composer.json
CHANGED
package/extension.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fleetbase/fleetops-engine",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.33",
|
|
4
4
|
"description": "Fleet & Transport Management Extension for Fleetbase",
|
|
5
5
|
"fleetbase": {
|
|
6
6
|
"route": "fleet-ops"
|
|
@@ -42,9 +42,9 @@
|
|
|
42
42
|
},
|
|
43
43
|
"dependencies": {
|
|
44
44
|
"@babel/core": "^7.23.2",
|
|
45
|
-
"@fleetbase/ember-core": "^0.3.
|
|
46
|
-
"@fleetbase/ember-ui": "^0.3.
|
|
47
|
-
"@fleetbase/fleetops-data": "^0.1.
|
|
45
|
+
"@fleetbase/ember-core": "^0.3.10",
|
|
46
|
+
"@fleetbase/ember-ui": "^0.3.17",
|
|
47
|
+
"@fleetbase/fleetops-data": "^0.1.25",
|
|
48
48
|
"@fleetbase/leaflet-routing-machine": "^3.2.17",
|
|
49
49
|
"@fortawesome/ember-fontawesome": "^2.0.0",
|
|
50
50
|
"@fortawesome/fontawesome-svg-core": "6.4.0",
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
<?php
|
|
2
|
+
|
|
3
|
+
namespace Fleetbase\FleetOps\Console\Commands;
|
|
4
|
+
|
|
5
|
+
use Fleetbase\FleetOps\Mail\CustomerCredentialsMail;
|
|
6
|
+
use Fleetbase\FleetOps\Models\Contact;
|
|
7
|
+
use Fleetbase\Models\Company;
|
|
8
|
+
use Fleetbase\Models\User;
|
|
9
|
+
use Illuminate\Console\Command;
|
|
10
|
+
use Illuminate\Support\Facades\Mail;
|
|
11
|
+
|
|
12
|
+
class TestEmail extends Command
|
|
13
|
+
{
|
|
14
|
+
/**
|
|
15
|
+
* The name and signature of the console command.
|
|
16
|
+
*
|
|
17
|
+
* @var string
|
|
18
|
+
*/
|
|
19
|
+
protected $signature = 'fleetops:test-email {email} {--type=customer_credentials : The type of email to test}';
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* The console command description.
|
|
23
|
+
*
|
|
24
|
+
* @var string
|
|
25
|
+
*/
|
|
26
|
+
protected $description = 'Test FleetOps email templates';
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Execute the console command.
|
|
30
|
+
*
|
|
31
|
+
* @return int
|
|
32
|
+
*/
|
|
33
|
+
public function handle()
|
|
34
|
+
{
|
|
35
|
+
$email = $this->argument('email');
|
|
36
|
+
$type = $this->option('type');
|
|
37
|
+
|
|
38
|
+
$this->info('Sending test email...');
|
|
39
|
+
$this->info("Type: {$type}");
|
|
40
|
+
$this->info("To: {$email}");
|
|
41
|
+
|
|
42
|
+
try {
|
|
43
|
+
switch ($type) {
|
|
44
|
+
case 'customer_credentials':
|
|
45
|
+
$this->sendCustomerCredentialsEmail($email);
|
|
46
|
+
break;
|
|
47
|
+
|
|
48
|
+
default:
|
|
49
|
+
$this->error("Unknown email type: {$type}");
|
|
50
|
+
return Command::FAILURE;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
$this->info('✓ Test email sent successfully!');
|
|
54
|
+
return Command::SUCCESS;
|
|
55
|
+
} catch (\Exception $e) {
|
|
56
|
+
$this->error('Failed to send test email: ' . $e->getMessage());
|
|
57
|
+
return Command::FAILURE;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Send a test customer credentials email.
|
|
63
|
+
*
|
|
64
|
+
* @param string $email
|
|
65
|
+
* @return void
|
|
66
|
+
*/
|
|
67
|
+
private function sendCustomerCredentialsEmail(string $email): void
|
|
68
|
+
{
|
|
69
|
+
// Create a mock user
|
|
70
|
+
$user = new User([
|
|
71
|
+
'name' => 'Test Customer',
|
|
72
|
+
'email' => $email,
|
|
73
|
+
]);
|
|
74
|
+
|
|
75
|
+
// Create a mock company
|
|
76
|
+
$company = new Company([
|
|
77
|
+
'name' => 'Test Company',
|
|
78
|
+
'public_id' => 'test_company_123',
|
|
79
|
+
]);
|
|
80
|
+
|
|
81
|
+
// Create a mock customer
|
|
82
|
+
$customer = new Contact([
|
|
83
|
+
'name' => 'Test Customer',
|
|
84
|
+
'email' => $email,
|
|
85
|
+
'phone' => '+1234567890',
|
|
86
|
+
]);
|
|
87
|
+
|
|
88
|
+
// Set relations
|
|
89
|
+
$customer->setRelation('company', $company);
|
|
90
|
+
$customer->setRelation('user', $user);
|
|
91
|
+
|
|
92
|
+
// Mock password
|
|
93
|
+
$plaintextPassword = 'TestPassword123!';
|
|
94
|
+
|
|
95
|
+
// Send the email
|
|
96
|
+
Mail::to($email)->send(new CustomerCredentialsMail($plaintextPassword, $customer));
|
|
97
|
+
}
|
|
98
|
+
}
|
|
@@ -62,6 +62,9 @@ class DriverController extends Controller
|
|
|
62
62
|
// Apply user infos
|
|
63
63
|
$userDetails = User::applyUserInfoFromRequest($request, $userDetails);
|
|
64
64
|
|
|
65
|
+
// Set company_uuid before creating user
|
|
66
|
+
$userDetails['company_uuid'] = $company->uuid;
|
|
67
|
+
|
|
65
68
|
// create user account for driver
|
|
66
69
|
$user = User::create($userDetails);
|
|
67
70
|
|
|
@@ -27,12 +27,10 @@ class VehicleController extends Controller
|
|
|
27
27
|
{
|
|
28
28
|
// get request input
|
|
29
29
|
$input = $request->only(['status', 'make', 'model', 'year', 'trim', 'type', 'plate_number', 'vin', 'meta', 'online', 'location', 'altitude', 'heading', 'speed']);
|
|
30
|
+
|
|
30
31
|
// make sure company is set
|
|
31
32
|
$input['company_uuid'] = session('company');
|
|
32
33
|
|
|
33
|
-
// create instance of vehicle model
|
|
34
|
-
$vehicle = new Vehicle();
|
|
35
|
-
|
|
36
34
|
// set default online
|
|
37
35
|
if (!isset($input['online'])) {
|
|
38
36
|
$input['online'] = 0;
|
|
@@ -51,11 +49,8 @@ class VehicleController extends Controller
|
|
|
51
49
|
$input['location'] = Utils::getPointFromCoordinates($request->only(['latitude', 'longitude']));
|
|
52
50
|
}
|
|
53
51
|
|
|
54
|
-
//
|
|
55
|
-
$vehicle =
|
|
56
|
-
|
|
57
|
-
// save the vehicle
|
|
58
|
-
$vehicle->save();
|
|
52
|
+
// create the vehicle (fires 'created' event for billing resource tracking)
|
|
53
|
+
$vehicle = Vehicle::create($input);
|
|
59
54
|
|
|
60
55
|
// driver assignment
|
|
61
56
|
if ($request->has('driver')) {
|
|
@@ -136,7 +136,37 @@ class DriverController extends FleetOpsController
|
|
|
136
136
|
|
|
137
137
|
if ($input->has('user_uuid')) {
|
|
138
138
|
$user = User::where('uuid', $input->get('user_uuid'))->first();
|
|
139
|
-
|
|
139
|
+
|
|
140
|
+
// If user doesn't exist with provided UUID, create new user
|
|
141
|
+
if (!$user) {
|
|
142
|
+
$userInput = $input
|
|
143
|
+
->only(['name', 'password', 'email', 'phone', 'status', 'avatar_uuid'])
|
|
144
|
+
->filter()
|
|
145
|
+
->toArray();
|
|
146
|
+
|
|
147
|
+
// handle `photo_uuid`
|
|
148
|
+
if (isset($input['photo_uuid']) && Str::isUuid($input['photo_uuid'])) {
|
|
149
|
+
$userInput['avatar_uuid'] = $input['photo_uuid'];
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// Make sure password is set
|
|
153
|
+
if (empty($userInput['password'])) {
|
|
154
|
+
$userInput['password'] = Str::random(14);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// Set user company
|
|
158
|
+
$userInput['company_uuid'] = session('company', $company->uuid);
|
|
159
|
+
|
|
160
|
+
// Apply user infos
|
|
161
|
+
$userInput = User::applyUserInfoFromRequest($request, $userInput);
|
|
162
|
+
|
|
163
|
+
// Create user account
|
|
164
|
+
$user = User::create($userInput);
|
|
165
|
+
|
|
166
|
+
// Set the user type to driver
|
|
167
|
+
$user->setType('driver');
|
|
168
|
+
} elseif ($input->has('photo_uuid')) {
|
|
169
|
+
// Update existing user's avatar if photo provided
|
|
140
170
|
$user->update(['avatar_uuid' => $input->get('photo_uuid')]);
|
|
141
171
|
}
|
|
142
172
|
} else {
|
|
@@ -139,6 +139,7 @@ class LiveController extends Controller
|
|
|
139
139
|
|
|
140
140
|
if ($active) {
|
|
141
141
|
$query->whereHas('driverAssigned');
|
|
142
|
+
$query->whereNotIn('status', ['created', 'completed', 'expired', 'order_canceled', 'canceled', 'pending']);
|
|
142
143
|
}
|
|
143
144
|
|
|
144
145
|
if ($unassigned) {
|
|
@@ -166,7 +167,7 @@ class LiveController extends Controller
|
|
|
166
167
|
|
|
167
168
|
return LiveCacheService::remember('drivers', $cacheParams, function () use ($bounds) {
|
|
168
169
|
$query = Driver::where(['company_uuid' => session('company')])
|
|
169
|
-
->with(['user', 'vehicle'
|
|
170
|
+
->with(['user', 'vehicle'])
|
|
170
171
|
->applyDirectivesForPermissions('fleet-ops list driver');
|
|
171
172
|
|
|
172
173
|
// Filter out drivers with invalid coordinates
|
|
@@ -29,14 +29,12 @@ use Fleetbase\FleetOps\Models\Waypoint;
|
|
|
29
29
|
use Fleetbase\FleetOps\Support\Utils;
|
|
30
30
|
use Fleetbase\Http\Requests\ExportRequest;
|
|
31
31
|
use Fleetbase\Http\Requests\Internal\BulkActionRequest;
|
|
32
|
-
use Fleetbase\Http\Requests\Internal\BulkDeleteRequest;
|
|
33
32
|
use Fleetbase\Models\File;
|
|
34
33
|
use Fleetbase\Models\Type;
|
|
35
34
|
use Fleetbase\Support\TemplateString;
|
|
36
35
|
use Illuminate\Database\Eloquent\ModelNotFoundException;
|
|
37
36
|
use Illuminate\Database\QueryException;
|
|
38
37
|
use Illuminate\Http\Request;
|
|
39
|
-
use Illuminate\Support\Collection;
|
|
40
38
|
use Illuminate\Support\Facades\Cache;
|
|
41
39
|
use Illuminate\Support\Facades\DB;
|
|
42
40
|
use Illuminate\Support\Facades\Validator;
|
|
@@ -330,36 +328,6 @@ class OrderController extends FleetOpsController
|
|
|
330
328
|
);
|
|
331
329
|
}
|
|
332
330
|
|
|
333
|
-
/**
|
|
334
|
-
* Updates a order to canceled and updates order activity.
|
|
335
|
-
*
|
|
336
|
-
* @return \Illuminate\Http\Response
|
|
337
|
-
*/
|
|
338
|
-
public function bulkDelete(BulkDeleteRequest $request)
|
|
339
|
-
{
|
|
340
|
-
$ids = $request->input('ids', []);
|
|
341
|
-
|
|
342
|
-
if (!$ids) {
|
|
343
|
-
return response()->error('Nothing to delete.');
|
|
344
|
-
}
|
|
345
|
-
|
|
346
|
-
/** @var Order */
|
|
347
|
-
$count = Order::whereIn('uuid', $ids)->count();
|
|
348
|
-
$deleted = Order::whereIn('uuid', $ids)->delete();
|
|
349
|
-
|
|
350
|
-
if (!$deleted) {
|
|
351
|
-
return response()->error('Failed to bulk delete orders.');
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
return response()->json(
|
|
355
|
-
[
|
|
356
|
-
'status' => 'OK',
|
|
357
|
-
'message' => 'Deleted ' . $count . ' orders',
|
|
358
|
-
'count' => $count,
|
|
359
|
-
]
|
|
360
|
-
);
|
|
361
|
-
}
|
|
362
|
-
|
|
363
331
|
/**
|
|
364
332
|
* Updates a order to canceled and updates order activity.
|
|
365
333
|
*
|
|
@@ -10,7 +10,6 @@ use Fleetbase\FleetOps\Models\Place;
|
|
|
10
10
|
use Fleetbase\FleetOps\Support\Geocoding;
|
|
11
11
|
use Fleetbase\Http\Requests\ExportRequest;
|
|
12
12
|
use Fleetbase\Http\Requests\ImportRequest;
|
|
13
|
-
use Fleetbase\Http\Requests\Internal\BulkDeleteRequest;
|
|
14
13
|
use Fleetbase\LaravelMysqlSpatial\Types\Point;
|
|
15
14
|
use Illuminate\Http\Request;
|
|
16
15
|
use Illuminate\Support\Str;
|
|
@@ -150,38 +149,6 @@ class PlaceController extends FleetOpsController
|
|
|
150
149
|
return Excel::download(new PlaceExport($selections), $fileName);
|
|
151
150
|
}
|
|
152
151
|
|
|
153
|
-
/**
|
|
154
|
-
* Bulk deletes resources.
|
|
155
|
-
*
|
|
156
|
-
* @return \Illuminate\Http\Response
|
|
157
|
-
*/
|
|
158
|
-
public function bulkDelete(BulkDeleteRequest $request)
|
|
159
|
-
{
|
|
160
|
-
$ids = $request->input('ids', []);
|
|
161
|
-
|
|
162
|
-
if (!$ids) {
|
|
163
|
-
return response()->error('Nothing to delete.');
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
/**
|
|
167
|
-
* @var \Fleetbase\Models\Place
|
|
168
|
-
*/
|
|
169
|
-
$count = Place::whereIn('uuid', $ids)->applyDirectivesForPermissions('fleet-ops list place')->count();
|
|
170
|
-
$deleted = Place::whereIn('uuid', $ids)->applyDirectivesForPermissions('fleet-ops list place')->delete();
|
|
171
|
-
|
|
172
|
-
if (!$deleted) {
|
|
173
|
-
return response()->error('Failed to bulk delete places.');
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
return response()->json(
|
|
177
|
-
[
|
|
178
|
-
'status' => 'OK',
|
|
179
|
-
'message' => 'Deleted ' . $count . ' places',
|
|
180
|
-
],
|
|
181
|
-
200
|
|
182
|
-
);
|
|
183
|
-
}
|
|
184
|
-
|
|
185
152
|
/**
|
|
186
153
|
* Get all avatar options for an vehicle.
|
|
187
154
|
*
|
|
@@ -9,7 +9,6 @@ use Fleetbase\FleetOps\Models\Driver;
|
|
|
9
9
|
use Fleetbase\FleetOps\Models\Vendor;
|
|
10
10
|
use Fleetbase\Http\Requests\ExportRequest;
|
|
11
11
|
use Fleetbase\Http\Requests\ImportRequest;
|
|
12
|
-
use Fleetbase\Http\Requests\Internal\BulkDeleteRequest;
|
|
13
12
|
use Illuminate\Http\Request;
|
|
14
13
|
use Illuminate\Support\Facades\DB;
|
|
15
14
|
use Illuminate\Support\Str;
|
|
@@ -85,36 +84,6 @@ class VendorController extends FleetOpsController
|
|
|
85
84
|
return Excel::download(new VendorExport($selections), $fileName);
|
|
86
85
|
}
|
|
87
86
|
|
|
88
|
-
/**
|
|
89
|
-
* Bulk delete resources.
|
|
90
|
-
*
|
|
91
|
-
* @return \Illuminate\Http\Response
|
|
92
|
-
*/
|
|
93
|
-
public function bulkDelete(BulkDeleteRequest $request)
|
|
94
|
-
{
|
|
95
|
-
$ids = $request->input('ids', []);
|
|
96
|
-
|
|
97
|
-
if (!$ids) {
|
|
98
|
-
return response()->error('Nothing to delete.');
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
/** @var \Fleetbase\Models\Vendor */
|
|
102
|
-
$count = Vendor::whereIn('uuid', $ids)->count();
|
|
103
|
-
$deleted = Vendor::whereIn('uuid', $ids)->delete();
|
|
104
|
-
|
|
105
|
-
if (!$deleted) {
|
|
106
|
-
return response()->error('Failed to bulk delete vendors.');
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
return response()->json(
|
|
110
|
-
[
|
|
111
|
-
'status' => 'OK',
|
|
112
|
-
'message' => 'Deleted ' . $count . ' vendors',
|
|
113
|
-
],
|
|
114
|
-
200
|
|
115
|
-
);
|
|
116
|
-
}
|
|
117
|
-
|
|
118
87
|
/**
|
|
119
88
|
* Get all status options for an vehicle.
|
|
120
89
|
*
|
|
@@ -116,7 +116,7 @@ class OrderFilter extends Filter
|
|
|
116
116
|
$this->builder->where(
|
|
117
117
|
function ($q) {
|
|
118
118
|
$q->whereHas('driverAssigned');
|
|
119
|
-
$q->whereNotIn('status', ['created', '
|
|
119
|
+
$q->whereNotIn('status', ['created', 'completed', 'expired', 'order_canceled', 'canceled', 'pending']);
|
|
120
120
|
}
|
|
121
121
|
);
|
|
122
122
|
}
|
|
@@ -36,8 +36,8 @@ class CreateServiceRateRequest extends FleetbaseRequest
|
|
|
36
36
|
'base_fee' => ['numeric'],
|
|
37
37
|
'per_meter_unit' => ['required_if:rate_calculation_method,per_meter', 'string', 'in:km,m'],
|
|
38
38
|
'per_meter_flat_rate_fee' => ['required_if:rate_calculation_method,per_meter', 'numeric'],
|
|
39
|
-
'meter_fees' => [Rule::requiredIf(function (
|
|
40
|
-
return in_array($input
|
|
39
|
+
'meter_fees' => [Rule::requiredIf(function () {
|
|
40
|
+
return in_array($this->input('rate_calculation_method'), ['fixed_meter', 'fixed_rate']);
|
|
41
41
|
}), 'array'],
|
|
42
42
|
'meter_fees.*.distance' => ['numeric'],
|
|
43
43
|
'meter_fees.*.fee' => ['numeric'],
|
|
@@ -3,7 +3,9 @@
|
|
|
3
3
|
namespace Fleetbase\FleetOps\Http\Requests\Internal;
|
|
4
4
|
|
|
5
5
|
use Fleetbase\FleetOps\Http\Requests\CreateDriverRequest as CreateDriverApiRequest;
|
|
6
|
+
use Fleetbase\FleetOps\Rules\ResolvablePoint;
|
|
6
7
|
use Fleetbase\Support\Auth;
|
|
8
|
+
use Illuminate\Validation\Rule;
|
|
7
9
|
|
|
8
10
|
class CreateDriverRequest extends CreateDriverApiRequest
|
|
9
11
|
{
|
|
@@ -25,13 +27,75 @@ class CreateDriverRequest extends CreateDriverApiRequest
|
|
|
25
27
|
public function rules()
|
|
26
28
|
{
|
|
27
29
|
$isCreating = $this->isMethod('POST');
|
|
30
|
+
$isCreatingWithUser = $this->filled('driver.user_uuid');
|
|
31
|
+
$shouldValidateUserAttributes = $isCreating && !$isCreatingWithUser;
|
|
28
32
|
|
|
29
33
|
return [
|
|
30
|
-
|
|
31
|
-
'
|
|
32
|
-
'
|
|
33
|
-
|
|
34
|
-
|
|
34
|
+
// Required fields for driver creation
|
|
35
|
+
'name' => [Rule::requiredIf($shouldValidateUserAttributes), 'nullable', 'string', 'max:255'],
|
|
36
|
+
'email' => [
|
|
37
|
+
Rule::requiredIf($shouldValidateUserAttributes),
|
|
38
|
+
Rule::when($this->filled('email'), ['email']),
|
|
39
|
+
Rule::when($shouldValidateUserAttributes, [Rule::unique('users')->whereNull('deleted_at')])
|
|
40
|
+
],
|
|
41
|
+
'phone' => [
|
|
42
|
+
Rule::requiredIf($shouldValidateUserAttributes),
|
|
43
|
+
Rule::when($shouldValidateUserAttributes, [Rule::unique('users')->whereNull('deleted_at')])
|
|
44
|
+
],
|
|
45
|
+
|
|
46
|
+
// Optional fields
|
|
47
|
+
'password' => 'nullable|string|min:8',
|
|
48
|
+
'drivers_license_number' => 'nullable|string|max:255',
|
|
49
|
+
'internal_id' => 'nullable|string|max:255',
|
|
50
|
+
'country' => 'nullable|string|size:2',
|
|
51
|
+
'city' => 'nullable|string|max:255',
|
|
52
|
+
'vehicle' => 'nullable|string|starts_with:vehicle_|exists:vehicles,public_id',
|
|
53
|
+
'status' => 'nullable|string|in:active,inactive',
|
|
54
|
+
'vendor' => 'nullable|exists:vendors,public_id',
|
|
55
|
+
'job' => 'nullable|exists:orders,public_id',
|
|
56
|
+
'location' => ['nullable', new ResolvablePoint()],
|
|
57
|
+
'latitude' => ['nullable', 'required_with:longitude', 'numeric'],
|
|
58
|
+
'longitude' => ['nullable', 'required_with:latitude', 'numeric'],
|
|
59
|
+
|
|
60
|
+
// Photo/avatar
|
|
61
|
+
'photo_uuid' => 'nullable|exists:files,uuid',
|
|
62
|
+
'avatar_uuid' => 'nullable|exists:files,uuid',
|
|
63
|
+
];
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Get custom attributes for validator errors.
|
|
68
|
+
*
|
|
69
|
+
* @return array
|
|
70
|
+
*/
|
|
71
|
+
public function attributes()
|
|
72
|
+
{
|
|
73
|
+
return [
|
|
74
|
+
'name' => 'driver name',
|
|
75
|
+
'email' => 'email address',
|
|
76
|
+
'phone' => 'phone number',
|
|
77
|
+
'drivers_license_number' => 'driver\'s license number',
|
|
78
|
+
'internal_id' => 'internal ID',
|
|
79
|
+
'photo_uuid' => 'photo',
|
|
80
|
+
'avatar_uuid' => 'avatar',
|
|
81
|
+
];
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Get custom messages for validator errors.
|
|
86
|
+
*
|
|
87
|
+
* @return array
|
|
88
|
+
*/
|
|
89
|
+
public function messages()
|
|
90
|
+
{
|
|
91
|
+
return [
|
|
92
|
+
'name.required' => 'Driver name is required.',
|
|
93
|
+
'email.required' => 'Email address is required.',
|
|
94
|
+
'email.email' => 'Please provide a valid email address.',
|
|
95
|
+
'email.unique' => 'This email address is already registered.',
|
|
96
|
+
'phone.required' => 'Phone number is required.',
|
|
97
|
+
'phone.unique' => 'This phone number is already registered.',
|
|
98
|
+
'password.min' => 'Password must be at least 8 characters.',
|
|
35
99
|
];
|
|
36
100
|
}
|
|
37
101
|
}
|
|
@@ -84,6 +84,7 @@ class Order extends FleetbaseResource
|
|
|
84
84
|
'pod_method' => $this->pod_method,
|
|
85
85
|
'pod_required' => (bool) data_get($this, 'pod_required', false),
|
|
86
86
|
'dispatched' => (bool) data_get($this, 'dispatched', false),
|
|
87
|
+
'started' => (bool) data_get($this, 'started', false),
|
|
87
88
|
'adhoc' => (bool) data_get($this, 'adhoc', false),
|
|
88
89
|
'adhoc_distance' => (int) $this->getAdhocDistance(),
|
|
89
90
|
'distance' => (int) $this->distance,
|
|
@@ -262,12 +262,12 @@ class Driver extends Model
|
|
|
262
262
|
|
|
263
263
|
public function currentJob(): BelongsTo|Builder
|
|
264
264
|
{
|
|
265
|
-
return $this->belongsTo(Order::class)->
|
|
265
|
+
return $this->belongsTo(Order::class)->without(['driver']);
|
|
266
266
|
}
|
|
267
267
|
|
|
268
268
|
public function currentOrder(): BelongsTo|Builder
|
|
269
269
|
{
|
|
270
|
-
return $this->belongsTo(Order::class, 'current_job_uuid')->
|
|
270
|
+
return $this->belongsTo(Order::class, 'current_job_uuid')->without(['driver']);
|
|
271
271
|
}
|
|
272
272
|
|
|
273
273
|
public function jobs(): HasMany|Builder
|
|
@@ -333,6 +333,28 @@ class Place extends Model
|
|
|
333
333
|
{
|
|
334
334
|
$instance = (new static())->fillWithGoogleAddress($address);
|
|
335
335
|
|
|
336
|
+
// Before saving or returning this instance check the database for a duplicate address
|
|
337
|
+
// it cannot have any owner, and must belong to this session
|
|
338
|
+
if ($companyUuid = session('company')) {
|
|
339
|
+
$duplicate = static::where([
|
|
340
|
+
'company_uuid' => $companyUuid,
|
|
341
|
+
'street1' => $instance->street1,
|
|
342
|
+
'city' => $instance->city,
|
|
343
|
+
'country' => $instance->country,
|
|
344
|
+
])
|
|
345
|
+
->when(
|
|
346
|
+
$instance->postal_code !== null,
|
|
347
|
+
fn ($q) => $q->where('postal_code', $instance->postal_code),
|
|
348
|
+
fn ($q) => $q->whereNull('postal_code')
|
|
349
|
+
)
|
|
350
|
+
->whereNull('owner_uuid')
|
|
351
|
+
->first();
|
|
352
|
+
|
|
353
|
+
if ($duplicate) {
|
|
354
|
+
return $duplicate;
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
|
|
336
358
|
if ($saveInstance) {
|
|
337
359
|
$instance->save();
|
|
338
360
|
}
|
|
@@ -18,6 +18,20 @@ class OrderObserver
|
|
|
18
18
|
$this->invalidateCache($order);
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
+
/**
|
|
22
|
+
* Handle the Order "updating" event.
|
|
23
|
+
*
|
|
24
|
+
* This event is fired before the order is persisted to the database.
|
|
25
|
+
* It is used to mutate attributes as part of the same update operation
|
|
26
|
+
* without triggering additional save cycles.
|
|
27
|
+
*
|
|
28
|
+
* @param Order $order The order being updated
|
|
29
|
+
*/
|
|
30
|
+
public function updating(Order $order): void
|
|
31
|
+
{
|
|
32
|
+
$this->ensureOrderStarted($order);
|
|
33
|
+
}
|
|
34
|
+
|
|
21
35
|
/**
|
|
22
36
|
* Handle the Order "updated" event.
|
|
23
37
|
*
|
|
@@ -62,4 +76,40 @@ class OrderObserver
|
|
|
62
76
|
Cache::forget("order:{$order->uuid}:tracker");
|
|
63
77
|
}
|
|
64
78
|
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Detects when an order has just transitioned to the "started" status
|
|
82
|
+
* and initializes start-related fields.
|
|
83
|
+
*
|
|
84
|
+
* This method should be called during the "updating" lifecycle event
|
|
85
|
+
* to ensure that the changes are persisted as part of the same database
|
|
86
|
+
* update and do not trigger additional observer events.
|
|
87
|
+
*
|
|
88
|
+
* An order is considered "started" when:
|
|
89
|
+
* - The "status" attribute is being changed in the current update
|
|
90
|
+
* - The previous status was not "started"
|
|
91
|
+
* - The new status is "started"
|
|
92
|
+
*
|
|
93
|
+
* When these conditions are met, the order's start timestamp and
|
|
94
|
+
* started flag are set if they have not already been initialized.
|
|
95
|
+
*
|
|
96
|
+
* @param Order $order The order being evaluated for a start transition
|
|
97
|
+
*/
|
|
98
|
+
protected function ensureOrderStarted(Order $order): void
|
|
99
|
+
{
|
|
100
|
+
if (
|
|
101
|
+
$order->isDirty('status')
|
|
102
|
+
&& $order->getOriginal('status') === 'dispatched'
|
|
103
|
+
&& $order->status === 'started'
|
|
104
|
+
) {
|
|
105
|
+
// Only set defaults if not explicitly provided
|
|
106
|
+
if (is_null($order->started_at)) {
|
|
107
|
+
$order->started_at = now();
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
if (!$order->started) {
|
|
111
|
+
$order->started = true;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
65
115
|
}
|
|
@@ -60,6 +60,7 @@ class FleetOpsServiceProvider extends CoreServiceProvider
|
|
|
60
60
|
\Fleetbase\FleetOps\Console\Commands\PurgeUnpurchasedServiceQuotes::class,
|
|
61
61
|
\Fleetbase\FleetOps\Console\Commands\SendDriverNotification::class,
|
|
62
62
|
\Fleetbase\FleetOps\Console\Commands\ReplayVehicleLocations::class,
|
|
63
|
+
\Fleetbase\FleetOps\Console\Commands\TestEmail::class,
|
|
63
64
|
];
|
|
64
65
|
|
|
65
66
|
/**
|