Merge pull request #307 from copenhagentruckwash/copilot/update-limited-backoffice-roles
Fix limited-backoffice role permissions and enforce department access on order mutations
This commit is contained in:
@@ -29,6 +29,21 @@ class limited_backoffice_service
|
||||
self::PERMISSION_PUBLIC_EMPLOYEE_DATA,
|
||||
];
|
||||
|
||||
/**
|
||||
* Permissions that are always granted to managed employees when present in a role preset,
|
||||
* regardless of whether the creating manager holds those permissions themselves.
|
||||
*
|
||||
* @var array<int, string>
|
||||
*/
|
||||
private const ROLE_UNCONDITIONAL_PERMISSIONS = [
|
||||
'list_departments',
|
||||
'list_department_daily_reports',
|
||||
'list_notifications',
|
||||
'list_own_notifications',
|
||||
'statistics_orders_new',
|
||||
'statistics_bookings_new',
|
||||
];
|
||||
|
||||
/**
|
||||
* @var array<string, array{label:string,description:string,permissions:array<int,string>}>
|
||||
*/
|
||||
@@ -47,6 +62,7 @@ class limited_backoffice_service
|
||||
'permissions' => [
|
||||
'user',
|
||||
'permissions_list_own',
|
||||
'list_departments',
|
||||
'list_orders',
|
||||
'fetch_order',
|
||||
'add_order',
|
||||
@@ -89,6 +105,11 @@ class limited_backoffice_service
|
||||
'add_bookings',
|
||||
'complete_bookings',
|
||||
'resend_booking_confirmations',
|
||||
'list_department_daily_reports',
|
||||
'list_notifications',
|
||||
'list_own_notifications',
|
||||
'statistics_orders_new',
|
||||
'statistics_bookings_new',
|
||||
],
|
||||
],
|
||||
'booking_coordinator' => [
|
||||
@@ -97,6 +118,7 @@ class limited_backoffice_service
|
||||
'permissions' => [
|
||||
'user',
|
||||
'permissions_list_own',
|
||||
'list_departments',
|
||||
'list_orders',
|
||||
'list_bookings',
|
||||
'list_own_bookings',
|
||||
@@ -108,6 +130,11 @@ class limited_backoffice_service
|
||||
'department_timebookings_entries_get',
|
||||
'department_timebookings_entries_post',
|
||||
'department_timebookings_entries_put',
|
||||
'list_department_daily_reports',
|
||||
'list_notifications',
|
||||
'list_own_notifications',
|
||||
'statistics_orders_new',
|
||||
'statistics_bookings_new',
|
||||
],
|
||||
],
|
||||
'operations_lead' => [
|
||||
@@ -116,6 +143,7 @@ class limited_backoffice_service
|
||||
'permissions' => [
|
||||
'user',
|
||||
'permissions_list_own',
|
||||
'list_departments',
|
||||
'list_orders',
|
||||
'fetch_order',
|
||||
'add_order',
|
||||
@@ -159,6 +187,9 @@ class limited_backoffice_service
|
||||
'add_bookings',
|
||||
'complete_bookings',
|
||||
'resend_booking_confirmations',
|
||||
'list_department_daily_reports',
|
||||
'list_notifications',
|
||||
'list_own_notifications',
|
||||
'statistics_orders_new',
|
||||
'statistics_bookings_new',
|
||||
],
|
||||
@@ -169,6 +200,7 @@ class limited_backoffice_service
|
||||
'permissions' => [
|
||||
'user',
|
||||
'permissions_list_own',
|
||||
'list_departments',
|
||||
'list_orders',
|
||||
'fetch_order',
|
||||
'add_order',
|
||||
@@ -212,6 +244,9 @@ class limited_backoffice_service
|
||||
'add_bookings',
|
||||
'complete_bookings',
|
||||
'resend_booking_confirmations',
|
||||
'list_department_daily_reports',
|
||||
'list_notifications',
|
||||
'list_own_notifications',
|
||||
'statistics_orders_new',
|
||||
'statistics_bookings_new',
|
||||
self::PERMISSION_ACCESS,
|
||||
@@ -417,6 +452,22 @@ class limited_backoffice_service
|
||||
'group' => 'time_bookings',
|
||||
'capability' => 'edit_time_booking_entries',
|
||||
],
|
||||
'list_departments' => [
|
||||
'group' => 'departments',
|
||||
'capability' => 'view_departments',
|
||||
],
|
||||
'list_department_daily_reports' => [
|
||||
'group' => 'departments',
|
||||
'capability' => 'view_daily_reports',
|
||||
],
|
||||
'list_notifications' => [
|
||||
'group' => 'notifications',
|
||||
'capability' => 'view_notifications',
|
||||
],
|
||||
'list_own_notifications' => [
|
||||
'group' => 'notifications',
|
||||
'capability' => 'view_notifications',
|
||||
],
|
||||
'statistics_orders_new' => [
|
||||
'group' => 'reports',
|
||||
'capability' => 'view_order_statistics',
|
||||
@@ -444,6 +495,7 @@ class limited_backoffice_service
|
||||
*/
|
||||
private const ROLE_PERMISSION_GROUP_ORDER = [
|
||||
'account',
|
||||
'departments',
|
||||
'orders',
|
||||
'products',
|
||||
'customers',
|
||||
@@ -452,6 +504,7 @@ class limited_backoffice_service
|
||||
'scanner',
|
||||
'bookings',
|
||||
'time_bookings',
|
||||
'notifications',
|
||||
'reports',
|
||||
'limited_backoffice',
|
||||
];
|
||||
@@ -1600,6 +1653,11 @@ class limited_backoffice_service
|
||||
continue;
|
||||
}
|
||||
|
||||
if (in_array($permission, self::ROLE_UNCONDITIONAL_PERMISSIONS, true)) {
|
||||
$permissions[] = $permission;
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($manager->hasPermission($permission)) {
|
||||
$permissions[] = $permission;
|
||||
}
|
||||
|
||||
@@ -75,13 +75,12 @@ class orderItemsRoute
|
||||
if (!$order->exists()) {
|
||||
$response->error('Order not found', 404);
|
||||
}
|
||||
// Check if the user has access to the department
|
||||
self::requireDepartmentAccess((string)(int)$order->department_id->value());
|
||||
$product = (new products_o())->getProductById((int)$data['product_id']);
|
||||
if (!$product->exists()) {
|
||||
$response->error('Product not found', 404);
|
||||
}
|
||||
if ($product->requiresOrderItemNote() && trim((string)($notes ?? '')) === '') {
|
||||
$response->error('Notes is required for this product', 400);
|
||||
}
|
||||
$customerRuleViolation = (new customer_product_rule_service())
|
||||
->firstViolationForOrderItem((int)$data['order_id'], (int)$data['product_id'], $related_item_id);
|
||||
if ($customerRuleViolation !== null) {
|
||||
@@ -95,6 +94,9 @@ class orderItemsRoute
|
||||
);
|
||||
$response->error($customerRuleViolation['message'], 400);
|
||||
}
|
||||
if ($product->requiresOrderItemNote() && trim((string)($notes ?? '')) === '') {
|
||||
$response->error('Notes is required for this product', 400);
|
||||
}
|
||||
|
||||
// Add the order item to the order This is done individually, to make the notes to the individual order items possible
|
||||
$order_items = (new order_items_o());
|
||||
@@ -173,18 +175,38 @@ class orderItemsRoute
|
||||
|
||||
$this->delete('/order/items', function () {
|
||||
// Require the user to be logged in
|
||||
global $response;
|
||||
global $response, $db;
|
||||
$this->requirePermission('delete_order_items');
|
||||
// Get the user object
|
||||
$user = (new authentication())->get_user();
|
||||
// Check if the request was successful
|
||||
if ($user) {
|
||||
// Get the query data
|
||||
$data = $_GET;
|
||||
// Check if the required fields are set
|
||||
if (!isset($data['id'])) {
|
||||
// Get the order item id from the query string or request body
|
||||
$itemIdRaw = $this->fromRequest('id');
|
||||
if ($itemIdRaw === null || $itemIdRaw === '') {
|
||||
$response->error('Order Item ID is required', 400);
|
||||
}
|
||||
$data = ['id' => $itemIdRaw];
|
||||
// Look up the order item to check department access
|
||||
$itemId = (int)$data['id'];
|
||||
$stmt = $db->prepare('SELECT oi.order_id FROM order_items oi WHERE oi.id = ? LIMIT 1');
|
||||
if ($stmt === false) {
|
||||
(new logs_o())->add('order_items', 'global', 1, 0, 'DELETE_ORDER_ITEMS', 'Database error while preparing department access check query');
|
||||
$response->error('Database error while checking department access', 500);
|
||||
}
|
||||
$stmt->bind_param('i', $itemId);
|
||||
$stmt->execute();
|
||||
$orderItemRow = $stmt->get_result()->fetch_assoc();
|
||||
$stmt->close();
|
||||
if ($orderItemRow !== null) {
|
||||
$orderForAccess = (new orders_o())->getOrderById((int)$orderItemRow['order_id']);
|
||||
if (!$orderForAccess->exists()) {
|
||||
$response->error('Order not found', 404);
|
||||
}
|
||||
self::requireDepartmentAccess((string)(int)$orderForAccess->department_id->value());
|
||||
} else {
|
||||
$response->error('Order item not found', 404);
|
||||
}
|
||||
// Delete the order item
|
||||
(new order_items_o())->removeOrderItem((int)$data['id']);
|
||||
// Return the list of departments
|
||||
@@ -261,6 +283,8 @@ class orderItemsRoute
|
||||
if (!$order->exists()) {
|
||||
$response->error('Order not found', 404);
|
||||
}
|
||||
// Check if the user has access to the department
|
||||
self::requireDepartmentAccess((string)(int)$order->department_id->value());
|
||||
|
||||
$canAccessAllOrderItems = $this->hasPermission('list_order_items');
|
||||
if (!$canAccessAllOrderItems && !$order->isOwnOrder((int)$user->customer_number->value())) {
|
||||
|
||||
@@ -172,6 +172,8 @@ class ordersRoute
|
||||
if (!(new departments_o())->getDepartmentById((int)$data['department_id'])) {
|
||||
$response->error('Department not found', 400);
|
||||
}
|
||||
// Check if the user has access to the department
|
||||
self::requireDepartmentAccess((string)(int)$data['department_id']);
|
||||
// Make sure the customer number set is valid
|
||||
$targetUser = (new users_o())->getUserByCustomerNumber((int)$data['customer_id']);
|
||||
if (!$targetUser->exists()) {
|
||||
@@ -472,6 +474,8 @@ class ordersRoute
|
||||
if (!$order->exists()) {
|
||||
$response->error('Order not found', 400);
|
||||
}
|
||||
// Check if the user has access to the department
|
||||
self::requireDepartmentAccess((string)(int)$order->department_id->value());
|
||||
// Get the base64 file
|
||||
$base64_file = (string)$this->getParameter('base64_file');
|
||||
$attachment_store = new attachment_store();
|
||||
@@ -530,6 +534,8 @@ class ordersRoute
|
||||
if (!$order->exists()) {
|
||||
$response->error('Order not found', 400);
|
||||
}
|
||||
// Check if the user has access to the department
|
||||
self::requireDepartmentAccess((string)(int)$order->department_id->value());
|
||||
// Delete the attachment
|
||||
$order->removeAttachment((int)$attachment_id);
|
||||
// Log the incident
|
||||
@@ -568,6 +574,8 @@ class ordersRoute
|
||||
if (!$order->exists()) {
|
||||
$response->error('Order not found', 400);
|
||||
}
|
||||
// Check if the user has access to the department
|
||||
self::requireDepartmentAccess((string)(int)$order->department_id->value());
|
||||
// Mark the order as completed
|
||||
$order->markAsCompleted((string)$user->display_name->value());
|
||||
// Log the incident
|
||||
@@ -1154,7 +1162,8 @@ class ordersRoute
|
||||
}
|
||||
// Admin/department path (requires edit_order)
|
||||
self::requirePermission($permission_other);
|
||||
/** Departmental access */
|
||||
/** Departmental access — user must have access to the order's current department */
|
||||
self::requireDepartmentAccess((string)(int)$order->department_id->value());
|
||||
$originalCustomerNumber = (int)$order->customer_id->value();
|
||||
$newCustomerNumber = $originalCustomerNumber;
|
||||
$shouldAutoReassignInvoiceCollection = false;
|
||||
@@ -1219,6 +1228,8 @@ class ordersRoute
|
||||
if (!(new departments_o())->getDepartmentById((int)$data['department_id'])) {
|
||||
$response->error('Department not found', 400);
|
||||
}
|
||||
// Check if the user has access to the target department
|
||||
self::requireDepartmentAccess((string)(int)$data['department_id']);
|
||||
$order->department_id->set((int)$data['department_id']);
|
||||
}
|
||||
// If the booking ID is set, validate it
|
||||
@@ -1362,11 +1373,7 @@ class ordersRoute
|
||||
{
|
||||
try {
|
||||
$user = (new authentication())->get_user();
|
||||
if ($user !== false && isset($user->customer_number) && (int)$user->customer_number->value() === $customerNumber) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return $this->hasDepartmentAccess((string)$departmentId);
|
||||
return $user !== false && isset($user->customer_number) && (int)$user->customer_number->value() === $customerNumber;
|
||||
} catch (\Throwable) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ function limited_backoffice_all_role_permissions(): array
|
||||
return [
|
||||
'user',
|
||||
'permissions_list_own',
|
||||
'list_departments',
|
||||
'list_orders',
|
||||
'fetch_order',
|
||||
'add_order',
|
||||
@@ -71,6 +72,9 @@ function limited_backoffice_all_role_permissions(): array
|
||||
'department_timebookings_entries_get',
|
||||
'department_timebookings_entries_post',
|
||||
'department_timebookings_entries_put',
|
||||
'list_department_daily_reports',
|
||||
'list_notifications',
|
||||
'list_own_notifications',
|
||||
'statistics_orders_new',
|
||||
'statistics_bookings_new',
|
||||
];
|
||||
@@ -887,6 +891,7 @@ it('creates updates lists and deactivates scoped employees without exposing raw
|
||||
});
|
||||
foreach ([
|
||||
'list_orders',
|
||||
'list_departments',
|
||||
'fetch_order',
|
||||
'add_order',
|
||||
'edit_order',
|
||||
@@ -928,6 +933,9 @@ it('creates updates lists and deactivates scoped employees without exposing raw
|
||||
'department_timebookings_entries_get',
|
||||
'department_timebookings_entries_post',
|
||||
'department_timebookings_entries_put',
|
||||
'list_department_daily_reports',
|
||||
'list_notifications',
|
||||
'list_own_notifications',
|
||||
'statistics_orders_new',
|
||||
'statistics_bookings_new',
|
||||
'limited_backoffice_access',
|
||||
@@ -1075,7 +1083,7 @@ it('creates updates lists and deactivates scoped employees without exposing raw
|
||||
});
|
||||
});
|
||||
|
||||
it('caps limited employee permissions to the manager permissions and selected departments', function (): void {
|
||||
it('caps manager-gated limited employee permissions while keeping baseline role permissions', function (): void {
|
||||
api_test_covers('POST /limited-backoffice/employees', 'auth');
|
||||
api_test_covers('GET /limited-backoffice/roles', 'auth');
|
||||
|
||||
@@ -1088,9 +1096,11 @@ it('caps limited employee permissions to the manager permissions and selected de
|
||||
$rolesByKey = array_column($roles->data(), null, 'key');
|
||||
$operationsLeadGroups = array_column($rolesByKey['operations_lead']['permission_groups'] ?? [], 'capabilities', 'key');
|
||||
expect($operationsLeadGroups['account'] ?? null)->toBe(['sign_in', 'view_own_permissions']);
|
||||
expect($operationsLeadGroups['departments'] ?? null)->toBe(['view_departments', 'view_daily_reports']);
|
||||
expect($operationsLeadGroups['orders'] ?? null)->toBe(['view_orders']);
|
||||
expect($operationsLeadGroups['notifications'] ?? null)->toBe(['view_notifications']);
|
||||
expect($operationsLeadGroups['reports'] ?? null)->toBe(['view_order_statistics', 'view_booking_statistics']);
|
||||
expect($roles->body)->not->toContain('create_orders');
|
||||
expect($roles->body)->not->toContain('view_order_statistics');
|
||||
|
||||
$created = api_client()->post('/limited-backoffice/employees', [
|
||||
'display_name' => 'Limited Capped Lead',
|
||||
@@ -1122,7 +1132,13 @@ it('caps limited employee permissions to the manager permissions and selected de
|
||||
->toContain('user')
|
||||
->toContain('permissions_list_own')
|
||||
->toContain('employee_public_data')
|
||||
->toContain('list_departments')
|
||||
->toContain('list_orders')
|
||||
->toContain('list_department_daily_reports')
|
||||
->toContain('list_notifications')
|
||||
->toContain('list_own_notifications')
|
||||
->toContain('statistics_orders_new')
|
||||
->toContain('statistics_bookings_new')
|
||||
->toContain('department_access_' . (int)$department['id'])
|
||||
->not->toContain('add_order')
|
||||
->not->toContain('fetch_order')
|
||||
@@ -1130,7 +1146,6 @@ it('caps limited employee permissions to the manager permissions and selected de
|
||||
->not->toContain('search_customers')
|
||||
->not->toContain('add_bookings')
|
||||
->not->toContain('delete_order')
|
||||
->not->toContain('statistics_orders_new')
|
||||
->not->toContain(limited_backoffice_service::PERMISSION_MANAGE_EMPLOYEES);
|
||||
});
|
||||
|
||||
@@ -1606,3 +1621,554 @@ it('rejects invalid limited backoffice employee contact details', function (): v
|
||||
->assertSuccess(false)
|
||||
->assertMessage('Phone number must be 4-15 digits.');
|
||||
});
|
||||
|
||||
it('includes list_departments in all active limited backoffice role presets', function (): void {
|
||||
foreach (['cashier', 'booking_coordinator', 'operations_lead', 'department_admin'] as $roleKey) {
|
||||
$permissions = limited_backoffice_role_preset_permissions($roleKey);
|
||||
expect($permissions)->toContain('list_departments');
|
||||
}
|
||||
});
|
||||
|
||||
it('includes list_department_daily_reports (dagsopgørelse) in all active limited backoffice role presets', function (): void {
|
||||
foreach (['cashier', 'booking_coordinator', 'operations_lead', 'department_admin'] as $roleKey) {
|
||||
$permissions = limited_backoffice_role_preset_permissions($roleKey);
|
||||
expect($permissions)->toContain('list_department_daily_reports');
|
||||
}
|
||||
});
|
||||
|
||||
it('includes list_notifications and list_own_notifications (Notifikationer) in all active limited backoffice role presets', function (): void {
|
||||
foreach (['cashier', 'booking_coordinator', 'operations_lead', 'department_admin'] as $roleKey) {
|
||||
$permissions = limited_backoffice_role_preset_permissions($roleKey);
|
||||
expect($permissions)
|
||||
->toContain('list_notifications')
|
||||
->toContain('list_own_notifications');
|
||||
}
|
||||
});
|
||||
|
||||
it('includes statistics_orders_new and statistics_bookings_new (Overblik) in all active limited backoffice role presets', function (): void {
|
||||
foreach (['cashier', 'booking_coordinator', 'operations_lead', 'department_admin'] as $roleKey) {
|
||||
$permissions = limited_backoffice_role_preset_permissions($roleKey);
|
||||
expect($permissions)
|
||||
->toContain('statistics_orders_new')
|
||||
->toContain('statistics_bookings_new');
|
||||
}
|
||||
});
|
||||
|
||||
it('assigns list_departments to managed employees and enforces list_departments permission on GET /departments', function (): void {
|
||||
api_test_covers('GET /departments', 'limited backoffice employee');
|
||||
|
||||
$department = api_fixtures()->createDepartment(['name' => 'LB Dept List Test']);
|
||||
$manager = limited_backoffice_manager_session([(int)$department['id']]);
|
||||
|
||||
$result = api_client()->post('/limited-backoffice/employees', [
|
||||
'role_key' => 'cashier',
|
||||
'department_ids' => [(int)$department['id']],
|
||||
'display_name' => 'Dept List Test Employee',
|
||||
'email' => 'dept-list-test@example.test',
|
||||
'password' => 'Secret123!',
|
||||
], $manager['headers']);
|
||||
|
||||
$result->assertStatus(200)->assertEnvelope()->assertSuccess();
|
||||
$employeeId = (int)($result->data()['id'] ?? 0);
|
||||
expect($employeeId)->toBeGreaterThan(0);
|
||||
limited_backoffice_cleanup_created_employee($employeeId);
|
||||
|
||||
$stmt = api_test_runtime()->db()->prepare(
|
||||
'SELECT `managed_group_id` FROM `limited_backoffice_employees` WHERE `user_id` = ? LIMIT 1'
|
||||
);
|
||||
$stmt->bind_param('i', $employeeId);
|
||||
$stmt->execute();
|
||||
$employeeRow = $stmt->get_result()->fetch_assoc();
|
||||
$stmt->close();
|
||||
$managedGroupId = (int)($employeeRow['managed_group_id'] ?? 0);
|
||||
expect($managedGroupId)->toBeGreaterThan(0);
|
||||
|
||||
$permStmt = api_test_runtime()->db()->prepare(
|
||||
'SELECT 1 FROM `groups_permissions` WHERE `group_id` = ? AND `permission` = ? LIMIT 1'
|
||||
);
|
||||
$listDepts = 'list_departments';
|
||||
$permStmt->bind_param('is', $managedGroupId, $listDepts);
|
||||
$permStmt->execute();
|
||||
$permRow = $permStmt->get_result()->fetch_assoc();
|
||||
$permStmt->close();
|
||||
expect($permRow)->not->toBeNull('Managed employee must have list_departments permission');
|
||||
});
|
||||
|
||||
it('assigns list_department_daily_reports to managed employees', function (): void {
|
||||
$department = api_fixtures()->createDepartment(['name' => 'LB Daily Report Perm Test']);
|
||||
$manager = limited_backoffice_manager_session([(int)$department['id']]);
|
||||
|
||||
$result = api_client()->post('/limited-backoffice/employees', [
|
||||
'role_key' => 'cashier',
|
||||
'department_ids' => [(int)$department['id']],
|
||||
'display_name' => 'Daily Report Perm Employee',
|
||||
'email' => 'daily-report-perm@example.test',
|
||||
'password' => 'Secret123!',
|
||||
], $manager['headers']);
|
||||
|
||||
$result->assertStatus(200)->assertEnvelope()->assertSuccess();
|
||||
$employeeId = (int)($result->data()['id'] ?? 0);
|
||||
expect($employeeId)->toBeGreaterThan(0);
|
||||
limited_backoffice_cleanup_created_employee($employeeId);
|
||||
|
||||
$stmt = api_test_runtime()->db()->prepare(
|
||||
'SELECT `managed_group_id` FROM `limited_backoffice_employees` WHERE `user_id` = ? LIMIT 1'
|
||||
);
|
||||
$stmt->bind_param('i', $employeeId);
|
||||
$stmt->execute();
|
||||
$employeeRow = $stmt->get_result()->fetch_assoc();
|
||||
$stmt->close();
|
||||
$managedGroupId = (int)($employeeRow['managed_group_id'] ?? 0);
|
||||
expect($managedGroupId)->toBeGreaterThan(0);
|
||||
|
||||
$permStmt = api_test_runtime()->db()->prepare(
|
||||
'SELECT 1 FROM `groups_permissions` WHERE `group_id` = ? AND `permission` = ? LIMIT 1'
|
||||
);
|
||||
$dailyReports = 'list_department_daily_reports';
|
||||
$permStmt->bind_param('is', $managedGroupId, $dailyReports);
|
||||
$permStmt->execute();
|
||||
$permRow = $permStmt->get_result()->fetch_assoc();
|
||||
$permStmt->close();
|
||||
expect($permRow)->not->toBeNull('Managed employee must have list_department_daily_reports permission');
|
||||
});
|
||||
|
||||
it('assigns list_notifications and list_own_notifications to managed employees', function (): void {
|
||||
$department = api_fixtures()->createDepartment(['name' => 'LB Notifications Perm Test']);
|
||||
$manager = limited_backoffice_manager_session([(int)$department['id']]);
|
||||
|
||||
$result = api_client()->post('/limited-backoffice/employees', [
|
||||
'role_key' => 'cashier',
|
||||
'department_ids' => [(int)$department['id']],
|
||||
'display_name' => 'Notifications Perm Employee',
|
||||
'email' => 'notifications-perm@example.test',
|
||||
'password' => 'Secret123!',
|
||||
], $manager['headers']);
|
||||
|
||||
$result->assertStatus(200)->assertEnvelope()->assertSuccess();
|
||||
$employeeId = (int)($result->data()['id'] ?? 0);
|
||||
expect($employeeId)->toBeGreaterThan(0);
|
||||
limited_backoffice_cleanup_created_employee($employeeId);
|
||||
|
||||
$stmt = api_test_runtime()->db()->prepare(
|
||||
'SELECT `managed_group_id` FROM `limited_backoffice_employees` WHERE `user_id` = ? LIMIT 1'
|
||||
);
|
||||
$stmt->bind_param('i', $employeeId);
|
||||
$stmt->execute();
|
||||
$employeeRow = $stmt->get_result()->fetch_assoc();
|
||||
$stmt->close();
|
||||
$managedGroupId = (int)($employeeRow['managed_group_id'] ?? 0);
|
||||
expect($managedGroupId)->toBeGreaterThan(0);
|
||||
|
||||
foreach (['list_notifications', 'list_own_notifications'] as $perm) {
|
||||
$stmt = api_test_runtime()->db()->prepare(
|
||||
'SELECT 1 FROM `groups_permissions` WHERE `group_id` = ? AND `permission` = ? LIMIT 1'
|
||||
);
|
||||
$stmt->bind_param('is', $managedGroupId, $perm);
|
||||
$stmt->execute();
|
||||
$permRow = $stmt->get_result()->fetch_assoc();
|
||||
$stmt->close();
|
||||
expect($permRow)->not->toBeNull("Managed employee must have $perm permission");
|
||||
}
|
||||
});
|
||||
|
||||
it('assigns statistics permissions (Overblik) to all active limited backoffice role presets including cashier and booking_coordinator', function (): void {
|
||||
$department = api_fixtures()->createDepartment(['name' => 'LB Stats Perm Test']);
|
||||
$manager = limited_backoffice_manager_session([(int)$department['id']]);
|
||||
|
||||
foreach (['cashier', 'booking_coordinator'] as $roleKey) {
|
||||
$result = api_client()->post('/limited-backoffice/employees', [
|
||||
'role_key' => $roleKey,
|
||||
'department_ids' => [(int)$department['id']],
|
||||
'display_name' => 'Stats Perm ' . $roleKey,
|
||||
'email' => 'stats-perm-' . $roleKey . '@example.test',
|
||||
'password' => 'Secret123!',
|
||||
], $manager['headers']);
|
||||
|
||||
$result->assertStatus(200)->assertEnvelope()->assertSuccess();
|
||||
$employeeId = (int)($result->data()['id'] ?? 0);
|
||||
expect($employeeId)->toBeGreaterThan(0);
|
||||
limited_backoffice_cleanup_created_employee($employeeId);
|
||||
|
||||
$stmtEmp = api_test_runtime()->db()->prepare(
|
||||
'SELECT `managed_group_id` FROM `limited_backoffice_employees` WHERE `user_id` = ? LIMIT 1'
|
||||
);
|
||||
$stmtEmp->bind_param('i', $employeeId);
|
||||
$stmtEmp->execute();
|
||||
$employeeRow = $stmtEmp->get_result()->fetch_assoc();
|
||||
$stmtEmp->close();
|
||||
$managedGroupId = (int)($employeeRow['managed_group_id'] ?? 0);
|
||||
expect($managedGroupId)->toBeGreaterThan(0);
|
||||
|
||||
foreach (['statistics_orders_new', 'statistics_bookings_new'] as $perm) {
|
||||
$stmt = api_test_runtime()->db()->prepare(
|
||||
'SELECT 1 FROM `groups_permissions` WHERE `group_id` = ? AND `permission` = ? LIMIT 1'
|
||||
);
|
||||
$stmt->bind_param('is', $managedGroupId, $perm);
|
||||
$stmt->execute();
|
||||
$permRow = $stmt->get_result()->fetch_assoc();
|
||||
$stmt->close();
|
||||
expect($permRow)->not->toBeNull("$roleKey managed employee must have $perm permission");
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
it('enforces department access when creating an order via POST /orders', function (): void {
|
||||
api_test_covers('POST /orders', 'department access');
|
||||
|
||||
$allowedDepartment = api_fixtures()->createDepartment(['name' => 'Order Create Allowed Dept']);
|
||||
$deniedDepartment = api_fixtures()->createDepartment(['name' => 'Order Create Denied Dept']);
|
||||
$customer = api_fixtures()->createUser(['display_name' => 'Order Create Customer']);
|
||||
|
||||
$session = api_fixtures()->createUserSession([
|
||||
'add_order',
|
||||
'department_access_' . (int)$allowedDepartment['id'],
|
||||
]);
|
||||
|
||||
// Should succeed for accessible department
|
||||
$created = api_client()->post('/orders', [
|
||||
'customer_id' => $customer['customer_number'],
|
||||
'department_id' => (int)$allowedDepartment['id'],
|
||||
'reference' => 'DEPT-ACCESS-TEST',
|
||||
'notes' => '',
|
||||
'reg_1' => 'ABCD111',
|
||||
], $session['headers']);
|
||||
$created->assertStatus(200)->assertEnvelope()->assertSuccess();
|
||||
|
||||
// Should fail for inaccessible department
|
||||
api_client()->post('/orders', [
|
||||
'customer_id' => $customer['customer_number'],
|
||||
'department_id' => (int)$deniedDepartment['id'],
|
||||
'reference' => 'DEPT-ACCESS-DENIED',
|
||||
'notes' => '',
|
||||
'reg_1' => 'ABCD222',
|
||||
], $session['headers'])
|
||||
->assertStatus(403)
|
||||
->assertEnvelope()
|
||||
->assertSuccess(false)
|
||||
->assertMissingPermissions(['department_access_' . (int)$deniedDepartment['id']]);
|
||||
});
|
||||
|
||||
it('enforces department access on the existing order when editing via PUT /order', function (): void {
|
||||
api_test_covers('PUT /order', 'department access');
|
||||
|
||||
$allowedDepartment = api_fixtures()->createDepartment(['name' => 'Order Edit Allowed Dept']);
|
||||
$deniedDepartment = api_fixtures()->createDepartment(['name' => 'Order Edit Denied Dept']);
|
||||
$customer = api_fixtures()->createUser(['display_name' => 'Order Edit Customer']);
|
||||
|
||||
$orderInAllowed = api_fixtures()->createOrder([
|
||||
'customer_id' => $customer['customer_number'],
|
||||
'department_id' => (int)$allowedDepartment['id'],
|
||||
]);
|
||||
$orderInDenied = api_fixtures()->createOrder([
|
||||
'customer_id' => $customer['customer_number'],
|
||||
'department_id' => (int)$deniedDepartment['id'],
|
||||
]);
|
||||
|
||||
$session = api_fixtures()->createUserSession([
|
||||
'edit_order',
|
||||
'department_access_' . (int)$allowedDepartment['id'],
|
||||
]);
|
||||
|
||||
// Should succeed editing order in accessible department
|
||||
api_client()->put('/order', [
|
||||
'id' => (int)$orderInAllowed['id'],
|
||||
'notes' => 'updated',
|
||||
], $session['headers'])
|
||||
->assertStatus(200)
|
||||
->assertEnvelope()
|
||||
->assertSuccess();
|
||||
|
||||
// Should fail editing order in inaccessible department
|
||||
api_client()->put('/order', [
|
||||
'id' => (int)$orderInDenied['id'],
|
||||
'notes' => 'should be denied',
|
||||
], $session['headers'])
|
||||
->assertStatus(403)
|
||||
->assertEnvelope()
|
||||
->assertSuccess(false)
|
||||
->assertMissingPermissions(['department_access_' . (int)$deniedDepartment['id']]);
|
||||
});
|
||||
|
||||
it('enforces department access when moving an order to a new department via PUT /order', function (): void {
|
||||
api_test_covers('PUT /order', 'department access move');
|
||||
|
||||
$allowedDepartment = api_fixtures()->createDepartment(['name' => 'Order Move Allowed Dept']);
|
||||
$deniedDepartment = api_fixtures()->createDepartment(['name' => 'Order Move Denied Dept']);
|
||||
$customer = api_fixtures()->createUser(['display_name' => 'Order Move Customer']);
|
||||
|
||||
$order = api_fixtures()->createOrder([
|
||||
'customer_id' => $customer['customer_number'],
|
||||
'department_id' => (int)$allowedDepartment['id'],
|
||||
]);
|
||||
|
||||
$sessionBothDepts = api_fixtures()->createUserSession([
|
||||
'edit_order',
|
||||
'department_access_' . (int)$allowedDepartment['id'],
|
||||
'department_access_' . (int)$deniedDepartment['id'],
|
||||
]);
|
||||
|
||||
$sessionOnlyAllowed = api_fixtures()->createUserSession([
|
||||
'edit_order',
|
||||
'department_access_' . (int)$allowedDepartment['id'],
|
||||
]);
|
||||
|
||||
// Should fail when moving to inaccessible department (user only has access to allowedDepartment)
|
||||
api_client()->put('/order', [
|
||||
'id' => (int)$order['id'],
|
||||
'department_id' => (int)$deniedDepartment['id'],
|
||||
], $sessionOnlyAllowed['headers'])
|
||||
->assertStatus(403)
|
||||
->assertEnvelope()
|
||||
->assertSuccess(false)
|
||||
->assertMissingPermissions(['department_access_' . (int)$deniedDepartment['id']]);
|
||||
|
||||
// Should succeed when user has access to both departments
|
||||
api_client()->put('/order', [
|
||||
'id' => (int)$order['id'],
|
||||
'department_id' => (int)$deniedDepartment['id'],
|
||||
], $sessionBothDepts['headers'])
|
||||
->assertStatus(200)
|
||||
->assertEnvelope()
|
||||
->assertSuccess();
|
||||
});
|
||||
|
||||
it('enforces department access when marking an order as completed via POST /orders/mark_as_completed', function (): void {
|
||||
api_test_covers('POST /orders/mark_as_completed', 'department access');
|
||||
|
||||
$allowedDepartment = api_fixtures()->createDepartment(['name' => 'Order Complete Allowed Dept']);
|
||||
$deniedDepartment = api_fixtures()->createDepartment(['name' => 'Order Complete Denied Dept']);
|
||||
$customer = api_fixtures()->createUser(['display_name' => 'Order Complete Customer']);
|
||||
|
||||
$orderAllowed = api_fixtures()->createOrder([
|
||||
'customer_id' => $customer['customer_number'],
|
||||
'department_id' => (int)$allowedDepartment['id'],
|
||||
]);
|
||||
$orderDenied = api_fixtures()->createOrder([
|
||||
'customer_id' => $customer['customer_number'],
|
||||
'department_id' => (int)$deniedDepartment['id'],
|
||||
]);
|
||||
|
||||
$session = api_fixtures()->createUserSession([
|
||||
'mark_order_as_completed',
|
||||
'department_access_' . (int)$allowedDepartment['id'],
|
||||
]);
|
||||
|
||||
// Should fail for inaccessible department
|
||||
api_client()->post('/orders/mark_as_completed', [
|
||||
'id' => (int)$orderDenied['id'],
|
||||
], $session['headers'])
|
||||
->assertStatus(403)
|
||||
->assertEnvelope()
|
||||
->assertSuccess(false)
|
||||
->assertMissingPermissions(['department_access_' . (int)$deniedDepartment['id']]);
|
||||
|
||||
// Should succeed for accessible department
|
||||
api_client()->post('/orders/mark_as_completed', [
|
||||
'id' => (int)$orderAllowed['id'],
|
||||
], $session['headers'])
|
||||
->assertStatus(200)
|
||||
->assertEnvelope()
|
||||
->assertSuccess();
|
||||
});
|
||||
|
||||
it('enforces department access when deleting order attachments via DELETE /orders/attachments', function (): void {
|
||||
api_test_covers('DELETE /orders/attachments', 'department access');
|
||||
|
||||
$allowedDepartment = api_fixtures()->createDepartment(['name' => 'Order Del Attach Allowed']);
|
||||
$deniedDepartment = api_fixtures()->createDepartment(['name' => 'Order Del Attach Denied']);
|
||||
$customer = api_fixtures()->createUser(['display_name' => 'Order Del Attach Customer']);
|
||||
|
||||
$orderAllowed = api_fixtures()->createOrder([
|
||||
'customer_id' => $customer['customer_number'],
|
||||
'department_id' => (int)$allowedDepartment['id'],
|
||||
]);
|
||||
$orderDenied = api_fixtures()->createOrder([
|
||||
'customer_id' => $customer['customer_number'],
|
||||
'department_id' => (int)$deniedDepartment['id'],
|
||||
]);
|
||||
$attachmentAllowed = api_fixtures()->createOrderAttachment(['order_id' => (int)$orderAllowed['id']]);
|
||||
$attachmentDenied = api_fixtures()->createOrderAttachment(['order_id' => (int)$orderDenied['id']]);
|
||||
|
||||
$session = api_fixtures()->createUserSession([
|
||||
'delete_order_attachments',
|
||||
'department_access_' . (int)$allowedDepartment['id'],
|
||||
]);
|
||||
|
||||
// Should fail for inaccessible department
|
||||
api_client()->delete('/orders/attachments', [
|
||||
'order_id' => (int)$orderDenied['id'],
|
||||
'attachment_id' => (int)$attachmentDenied['id'],
|
||||
], $session['headers'])
|
||||
->assertStatus(403)
|
||||
->assertEnvelope()
|
||||
->assertSuccess(false)
|
||||
->assertMissingPermissions(['department_access_' . (int)$deniedDepartment['id']]);
|
||||
|
||||
// Should succeed for accessible department
|
||||
api_client()->delete('/orders/attachments', [
|
||||
'order_id' => (int)$orderAllowed['id'],
|
||||
'attachment_id' => (int)$attachmentAllowed['id'],
|
||||
], $session['headers'])
|
||||
->assertStatus(200)
|
||||
->assertEnvelope()
|
||||
->assertSuccess();
|
||||
});
|
||||
|
||||
it('enforces department access when adding order items via POST /order/items', function (): void {
|
||||
api_test_covers('POST /order/items', 'department access');
|
||||
|
||||
$allowedDepartment = api_fixtures()->createDepartment(['name' => 'Order Item Add Allowed']);
|
||||
$deniedDepartment = api_fixtures()->createDepartment(['name' => 'Order Item Add Denied']);
|
||||
$customer = api_fixtures()->createUser(['display_name' => 'Order Item Add Customer']);
|
||||
$category = api_fixtures()->createCategory(['name' => 'Order Item Dept Access Category']);
|
||||
$product = api_fixtures()->createProduct(['name' => 'Order Item Dept Access Product', 'category' => $category['id']]);
|
||||
|
||||
$orderAllowed = api_fixtures()->createOrder([
|
||||
'customer_id' => $customer['customer_number'],
|
||||
'department_id' => (int)$allowedDepartment['id'],
|
||||
]);
|
||||
$orderDenied = api_fixtures()->createOrder([
|
||||
'customer_id' => $customer['customer_number'],
|
||||
'department_id' => (int)$deniedDepartment['id'],
|
||||
]);
|
||||
|
||||
$session = api_fixtures()->createUserSession([
|
||||
'add_order_items',
|
||||
'department_access_' . (int)$allowedDepartment['id'],
|
||||
]);
|
||||
|
||||
// Should fail for inaccessible department
|
||||
api_client()->post('/order/items', [
|
||||
'order_id' => (int)$orderDenied['id'],
|
||||
'product_id' => (int)$product['id'],
|
||||
'quantity' => 1,
|
||||
], $session['headers'])
|
||||
->assertStatus(403)
|
||||
->assertEnvelope()
|
||||
->assertSuccess(false)
|
||||
->assertMissingPermissions(['department_access_' . (int)$deniedDepartment['id']]);
|
||||
|
||||
// Should succeed for accessible department
|
||||
api_client()->post('/order/items', [
|
||||
'order_id' => (int)$orderAllowed['id'],
|
||||
'product_id' => (int)$product['id'],
|
||||
'quantity' => 1,
|
||||
], $session['headers'])
|
||||
->assertStatus(200)
|
||||
->assertEnvelope()
|
||||
->assertSuccess();
|
||||
});
|
||||
|
||||
it('enforces department access when deleting order items via DELETE /order/items', function (): void {
|
||||
api_test_covers('DELETE /order/items', 'department access');
|
||||
|
||||
$allowedDepartment = api_fixtures()->createDepartment(['name' => 'Order Item Del Allowed']);
|
||||
$deniedDepartment = api_fixtures()->createDepartment(['name' => 'Order Item Del Denied']);
|
||||
$customer = api_fixtures()->createUser(['display_name' => 'Order Item Del Customer']);
|
||||
$category = api_fixtures()->createCategory(['name' => 'Order Item Del Category']);
|
||||
$product = api_fixtures()->createProduct(['name' => 'Order Item Del Product', 'category' => $category['id']]);
|
||||
|
||||
$orderAllowed = api_fixtures()->createOrder([
|
||||
'customer_id' => $customer['customer_number'],
|
||||
'department_id' => (int)$allowedDepartment['id'],
|
||||
]);
|
||||
$orderDenied = api_fixtures()->createOrder([
|
||||
'customer_id' => $customer['customer_number'],
|
||||
'department_id' => (int)$deniedDepartment['id'],
|
||||
]);
|
||||
|
||||
$session = api_fixtures()->createUserSession([
|
||||
'delete_order_items',
|
||||
'department_access_' . (int)$allowedDepartment['id'],
|
||||
]);
|
||||
|
||||
$superuserSession = api_fixtures()->createUserSession([], ['group_id' => 1]);
|
||||
$itemAllowedResponse = api_client()->post('/order/items', [
|
||||
'order_id' => (int)$orderAllowed['id'],
|
||||
'product_id' => (int)$product['id'],
|
||||
'quantity' => 1,
|
||||
], $superuserSession['headers']);
|
||||
$itemAllowed = $itemAllowedResponse->data();
|
||||
|
||||
$itemDeniedResponse = api_client()->post('/order/items', [
|
||||
'order_id' => (int)$orderDenied['id'],
|
||||
'product_id' => (int)$product['id'],
|
||||
'quantity' => 1,
|
||||
], $superuserSession['headers']);
|
||||
$itemDenied = $itemDeniedResponse->data();
|
||||
|
||||
// Should fail for inaccessible department
|
||||
api_client()->delete('/order/items', ['id' => (int)$itemDenied['id']], $session['headers'])
|
||||
->assertStatus(403)
|
||||
->assertEnvelope()
|
||||
->assertSuccess(false)
|
||||
->assertMissingPermissions(['department_access_' . (int)$deniedDepartment['id']]);
|
||||
|
||||
// Should succeed for accessible department
|
||||
api_client()->delete('/order/items', ['id' => (int)$itemAllowed['id']], $session['headers'])
|
||||
->assertStatus(200)
|
||||
->assertEnvelope()
|
||||
->assertSuccess();
|
||||
});
|
||||
|
||||
it('enforces department access when editing order items via PUT /order/items', function (): void {
|
||||
api_test_covers('PUT /order/items', 'department access');
|
||||
|
||||
$allowedDepartment = api_fixtures()->createDepartment(['name' => 'Order Item Edit Allowed']);
|
||||
$deniedDepartment = api_fixtures()->createDepartment(['name' => 'Order Item Edit Denied']);
|
||||
$customer = api_fixtures()->createUser(['display_name' => 'Order Item Edit Customer']);
|
||||
$category = api_fixtures()->createCategory(['name' => 'Order Item Edit Category']);
|
||||
$product = api_fixtures()->createProduct(['name' => 'Order Item Edit Product', 'category' => $category['id']]);
|
||||
|
||||
$orderAllowed = api_fixtures()->createOrder([
|
||||
'customer_id' => $customer['customer_number'],
|
||||
'department_id' => (int)$allowedDepartment['id'],
|
||||
]);
|
||||
$orderDenied = api_fixtures()->createOrder([
|
||||
'customer_id' => $customer['customer_number'],
|
||||
'department_id' => (int)$deniedDepartment['id'],
|
||||
]);
|
||||
$itemAllowed = api_fixtures()->createOrderItem([
|
||||
'order_id' => (int)$orderAllowed['id'],
|
||||
'product_id' => (int)$product['id'],
|
||||
'cashier_id' => 1,
|
||||
'price' => 100,
|
||||
]);
|
||||
$itemDenied = api_fixtures()->createOrderItem([
|
||||
'order_id' => (int)$orderDenied['id'],
|
||||
'product_id' => (int)$product['id'],
|
||||
'cashier_id' => 1,
|
||||
'price' => 200,
|
||||
]);
|
||||
|
||||
$session = api_fixtures()->createUserSession([
|
||||
'edit_order_items',
|
||||
'list_order_items',
|
||||
'department_access_' . (int)$allowedDepartment['id'],
|
||||
]);
|
||||
|
||||
// Should fail for inaccessible department
|
||||
api_client()->put('/order/items', [
|
||||
'id' => (int)$itemDenied['id'],
|
||||
'price' => 999,
|
||||
'notes' => '',
|
||||
'reference' => '',
|
||||
'quantity' => 1,
|
||||
], $session['headers'])
|
||||
->assertStatus(403)
|
||||
->assertEnvelope()
|
||||
->assertSuccess(false)
|
||||
->assertMissingPermissions(['department_access_' . (int)$deniedDepartment['id']]);
|
||||
|
||||
// Should succeed for accessible department
|
||||
api_client()->put('/order/items', [
|
||||
'id' => (int)$itemAllowed['id'],
|
||||
'price' => 150,
|
||||
'notes' => '',
|
||||
'reference' => '',
|
||||
'quantity' => 1,
|
||||
], $session['headers'])
|
||||
->assertStatus(200)
|
||||
->assertEnvelope()
|
||||
->assertSuccess();
|
||||
});
|
||||
|
||||
@@ -150,7 +150,7 @@ it('uses a product fixed price instead of the best discount when adding an order
|
||||
'cashier_id' => $cashier['id'],
|
||||
'reference' => 'FIXED-PRICE',
|
||||
]);
|
||||
$session = api_fixtures()->createUserSession(['add_order_items', 'list_products']);
|
||||
$session = api_fixtures()->createUserSession(['add_order_items', 'list_products', 'department_access_' . $department['id']]);
|
||||
|
||||
$productResponse = api_client()->get(
|
||||
'/products?final_price=true&id=' . $product['id'] . '&customer_id=' . $customer['customer_number'],
|
||||
@@ -281,7 +281,7 @@ it('does not allow clearing notes for order items whose product requires notes',
|
||||
'quantity' => 1,
|
||||
'notes' => 'Initial note',
|
||||
]);
|
||||
$session = api_fixtures()->createUserSession(['edit_order_items', 'list_order_items']);
|
||||
$session = api_fixtures()->createUserSession(['edit_order_items', 'list_order_items', 'department_access_' . $department['id']]);
|
||||
|
||||
api_client()
|
||||
->put('/order/items', [
|
||||
|
||||
@@ -73,7 +73,7 @@ it('creates orders through the orders endpoint', function (): void {
|
||||
|
||||
$customer = api_fixtures()->createUser(['display_name' => 'Order Create Customer']);
|
||||
$department = api_fixtures()->createDepartment(['name' => 'Order Create Department']);
|
||||
$session = api_fixtures()->createUserSession(['add_order']);
|
||||
$session = api_fixtures()->createUserSession(['add_order', 'department_access_' . $department['id']]);
|
||||
|
||||
$response = api_client()->post('/orders', [
|
||||
'customer_id' => $customer['customer_number'],
|
||||
@@ -128,7 +128,7 @@ it('defaults order PO only from a matching active booking', function (): void {
|
||||
'po' => 'DELETED-BOOKING-PO',
|
||||
'deleted_at' => date('Y-m-d H:i:s'),
|
||||
]);
|
||||
$session = api_fixtures()->createUserSession(['add_order', 'edit_order'], [
|
||||
$session = api_fixtures()->createUserSession(['add_order', 'edit_order', 'department_access_' . $department['id']], [
|
||||
'customer_number' => $customer['customer_number'],
|
||||
]);
|
||||
|
||||
@@ -149,7 +149,7 @@ it('defaults order PO only from a matching active booking', function (): void {
|
||||
$matchingOrderId = (int)($createResponse->data()['id'] ?? 0);
|
||||
expect($createResponse->data()['po'] ?? null)->toBe('MATCHING-BOOKING-PO');
|
||||
|
||||
$unauthorizedSession = api_fixtures()->createUserSession(['add_order'], [
|
||||
$unauthorizedSession = api_fixtures()->createUserSession(['add_order', 'department_access_' . $department['id']], [
|
||||
'customer_number' => $otherCustomer['customer_number'],
|
||||
]);
|
||||
$unauthorizedResponse = api_client()->post('/orders', [
|
||||
@@ -233,7 +233,7 @@ it('rejects invalid order creation requests', function (): void {
|
||||
|
||||
$customer = api_fixtures()->createUser();
|
||||
$department = api_fixtures()->createDepartment();
|
||||
$session = api_fixtures()->createUserSession(['add_order']);
|
||||
$session = api_fixtures()->createUserSession(['add_order', 'department_access_' . $department['id']]);
|
||||
|
||||
api_client()->post('/orders', [
|
||||
'customer_id' => $customer['customer_number'],
|
||||
@@ -262,7 +262,7 @@ it('updates orders through the primary and legacy endpoints', function (): void
|
||||
'notes' => 'Before update',
|
||||
'reg_1' => 'BEFORE1',
|
||||
]);
|
||||
$session = api_fixtures()->createUserSession(['edit_order']);
|
||||
$session = api_fixtures()->createUserSession(['edit_order', 'department_access_' . $department['id']]);
|
||||
|
||||
api_client()->put('/orders', [
|
||||
'id' => $order['id'],
|
||||
|
||||
Reference in New Issue
Block a user