Merge pull request #302 from copenhagentruckwash/codex/customer-orderbooking-create-without-permission

Allow customer order booking creation without booking permission
This commit is contained in:
Jeppe B
2026-07-06 16:39:17 +02:00
committed by GitHub
5 changed files with 228 additions and 22 deletions
@@ -301,6 +301,42 @@ it('lists department categories for a department', function (): void {
->and($response->data()[0]['category']['id'] ?? null)->toBe($category['id']);
});
it('lets customer booking sessions list department categories without the management permission', function (): void {
api_test_covers('GET /departments/categories', 'auth');
$customerSession = api_fixtures()->createUserSession(['user']);
$department = api_fixtures()->createDepartment();
$category = api_fixtures()->createCategory([
'name' => 'Customer Department Category',
]);
api_fixtures()->linkDepartmentCategory((int)$department['id'], (int)$category['id']);
$customerResponse = api_client()->get('/departments/categories?id=' . $department['id'], $customerSession['headers']);
$customerResponse
->assertStatus(200)
->assertEnvelope()
->assertSuccess();
expect($customerResponse->data())
->toBeArray()
->toHaveCount(1)
->and($customerResponse->data()[0]['category']['id'] ?? null)->toBe($category['id']);
$subuserSession = api_fixtures()->createSubuserSession((int)$customerSession['user']['customer_number'], []);
$subuserResponse = api_client()->get('/departments/categories?id=' . $department['id'], $subuserSession['headers']);
$subuserResponse
->assertStatus(200)
->assertEnvelope()
->assertSuccess();
expect($subuserResponse->data())
->toBeArray()
->toHaveCount(1)
->and($subuserResponse->data()[0]['category']['id'] ?? null)->toBe($category['id']);
});
it('rejects invalid department category requests', function (): void {
api_test_covers('GET /departments/categories', 'failure');
@@ -0,0 +1,144 @@
<?php
declare(strict_types=1);
usesApiSuite();
function order_booking_create_payload(array $customer, array $department, array $product, string $reference): array
{
return [
'customer_number' => (int)$customer['customer_number'],
'department' => (int)$department['id'],
'reg_1' => $reference,
'datetime' => '2026-07-07 10:00:00',
'note' => '',
'reference' => $reference,
'po' => '',
'pickup' => false,
'items' => [
[
'id' => (int)$product['id'],
'quantity' => 1,
],
],
];
}
function order_booking_create_department(string $name): array
{
$branding = api_fixtures()->createBranding([
'name' => $name . ' Brand',
'address' => 'API Booking Street 1',
]);
return api_fixtures()->createDepartment([
'name' => $name,
'branding' => (int)$branding['id'],
]);
}
it('lets customers create their own order bookings without booking permissions', function (): void {
api_test_covers('POST /order-bookings', 'auth');
$session = api_fixtures()->createUserSession(['user']);
$department = order_booking_create_department('Own Booking Department');
$product = api_fixtures()->createProduct(['name' => 'Own Booking Product']);
$response = api_client()->post(
'/order-bookings',
order_booking_create_payload($session['user'], $department, $product, 'OWNBOOK1'),
$session['headers']
);
$response
->assertStatus(200)
->assertEnvelope()
->assertSuccess();
$bookingId = (int)($response->data()['id'] ?? 0);
expect($bookingId)->toBeGreaterThan(0);
$row = api_fixtures()->fetchRowById('order_bookings', $bookingId);
expect($row)->not->toBeNull();
expect((int)($row['customer_number'] ?? 0))->toBe((int)$session['user']['customer_number']);
api_fixtures()->cleanupDeleteById('order_bookings', $bookingId);
});
it('lets subusers create own customer order bookings without the bookings add node', function (): void {
api_test_covers('POST /order-bookings', 'auth');
$customer = api_fixtures()->createUser(['display_name' => 'Subuser Booking Customer']);
$session = api_fixtures()->createSubuserSession((int)$customer['customer_number'], []);
$department = order_booking_create_department('Subuser Booking Department');
$product = api_fixtures()->createProduct(['name' => 'Subuser Booking Product']);
$response = api_client()->post(
'/order-bookings',
order_booking_create_payload($customer, $department, $product, 'SUBBOOK1'),
$session['headers']
);
$response
->assertStatus(200)
->assertEnvelope()
->assertSuccess();
$bookingId = (int)($response->data()['id'] ?? 0);
expect($bookingId)->toBeGreaterThan(0);
$row = api_fixtures()->fetchRowById('order_bookings', $bookingId);
expect($row)->not->toBeNull();
expect((int)($row['customer_number'] ?? 0))->toBe((int)$customer['customer_number']);
api_fixtures()->cleanupDeleteById('order_bookings', $bookingId);
});
it('still requires elevated access for creating another customer order booking', function (): void {
api_test_covers('POST /order-bookings', 'auth');
$session = api_fixtures()->createUserSession(['user']);
$otherCustomer = api_fixtures()->createUser(['display_name' => 'Other Booking Customer']);
$department = api_fixtures()->createDepartment(['name' => 'Other Booking Department']);
$product = api_fixtures()->createProduct(['name' => 'Other Booking Product']);
$response = api_client()->post(
'/order-bookings',
order_booking_create_payload($otherCustomer, $department, $product, 'OTHBOOK1'),
$session['headers']
);
$response
->assertStatus(403)
->assertEnvelope()
->assertSuccess(false)
->assertMissingPermissions(['add_bookings']);
});
it('lets department-scoped users create order bookings for another customer', function (): void {
api_test_covers('POST /order-bookings', 'happy');
$customer = api_fixtures()->createUser(['display_name' => 'Department Booking Customer']);
$department = order_booking_create_department('Department Scoped Booking Department');
$product = api_fixtures()->createProduct(['name' => 'Department Scoped Booking Product']);
$session = api_fixtures()->createUserSession([
'add_bookings',
'department_access_' . $department['id'],
]);
$response = api_client()->post(
'/order-bookings',
order_booking_create_payload($customer, $department, $product, 'DEPTBOOK'),
$session['headers']
);
$response
->assertStatus(200)
->assertEnvelope()
->assertSuccess();
$bookingId = (int)($response->data()['id'] ?? 0);
expect($bookingId)->toBeGreaterThan(0);
api_fixtures()->cleanupDeleteById('order_bookings', $bookingId);
});
@@ -147,7 +147,7 @@ it('only allows tankcleaning products for only tankcleaning customers', function
->assertStatus(400)
->assertEnvelope()
->assertSuccess(false)
->assertMessage(\classes\customer_order_product_policy::ONLY_TANKCLEANING_MESSAGE);
->assertMessage(\classes\customer_product_rule_service::BLOCK_MESSAGE);
$response = api_client()->post('/order/items', [
'order_id' => $order['id'],
@@ -274,7 +274,9 @@ it('blocks addon products added as standalone additional order items for custome
->assertEnvelope()
->assertSuccess();
post_order_item($fixture['order'], $addonProduct, $fixture['session']['headers'])
post_order_item($fixture['order'], $addonProduct, $fixture['session']['headers'], [
'notes' => 'Addon customer rule check',
])
->assertStatus(400)
->assertEnvelope()
->assertSuccess(false)