Guard chauffeur vehicles on legacy schemas
This commit is contained in:
@@ -249,7 +249,7 @@ class subusersRoute
|
||||
}
|
||||
|
||||
$vehicle->getObjectProperties();
|
||||
if ($vehicle->deleted_at->value() !== null) {
|
||||
if ($this->customerVehiclesHaveDeletedAtColumn() && $vehicle->deleted_at->value() !== null) {
|
||||
if ($strict) {
|
||||
$response->error('Assigned vehicle not found', 404);
|
||||
}
|
||||
@@ -271,6 +271,11 @@ class subusersRoute
|
||||
];
|
||||
}
|
||||
|
||||
private function customerVehiclesHaveDeletedAtColumn(): bool
|
||||
{
|
||||
return (new customer_vehicles_o())->columnsExist(['deleted_at']);
|
||||
}
|
||||
|
||||
private function validateAssignedVehicleIdForCustomer(mixed $value, int $customerNumber): ?int
|
||||
{
|
||||
$vehicleId = $this->normalizeAssignedVehicleId($value);
|
||||
@@ -1267,10 +1272,13 @@ class subusersRoute
|
||||
}
|
||||
|
||||
$whereSql = 'WHERE ' . implode(' AND ', $where);
|
||||
$vehicleDeletedAtJoinSql = $this->customerVehiclesHaveDeletedAtColumn()
|
||||
? ' AND cv.`deleted_at` IS NULL'
|
||||
: '';
|
||||
$fromSql = "
|
||||
FROM `subuser_grants` g
|
||||
INNER JOIN `subusers` s ON s.`id` = g.`subuser`
|
||||
LEFT JOIN `customer_vehicles` cv ON cv.`id` = g.`assigned_vehicle_id` AND cv.`deleted_at` IS NULL
|
||||
LEFT JOIN `customer_vehicles` cv ON cv.`id` = g.`assigned_vehicle_id`{$vehicleDeletedAtJoinSql}
|
||||
";
|
||||
|
||||
$countSql = "SELECT COUNT(DISTINCT s.`id`) AS `count` $fromSql $whereSql";
|
||||
@@ -1374,7 +1382,7 @@ class subusersRoute
|
||||
g.`created_at` AS `grant_created_at`,
|
||||
g.`updated_at` AS `grant_updated_at`
|
||||
FROM `subuser_grants` g
|
||||
LEFT JOIN `customer_vehicles` cv ON cv.`id` = g.`assigned_vehicle_id` AND cv.`deleted_at` IS NULL
|
||||
LEFT JOIN `customer_vehicles` cv ON cv.`id` = g.`assigned_vehicle_id`{$vehicleDeletedAtJoinSql}
|
||||
WHERE " . implode(' AND ', $grantWhere) . "
|
||||
ORDER BY g.`enabled` DESC, g.`billing_customer_number` ASC, g.`id` DESC
|
||||
";
|
||||
|
||||
@@ -4,6 +4,28 @@ declare(strict_types=1);
|
||||
|
||||
usesApiSuite();
|
||||
|
||||
function subusers_without_customer_vehicles_deleted_at(callable $callback): void
|
||||
{
|
||||
$db = api_test_runtime()->db();
|
||||
$column = $db->query("SHOW COLUMNS FROM `customer_vehicles` LIKE 'deleted_at'");
|
||||
if ($column === false) {
|
||||
throw new RuntimeException('Unable to inspect customer_vehicles.deleted_at test column.');
|
||||
}
|
||||
|
||||
$hadColumn = (int)$column->num_rows > 0;
|
||||
if ($hadColumn) {
|
||||
$db->query('ALTER TABLE `customer_vehicles` DROP COLUMN `deleted_at`');
|
||||
}
|
||||
|
||||
try {
|
||||
$callback();
|
||||
} finally {
|
||||
if ($hadColumn) {
|
||||
$db->query('ALTER TABLE `customer_vehicles` ADD COLUMN `deleted_at` DATETIME NULL');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const SUBUSER_PASSWORD_POLICY_MESSAGE = 'Password must contain at least one uppercase letter, one lowercase letter, and one number';
|
||||
|
||||
it('lists subusers when an existing grant has legacy zero permissions', function (): void {
|
||||
@@ -411,6 +433,79 @@ it('lists chauffeurs once with grouped grants across customers for superusers',
|
||||
expect($secondRow['grants'][0]['customer_name'] ?? null)->toBe('Fleet Customer Beta');
|
||||
});
|
||||
|
||||
it('lists superuser chauffeurs with assigned vehicles when customer vehicles has no deleted at column', function (): void {
|
||||
subusers_without_customer_vehicles_deleted_at(function (): void {
|
||||
api_test_covers('GET /superuser/subusers', 'happy');
|
||||
api_test_covers('GET /superuser/users/{user_id}/subusers', 'happy');
|
||||
|
||||
$customerVehiclesDeletedAtColumn = api_test_runtime()->db()->query(
|
||||
"SHOW COLUMNS FROM `customer_vehicles` LIKE 'deleted_at'"
|
||||
);
|
||||
expect($customerVehiclesDeletedAtColumn)->not->toBeFalse();
|
||||
expect((int)$customerVehiclesDeletedAtColumn->num_rows)->toBe(0);
|
||||
|
||||
$session = api_fixtures()->createUserSession(['list_subusers']);
|
||||
$suffix = (string)random_int(100000, 999999);
|
||||
$customer = api_fixtures()->createUser([
|
||||
'display_name' => 'Legacy Subuser Vehicle Customer ' . $suffix,
|
||||
'economic_customer_name' => 'Legacy Subuser Vehicle Customer ' . $suffix,
|
||||
]);
|
||||
$subuser = api_fixtures()->createSubuser([
|
||||
'name' => 'Legacy Vehicle Driver ' . $suffix,
|
||||
]);
|
||||
$vehicle = api_fixtures()->createVehicle([
|
||||
'customer_id' => (int)$customer['customer_number'],
|
||||
'type' => 1,
|
||||
'reg' => 'legacy' . $suffix,
|
||||
]);
|
||||
$grantId = api_fixtures()->grantSubuser(
|
||||
(int)$subuser['id'],
|
||||
(int)$customer['customer_number'],
|
||||
['VEHICLES_LIST'],
|
||||
['assigned_vehicle_id' => (int)$vehicle['id']]
|
||||
);
|
||||
|
||||
$list = api_client()->get('/superuser/subusers?page=1&limit=20', $session['headers']);
|
||||
$search = api_client()->get(
|
||||
'/superuser/subusers?page=1&limit=20&search=' . rawurlencode((string)$vehicle['reg']),
|
||||
$session['headers']
|
||||
);
|
||||
$scoped = api_client()->get(
|
||||
'/superuser/users/' . $customer['id'] . '/subusers?page=1&limit=20',
|
||||
$session['headers']
|
||||
);
|
||||
|
||||
foreach ([$list, $search, $scoped] as $response) {
|
||||
$response
|
||||
->assertStatus(200)
|
||||
->assertEnvelope()
|
||||
->assertSuccess();
|
||||
}
|
||||
|
||||
$matchingSearchRows = array_values(array_filter(
|
||||
is_array($search->data()) ? $search->data() : [],
|
||||
static fn (mixed $item): bool => is_array($item) && (int)($item['id'] ?? 0) === (int)$subuser['id']
|
||||
));
|
||||
expect($matchingSearchRows)->toHaveCount(1);
|
||||
|
||||
$matchingScopedRows = array_values(array_filter(
|
||||
is_array($scoped->data()) ? $scoped->data() : [],
|
||||
static fn (mixed $item): bool => is_array($item) && (int)($item['id'] ?? 0) === (int)$subuser['id']
|
||||
));
|
||||
expect($matchingScopedRows)->toHaveCount(1);
|
||||
|
||||
$grantsById = [];
|
||||
foreach ($matchingScopedRows[0]['grants'] ?? [] as $grant) {
|
||||
$grantsById[(int)$grant['grant_id']] = $grant;
|
||||
}
|
||||
|
||||
expect($grantsById)->toHaveKey($grantId);
|
||||
expect($grantsById[$grantId]['assigned_vehicle_id'])->toBe((int)$vehicle['id']);
|
||||
expect($grantsById[$grantId]['assigned_vehicle_reg'])->toBe(strtoupper((string)$vehicle['reg']));
|
||||
expect($scoped->meta()['pagination']['total'] ?? null)->toBeGreaterThanOrEqual(1);
|
||||
});
|
||||
});
|
||||
|
||||
it('paginates superuser chauffeur lists by unique chauffeur instead of grant count', function (): void {
|
||||
$session = api_fixtures()->createUserSession(['list_subusers']);
|
||||
$suffix = (string)random_int(100000, 999999);
|
||||
|
||||
Reference in New Issue
Block a user