(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'], ]); } function order_booking_create_department_price(int $departmentId, int $productId, int $price): void { $statement = api_test_runtime()->db()->prepare( 'INSERT INTO `product_department_prices` (`department_id`, `product_id`, `price`) VALUES (?, ?, ?) ON DUPLICATE KEY UPDATE `price` = VALUES(`price`)' ); $statement->bind_param('iii', $departmentId, $productId, $price); $statement->execute(); $statement->close(); } 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('normalizes booking item prices from server-side customer and department pricing', function (): void { api_test_covers('POST /order-bookings', 'pricing'); $session = api_fixtures()->createUserSession(['user']); $department = order_booking_create_department('Own Booking Pricing Department'); $product = api_fixtures()->createProduct([ 'name' => 'Booking Price Normalized Product', 'price' => 0, 'is_wash' => 0, 'display_in_booking_form' => 1, ]); order_booking_create_department_price((int)$department['id'], (int)$product['id'], 425); $payload = order_booking_create_payload($session['user'], $department, $product, 'PRICEFIX1'); $payload['items'][0]['price'] = 0; $response = api_client()->post('/order-bookings', $payload, $session['headers']); $response ->assertStatus(200) ->assertEnvelope() ->assertSuccess(); $bookingId = (int)($response->data()['id'] ?? 0); expect($bookingId)->toBeGreaterThan(0); $responseItems = $response->data()['items'] ?? []; expect($responseItems) ->toBeArray() ->and((int)($responseItems[0]['price'] ?? 0))->toBe(425); $row = api_fixtures()->fetchRowById('order_bookings', $bookingId); $storedItems = json_decode((string)($row['items'] ?? '[]'), true); expect($storedItems) ->toBeArray() ->and((int)($storedItems[0]['price'] ?? 0))->toBe(425); api_fixtures()->cleanupDeleteWhere('product_department_prices', [ 'department_id' => (int)$department['id'], 'product_id' => (int)$product['id'], ]); api_fixtures()->cleanupDeleteById('order_bookings', $bookingId); }); it('blocks subusers creating 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(403) ->assertEnvelope() ->assertSuccess(false) ->assertMissingPermissions(['add_own_bookings']); }); it('lets subusers create own customer order bookings with the bookings add node', function (): void { api_test_covers('POST /order-bookings', 'auth'); $customer = api_fixtures()->createUser(['display_name' => 'Subuser Booking Customer With Add']); $session = api_fixtures()->createSubuserSession((int)$customer['customer_number'], ['BOOKINGS_ADD']); $department = order_booking_create_department('Subuser Booking Add Department'); $product = api_fixtures()->createProduct(['name' => 'Subuser Booking Add Product']); $response = api_client()->post( '/order-bookings', order_booking_create_payload($customer, $department, $product, 'SUBBOOK2'), $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); });