@fleetbase/fleetops-engine 0.6.68 → 0.6.69
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/RELEASE.md +11 -9
- package/addon/components/contact/form.hbs +13 -2
- package/addon/components/customer/details.hbs +17 -17
- package/addon/components/customer/form.hbs +13 -2
- package/addon/components/customer/form.js +0 -56
- package/addon/components/driver/details.hbs +13 -19
- package/addon/components/driver/form.hbs +20 -35
- package/addon/components/driver/form.js +0 -58
- package/addon/components/modals/driver-form.hbs +2 -2
- package/addon/components/modals/reset-profile-credentials.hbs +23 -0
- package/addon/components/modals/reset-profile-credentials.js +70 -0
- package/addon/components/profile-identity-hint.hbs +13 -0
- package/addon/components/profile-identity-hint.js +49 -0
- package/addon/controllers/management/drivers/index.js +4 -0
- package/addon/extension.js +4 -2
- package/addon/services/contact-actions.js +50 -89
- package/addon/services/driver-actions.js +98 -0
- package/addon/utils/profile-login-actions.js +172 -0
- package/app/components/modals/{reset-customer-credentials.js → reset-profile-credentials.js} +1 -1
- package/app/components/profile-identity-hint.js +1 -0
- package/composer.json +1 -1
- package/extension.json +1 -1
- package/package.json +3 -3
- package/scripts/pest-bootstrap.php +12 -0
- package/server/migrations/2026_09_21_000001_convert_profile_only_users_to_managed_accounts.php +132 -0
- package/server/resources/views/mail/driver-credentials.blade.php +21 -0
- package/server/src/Console/Commands/FixCustomerCompanies.php +1 -1
- package/server/src/Console/Commands/FixDriverCompanies.php +1 -1
- package/server/src/Exceptions/ProfileIdentityConflictException.php +34 -0
- package/server/src/Http/Controllers/Api/v1/CustomerController.php +14 -0
- package/server/src/Http/Controllers/Api/v1/DriverController.php +56 -39
- package/server/src/Http/Controllers/Internal/v1/ContactController.php +5 -17
- package/server/src/Http/Controllers/Internal/v1/CustomerController.php +40 -4
- package/server/src/Http/Controllers/Internal/v1/DriverController.php +182 -162
- package/server/src/Http/Controllers/Internal/v1/FleetOpsLookupController.php +47 -0
- package/server/src/Http/Controllers/Internal/v1/OrderController.php +156 -27
- package/server/src/Http/Requests/CreateDriverRequest.php +6 -2
- package/server/src/Http/Requests/Internal/CreateDriverRequest.php +9 -18
- package/server/src/Http/Resources/v1/Contact.php +3 -0
- package/server/src/Http/Resources/v1/Driver.php +3 -0
- package/server/src/Mail/DriverCredentialsMail.php +55 -0
- package/server/src/Models/Contact.php +55 -90
- package/server/src/Models/Sensor.php +3 -5
- package/server/src/Observers/ContactObserver.php +2 -22
- package/server/src/Observers/DriverObserver.php +5 -6
- package/server/src/Providers/FleetOpsServiceProvider.php +15 -0
- package/server/src/Support/Ai/Capabilities/AbstractFleetOpsAICapability.php +62 -5
- package/server/src/Support/Ai/Capabilities/AssetStatusCapability.php +24 -5
- package/server/src/Support/Ai/Capabilities/CreateOrderPreviewCapability.php +6 -2
- package/server/src/Support/Ai/Capabilities/OrderInsightsCapability.php +40 -7
- package/server/src/Support/Ai/Capabilities/SearchResourcesCapability.php +86 -24
- package/server/src/Support/Ai/FleetOpsAiConsoleCommands.php +122 -0
- package/server/src/Support/Ai/Tools/CreateOrderTool.php +86 -0
- package/server/src/Support/Ai/Tools/ImportOrdersTool.php +53 -0
- package/server/src/Support/Ai/Tools/OptimizeOrderRouteTool.php +70 -0
- package/server/src/Support/Ai/Tools/SearchResourcesTool.php +73 -0
- package/server/src/Support/ProfileAccountManager.php +405 -0
- package/server/src/routes.php +5 -0
- package/server/tests/AiOperationalQueryCapabilityTest.php +1 -0
- package/server/tests/ApiCustomerControllerContractsTest.php +21 -0
- package/server/tests/ApiDriverControllerContractsTest.php +102 -20
- package/server/tests/CommandContractsTest.php +4 -4
- package/server/tests/ControllerHelperContractsTest.php +3 -11
- package/server/tests/Feature/Http/AfaqyRealtimeIngestionTest.php +5 -0
- package/server/tests/Feature/Http/Api/DriverControllerAuthFlowsTest.php +36 -0
- package/server/tests/Feature/Http/Api/DriverControllerSessionHelpersTest.php +19 -6
- package/server/tests/Feature/Http/Api/ProfileOnlyUserConversionMigrationTest.php +201 -0
- package/server/tests/Feature/Http/Internal/ContactControllerEndpointsTest.php +6 -4
- package/server/tests/Feature/Http/Internal/CustomerControllerCredentialsTest.php +6 -2
- package/server/tests/Feature/Http/Internal/DriverControllerContractsTest.php +23 -0
- package/server/tests/Feature/Http/Internal/DriverControllerCreateRecordTest.php +42 -17
- package/server/tests/Feature/Http/Internal/DriverControllerExistingUserAdoptionTest.php +168 -277
- package/server/tests/Feature/Http/Internal/DriverControllerLoginManagementTest.php +716 -0
- package/server/tests/Feature/Http/Internal/OrderControllerContractsTest.php +10 -3
- package/server/tests/Feature/Http/Internal/OrderControllerImportFromFilesTest.php +18 -0
- package/server/tests/Feature/Http/Internal/OrderControllerTenantScopingTest.php +411 -0
- package/server/tests/Feature/Http/Internal/OrderControllerUpstreamNotFoundTest.php +8 -20
- package/server/tests/InternalCustomerControllerContractsTest.php +32 -1
- package/server/tests/ModelAccessorContractsTest.php +82 -20
- package/server/tests/ObserverContractsTest.php +27 -2
- package/server/tests/RequestContractsTest.php +19 -11
- package/server/tests/SupportJobAndAiCoverageTest.php +3 -3
- package/server/tests/TelematicControllerContractsTest.php +22 -4
- package/server/tests/Unit/Models/ContactCustomerUserTest.php +90 -14
- package/server/tests/Unit/Models/ContactTest.php +48 -5
- package/server/tests/Unit/Models/SensorTest.php +2 -2
- package/server/tests/Unit/Models/SmallModelSurfacesTest.php +31 -32
- package/server/tests/Unit/Observers/ObserverListenerSeamsTest.php +7 -2
- package/server/tests/Unit/Providers/FleetOpsAiRegistrationTest.php +15 -2
- package/server/tests/Unit/Support/Ai/AssetStatusCapabilityQueriesTest.php +42 -0
- package/server/tests/Unit/Support/Ai/Capabilities/OrderInsightsCapabilityTest.php +19 -1
- package/server/tests/Unit/Support/Ai/Capabilities/SearchResourcesCapabilityTest.php +85 -1
- package/server/tests/Unit/Support/Ai/FleetOpsAiConsoleCommandsTest.php +31 -0
- package/server/tests/Unit/Support/Ai/Tools/FleetOpsAiToolsTest.php +246 -0
- package/server/tests/Unit/Support/AiCapabilityPermissionsTest.php +3 -3
- package/server/tests/Unit/Support/ProfileAccountManagerTest.php +643 -0
- package/translations/en-us.yaml +37 -2
- package/translations/uk-ua.yaml +3475 -0
- package/addon/components/modals/reset-customer-credentials.hbs +0 -18
- package/addon/components/modals/reset-customer-credentials.js +0 -55
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
namespace Fleetbase\FleetOps\Http\Controllers\Internal\v1;
|
|
4
4
|
|
|
5
5
|
use Fleetbase\Exceptions\FleetbaseRequestValidationException;
|
|
6
|
+
use Fleetbase\FleetOps\Exceptions\ProfileIdentityConflictException;
|
|
6
7
|
use Fleetbase\FleetOps\Exports\DriverExport;
|
|
7
8
|
use Fleetbase\FleetOps\Http\Controllers\Api\v1\DriverController as ApiDriverController;
|
|
8
9
|
use Fleetbase\FleetOps\Http\Controllers\FleetOpsController;
|
|
@@ -13,11 +14,11 @@ use Fleetbase\FleetOps\Imports\DriverImport;
|
|
|
13
14
|
use Fleetbase\FleetOps\Models\Driver;
|
|
14
15
|
use Fleetbase\FleetOps\Models\Order;
|
|
15
16
|
use Fleetbase\FleetOps\Models\Vehicle;
|
|
17
|
+
use Fleetbase\FleetOps\Support\ProfileAccountManager;
|
|
16
18
|
use Fleetbase\FleetOps\Support\Utils;
|
|
17
19
|
use Fleetbase\Http\Requests\ExportRequest;
|
|
18
20
|
use Fleetbase\Http\Requests\ImportRequest;
|
|
19
21
|
use Fleetbase\LaravelMysqlSpatial\Types\Point;
|
|
20
|
-
use Fleetbase\Models\Invite;
|
|
21
22
|
use Fleetbase\Models\User;
|
|
22
23
|
use Fleetbase\Models\VerificationCode;
|
|
23
24
|
use Fleetbase\Support\Auth;
|
|
@@ -58,72 +59,6 @@ class DriverController extends FleetOpsController
|
|
|
58
59
|
$validator = Validator::make($input, $rules);
|
|
59
60
|
|
|
60
61
|
if ($validator->fails()) {
|
|
61
|
-
// here if the user exists already
|
|
62
|
-
// within organization: offer to create driver record
|
|
63
|
-
// outside organization: invite to join organization AS DRIVER
|
|
64
|
-
if ($validator->errors()->hasAny(['phone', 'email'])) {
|
|
65
|
-
// get existing user
|
|
66
|
-
$existingUser = null;
|
|
67
|
-
|
|
68
|
-
// if values provided for user lookup
|
|
69
|
-
if (!empty($input['phone']) || !empty($input['email'])) {
|
|
70
|
-
$existingUserQuery = User::query();
|
|
71
|
-
|
|
72
|
-
if (!empty($input['phone']) && is_string($input['phone'])) {
|
|
73
|
-
$existingUserQuery->orWhere(function ($q) use ($input) {
|
|
74
|
-
$q->where('phone', $input['phone'])->whereNotNull('phone');
|
|
75
|
-
});
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
if (!empty($input['email']) && is_string($input['email'])) {
|
|
79
|
-
$existingUserQuery->orWhere(function ($q) use ($input) {
|
|
80
|
-
$q->where('email', $input['email'])->whereNotNull('email');
|
|
81
|
-
});
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
$existingUser = $existingUserQuery->first();
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
if ($existingUser) {
|
|
88
|
-
// if exists in organization create driver profile for user
|
|
89
|
-
$isOrganizationMember = $existingUser->companies()->where('companies.uuid', session('company'))->exists();
|
|
90
|
-
|
|
91
|
-
// Check if driver profile also already exists
|
|
92
|
-
$existingDriverProfile = Driver::where(['company_uuid' => session('company'), 'user_uuid' => $existingUser->uuid])->first();
|
|
93
|
-
if ($existingDriverProfile) {
|
|
94
|
-
return ['driver' => new $this->resource($existingDriverProfile)];
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
// create driver profile for user
|
|
98
|
-
$input = collect($input)
|
|
99
|
-
->except(['name', 'password', 'email', 'phone', 'meta', 'avatar_uuid', 'photo_uuid', 'status'])
|
|
100
|
-
->filter()
|
|
101
|
-
->toArray();
|
|
102
|
-
|
|
103
|
-
// Get current session company
|
|
104
|
-
$company = Auth::getCompany();
|
|
105
|
-
$input['company_uuid'] = session('company', $company->uuid);
|
|
106
|
-
$input['user_uuid'] = $existingUser->uuid;
|
|
107
|
-
$input['slug'] = $existingUser->slug;
|
|
108
|
-
|
|
109
|
-
// If no location provided set
|
|
110
|
-
if (empty($input['location'])) {
|
|
111
|
-
$input['location'] = new Point(0, 0);
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
// create the profile
|
|
115
|
-
$driverProfile = Driver::create($input);
|
|
116
|
-
|
|
117
|
-
// If not already a member of the company assign them to the company and send the user an invite
|
|
118
|
-
if (!$isOrganizationMember && $company) {
|
|
119
|
-
$existingUser->assignCompany($company);
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
return ['driver' => new $this->resource($driverProfile)];
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
// check from validator object if phone or email is not unique
|
|
127
62
|
return $createDriverRequest->responseWithErrors($validator);
|
|
128
63
|
}
|
|
129
64
|
|
|
@@ -134,104 +69,36 @@ class DriverController extends FleetOpsController
|
|
|
134
69
|
$input = collect($input);
|
|
135
70
|
|
|
136
71
|
// Get current session company
|
|
137
|
-
$company
|
|
72
|
+
$company = Auth::getCompany();
|
|
138
73
|
if (!$company) {
|
|
139
74
|
throw new \Exception('Unable to create driver.');
|
|
140
75
|
}
|
|
141
76
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
->filter()
|
|
158
|
-
->toArray();
|
|
159
|
-
|
|
160
|
-
// handle `photo_uuid`
|
|
161
|
-
if (isset($input['photo_uuid']) && Str::isUuid($input['photo_uuid'])) {
|
|
162
|
-
$userInput['avatar_uuid'] = $input['photo_uuid'];
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
// Make sure password is set
|
|
166
|
-
if (empty($userInput['password'])) {
|
|
167
|
-
$userInput['password'] = Str::random(14);
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
// Set user company
|
|
171
|
-
$userInput['company_uuid'] = session('company', $company->uuid);
|
|
172
|
-
|
|
173
|
-
// Apply user infos
|
|
174
|
-
$userInput = User::applyUserInfoFromRequest($request, $userInput);
|
|
175
|
-
|
|
176
|
-
// Create user account
|
|
177
|
-
$user = User::create($userInput);
|
|
178
|
-
|
|
179
|
-
// Set the user type to driver
|
|
180
|
-
$user->setType('driver');
|
|
181
|
-
} elseif ($input->has('photo_uuid')) {
|
|
182
|
-
// Update existing user's avatar if photo provided
|
|
183
|
-
$user->update(['avatar_uuid' => $input->get('photo_uuid')]);
|
|
184
|
-
}
|
|
185
|
-
} else {
|
|
186
|
-
$userInput = $input
|
|
187
|
-
->only(['name', 'password', 'email', 'phone', 'status', 'avatar_uuid'])
|
|
188
|
-
->filter()
|
|
189
|
-
->toArray();
|
|
190
|
-
|
|
191
|
-
// handle `photo_uuid`
|
|
192
|
-
if (isset($input['photo_uuid']) && Str::isUuid($input['photo_uuid'])) {
|
|
193
|
-
$userInput['avatar_uuid'] = $input['photo_uuid'];
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
// Make sure password is set
|
|
197
|
-
if (empty($userInput['password'])) {
|
|
198
|
-
$userInput['password'] = Str::random(14);
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
// Set user company
|
|
202
|
-
$userInput['company_uuid'] = session('company', $company->uuid);
|
|
203
|
-
|
|
204
|
-
// Apply user infos
|
|
205
|
-
$userInput = User::applyUserInfoFromRequest($request, $userInput);
|
|
206
|
-
|
|
207
|
-
// Create user account
|
|
208
|
-
$user = User::create($userInput);
|
|
209
|
-
|
|
210
|
-
// Set the user type to driver
|
|
211
|
-
$user->setType('driver');
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
// if exists in organization create driver profile for user
|
|
215
|
-
$isOrganizationMember = $user->companies()->where('companies.uuid', session('company'))->exists();
|
|
77
|
+
// The login account is managed by the driver profile: a team member
|
|
78
|
+
// with the email/phone is linked, otherwise a driver account is created.
|
|
79
|
+
$avatarUuid = Str::isUuid($input->get('photo_uuid')) ? $input->get('photo_uuid') : $input->get('avatar_uuid');
|
|
80
|
+
$user = ProfileAccountManager::resolveForProfile(
|
|
81
|
+
$company->uuid,
|
|
82
|
+
'driver',
|
|
83
|
+
$input->get('name'),
|
|
84
|
+
$input->get('email'),
|
|
85
|
+
$input->get('phone'),
|
|
86
|
+
User::applyUserInfoFromRequest($request, array_filter([
|
|
87
|
+
'password' => $input->get('password'),
|
|
88
|
+
'status' => 'active',
|
|
89
|
+
'avatar_uuid' => $avatarUuid,
|
|
90
|
+
]))
|
|
91
|
+
);
|
|
216
92
|
|
|
217
93
|
// Prepare input
|
|
218
94
|
$input = $input
|
|
219
|
-
->except(['name', 'password', 'email', 'phone', 'meta', 'avatar_uuid', 'photo_uuid', 'status'])
|
|
95
|
+
->except(['name', 'password', 'email', 'phone', 'meta', 'avatar_uuid', 'photo_uuid', 'status', 'user_uuid', 'user'])
|
|
220
96
|
->filter()
|
|
221
97
|
->toArray();
|
|
222
98
|
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
// Set user type as driver and set role to driver
|
|
229
|
-
if ($user->type === 'driver') {
|
|
230
|
-
$user->assignSingleRole('Driver');
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
$input['user_uuid'] = $user->uuid;
|
|
234
|
-
$input['slug'] = $user->slug;
|
|
99
|
+
$input['company_uuid'] = $company->uuid;
|
|
100
|
+
$input['user_uuid'] = $user->uuid;
|
|
101
|
+
$input['slug'] = $user->slug;
|
|
235
102
|
|
|
236
103
|
// If no location provided set
|
|
237
104
|
if (empty($input['location'])) {
|
|
@@ -248,6 +115,8 @@ class DriverController extends FleetOpsController
|
|
|
248
115
|
);
|
|
249
116
|
|
|
250
117
|
return ['driver' => new $this->resource($record)];
|
|
118
|
+
} catch (ProfileIdentityConflictException $e) {
|
|
119
|
+
return response()->error($e->getMessage(), 422, ['field' => $e->getField()]);
|
|
251
120
|
} catch (QueryException $e) {
|
|
252
121
|
return response()->error(env('DEBUG') ? $e->getMessage() : 'Error occurred while trying to create a ' . $this->resourceSingularlName);
|
|
253
122
|
} catch (FleetbaseRequestValidationException $e) {
|
|
@@ -287,18 +156,15 @@ class DriverController extends FleetOpsController
|
|
|
287
156
|
function (&$request, &$driver, &$input) {
|
|
288
157
|
$driver->load(['user'])->guard(['user_uuid']);
|
|
289
158
|
$input = collect($input);
|
|
290
|
-
$userInput = $input->only(['name', 'password', 'email', 'phone', 'avatar_uuid'])->reject(fn ($value) => $value === null)->toArray();
|
|
159
|
+
$userInput = $input->only(['name', 'password', 'email', 'phone', 'avatar_uuid'])->reject(fn ($value, $key) => $value === null && !in_array($key, ['email', 'phone'], true))->toArray();
|
|
291
160
|
// handle `photo_uuid`
|
|
292
161
|
if (isset($input['photo_uuid']) && Str::isUuid($input['photo_uuid'])) {
|
|
293
162
|
$userInput['avatar_uuid'] = $input['photo_uuid'];
|
|
294
163
|
}
|
|
295
|
-
$input = $input->except(['name', 'password', 'email', 'phone', 'meta', 'avatar_uuid', 'photo_uuid'])->toArray();
|
|
164
|
+
$input = $input->except(['name', 'password', 'email', 'phone', 'meta', 'avatar_uuid', 'photo_uuid', 'user_uuid', 'user'])->toArray();
|
|
296
165
|
|
|
297
|
-
// Update driver
|
|
298
|
-
$
|
|
299
|
-
if ($driverUser && !empty($userInput)) {
|
|
300
|
-
$driverUser->update($userInput);
|
|
301
|
-
}
|
|
166
|
+
// Update the driver's login account through its proxy fields
|
|
167
|
+
ProfileAccountManager::syncProxyFields($driver->getUser(), $userInput);
|
|
302
168
|
|
|
303
169
|
// Flush cache
|
|
304
170
|
$driver->flushAttributesCache();
|
|
@@ -318,6 +184,8 @@ class DriverController extends FleetOpsController
|
|
|
318
184
|
);
|
|
319
185
|
|
|
320
186
|
return ['driver' => new $this->resource($record)];
|
|
187
|
+
} catch (ProfileIdentityConflictException $e) {
|
|
188
|
+
return response()->error($e->getMessage(), 422, ['field' => $e->getField()]);
|
|
321
189
|
} catch (QueryException $e) {
|
|
322
190
|
return response()->error(env('DEBUG') ? $e->getMessage() : 'Error occurred while trying to update a ' . $this->resourceSingularlName);
|
|
323
191
|
} catch (FleetbaseRequestValidationException $e) {
|
|
@@ -327,6 +195,150 @@ class DriverController extends FleetOpsController
|
|
|
327
195
|
}
|
|
328
196
|
}
|
|
329
197
|
|
|
198
|
+
/**
|
|
199
|
+
* Generate a new password for the driver's login and send it to them.
|
|
200
|
+
*
|
|
201
|
+
* @return JsonResponse
|
|
202
|
+
*/
|
|
203
|
+
public function sendCredentials(string $id)
|
|
204
|
+
{
|
|
205
|
+
[$driver, $user, $error] = $this->resolveDriverLogin($id);
|
|
206
|
+
if ($error) {
|
|
207
|
+
return $error;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
try {
|
|
211
|
+
$sentVia = ProfileAccountManager::sendCredentials($driver, $user);
|
|
212
|
+
} catch (\Exception $e) {
|
|
213
|
+
return response()->error($e->getMessage());
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
return response()->json(['status' => 'ok', 'sent_via' => $sentVia, 'driver' => $this->driverLoginPayload($driver)]);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* Set a new password for the driver's login, optionally sending it to them.
|
|
221
|
+
*
|
|
222
|
+
* @return JsonResponse
|
|
223
|
+
*/
|
|
224
|
+
public function resetCredentials(Request $request, string $id)
|
|
225
|
+
{
|
|
226
|
+
$password = $request->input('password');
|
|
227
|
+
if (!is_string($password) || strlen($password) < 8) {
|
|
228
|
+
return response()->error('Password must be at least 8 characters.');
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
if ($password !== $request->input('password_confirmation')) {
|
|
232
|
+
return response()->error('Passwords do not match.');
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
[$driver, $user, $error] = $this->resolveDriverLogin($id);
|
|
236
|
+
if ($error) {
|
|
237
|
+
return $error;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
$user->changePassword($password);
|
|
241
|
+
|
|
242
|
+
try {
|
|
243
|
+
if ($request->boolean('send_credentials')) {
|
|
244
|
+
ProfileAccountManager::deliverCredentials($driver, $user, $password);
|
|
245
|
+
}
|
|
246
|
+
} catch (\Exception $e) {
|
|
247
|
+
return response()->error($e->getMessage());
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
return response()->json(['status' => 'ok', 'driver' => $this->driverLoginPayload($driver)]);
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
/**
|
|
254
|
+
* Stop the driver from signing in to the driver app.
|
|
255
|
+
*
|
|
256
|
+
* @return JsonResponse
|
|
257
|
+
*/
|
|
258
|
+
public function deactivateLogin(string $id)
|
|
259
|
+
{
|
|
260
|
+
[$driver, $user, $error] = $this->resolveDriverLogin($id);
|
|
261
|
+
if ($error) {
|
|
262
|
+
return $error;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
$user->deactivate();
|
|
266
|
+
|
|
267
|
+
// Sign the driver out of the driver app
|
|
268
|
+
$user->tokens()->delete();
|
|
269
|
+
|
|
270
|
+
return response()->json(['status' => 'ok', 'driver' => $this->driverLoginPayload($driver)]);
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
/**
|
|
274
|
+
* Allow the driver to sign in to the driver app again.
|
|
275
|
+
*
|
|
276
|
+
* @return JsonResponse
|
|
277
|
+
*/
|
|
278
|
+
public function reactivateLogin(string $id)
|
|
279
|
+
{
|
|
280
|
+
[$driver, $user, $error] = $this->resolveDriverLogin($id);
|
|
281
|
+
if ($error) {
|
|
282
|
+
return $error;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
$user->activate();
|
|
286
|
+
|
|
287
|
+
return response()->json(['status' => 'ok', 'driver' => $this->driverLoginPayload($driver)]);
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
/**
|
|
291
|
+
* Find the driver and its managed login account. A driver linked to a team
|
|
292
|
+
* member's account has its login managed in IAM.
|
|
293
|
+
*
|
|
294
|
+
* @return array{0: ?Driver, 1: ?User, 2: ?JsonResponse}
|
|
295
|
+
*/
|
|
296
|
+
protected function resolveDriverLogin(string $id): array
|
|
297
|
+
{
|
|
298
|
+
$driver = Driver::where('company_uuid', session('company'))
|
|
299
|
+
->where(function ($query) use ($id) {
|
|
300
|
+
$query->where('uuid', $id)->orWhere('public_id', $id);
|
|
301
|
+
})
|
|
302
|
+
->first();
|
|
303
|
+
|
|
304
|
+
if (!$driver) {
|
|
305
|
+
return [null, null, response()->error('Driver not found.', 404)];
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
$user = $driver->user_uuid ? User::where('uuid', $driver->user_uuid)->first() : null;
|
|
309
|
+
if (!$user) {
|
|
310
|
+
return [$driver, null, response()->error('This driver has no login account.')];
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
if (ProfileAccountManager::isStaffAccount($user)) {
|
|
314
|
+
return [$driver, $user, response()->error('This driver signs in with a team member account. Manage this login in IAM.', 422)];
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
return [$driver, $user, null];
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
protected function driverLoginPayload(Driver $driver): array
|
|
321
|
+
{
|
|
322
|
+
$user = User::where('uuid', $driver->user_uuid)->first();
|
|
323
|
+
|
|
324
|
+
return [
|
|
325
|
+
'id' => $driver->public_id,
|
|
326
|
+
'uuid' => $driver->uuid,
|
|
327
|
+
'user_uuid' => $driver->user_uuid,
|
|
328
|
+
'is_staff_linked' => ProfileAccountManager::isStaffAccount($user),
|
|
329
|
+
'login_status' => $user?->status,
|
|
330
|
+
'user' => $user ? [
|
|
331
|
+
'id' => $user->public_id,
|
|
332
|
+
'uuid' => $user->uuid,
|
|
333
|
+
'name' => $user->name,
|
|
334
|
+
'email' => $user->email,
|
|
335
|
+
'phone' => $user->phone,
|
|
336
|
+
'status' => $user->status,
|
|
337
|
+
'session_status' => $user->session_status,
|
|
338
|
+
] : null,
|
|
339
|
+
];
|
|
340
|
+
}
|
|
341
|
+
|
|
330
342
|
/**
|
|
331
343
|
* Get all status options for an driver.
|
|
332
344
|
*
|
|
@@ -635,6 +647,10 @@ class DriverController extends FleetOpsController
|
|
|
635
647
|
return response()->error('No driver with this phone # found.');
|
|
636
648
|
}
|
|
637
649
|
|
|
650
|
+
if (ApiDriverController::isLoginDeactivated($user)) {
|
|
651
|
+
return response()->error(ApiDriverController::DEACTIVATED_LOGIN_MESSAGE, 403);
|
|
652
|
+
}
|
|
653
|
+
|
|
638
654
|
// Generate verification token
|
|
639
655
|
static::generateDriverLoginVerification($user);
|
|
640
656
|
|
|
@@ -663,6 +679,10 @@ class DriverController extends FleetOpsController
|
|
|
663
679
|
return response()->error('Unable to verify code.');
|
|
664
680
|
}
|
|
665
681
|
|
|
682
|
+
if (ApiDriverController::isLoginDeactivated($user)) {
|
|
683
|
+
return response()->error(ApiDriverController::DEACTIVATED_LOGIN_MESSAGE, 403);
|
|
684
|
+
}
|
|
685
|
+
|
|
666
686
|
// Find and verify code
|
|
667
687
|
$verificationCode = static::verificationCodeExists($user, $code, $for);
|
|
668
688
|
if (!$verificationCode && !ApiDriverController::verificationBypassMatches($identity, $code)) {
|
|
@@ -3,16 +3,63 @@
|
|
|
3
3
|
namespace Fleetbase\FleetOps\Http\Controllers\Internal\v1;
|
|
4
4
|
|
|
5
5
|
use Fleetbase\FleetOps\Models\Contact;
|
|
6
|
+
use Fleetbase\FleetOps\Models\Driver;
|
|
6
7
|
use Fleetbase\FleetOps\Models\IntegratedVendor;
|
|
7
8
|
use Fleetbase\FleetOps\Models\Vendor;
|
|
9
|
+
use Fleetbase\FleetOps\Support\ProfileAccountManager;
|
|
8
10
|
use Fleetbase\FleetOps\Support\Utils;
|
|
9
11
|
use Fleetbase\Http\Controllers\Controller;
|
|
12
|
+
use Fleetbase\Models\User;
|
|
10
13
|
use Illuminate\Http\Request;
|
|
11
14
|
use Illuminate\Support\Arr;
|
|
12
15
|
use Illuminate\Support\Str;
|
|
13
16
|
|
|
14
17
|
class FleetOpsLookupController extends Controller
|
|
15
18
|
{
|
|
19
|
+
/**
|
|
20
|
+
* Describes what an email/phone entered on a driver, contact or customer
|
|
21
|
+
* form resolves to: the team member the profile would be linked to, or why
|
|
22
|
+
* the email/phone can't be used.
|
|
23
|
+
*
|
|
24
|
+
* @return \Illuminate\Http\JsonResponse
|
|
25
|
+
*/
|
|
26
|
+
public function profileIdentity(Request $request)
|
|
27
|
+
{
|
|
28
|
+
$type = $request->input('type', 'driver');
|
|
29
|
+
if (!in_array($type, ProfileAccountManager::MANAGED_TYPES, true)) {
|
|
30
|
+
return response()->error('Invalid profile type.', 422);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
$currentUser = $this->currentProfileUser($type, $request->input('ignore'));
|
|
34
|
+
$lookup = ProfileAccountManager::lookup(session('company'), $type, $request->input('email'), $request->input('phone'), $currentUser);
|
|
35
|
+
$staff = $lookup['staff'];
|
|
36
|
+
|
|
37
|
+
return response()->json([
|
|
38
|
+
'staff' => $staff ? ['uuid' => $staff->uuid, 'name' => $staff->name, 'email' => $staff->email, 'phone' => $staff->phone] : null,
|
|
39
|
+
'conflict' => $lookup['conflict'],
|
|
40
|
+
]);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* The account of the profile being edited, so its own email/phone isn't
|
|
45
|
+
* reported as taken.
|
|
46
|
+
*/
|
|
47
|
+
private function currentProfileUser(string $type, ?string $id): ?User
|
|
48
|
+
{
|
|
49
|
+
if (!$id) {
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
$model = $type === 'driver' ? Driver::withoutGlobalScopes() : Contact::query();
|
|
54
|
+
$profile = $model->where('company_uuid', session('company'))
|
|
55
|
+
->where(function ($query) use ($id) {
|
|
56
|
+
$query->where('uuid', $id)->orWhere('public_id', $id);
|
|
57
|
+
})
|
|
58
|
+
->first();
|
|
59
|
+
|
|
60
|
+
return $profile?->user_uuid ? User::where('uuid', $profile->user_uuid)->first() : null;
|
|
61
|
+
}
|
|
62
|
+
|
|
16
63
|
/**
|
|
17
64
|
* Returns a collection of polymorphic resources as JSON.
|
|
18
65
|
*
|