Add superuser department overview API
This commit is contained in:
@@ -12523,6 +12523,40 @@ paths:
|
||||
application/json:
|
||||
schema: {}
|
||||
|
||||
/superuser/departments/{id}/overview:
|
||||
get:
|
||||
tags:
|
||||
- Departments
|
||||
summary: Get superuser department overview
|
||||
description: Returns the selected department metadata and operational overview metrics for a superuser without requiring scoped department access.
|
||||
operationId: getSuperuserDepartmentOverview
|
||||
parameters:
|
||||
- name: id
|
||||
in: path
|
||||
required: true
|
||||
schema: {type: integer}
|
||||
- name: date
|
||||
in: query
|
||||
required: true
|
||||
schema: {type: string}
|
||||
- name: date_to
|
||||
in: query
|
||||
required: false
|
||||
schema: {type: string}
|
||||
responses:
|
||||
'200':
|
||||
description: Superuser department overview loaded successfully
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/SuperuserDepartmentOverviewResponse'
|
||||
'400':
|
||||
$ref: '#/components/responses/BadRequest'
|
||||
'403':
|
||||
$ref: '#/components/responses/Forbidden'
|
||||
'404':
|
||||
$ref: '#/components/responses/NotFound'
|
||||
|
||||
/superuser/department/branding:
|
||||
put:
|
||||
tags:
|
||||
@@ -21550,6 +21584,21 @@ components:
|
||||
data:
|
||||
$ref: '#/components/schemas/DepartmentDailyReportOverviewPayload'
|
||||
|
||||
SuperuserDepartmentOverviewPayload:
|
||||
type: object
|
||||
properties:
|
||||
department:
|
||||
$ref: '#/components/schemas/Department'
|
||||
overview:
|
||||
$ref: '#/components/schemas/DepartmentDailyReportOverviewPayload'
|
||||
|
||||
SuperuserDepartmentOverviewResponse:
|
||||
type: object
|
||||
properties:
|
||||
success: { type: boolean, example: true }
|
||||
data:
|
||||
$ref: '#/components/schemas/SuperuserDepartmentOverviewPayload'
|
||||
|
||||
DepartmentDailyReportTransactionCountPayload:
|
||||
type: object
|
||||
properties:
|
||||
|
||||
@@ -812,6 +812,53 @@ class departmentDailyReportsRoute
|
||||
]
|
||||
);
|
||||
|
||||
$this->get('/superuser/departments/{id}/overview', function () {
|
||||
global $response;
|
||||
$this->requirePermission('superuser_fetch_department');
|
||||
|
||||
$user = (new authentication())->get_user();
|
||||
if (!$user) {
|
||||
(new logs_o())->add('departments', 'global', 1, 0, 'SUPERUSER_DEPARTMENT_OVERVIEW', 'No user found, or invalid session');
|
||||
$response->error('Invalid session', 400);
|
||||
return;
|
||||
}
|
||||
|
||||
$department_id_param = (string)($this->fromRoute('id') ?? '');
|
||||
if (!ctype_digit($department_id_param) || (int)$department_id_param <= 0) {
|
||||
$response->error('Parameter id must be a positive integer', 400);
|
||||
return;
|
||||
}
|
||||
|
||||
self::requireParameters([
|
||||
'date',
|
||||
]);
|
||||
|
||||
self::validateDateLocally();
|
||||
$date_to = $this->getDate_to();
|
||||
$department_id = (int)$department_id_param;
|
||||
$department = (new departments_o())->select($department_id);
|
||||
|
||||
if (!$department->exists()) {
|
||||
$response->error('Department not found', 404);
|
||||
return;
|
||||
}
|
||||
|
||||
(new logs_o())->add('departments', 'global', 1, $user->id, 'SUPERUSER_DEPARTMENT_OVERVIEW', 'Successfully loaded superuser department overview');
|
||||
|
||||
$response->success([
|
||||
'department' => $department->asArray(['slack_webhook' => false]),
|
||||
'overview' => $this->buildDailyReportOverview(
|
||||
[$department_id],
|
||||
(string)self::getParameter('date'),
|
||||
$date_to
|
||||
),
|
||||
]);
|
||||
},
|
||||
[
|
||||
'superuser_fetch_department' => 'Get the superuser department overview'
|
||||
]
|
||||
);
|
||||
|
||||
$this->get('/departments/daily-reports/overview', function () {
|
||||
global $response;
|
||||
$this->requirePermission('list_department_daily_reports');
|
||||
|
||||
@@ -0,0 +1,100 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
usesApiSuite();
|
||||
|
||||
it('loads a single department overview for superusers without department scoped access', function (): void {
|
||||
api_test_covers('GET /superuser/departments/{id}/overview', 'happy');
|
||||
|
||||
$department = api_fixtures()->createDepartment([
|
||||
'name' => 'Overview Department ' . uniqid('', false),
|
||||
'description' => 'Department overview fixture',
|
||||
'economic_department_id' => 42,
|
||||
'visible' => 1,
|
||||
]);
|
||||
$departmentRow = api_fixtures()->fetchRowById('departments', (int)$department['id']);
|
||||
$session = api_fixtures()->createUserSession([
|
||||
'superuser_fetch_department',
|
||||
]);
|
||||
|
||||
$response = api_client()->get(
|
||||
'/superuser/departments/' . $department['id'] . '/overview?date=2026-07-06&date_to=2026-07-06',
|
||||
$session['headers']
|
||||
);
|
||||
|
||||
$response
|
||||
->assertStatus(200)
|
||||
->assertEnvelope()
|
||||
->assertSuccess();
|
||||
|
||||
$payload = $response->data();
|
||||
|
||||
expect($payload)->toBeArray();
|
||||
expect($payload['department'])
|
||||
->toBeArray()
|
||||
->toHaveKey('id', (int)$department['id'])
|
||||
->toHaveKey('name', $departmentRow['name'])
|
||||
->toHaveKey('description', 'Department overview fixture')
|
||||
->toHaveKey('economic_department_id', 42);
|
||||
|
||||
expect($payload['overview'])
|
||||
->toBeArray()
|
||||
->toHaveKey('department_ids', [(int)$department['id']])
|
||||
->toHaveKey('date', '2026-07-06')
|
||||
->toHaveKey('date_to', '2026-07-06');
|
||||
|
||||
expect($payload['overview']['metrics'])
|
||||
->toBeArray()
|
||||
->toHaveKeys([
|
||||
'bookings',
|
||||
'complaints',
|
||||
'night_washes',
|
||||
'revenue',
|
||||
'washes',
|
||||
'products_sold',
|
||||
'transactions',
|
||||
'water_usage',
|
||||
'overtime',
|
||||
]);
|
||||
expect($payload['overview']['metrics']['revenue']['state'])->toBe('ready');
|
||||
expect($payload['overview']['metrics']['revenue']['value'])->toBe(0);
|
||||
expect($payload['overview']['products'])->toBeArray();
|
||||
});
|
||||
|
||||
it('rejects superuser department overview requests without permission or valid input', function (): void {
|
||||
api_test_covers('GET /superuser/departments/{id}/overview', 'auth');
|
||||
api_test_covers('GET /superuser/departments/{id}/overview', 'failure');
|
||||
|
||||
$department = api_fixtures()->createDepartment();
|
||||
$unauthorizedSession = api_fixtures()->createUserSession([]);
|
||||
|
||||
api_client()->get(
|
||||
'/superuser/departments/' . $department['id'] . '/overview?date=2026-07-06',
|
||||
$unauthorizedSession['headers']
|
||||
)
|
||||
->assertStatus(403)
|
||||
->assertEnvelope()
|
||||
->assertSuccess(false)
|
||||
->assertMissingPermissions(['superuser_fetch_department']);
|
||||
|
||||
$session = api_fixtures()->createUserSession(['superuser_fetch_department']);
|
||||
|
||||
api_client()->get('/superuser/departments/bad/overview?date=2026-07-06', $session['headers'])
|
||||
->assertStatus(400)
|
||||
->assertEnvelope()
|
||||
->assertSuccess(false)
|
||||
->assertMessage('Parameter id must be a positive integer');
|
||||
|
||||
api_client()->get('/superuser/departments/' . $department['id'] . '/overview', $session['headers'])
|
||||
->assertStatus(400)
|
||||
->assertEnvelope()
|
||||
->assertSuccess(false)
|
||||
->assertMessage('Missing required parameters: date');
|
||||
|
||||
api_client()->get('/superuser/departments/99999999/overview?date=2026-07-06', $session['headers'])
|
||||
->assertStatus(404)
|
||||
->assertEnvelope()
|
||||
->assertSuccess(false)
|
||||
->assertMessage('Department not found');
|
||||
});
|
||||
@@ -19,6 +19,7 @@ return [
|
||||
'GET /branding',
|
||||
'POST /branding',
|
||||
'PUT /branding',
|
||||
'GET /superuser/departments/{id}/overview',
|
||||
'PUT /superuser/department/branding',
|
||||
'POST /bird/voice/calls/webhook/inbound',
|
||||
],
|
||||
|
||||
+3
@@ -31,8 +31,11 @@ it('documents the daily report overview endpoint and reusable schemas in openapi
|
||||
$content = department_daily_reports_openapi_content_or_skip();
|
||||
|
||||
expect($content)->toContain('/departments/daily-reports/overview:');
|
||||
expect($content)->toContain('/superuser/departments/{id}/overview:');
|
||||
expect($content)->toContain('operationId: getDailyReportOverview');
|
||||
expect($content)->toContain('operationId: getSuperuserDepartmentOverview');
|
||||
expect($content)->toContain('DepartmentDailyReportOverviewResponse:');
|
||||
expect($content)->toContain('SuperuserDepartmentOverviewResponse:');
|
||||
expect($content)->toContain('DepartmentDailyReportMetric:');
|
||||
expect($content)->toContain('DepartmentDailyReportProductTile:');
|
||||
expect($content)->toContain('- name: department_ids');
|
||||
|
||||
@@ -342,6 +342,8 @@ it('wires the overview route to batched repository methods and overview path', f
|
||||
$objectContent = (string)file_get_contents(app_path('objects/department_daily_reports_o.php'));
|
||||
|
||||
expect($routeContent)->toContain('/departments/daily-reports/overview');
|
||||
expect($routeContent)->toContain('/superuser/departments/{id}/overview');
|
||||
expect($routeContent)->toContain('superuser_fetch_department');
|
||||
expect($routeContent)->toContain('/departments/daily-reports/complaints');
|
||||
expect($routeContent)->toContain('outsideHoursStatisticsService');
|
||||
expect($routeContent)->toContain('dailyReportComplaintsRepository');
|
||||
|
||||
Reference in New Issue
Block a user