Files
api/services/nginx/app/routes/departmentsRoute.php
T

616 lines
28 KiB
PHP

<?php
namespace routes;
use classes\authentication;
use classes\selfserve;
use objects\categories_o;
use objects\department_categories_o;
use objects\department_lanes_o;
use objects\department_variables_o;
use objects\departments_o;
use objects\logs_o;
use objects\orders_o;
use traits\route_t;
class departmentsRoute
{
use route_t;
private function buildDepartmentListFilters(departments_o $departments, bool $canListArchived): string
{
global $response;
$filters = $response->getRequestParameter('filters') ?? [];
if (is_string($filters) || is_array($filters)) {
$filters = $departments->filter_string_to_array($filters);
} else {
$filters = [];
}
$archived = 0;
if (
$canListArchived
&& array_key_exists('archived', $filters)
&& self::isTruthyBooleanValue($filters['archived'])
) {
$archived = 1;
}
unset($filters['visible'], $filters['archived']);
$filters['visible'] = 1;
$filters['archived'] = $archived;
return $departments->array_to_filters($filters);
}
private static function isTruthyBooleanValue(mixed $value): bool
{
if (is_array($value)) {
foreach ($value as $singleValue) {
if (self::isTruthyBooleanValue($singleValue)) {
return true;
}
}
return false;
}
if (is_bool($value)) {
return $value;
}
if (is_numeric($value)) {
return (int)$value === 1;
}
if (is_string($value)) {
return in_array(strtolower(trim($value)), ['1', 'true', 'yes', 'on'], true);
}
return false;
}
public function run(): void
{
$this->get('/departments', function () {
// Require the user to be logged in
global $response;
$this->requirePermission('list_departments');
// Get the user object
$user = (new authentication())->get_user();
// Check if the request was successful
if ($user) {
// Log the incident
(new logs_o())->add('departments', 'global', 1, $user->id, 'LIST_DEPARTMENTS', 'Successfully listed departments');
// Check if the id is set in the request
if (self::isParametersSet(['id'])) {
// Return the department
$response->success(
(new departments_o())->select((int)self::getParameter('id'))->asArray([
'slack_webhook' => $user->hasPermission('view_slack_webhook')
])
);
}
$departments_o = new departments_o();
// Return the list of departments
$response->success(
$departments_o
->setSearchableFields([
// The fields that can be searched. This would otherwise make it possible to get secret information from the database, simply by searching for it and getting the result count back
'id',
'name',
'description',
'economic_department_id',
'visible',
'archived',
'longitude',
'latitude',
])
->listObjectsWithPaginationIfSet(
function ($department) use ($user) {
$tmp_department = [
'id' => (int)$department['id'],
'name' => (string)$department['name'],
'description' => $department['description'],
'economic_department_id' => (int)$department['economic_department_id'],
'created_at' => (string)$department['created_at'],
'updated_at' => (string)$department['updated_at'],
'visible' => (int)$department['visible'],
'dimension' => (int)$department['dimension'],
'branding' => (int)$department['branding'],
'archived' => (bool)(int)($department['archived'] ?? 0),
'longitude' => (float)$department['longitude'],
'latitude' => (float)$department['latitude'],
'order_priority' => (int)$department['order_priority'],
];
// If the user has the permission to view the slack webhook, add it to the response
if ($user->hasPermission('view_slack_webhook')) {
$tmp_department['slack_webhook'] = $department['slack_webhook'];
}
return $tmp_department;
},
$this->buildDepartmentListFilters(
$departments_o,
$user->hasPermission('superuser_fetch_department')
)
)
);
} else {
// Log the incident
(new logs_o())->add('departments', 'global', 1, 0, 'LIST_DEPARTMENTS', 'No user found, or invalid session');
// Return an error
$response->error('Invalid session', 400);
}
},
[
'list_departments' => 'List all departments',
'view_slack_webhook' => 'View the slack webhook'
]
);
$this->post('/departments', function () {
// Require the user to be logged in
global $response;
$this->requirePermission('add_department');
// Get the user object
$user = (new authentication())->get_user();
// Check if the request was successful
if ($user) {
// Get the post data
$data = json_decode(file_get_contents('php://input'), true);
// Check if the required fields are set
if (!isset($data['name'])) {
$response->error('Name is required', 400);
}
if (!isset($data['description'])) {
$response->error('Description is required', 400);
}
// Add the department
(new departments_o())->create($data['name'], $data['description']);
// Log the incident
(new logs_o())->add('departments', 'global', 1, $user->id, 'ADD_DEPARTMENT', 'Successfully added a department ' . $data['name']);
// Return a success message
$response->success(['message' => 'Department added successfully']);
} else {
// Log the incident
(new logs_o())->add('departments', 'global', 1, 0, 'ADD_DEPARTMENT', 'No user found, or invalid session');
// Return an error
$response->error('Invalid session', 400);
}
},
[
'add_department' => 'Add a department'
]
);
$this->put('/departments', function () {
// Require the user to be logged in
global $response;
self::requirePermission('edit_department');
// Get the user object
$user = (new authentication())->get_user();
// Check if the request was successful
if ($user) {
// Require the required fields
self::requireParameters(['id']);
// Validate the required fields
self::requireType((int)self::getParameter('id'), self::TYPE_INT());
// Get the department object
$department = (new departments_o())->select(self::getParameter('id'));
// Update the fields provided
if (self::isParametersSet(['name'])) {
$department->name->set(self::getParameter('name'));
}
if (self::isParametersSet(['description'])) {
$department->description->set(self::getParameter('description'));
}
if (self::isParametersSet(['economic_department_id'])) {
$department->economic_department_id->set(self::getParameter('economic_department_id'));
}
if (self::isParametersSet(['latitude'])) {
$department->latitude->set(self::getParameter('latitude'));
}
if (self::isParametersSet(['longitude'])) {
$department->longitude->set(self::getParameter('longitude'));
}
if (self::isParametersSet(['order_priority'])) {
$department->order_priority->set((int)self::getParameter('order_priority'));
}
if (self::isParametersSet(['archived'])) {
$department->archived->set(self::isTruthyBooleanValue(self::getParameter('archived')));
}
$department->objectChanged();
// Log the incident
(new logs_o())->add('departments', (int)self::getParameter('id'), 1, $user->id, 'EDIT_DEPARTMENT', 'Successfully edited a department');
// Return a success message
$response->success(['message' => 'Department updated successfully']);
} else {
// Log the incident
(new logs_o())->add('departments', 'global', 1, 0, 'EDIT_DEPARTMENT', 'No user found, or invalid session');
// Return an error
$response->error('Invalid session', 400);
}
},
[
'edit_department' => 'Edit a department'
]
);
$this->get('/departments/categories', function () {
// Require the user to be logged in
global $response;
self::requirePermission('list_department_categories');
// Get the user object
$user = (new authentication())->get_user();
// Check if the request was successful
if ($user) {
// Require the department id
self::requireParameters(['id']);
self::requireType((int)self::getParameter('id'), self::TYPE_INT());
// Get the department object
$department = (new departments_o())->select(self::getParameter('id'));
// Validate the department categories object
if (!$department->exists()) {
// Log the incident
(new logs_o())->add('departments', 'global', 1, $user->id, 'LIST_DEPARTMENT_CATEGORIES', 'Department categories not found');
// Return an error
$response->error('Department categories not found', 400);
}
// Get the department categories
$department_categories = new department_categories_o();
// Log the incident
(new logs_o())->add('departments', 'global', 1, $user->id, 'LIST_DEPARTMENT_CATEGORIES', 'Successfully listed department categories');
// Return the list of department categories
$response->success(
$department_categories
->getCategoriesForDepartment(self::getParameter('id'),
function ($department_category) {
return [
'id' => (int)$department_category['id'],
'department_id' => (int)$department_category['department_id'],
'category_id' => (int)$department_category['category_id'],
'created_at' => (string)$department_category['created_at'],
'updated_at' => $department_category['updated_at'],
'category' => $department_category['category']->asArray()
];
}
)
);
} else {
// Log the incident
(new logs_o())->add('departments', 'global', 1, 0, 'LIST_DEPARTMENT_CATEGORIES', 'No user found, or invalid session');
// Return an error
$response->error('Invalid session', 400);
}
},
[
'list_department_categories' => 'List all department categories'
]
);
$this->get('/departments/self-serve/enabled', function () {
// Require the user to be logged in
global $response;
self::requirePermission('view_department_selfserve_enabled');
// Get the user object
$user = (new authentication())->get_user();
// Check if the request was successful
if ($user) {
// Require the department id
self::requireParameters(['id']);
self::requireType((int)self::getParameter('id'), self::type_int());
// Get the department object
$department = (new departments_o())->select(self::getParameter('id'));
// Validate the department object
if (!$department->exists()) {
// Return an error
$response->error('Department not found', 404);
}
// Get the department variable
$department_variables = (new department_variables_o())->selectDepartment($department->id);
$enabled = $department_variables->getVariable('selfserve_enabled');
// Return the status
$response->success([
'enabled' => $enabled === true
]);
} else {
// Return an error
$response->error('Invalid session', 400);
}
},
[
'view_department_selfserve_enabled' => 'View if department self-serve is enabled'
]
);
$this->put('/departments/self-serve/enabled', function () {
// Require the user to be logged in
global $response;
self::requirePermission('edit_department_selfserve_enabled');
// Get the user object
$user = (new authentication())->get_user();
// Check if the request was successful
if ($user) {
// Require the department id and enabled status
self::requireParameters(['id', 'enabled']);
self::requireType((int)self::getParameter('id'), self::type_int());
self::requireType((bool)self::getParameter('enabled'), self::type_bool());
// Get the department object
$department = (new departments_o())->select(self::getParameter('id'));
// Validate the department object
if (!$department->exists()) {
// Return an error
$response->error('Department not found', 404);
}
// Check if the user has access to the department
self::requireDepartmentAccess($department);
// Set the department variable
$department_variables = (new department_variables_o())->selectDepartment($department->id);
$enabled = self::getParameter('enabled') === 'true' || self::getParameter('enabled') === true || self::getParameter('enabled') === 1 || self::getParameter('enabled') === '1';
$department_variables->set('selfserve_enabled', $enabled ? 'true' : 'false');
$this->syncDepartmentSelfServeRelayStates((int)$department->id, $enabled);
// Log the incident
(new logs_o())->add('departments', $department->id, 1, $user->id, 'EDIT_DEPARTMENT_SELFSERVE_ENABLED', 'Successfully edited department self-serve enabled status to ' . ($enabled ? 'true' : 'false'));
// Return a success message
$response->success(['message' => 'Department self-serve enabled status updated successfully']);
} else {
// Return an error
$response->error('Invalid session', 400);
}
},
[
'edit_department_selfserve_enabled' => 'Edit if department self-serve is enabled (Requires department access)',
'department_access_:id' => 'Access the department'
]
);
$this->post('/departments/categories', function () {
// Require the user to be logged in
global $response;
self::requirePermission('add_department_category');
// Get the user object
$user = (new authentication())->get_user();
// Check if the request was successful
if ($user) {
// Require the parameters
self::requireParameters(['department_id', 'category_id']);
self::requireType((int)self::getParameter('department_id'), self::TYPE_INT());
self::requireType((int)self::getParameter('category_id'), self::TYPE_INT());
// Get the department object
$department = (new departments_o())->select(self::getParameter('department_id'));
// Validate the department object
if (!$department->exists()) {
// Log the incident
(new logs_o())->add('departments', 'global', 1, $user->id, 'ADD_DEPARTMENT_CATEGORY', 'Department not found');
// Return an error
$response->error('Department not found', 400);
}
// Get the category object
$category = (new categories_o())->select(self::getParameter('category_id'));
// Validate the category object
if (!$category->exists()) {
// Log the incident
(new logs_o())->add('departments', 'global', 1, $user->id, 'ADD_DEPARTMENT_CATEGORY', 'Category not found');
// Return an error
$response->error('Category not found', 400);
}
// Add the department category
$department_categories = new department_categories_o();
$department_categories->add(
self::getParameter('department_id'),
self::getParameter('category_id')
);
// Log the incident
(new logs_o())->add('departments', 'global', 1, $user->id, 'ADD_DEPARTMENT_CATEGORY', 'Successfully added a department category');
// Return a success message
$response->success(['message' => 'Department category added successfully']);
} else {
// Log the incident
(new logs_o())->add('departments', 'global', 1, 0, 'ADD_DEPARTMENT_CATEGORY', 'No user found, or invalid session');
// Return an error
$response->error('Invalid session', 400);
}
},
[
'add_department_category' => 'Add a department category'
]
);
$this->delete('/departments/categories', function () {
// Require the user to be logged in
global $response;
self::requirePermission('delete_department_category');
// Get the user object
$user = (new authentication())->get_user();
// Check if the request was successful
if ($user) {
// Require the parameters
self::requireParameters(['id']);
self::requireType((int)self::getParameter('id'), self::TYPE_INT());
// Get the department category object
$department_category = (new department_categories_o())->select(self::getParameter('id'));
// Validate the department category object
if (!$department_category->exists()) {
// Log the incident
(new logs_o())->add('departments', 'global', 1, $user->id, 'DELETE_DEPARTMENT_CATEGORY', 'Department category not found');
// Return an error
$response->error('Department category not found', 400);
}
// Delete the department category
$department_category->delete();
// Log the incident
(new logs_o())->add('departments', 'global', 1, $user->id, 'DELETE_DEPARTMENT_CATEGORY', 'Successfully deleted a department category');
// Return a success message
$response->success(['message' => 'Department category deleted successfully']);
} else {
// Log the incident
(new logs_o())->add('departments', 'global', 1, 0, 'DELETE_DEPARTMENT_CATEGORY', 'No user found, or invalid session');
// Return an error
$response->error('Invalid session', 400);
}
},
[
'delete_department_category' => 'Delete a department category'
]
);
self::get('/departments/order/recommended', function () {
// Require the user to be logged in
global $response;
self::requirePermission('list_department_order_recommended');
// Get the user object
$user = (new authentication())->get_user();
// Check if the request was successful
if ($user) {
// Check if the required fields are set
self::requireParameters(['id']);
self::requireType((int)self::getParameter('id'), self::TYPE_INT());
// Get the order
$order = (new orders_o())->select((int)self::getParameter('id'));
$order->requireSelected();
// Check if the user is allowed to view the recommended order for the department
self::requireDepartmentAccess($order->department_id->value());
// Get the recommended order
$recommended_order = $order->getRecommendedOrder();
// Log the incident
(new logs_o())->add('departments', 'global', 1, $user->id, 'LIST_DEPARTMENT_ORDER_RECOMMENDED', 'Successfully listed the recommended department order');
// Return the recommended order
$response->success($recommended_order);
} else {
// Log the incident
(new logs_o())->add('departments', 'global', 1, 0, 'LIST_DEPARTMENT_ORDER_RECOMMENDED', 'No user found, or invalid session');
// Return an error
$response->error('Invalid session', 400);
}
},
[
'list_department_order_recommended' => 'List the recommended department order',
'order_access_:id' => 'Access the order',
'department_access_:id' => 'Access the department'
]
);
self::get('/departments/weekly-results', function () {
// Require the user to be logged in
global $response;
self::requirePermission('view_department_weekly_results');
// Get the user object
$user = (new authentication())->get_user();
// Check if the request was successful
if ($user) {
// Get the department results for the week
$departments_o = new departments_o();
$weeks = 3;
$results = [];
for ($i = 0; $i < $weeks; $i++) {
// Get the week number
$week_number = date('W', strtotime("-$i week"));
// Skip the next week, only show show weeks where at least monday at 00:00:00 is in the past, to prevent showing incomplete data for the current week
// Get the weeks monday
$week_start = strtotime("-$i week Monday");
if ($week_start < strtotime('now')) {
$week_monday = date('Y-m-d 00:00:00', $week_start);
// Get the weeks sunday from monday
$week_sunday = date('Y-m-d 23:59:59', strtotime("$week_monday +6 days"));
$results[$week_number] = [
'week_number' => $week_number,
'week_monday' => $week_monday,
'week_sunday' => $week_sunday,
'results' => $departments_o->sendSlackInternalStatisticNotification(
$week_monday,
$week_sunday,
[1, 2, 3, 4, 5, 6, 7],
// Default value
[
25,
[23, 24], // Used to merge two products into one percentage (Spot Free)
22,
27,
21,
26
],
true // Return the results instead of sending the notification (This is used to show the results in the frontend, instead of sending them to slack.
)
];
}
}
// Log the incident
(new logs_o())->add('departments', 'global', 1, $user->id, 'VIEW_DEPARTMENT_WEEKLY_RESULTS', 'Successfully viewed department weekly results');
// Return the results
$response->success($results);
} else {
// Log the incident
(new logs_o())->add('departments', 'global', 1, 0, 'VIEW_DEPARTMENT_WEEKLY_RESULTS', 'No user found, or invalid session');
// Return an error
$response->error('Invalid session', 400);
}
},
[
'view_department_weekly_results' => 'View department weekly results'
]
);
}
protected function syncDepartmentSelfServeRelayStates(int $departmentId, bool $enabled): void
{
if (!$enabled) {
// Self-serve disabled: do not mutate lane relay states.
return;
}
$selfserve = new selfserve();
$lanes = (new department_lanes_o())->getDepartmentLanes($departmentId);
foreach ($lanes as $department_lane) {
$lane_id = (int)$department_lane->id;
if ($lane_id <= 0) {
continue;
}
try {
$lane = $selfserve->lane($lane_id);
} catch (\Throwable) {
continue;
}
// Self-serve enabled: keep machine stack off.
$this->setOptionalLaneRelayState($lane, 'relay_machine_program_picker_id', static function () use ($lane): void {
$lane->setMachineProgramPickerRelayStatusHard(false);
});
$this->setOptionalLaneRelayState($lane, 'relay_machine_cleaner_id', static function () use ($lane): void {
$lane->setMachineCleanerRelayStatusHard(false);
});
try {
$lane->setMachineRelayStatusHard(false);
} catch (\Throwable) {}
}
}
protected function setOptionalLaneRelayState(object $lane, string $relayProperty, callable $callback): void
{
if (
empty($lane->department_lane)
|| !isset($lane->department_lane->{$relayProperty})
|| !is_object($lane->department_lane->{$relayProperty})
|| !method_exists($lane->department_lane->{$relayProperty}, 'value')
) {
return;
}
$relay_id = trim((string)$lane->department_lane->{$relayProperty}->value());
if ($relay_id === '') {
return;
}
try {
$callback();
} catch (\Throwable) {
// Best effort only; this endpoint should still update the department variable.
}
}
}