2193 lines
82 KiB
PHP
2193 lines
82 KiB
PHP
<?php
|
|
|
|
namespace routes;
|
|
|
|
use classes\authentication;
|
|
use classes\department_outside_hours_statistics_service;
|
|
use classes\workfeed;
|
|
use classes\workfeed_shift_time_resolver;
|
|
use customers\economicCustomers;
|
|
use DateInterval;
|
|
use DateTime;
|
|
use DateTimeZone;
|
|
use Exception;
|
|
use objects\department_daily_report_complaints_o;
|
|
use objects\department_daily_reports_o;
|
|
use objects\departments_o;
|
|
use objects\logs_o;
|
|
use objects\users_o;
|
|
use traits\route_t;
|
|
|
|
class departmentDailyReportsRoute
|
|
{
|
|
use route_t;
|
|
|
|
public function run(): void
|
|
{
|
|
$this->get('/departments/daily-reports', function () {
|
|
// Require the user to be logged in
|
|
global $response;
|
|
$this->requirePermission('list_department_daily_reports');
|
|
// 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_DEPARTMENT_DAILY_REPORTS', 'Successfully listed departments daily reports');
|
|
$department_daily_reports_o = new department_daily_reports_o();
|
|
// Return the list of departments
|
|
$response->success(
|
|
$department_daily_reports_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',
|
|
'water_usage',
|
|
'notes',
|
|
'filled_by',
|
|
'created_at',
|
|
'department_id',
|
|
])
|
|
->listObjectsWithPaginationIfSet(
|
|
function ($department_report) use ($user) {
|
|
return [
|
|
'id' => (int)$department_report['id'],
|
|
'department_id' => (int)$department_report['department_id'],
|
|
'water_usage' => (int)$department_report['water_usage'],
|
|
'notes' => (string)$department_report['notes'],
|
|
'filled_by' => (int)$department_report['filled_by'],
|
|
'created_at' => (string)$department_report['created_at'],
|
|
];
|
|
},
|
|
// This makes sure that the user can only see reports from the departments they explicitly have access to
|
|
$department_daily_reports_o->forceRestrictFilters(
|
|
[
|
|
// This makes sure that the user can only see orders from the departments they explicitly have access to
|
|
'department_id' => $user->getGroup()->getDepartments(),
|
|
]
|
|
)
|
|
)
|
|
);
|
|
} else {
|
|
// Log the incident
|
|
(new logs_o())->add('departments', 'global', 1, 0, 'LIST_DEPARTMENT_DAILY_REPORTS', 'No user found, or invalid session');
|
|
// Return an error
|
|
$response->error('Invalid session', 400);
|
|
}
|
|
},
|
|
[
|
|
'list_department_daily_reports' => 'List the department daily reports, provided the user has access to the department',
|
|
'department_access_:id' => 'Access the department'
|
|
]
|
|
);
|
|
|
|
$this->get('/departments/daily-reports/get', function () {
|
|
// Require the user to be logged in
|
|
global $response;
|
|
$this->requirePermission('list_department_daily_reports');
|
|
// 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',
|
|
'date',
|
|
]
|
|
);
|
|
// Validate the department_id
|
|
self::requireType(
|
|
(int)self::getParameter('id'),
|
|
self::type_int()
|
|
);
|
|
// Require the department_id to be above 0
|
|
self::requireMinValue(
|
|
(int)self::getParameter('id'),
|
|
1
|
|
);
|
|
/** Date parameter */
|
|
// Validate the date
|
|
self::validateDateLocally();
|
|
// Determine if the user has access to the department
|
|
self::requireDepartmentAccess((int)self::getParameter('id'));
|
|
// Log the incident
|
|
(new logs_o())->add('departments', 'global', 1, $user->id, 'LIST_DEPARTMENT_DAILY_REPORTS', 'Successfully listed departments daily reports');
|
|
// Return the list of departments
|
|
$department_daily_reports_o = new department_daily_reports_o();
|
|
try {
|
|
$department_daily_report_latest = $department_daily_reports_o->selectDepartmentReportByDate(
|
|
(int)self::getParameter('id'),
|
|
(string)self::getParameter('date')
|
|
);
|
|
} catch (Exception $e) {
|
|
// Create a new department daily report if it does not exist
|
|
$department_daily_report_latest = $department_daily_reports_o->add(
|
|
(int)self::getParameter('id'),
|
|
0,
|
|
0,
|
|
'',
|
|
$user->id,
|
|
(string)self::getParameter('date')
|
|
);
|
|
}
|
|
$response->success(
|
|
[
|
|
'id' => (int)$department_daily_report_latest->id,
|
|
'department_id' => (int)$department_daily_report_latest->department_id->value(),
|
|
'water_usage' => (int)$department_daily_report_latest->water_usage->value(),
|
|
'notes' => (string)$department_daily_report_latest->notes->value(),
|
|
'filled_by' => (int)$department_daily_report_latest->filled_by->value(),
|
|
'created_at' => (string)$department_daily_report_latest->created_at->value(),
|
|
]
|
|
);
|
|
} else {
|
|
// Log the incident
|
|
(new logs_o())->add('departments', 'global', 1, 0, 'LIST_DEPARTMENT_DAILY_REPORTS', 'No user found, or invalid session');
|
|
// Return an error
|
|
$response->error('Invalid session', 400);
|
|
}
|
|
},
|
|
[
|
|
'list_department_daily_reports' => 'List the department daily reports, provided the user has access to the department',
|
|
'department_access_:id' => 'Access the department'
|
|
]
|
|
);
|
|
|
|
$this->post('/departments/daily-reports', function () {
|
|
// Require the user to be logged in
|
|
global $response;
|
|
$this->requirePermission('create_department_daily_reports');
|
|
// 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(
|
|
[
|
|
'department_id',
|
|
'water_usage',
|
|
'notes',
|
|
'date'
|
|
]
|
|
);
|
|
// Validate the department_id
|
|
self::requireType(
|
|
(int)self::getParameter('department_id'),
|
|
self::type_int()
|
|
);
|
|
// Require the department_id to be above 0
|
|
self::requireMinValue(
|
|
(int)self::getParameter('department_id'),
|
|
1
|
|
);
|
|
// Validate the water_usage
|
|
self::requireType(
|
|
(int)self::getParameter('water_usage'),
|
|
self::type_int()
|
|
);
|
|
// Require the water_usage to be at least 0
|
|
self::requireMinValue(
|
|
(int)self::getParameter('water_usage'),
|
|
0
|
|
);
|
|
// Validate the notes
|
|
self::requireType(
|
|
(string)self::getParameter('notes'),
|
|
self::type_string()
|
|
);
|
|
// Require the notes to be at least 0 characters long
|
|
self::requireMinLength(
|
|
'notes',
|
|
0
|
|
);
|
|
// Require the notes to be at most 4000 characters long
|
|
self::requireMaxLength(
|
|
'notes',
|
|
4000
|
|
);
|
|
// Validate the date
|
|
self::requireType(
|
|
(string)self::getParameter('date'),
|
|
self::type_string()
|
|
);
|
|
// Require the date to be at least 0 characters long
|
|
self::requireMinLength(
|
|
'date',
|
|
0
|
|
);
|
|
// Require the date to be at most 10 characters long
|
|
self::requireMaxLength(
|
|
'date',
|
|
10
|
|
);
|
|
// Validate the date format (YYYY-MM-DD)
|
|
self::requireDateFormat(
|
|
(string)self::getParameter('date'),
|
|
self::FORMAT_DATE()
|
|
);
|
|
// Determine if the user has access to the department
|
|
self::requireDepartmentAccess((int)self::getParameter('department_id'));
|
|
// Check if there is already a report for today
|
|
if ((new department_daily_reports_o())->hasReport((int)self::getParameter('department_id'), (string)self::getParameter('date'))) {
|
|
$response->error('There is already a report for today', 400);
|
|
}
|
|
// Log the incident
|
|
(new logs_o())->add('departments', 'global', 1, $user->id, 'CREATE_DEPARTMENT_DAILY_REPORTS', 'Successfully created department daily report');
|
|
// Return the list of departments
|
|
$response->success(
|
|
(new department_daily_reports_o())
|
|
->add(
|
|
(int)self::getParameter('department_id'),
|
|
(int)self::getParameter('water_usage'),
|
|
(int)0,
|
|
(string)self::getParameter('notes'),
|
|
$user->id
|
|
)->asArray()
|
|
);
|
|
} else {
|
|
// Log the incident
|
|
(new logs_o())->add('departments', 'global', 1, 0, 'CREATE_DEPARTMENT_DAILY_REPORTS', 'No user found, or invalid session');
|
|
// Return an error
|
|
$response->error('Invalid session', 400);
|
|
}
|
|
},
|
|
[
|
|
'create_department_daily_reports' => 'Create a new department daily report',
|
|
'department_access_:id' => 'Access the department'
|
|
]
|
|
);
|
|
|
|
$this->post('/departments/daily-reports/complaints', function () {
|
|
global $response;
|
|
$this->requirePermission('create_department_daily_report_complaints');
|
|
|
|
$user = (new authentication())->get_user();
|
|
if (!$user) {
|
|
(new logs_o())->add('departments', 'global', 1, 0, 'CREATE_DEPARTMENT_DAILY_REPORT_COMPLAINT', 'No user found, or invalid session');
|
|
$response->error('Invalid session', 400);
|
|
return;
|
|
}
|
|
|
|
self::requireParameters([
|
|
'department_id',
|
|
'wash_date',
|
|
'category',
|
|
'description',
|
|
]);
|
|
|
|
self::requireType(
|
|
(int)self::getParameter('department_id'),
|
|
self::type_int()
|
|
);
|
|
self::requireMinValue(
|
|
(int)self::getParameter('department_id'),
|
|
1
|
|
);
|
|
|
|
self::requireType(
|
|
self::getParameter('description'),
|
|
self::type_string()
|
|
);
|
|
self::requireMinLength(
|
|
'description',
|
|
1
|
|
);
|
|
self::requireMaxLength(
|
|
'description',
|
|
4000
|
|
);
|
|
|
|
$description = trim((string)self::getParameter('description'));
|
|
if ($description === '') {
|
|
$response->error('Description is required', 400);
|
|
return;
|
|
}
|
|
|
|
$wash_date = $this->requireComplaintWashDateParameter('wash_date');
|
|
if ($wash_date === null) {
|
|
$response->error('Wash date is required', 400);
|
|
return;
|
|
}
|
|
|
|
$category = $this->requireComplaintCategoryParameter('category');
|
|
if ($category === null) {
|
|
$response->error('Category is required', 400);
|
|
return;
|
|
}
|
|
|
|
$customer_number = null;
|
|
if (
|
|
self::isParametersSet(['customer_number'])
|
|
&& self::getParameter('customer_number') !== null
|
|
&& trim((string)self::getParameter('customer_number')) !== ''
|
|
) {
|
|
self::requireType(
|
|
(int)self::getParameter('customer_number'),
|
|
self::type_int()
|
|
);
|
|
self::requireMinValue(
|
|
(int)self::getParameter('customer_number'),
|
|
1
|
|
);
|
|
|
|
$customer_number = (int)self::getParameter('customer_number');
|
|
$customer = (new users_o())->getOrImportCustomerByCustomerNumber($customer_number);
|
|
if ($customer === false || !$customer->exists()) {
|
|
$response->error('Customer not found', 400);
|
|
return;
|
|
}
|
|
}
|
|
|
|
self::requireDepartmentAccess((int)self::getParameter('department_id'));
|
|
|
|
$complaint = $this->dailyReportComplaintsRepository()->addComplaint(
|
|
(int)self::getParameter('department_id'),
|
|
$customer_number,
|
|
$wash_date,
|
|
$category,
|
|
$description,
|
|
(int)$user->id
|
|
);
|
|
|
|
(new logs_o())->add('departments', 'global', 1, $user->id, 'CREATE_DEPARTMENT_DAILY_REPORT_COMPLAINT', 'Successfully created department daily report complaint');
|
|
|
|
$response->success(
|
|
$this->dailyReportComplaintsRepository()->parseComplaint($complaint->asArray())
|
|
);
|
|
},
|
|
[
|
|
'create_department_daily_report_complaints' => 'Create department daily report customer complaints',
|
|
'department_access_:id' => 'Access the department'
|
|
]
|
|
);
|
|
|
|
$this->get('/departments/daily-reports/complaints/customers', function () {
|
|
global $response;
|
|
|
|
$user = (new authentication())->get_user();
|
|
if (!$user) {
|
|
(new logs_o())->add('departments', 'global', 1, 0, 'LIST_DEPARTMENT_DAILY_REPORT_COMPLAINT_CUSTOMERS', 'No user found, or invalid session');
|
|
$response->error('Invalid session', 400);
|
|
return;
|
|
}
|
|
|
|
$has_create_permission = $this->hasPermission('create_department_daily_report_complaints');
|
|
$has_edit_permission = $this->hasPermission('edit_department_daily_report_complaints');
|
|
if (!$has_create_permission && !$has_edit_permission) {
|
|
$this->emitForbidden([
|
|
'create_department_daily_report_complaints',
|
|
'edit_department_daily_report_complaints',
|
|
]);
|
|
return;
|
|
}
|
|
|
|
self::requireParameters(['search']);
|
|
self::requireType(
|
|
self::getParameter('search'),
|
|
self::type_string()
|
|
);
|
|
|
|
$search = trim((string)self::getParameter('search'));
|
|
if (mb_strlen($search) < 2) {
|
|
$response->error('Search must be at least 2 characters', 400);
|
|
return;
|
|
}
|
|
|
|
$limit = 10;
|
|
if (
|
|
self::isParametersSet(['limit'])
|
|
&& self::getParameter('limit') !== null
|
|
&& trim((string)self::getParameter('limit')) !== ''
|
|
) {
|
|
self::requireType(
|
|
(int)self::getParameter('limit'),
|
|
self::type_int()
|
|
);
|
|
self::requireMinValue(
|
|
(int)self::getParameter('limit'),
|
|
1
|
|
);
|
|
self::requireMaxValue(
|
|
(int)self::getParameter('limit'),
|
|
20
|
|
);
|
|
|
|
$limit = (int)self::getParameter('limit');
|
|
}
|
|
|
|
try {
|
|
$result = $this->complaintCustomerSearchService()->listCustomers(1, $limit, $search, null);
|
|
$collection = is_array($result->collection ?? null) ? $result->collection : [];
|
|
} catch (\Throwable $throwable) {
|
|
$upstream_message = $this->sanitizeComplaintCustomerLookupUpstreamErrorMessage($throwable);
|
|
|
|
(new logs_o())->add(
|
|
'departments',
|
|
'global',
|
|
1,
|
|
$user->id,
|
|
'LIST_DEPARTMENT_DAILY_REPORT_COMPLAINT_CUSTOMERS_FAILED',
|
|
'Failed to search complaint customers from e-conomic: ' . $upstream_message
|
|
);
|
|
|
|
$response->error([
|
|
'message' => 'Failed to fetch complaint customers from e-conomic',
|
|
'upstream_message' => $upstream_message,
|
|
], 502);
|
|
return;
|
|
}
|
|
|
|
$matches = array_values(array_filter(array_map(
|
|
static function (mixed $customer): ?array {
|
|
$customer_number = 0;
|
|
$customer_name = null;
|
|
|
|
if (is_object($customer)) {
|
|
$customer_number = (int)($customer->customerNumber ?? 0);
|
|
$name = trim((string)($customer->name ?? ''));
|
|
$customer_name = $name === '' ? null : $name;
|
|
} elseif (is_array($customer)) {
|
|
$customer_number = (int)($customer['customerNumber'] ?? 0);
|
|
$name = trim((string)($customer['name'] ?? ''));
|
|
$customer_name = $name === '' ? null : $name;
|
|
}
|
|
|
|
if ($customer_number <= 0) {
|
|
return null;
|
|
}
|
|
|
|
return [
|
|
'customer_number' => $customer_number,
|
|
'customer_name' => $customer_name,
|
|
];
|
|
},
|
|
$collection
|
|
)));
|
|
|
|
(new logs_o())->add('departments', 'global', 1, $user->id, 'LIST_DEPARTMENT_DAILY_REPORT_COMPLAINT_CUSTOMERS', 'Successfully searched complaint customers');
|
|
|
|
$response->success($matches);
|
|
},
|
|
[
|
|
'create_department_daily_report_complaints' => 'Search customers when creating department daily report customer complaints',
|
|
'edit_department_daily_report_complaints' => 'Search customers when editing department daily report customer complaints',
|
|
]
|
|
);
|
|
|
|
$this->get('/departments/daily-reports/complaints', function () {
|
|
global $response;
|
|
$this->requirePermission('list_department_daily_report_complaints');
|
|
|
|
$user = (new authentication())->get_user();
|
|
if (!$user) {
|
|
(new logs_o())->add('departments', 'global', 1, 0, 'LIST_DEPARTMENT_DAILY_REPORT_COMPLAINTS', 'No user found, or invalid session');
|
|
$response->error('Invalid session', 400);
|
|
return;
|
|
}
|
|
|
|
$repository = $this->dailyReportComplaintsRepository();
|
|
|
|
if (self::isParametersSet(['id'])) {
|
|
self::requireType(
|
|
(int)self::getParameter('id'),
|
|
self::type_int()
|
|
);
|
|
self::requireMinValue(
|
|
(int)self::getParameter('id'),
|
|
1
|
|
);
|
|
|
|
$complaint = $repository->select((int)self::getParameter('id'));
|
|
if (!$complaint->exists()) {
|
|
$response->error('Complaint not found', 404);
|
|
return;
|
|
}
|
|
|
|
self::requireDepartmentAccess((int)$complaint->department_id->value());
|
|
|
|
(new logs_o())->add('departments', 'global', 1, $user->id, 'GET_DEPARTMENT_DAILY_REPORT_COMPLAINT', 'Successfully retrieved department daily report complaint');
|
|
|
|
$response->success($repository->parseComplaint($complaint->asArray()));
|
|
return;
|
|
}
|
|
|
|
(new logs_o())->add('departments', 'global', 1, $user->id, 'LIST_DEPARTMENT_DAILY_REPORT_COMPLAINTS', 'Successfully listed department daily report complaints');
|
|
|
|
$response->success(
|
|
$repository
|
|
->setSearchableFields([
|
|
'id',
|
|
'department_id',
|
|
'customer_number',
|
|
'wash_date',
|
|
'category',
|
|
'description',
|
|
'created_by',
|
|
'created_at',
|
|
])
|
|
->listObjectsWithPaginationIfSet(
|
|
fn (array $complaint): array => $repository->parseComplaint($complaint),
|
|
$repository->forceRestrictFilters([
|
|
'department_id' => $user->getGroup()->getDepartments(),
|
|
])
|
|
)
|
|
);
|
|
},
|
|
[
|
|
'list_department_daily_report_complaints' => 'List department daily report customer complaints'
|
|
]
|
|
);
|
|
|
|
$this->put('/departments/daily-reports/complaints', function () {
|
|
global $response;
|
|
$this->requirePermission('edit_department_daily_report_complaints');
|
|
|
|
$user = (new authentication())->get_user();
|
|
if (!$user) {
|
|
(new logs_o())->add('departments', 'global', 1, 0, 'EDIT_DEPARTMENT_DAILY_REPORT_COMPLAINT', 'No user found, or invalid session');
|
|
$response->error('Invalid session', 400);
|
|
return;
|
|
}
|
|
|
|
self::requireParameters(['id']);
|
|
self::requireType(
|
|
(int)self::getParameter('id'),
|
|
self::type_int()
|
|
);
|
|
self::requireMinValue(
|
|
(int)self::getParameter('id'),
|
|
1
|
|
);
|
|
|
|
$complaint = $this->dailyReportComplaintsRepository()->select((int)self::getParameter('id'));
|
|
if (!$complaint->exists()) {
|
|
$response->error('Complaint not found', 404);
|
|
return;
|
|
}
|
|
|
|
self::requireDepartmentAccess((int)$complaint->department_id->value());
|
|
|
|
$updates = [];
|
|
|
|
if (self::isParametersSet(['department_id'])) {
|
|
self::requireType(
|
|
(int)self::getParameter('department_id'),
|
|
self::type_int()
|
|
);
|
|
self::requireMinValue(
|
|
(int)self::getParameter('department_id'),
|
|
1
|
|
);
|
|
|
|
$department = (new departments_o())->select((int)self::getParameter('department_id'));
|
|
if (!$department->exists()) {
|
|
$response->error('Department not found', 400);
|
|
return;
|
|
}
|
|
|
|
self::requireDepartmentAccess((int)self::getParameter('department_id'));
|
|
|
|
$updates['department_id'] = (int)self::getParameter('department_id');
|
|
}
|
|
|
|
if (self::isParametersSet(['description'])) {
|
|
self::requireType(
|
|
self::getParameter('description'),
|
|
self::type_string()
|
|
);
|
|
self::requireMinLength(
|
|
'description',
|
|
1
|
|
);
|
|
self::requireMaxLength(
|
|
'description',
|
|
4000
|
|
);
|
|
|
|
$description = trim((string)self::getParameter('description'));
|
|
if ($description === '') {
|
|
$response->error('Description is required', 400);
|
|
return;
|
|
}
|
|
|
|
$updates['description'] = $description;
|
|
}
|
|
|
|
if (self::isParametersSet(['wash_date'])) {
|
|
$wash_date = $this->requireComplaintWashDateParameter('wash_date');
|
|
if ($wash_date === null) {
|
|
$response->error('Wash date is required', 400);
|
|
return;
|
|
}
|
|
|
|
$updates['wash_date'] = $wash_date;
|
|
}
|
|
|
|
if (self::isParametersSet(['category'])) {
|
|
$category = $this->requireComplaintCategoryParameter('category');
|
|
if ($category === null) {
|
|
$response->error('Category is required', 400);
|
|
return;
|
|
}
|
|
|
|
$updates['category'] = $category;
|
|
}
|
|
|
|
if (self::isParametersSet(['customer_number'])) {
|
|
$raw_customer_number = self::getParameter('customer_number');
|
|
|
|
if ($raw_customer_number === null || trim((string)$raw_customer_number) === '') {
|
|
$updates['customer_number'] = null;
|
|
} else {
|
|
self::requireType(
|
|
(int)$raw_customer_number,
|
|
self::type_int()
|
|
);
|
|
self::requireMinValue(
|
|
(int)$raw_customer_number,
|
|
1
|
|
);
|
|
|
|
$customer_number = (int)$raw_customer_number;
|
|
$customer = (new users_o())->getOrImportCustomerByCustomerNumber($customer_number);
|
|
if ($customer === false || !$customer->exists()) {
|
|
$response->error('Customer not found', 400);
|
|
return;
|
|
}
|
|
|
|
$updates['customer_number'] = $customer_number;
|
|
}
|
|
}
|
|
|
|
if ($updates === []) {
|
|
$response->success(
|
|
$this->dailyReportComplaintsRepository()->parseComplaint($complaint->asArray())
|
|
);
|
|
return;
|
|
}
|
|
|
|
$complaint->update($updates);
|
|
|
|
(new logs_o())->add('departments', 'global', 1, $user->id, 'EDIT_DEPARTMENT_DAILY_REPORT_COMPLAINT', 'Successfully edited department daily report complaint');
|
|
|
|
$response->success(
|
|
$this->dailyReportComplaintsRepository()->parseComplaint($complaint->asArray())
|
|
);
|
|
},
|
|
[
|
|
'edit_department_daily_report_complaints' => 'Edit department daily report customer complaints'
|
|
]
|
|
);
|
|
|
|
$this->delete('/departments/daily-reports/complaints', function () {
|
|
global $response;
|
|
$this->requirePermission('delete_department_daily_report_complaints');
|
|
|
|
$user = (new authentication())->get_user();
|
|
if (!$user) {
|
|
(new logs_o())->add('departments', 'global', 1, 0, 'DELETE_DEPARTMENT_DAILY_REPORT_COMPLAINT', 'No user found, or invalid session');
|
|
$response->error('Invalid session', 400);
|
|
return;
|
|
}
|
|
|
|
self::requireParameters(['id']);
|
|
self::requireType(
|
|
(int)self::getParameter('id'),
|
|
self::type_int()
|
|
);
|
|
self::requireMinValue(
|
|
(int)self::getParameter('id'),
|
|
1
|
|
);
|
|
|
|
$complaint = $this->dailyReportComplaintsRepository()->select((int)self::getParameter('id'));
|
|
if (!$complaint->exists()) {
|
|
$response->error('Complaint not found', 404);
|
|
return;
|
|
}
|
|
|
|
self::requireDepartmentAccess((int)$complaint->department_id->value());
|
|
|
|
$complaint->deletePermanently();
|
|
|
|
(new logs_o())->add('departments', 'global', 1, $user->id, 'DELETE_DEPARTMENT_DAILY_REPORT_COMPLAINT', 'Successfully deleted department daily report complaint');
|
|
|
|
$response->success([
|
|
'message' => 'Complaint deleted successfully',
|
|
]);
|
|
},
|
|
[
|
|
'delete_department_daily_report_complaints' => 'Delete department daily report customer complaints'
|
|
]
|
|
);
|
|
|
|
$this->put('/departments/daily-reports', function () {
|
|
// Require the user to be logged in
|
|
global $response;
|
|
$this->requirePermission('update_department_daily_reports');
|
|
// 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',
|
|
]
|
|
);
|
|
// Validate the id
|
|
self::requireType(
|
|
(int)self::getParameter('id'),
|
|
self::type_int()
|
|
);
|
|
// Require the department_id to be above 0
|
|
self::requireMinValue(
|
|
(int)self::getParameter('id'),
|
|
1
|
|
);
|
|
// Check which fields are set
|
|
if (self::isParametersSet(['water_usage'])) {
|
|
// Validate the water_usage
|
|
self::requireType(
|
|
(int)self::getParameter('water_usage'),
|
|
self::type_int()
|
|
);
|
|
// Require the water_usage to be at least 0
|
|
self::requireMinValue(
|
|
(int)self::getParameter('water_usage'),
|
|
0
|
|
);
|
|
}
|
|
if (self::isParametersSet(['notes'])) {
|
|
// Validate the notes
|
|
self::requireType(
|
|
(string)self::getParameter('notes'),
|
|
self::type_string()
|
|
);
|
|
// Require the notes to be at least 0 characters long
|
|
self::requireMinLength(
|
|
(string)self::getParameter('notes'),
|
|
0
|
|
);
|
|
// Require the notes to be at most 4000 characters long
|
|
self::requireMaxLength(
|
|
(string)self::getParameter('notes'),
|
|
4000
|
|
);
|
|
}
|
|
// Get the department from the daily report
|
|
$department_daily_report = new department_daily_reports_o();
|
|
$department_daily_report->select((int)self::getParameter('id'));
|
|
if (!$department_daily_report->exists()) {
|
|
$response->error('Department daily report not found', 404);
|
|
}
|
|
// Get the department
|
|
$department = (new departments_o())->select((int)$department_daily_report->department_id->value());
|
|
if (!$department->exists()) {
|
|
$response->error('Department not found', 404);
|
|
}
|
|
// Require the user to have access to the department
|
|
self::requireDepartmentAccess((int)$department->id);
|
|
// Check the parameters that are set
|
|
if (self::isParametersSet(['water_usage'])) {
|
|
$department_daily_report->water_usage->set((int)self::getParameter('water_usage'));
|
|
}
|
|
if (self::isParametersSet(['notes'])) {
|
|
$department_daily_report->notes->set((string)self::getParameter('notes'));
|
|
}
|
|
// Log the incident
|
|
(new logs_o())->add('departments', 'global', 1, $user->id, 'UPDATE_DEPARTMENT_DAILY_REPORTS', 'Successfully updated department daily report');
|
|
// Return the department daily report
|
|
$response->success($department_daily_report->asArray());
|
|
} else {
|
|
// Log the incident
|
|
(new logs_o())->add('departments', 'global', 1, 0, 'UPDATE_DEPARTMENT_DAILY_REPORTS', 'No user found, or invalid session');
|
|
// Return an error
|
|
$response->error('Invalid session', 400);
|
|
}
|
|
},
|
|
[
|
|
'update_department_daily_reports' => 'Update a department daily report',
|
|
'department_access_:id' => 'Access the department'
|
|
]
|
|
);
|
|
|
|
$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');
|
|
$this->requirePermission('list_bookings');
|
|
|
|
$user = (new authentication())->get_user();
|
|
if (!$user) {
|
|
(new logs_o())->add('departments', 'global', 1, 0, 'LIST_DEPARTMENT_DAILY_REPORTS_OVERVIEW', 'No user found, or invalid session');
|
|
$response->error('Invalid session', 400);
|
|
return;
|
|
}
|
|
|
|
self::requireParameters([
|
|
'date',
|
|
'department_ids',
|
|
]);
|
|
|
|
self::validateDateLocally();
|
|
$date_to = $this->getDate_to();
|
|
$department_ids = $this->normalizeDepartmentIdsParameter(self::getParameter('department_ids'));
|
|
|
|
if ($department_ids === []) {
|
|
$response->error('At least one department_id must be provided', 400);
|
|
return;
|
|
}
|
|
|
|
foreach ($department_ids as $department_id) {
|
|
self::requireDepartmentAccess($department_id);
|
|
}
|
|
|
|
(new logs_o())->add('departments', 'global', 1, $user->id, 'LIST_DEPARTMENT_DAILY_REPORTS_OVERVIEW', 'Successfully listed departments daily reports overview');
|
|
|
|
$response->success(
|
|
$this->buildDailyReportOverview(
|
|
$department_ids,
|
|
(string)self::getParameter('date'),
|
|
$date_to
|
|
)
|
|
);
|
|
},
|
|
[
|
|
'list_department_daily_reports' => 'List the department daily reports overview for departments the user can access',
|
|
'list_bookings' => 'List order bookings for the overview bookings tile',
|
|
'department_access_:id' => 'Access the department'
|
|
]
|
|
);
|
|
|
|
$this->get('/departments/daily-reports/product-count', function () {
|
|
// Require the user to be logged in
|
|
global $response;
|
|
$this->requirePermission('list_department_daily_reports');
|
|
// 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(
|
|
[
|
|
'date',
|
|
'department_id',
|
|
'product_id',
|
|
// 'date-to' (Optional)
|
|
]
|
|
);
|
|
/** date parameter */
|
|
// Validate the date
|
|
self::requireType(
|
|
(string)self::getParameter('date'),
|
|
self::type_string()
|
|
);
|
|
// Require the date to be at least 0 characters long
|
|
self::requireMinLength(
|
|
'date',
|
|
0
|
|
);
|
|
// Require the date to be at most 10 characters long
|
|
self::requireMaxLength(
|
|
'date',
|
|
10
|
|
);
|
|
// Validate the date format (YYYY-MM-DD)
|
|
self::requireDateFormat(
|
|
(string)self::getParameter('date'),
|
|
'Y-m-d'
|
|
);
|
|
/** date-to parameter (Optional, defaults to date parameter if not set) */
|
|
$date_to = $this->getDate_to();
|
|
// Validate the department_id
|
|
self::requireType(
|
|
(int)self::getParameter('department_id'),
|
|
self::type_int()
|
|
);
|
|
// Require the department_id to be above 0
|
|
self::requireMinValue(
|
|
(int)self::getParameter('department_id'),
|
|
1
|
|
);
|
|
// Validate the product_id
|
|
self::requireType(
|
|
(int)self::getParameter('product_id'),
|
|
self::type_int()
|
|
);
|
|
// Require the product_id to be above 0
|
|
self::requireMinValue(
|
|
(int)self::getParameter('product_id'),
|
|
1
|
|
);
|
|
// Determine if the user has access to the department
|
|
self::requireDepartmentAccess((int)self::getParameter('department_id'));
|
|
|
|
$outside_hours_service = $this->outsideHoursStatisticsService();
|
|
// Log the incident
|
|
(new logs_o())->add('departments', 'global', 1, $user->id, 'LIST_DEPARTMENT_DAILY_REPORTS_PRODUCTS', 'Successfully listed departments daily reports products');
|
|
// Return the list of products sold on the selected date
|
|
$response->success(
|
|
[
|
|
'quantity' =>
|
|
(new department_daily_reports_o())
|
|
->getProductsSoldOnDate(
|
|
(string)self::getParameter('date'),
|
|
(int)self::getParameter('department_id'),
|
|
(int)self::getParameter('product_id'),
|
|
$date_to
|
|
),
|
|
'date' => (string)self::getParameter('date'),
|
|
'date_to' => $date_to,
|
|
'department_id' => (int)self::getParameter('department_id'),
|
|
'product_id' => (int)self::getParameter('product_id'),
|
|
'out_of' => (int)(new departments_o())->getTotalMaxAddonsInDepartment(
|
|
[(int)self::getParameter('product_id')],
|
|
(string)self::getParameter('date'),
|
|
$date_to,
|
|
(int)self::getParameter('department_id')
|
|
),
|
|
// This is the amount of products sold on the selected date, that this product could have been sold as a part of. (Addons)
|
|
//'out_of' => (int)(new department_daily_reports_o())->getProductsSoldWithAddonApplicable(
|
|
// (string)self::getParameter('date'),
|
|
// (int)self::getParameter('department_id'),
|
|
// (int)self::getParameter('product_id'),
|
|
// $date_to
|
|
//)
|
|
]
|
|
);
|
|
} else {
|
|
// Log the incident
|
|
(new logs_o())->add('departments', 'global', 1, 0, 'LIST_DEPARTMENT_DAILY_REPORTS_PRODUCTS', 'No user found, or invalid session');
|
|
// Return an error
|
|
$response->error('Invalid session', 400);
|
|
}
|
|
},
|
|
[
|
|
'list_department_daily_reports' => 'List the department daily reports, provided the user has access to the department',
|
|
'department_access_:id' => 'Access the department'
|
|
]
|
|
);
|
|
|
|
$this->get('/departments/daily-reports/transaction-count', function () {
|
|
// Require the user to be logged in
|
|
global $response;
|
|
$this->requirePermission('list_department_daily_reports');
|
|
// 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(
|
|
[
|
|
'date',
|
|
'department_id'
|
|
]
|
|
);
|
|
// Validate the date
|
|
self::validateDateLocally();
|
|
// Get the date to parameter, or return the date parameter if not set
|
|
$date_to = $this->getDate_to();
|
|
// Validate the department_id
|
|
self::requireType(
|
|
(int)self::getParameter('department_id'),
|
|
self::type_int()
|
|
);
|
|
// Require the department_id to be above 0
|
|
self::requireMinValue(
|
|
(int)self::getParameter('department_id'),
|
|
1
|
|
);
|
|
// Determine if the user has access to the department
|
|
self::requireDepartmentAccess((int)self::getParameter('department_id'));
|
|
|
|
$outside_hours_service = $this->outsideHoursStatisticsService();
|
|
// Log the incident
|
|
(new logs_o())->add('departments', 'global', 1, $user->id, 'LIST_DEPARTMENT_DAILY_REPORTS_PRODUCTS', 'Successfully listed departments daily reports products');
|
|
// Return the list of products sold on the selected date
|
|
$response->success(
|
|
[
|
|
'quantity' =>
|
|
(new department_daily_reports_o())
|
|
->getTransactionsOnDateCount(
|
|
(string)self::getParameter('date'),
|
|
(int)self::getParameter('department_id'),
|
|
$date_to
|
|
),
|
|
'products' =>
|
|
(new department_daily_reports_o())
|
|
->getTransactionProductsOnDateCount(
|
|
(string)self::getParameter('date'),
|
|
(int)self::getParameter('department_id'),
|
|
$date_to
|
|
),
|
|
'earnings' =>
|
|
(new department_daily_reports_o())
|
|
->getTransactionsOnDateEarnings(
|
|
(string)self::getParameter('date'),
|
|
(int)self::getParameter('department_id'),
|
|
$date_to
|
|
),
|
|
'washes' =>
|
|
(new department_daily_reports_o())
|
|
->getTransactionsOnDateWashesCount(
|
|
(string)self::getParameter('date'),
|
|
(int)self::getParameter('department_id'),
|
|
$date_to
|
|
),
|
|
'water_usage' =>
|
|
(new department_daily_reports_o())
|
|
->getTransactionsOnDateWaterUsage(
|
|
(string)self::getParameter('date'),
|
|
(int)self::getParameter('department_id'),
|
|
$date_to
|
|
),
|
|
'date' => (string)self::getParameter('date'),
|
|
'department_id' => (int)self::getParameter('department_id'),
|
|
'date_to' => $date_to,
|
|
'outside_hours' => $outside_hours_service->getSummary(
|
|
(string)self::getParameter('date'),
|
|
[(int)self::getParameter('department_id')],
|
|
$date_to
|
|
),
|
|
]
|
|
);
|
|
} else {
|
|
// Log the incident
|
|
(new logs_o())->add('departments', 'global', 1, 0, 'LIST_DEPARTMENT_DAILY_REPORTS_PRODUCTS', 'No user found, or invalid session');
|
|
// Return an error
|
|
$response->error('Invalid session', 400);
|
|
}
|
|
},
|
|
[
|
|
'list_department_daily_reports' => 'List the department daily reports, provided the user has access to the department',
|
|
'department_access_:id' => 'Access the department'
|
|
]
|
|
);
|
|
|
|
$this->get('/departments/daily-reports/outside-hours-trend', function () {
|
|
global $response;
|
|
$this->requirePermission('list_department_daily_reports');
|
|
|
|
$user = (new authentication())->get_user();
|
|
if (!$user) {
|
|
(new logs_o())->add('departments', 'global', 1, 0, 'LIST_DEPARTMENT_DAILY_REPORTS_OUTSIDE_HOURS_TREND', 'No user found, or invalid session');
|
|
$response->error('Invalid session', 400);
|
|
return;
|
|
}
|
|
|
|
self::requireParameters([
|
|
'date',
|
|
'date_to',
|
|
'department_ids',
|
|
]);
|
|
|
|
self::validateDateLocally();
|
|
self::requireDateFormat(
|
|
(string)self::getParameter('date_to'),
|
|
'Y-m-d'
|
|
);
|
|
|
|
$department_ids = $this->normalizeDepartmentIdsParameter(self::getParameter('department_ids'));
|
|
if ($department_ids === []) {
|
|
$response->error('At least one department_id must be provided', 400);
|
|
return;
|
|
}
|
|
|
|
foreach ($department_ids as $department_id) {
|
|
self::requireDepartmentAccess($department_id);
|
|
}
|
|
|
|
(new logs_o())->add('departments', 'global', 1, $user->id, 'LIST_DEPARTMENT_DAILY_REPORTS_OUTSIDE_HOURS_TREND', 'Successfully listed outside hours trend');
|
|
|
|
$response->success(
|
|
$this->outsideHoursStatisticsService()->getTrend(
|
|
(string)self::getParameter('date'),
|
|
(string)self::getParameter('date_to'),
|
|
$department_ids
|
|
)
|
|
);
|
|
},
|
|
[
|
|
'list_department_daily_reports' => 'List outside-hours trend for departments the user can access',
|
|
'department_access_:id' => 'Access the department'
|
|
]
|
|
);
|
|
|
|
$this->get('/departments/daily-reports/bookings-count', function () {
|
|
// Require the user to be logged in
|
|
global $response;
|
|
$this->requirePermission('list_bookings');
|
|
// 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(
|
|
[
|
|
'date',
|
|
'department_id'
|
|
]
|
|
);
|
|
// Validate the date
|
|
self::requireType(
|
|
(string)self::getParameter('date'),
|
|
self::type_string()
|
|
);
|
|
// Require the date to be at least 0 characters long
|
|
self::requireMinLength(
|
|
'date',
|
|
0
|
|
);
|
|
// Require the date to be at most 10 characters long
|
|
self::requireMaxLength(
|
|
'date',
|
|
10
|
|
);
|
|
// Validate the date format (YYYY-MM-DD)
|
|
self::requireDateFormat(
|
|
(string)self::getParameter('date'),
|
|
'Y-m-d'
|
|
);
|
|
// Validate the department_id
|
|
self::requireType(
|
|
(int)self::getParameter('department_id'),
|
|
self::type_int()
|
|
);
|
|
// Require the department_id to be above 0
|
|
self::requireMinValue(
|
|
(int)self::getParameter('department_id'),
|
|
1
|
|
);
|
|
// Determine if the user has access to the department
|
|
self::requireDepartmentAccess((int)self::getParameter('department_id'));
|
|
|
|
// Log the incident
|
|
(new logs_o())->add('departments', 'global', 1, $user->id, 'LIST_DEPARTMENT_DAILY_REPORTS_PRODUCTS', 'Successfully listed departments daily reports products');
|
|
// Return the list of products sold on the selected date
|
|
$response->success(
|
|
[
|
|
'pending' =>
|
|
(new department_daily_reports_o())
|
|
->getBookingsOnDateCount(
|
|
(string)self::getParameter('date'),
|
|
(int)self::getParameter('department_id'),
|
|
'pending'
|
|
),
|
|
'completed' =>
|
|
(new department_daily_reports_o())
|
|
->getBookingsOnDateCount(
|
|
(string)self::getParameter('date'),
|
|
(int)self::getParameter('department_id'),
|
|
'completed'
|
|
),
|
|
'cancelled' =>
|
|
(new department_daily_reports_o())
|
|
->getBookingsOnDateCount(
|
|
(string)self::getParameter('date'),
|
|
(int)self::getParameter('department_id'),
|
|
'cancelled'
|
|
),
|
|
|
|
]
|
|
);
|
|
} else {
|
|
// Log the incident
|
|
(new logs_o())->add('departments', 'global', 1, 0, 'LIST_DEPARTMENT_DAILY_REPORTS_PRODUCTS', 'No user found, or invalid session');
|
|
// Return an error
|
|
$response->error('Invalid session', 400);
|
|
}
|
|
},
|
|
[
|
|
'list_department_daily_reports' => 'List the department daily reports, provided the user has access to the department',
|
|
'department_access_:id' => 'Access the department'
|
|
]
|
|
);
|
|
}
|
|
|
|
private function requireComplaintWashDateParameter(string $parameter_name): ?string
|
|
{
|
|
self::requireType(
|
|
self::getParameter($parameter_name),
|
|
self::type_string()
|
|
);
|
|
self::requireMinLength(
|
|
$parameter_name,
|
|
1
|
|
);
|
|
self::requireMaxLength(
|
|
$parameter_name,
|
|
10
|
|
);
|
|
|
|
$wash_date = trim((string)self::getParameter($parameter_name));
|
|
if ($wash_date === '') {
|
|
return null;
|
|
}
|
|
|
|
self::requireDateFormat(
|
|
$wash_date,
|
|
self::FORMAT_DATE()
|
|
);
|
|
|
|
return $wash_date;
|
|
}
|
|
|
|
private function requireComplaintCategoryParameter(string $parameter_name): ?string
|
|
{
|
|
self::requireType(
|
|
self::getParameter($parameter_name),
|
|
self::type_string()
|
|
);
|
|
self::requireMinLength(
|
|
$parameter_name,
|
|
1
|
|
);
|
|
self::requireMaxLength(
|
|
$parameter_name,
|
|
64
|
|
);
|
|
|
|
$category = trim((string)self::getParameter($parameter_name));
|
|
if ($category === '') {
|
|
return null;
|
|
}
|
|
|
|
if (!department_daily_report_complaints_o::isValidCategory($category)) {
|
|
global $response;
|
|
$response->error('Invalid complaint category', 400);
|
|
}
|
|
|
|
return $category;
|
|
}
|
|
|
|
/**
|
|
* Get the date_to parameter, or return the date parameter if not set
|
|
* @return string
|
|
* @throws Exception if the date is set, but invalid
|
|
* @see validateDateLocally
|
|
* @see requireType
|
|
* @see requireMinLength
|
|
* @see requireMaxLength
|
|
* @see requireDateFormat
|
|
*/
|
|
function getDate_to(): string
|
|
{
|
|
$date_to = (string)self::getParameter('date');
|
|
if (self::isParametersSet(['date_to'])) {
|
|
// Validate the date
|
|
self::validateDateLocally('date_to');
|
|
$date_to = (string)self::getParameter('date_to');
|
|
}
|
|
return $date_to;
|
|
}
|
|
|
|
/**
|
|
* Validate a date locally without using requireType and other functions that would throw an error
|
|
* This is to avoid repeating the same code multiple times
|
|
* @param string $parameterName The name of the parameter to validate
|
|
* @throws Exception if the date is invalid
|
|
* @see requireType
|
|
* @see requireMinLength
|
|
* @see requireMaxLength
|
|
* @see requireDateFormat
|
|
* @see getDate_to
|
|
*/
|
|
private function validateDateLocally(string $parameterName = 'date'): void
|
|
{
|
|
// Require the date to be at least 0 characters long
|
|
self::requireMinLength(
|
|
$parameterName,
|
|
0
|
|
);
|
|
// Require the date to be at most 10 characters long
|
|
self::requireMaxLength(
|
|
$parameterName,
|
|
10
|
|
);
|
|
// Validate the date
|
|
self::requireDateFormat(
|
|
(string)self::getParameter($parameterName),
|
|
self::FORMAT_DATE()
|
|
);
|
|
}
|
|
|
|
/**
|
|
* @param array<int> $department_ids
|
|
* @return array{
|
|
* department_ids:array<int>,
|
|
* date:string,
|
|
* date_to:string,
|
|
* metrics:array<string,array<string,mixed>>,
|
|
* products:array<int,array<string,mixed>>
|
|
* }
|
|
* @throws Exception
|
|
*/
|
|
private function buildDailyReportOverview(array $department_ids, string $date, string $date_to): array
|
|
{
|
|
$repository = $this->dailyReportRepository();
|
|
$transaction_summary = $repository->getTransactionSummaryForDepartments($date, $department_ids, $date_to);
|
|
$booking_summary = $repository->getBookingSummaryForDepartments($date, $department_ids, $date_to);
|
|
|
|
$product_definitions = $this->getDailyReportProductDefinitions();
|
|
$product_summary_lookup = $repository->getProductOverviewForDepartments(
|
|
$date,
|
|
$department_ids,
|
|
array_column($product_definitions, 'product_id'),
|
|
$date_to
|
|
);
|
|
|
|
$complaints_metric = $this->buildComplaintsMetric($department_ids, $date, $date_to);
|
|
|
|
$night_wash_metric = $this->buildNightWashMetric($department_ids, $date, $date_to);
|
|
|
|
$overtime_metric = $this->buildOvertimeMetric($department_ids, $date, $date_to);
|
|
|
|
return $this->assembleDailyReportOverview(
|
|
$department_ids,
|
|
$date,
|
|
$date_to,
|
|
$transaction_summary,
|
|
$booking_summary,
|
|
$product_definitions,
|
|
$product_summary_lookup,
|
|
$complaints_metric,
|
|
$night_wash_metric,
|
|
$overtime_metric
|
|
);
|
|
}
|
|
|
|
/**
|
|
* @param array<int> $department_ids
|
|
* @param array{quantity:int,products:int,earnings:int,washes:int,water_usage:int} $transaction_summary
|
|
* @param array{completed:int,total:int} $booking_summary
|
|
* @param array<int,array{product_id:int,slug:string,title:string}> $product_definitions
|
|
* @param array<int,array{product_id:int,quantity:int,out_of:int}> $product_summary_lookup
|
|
* @param array<string,mixed> $complaints_metric
|
|
* @param array<string,mixed> $night_wash_metric
|
|
* @param array<string,mixed> $overtime_metric
|
|
* @return array{
|
|
* department_ids:array<int>,
|
|
* date:string,
|
|
* date_to:string,
|
|
* metrics:array<string,array<string,mixed>>,
|
|
* products:array<int,array<string,mixed>>
|
|
* }
|
|
*/
|
|
private function assembleDailyReportOverview(
|
|
array $department_ids,
|
|
string $date,
|
|
string $date_to,
|
|
array $transaction_summary,
|
|
array $booking_summary,
|
|
array $product_definitions,
|
|
array $product_summary_lookup,
|
|
array $complaints_metric,
|
|
array $night_wash_metric,
|
|
array $overtime_metric
|
|
): array {
|
|
$products = [];
|
|
foreach ($product_definitions as $definition) {
|
|
$product_id = (int)$definition['product_id'];
|
|
$product_summary = $product_summary_lookup[$product_id] ?? [
|
|
'product_id' => $product_id,
|
|
'quantity' => 0,
|
|
'out_of' => 0,
|
|
];
|
|
|
|
$products[] = [
|
|
'product_id' => $product_id,
|
|
'slug' => (string)$definition['slug'],
|
|
'title' => (string)$definition['title'],
|
|
'state' => 'ready',
|
|
'value' => (int)($product_summary['quantity'] ?? 0),
|
|
'out_of' => (int)($product_summary['out_of'] ?? 0),
|
|
];
|
|
}
|
|
|
|
return [
|
|
'department_ids' => array_values(array_map('intval', $department_ids)),
|
|
'date' => $date,
|
|
'date_to' => $date_to,
|
|
'metrics' => [
|
|
'bookings' => $this->metricPayload(
|
|
(int)($booking_summary['completed'] ?? 0),
|
|
(int)($booking_summary['total'] ?? 0)
|
|
),
|
|
'complaints' => $complaints_metric,
|
|
'night_washes' => $night_wash_metric,
|
|
'revenue' => $this->metricPayload((int)($transaction_summary['earnings'] ?? 0)),
|
|
'washes' => $this->metricPayload((int)($transaction_summary['washes'] ?? 0)),
|
|
'products_sold' => $this->metricPayload((int)($transaction_summary['products'] ?? 0)),
|
|
'transactions' => $this->metricPayload((int)($transaction_summary['quantity'] ?? 0)),
|
|
'water_usage' => $this->metricPayload((int)($transaction_summary['water_usage'] ?? 0)),
|
|
'overtime' => $overtime_metric,
|
|
],
|
|
'products' => $products,
|
|
];
|
|
}
|
|
|
|
/**
|
|
* @param mixed $department_ids
|
|
* @return array<int>
|
|
*/
|
|
private function normalizeDepartmentIdsParameter(mixed $department_ids): array
|
|
{
|
|
$normalized = [];
|
|
$appendDepartmentIds = function (mixed $value) use (&$appendDepartmentIds, &$normalized): void {
|
|
if (is_array($value)) {
|
|
foreach ($value as $nested_value) {
|
|
$appendDepartmentIds($nested_value);
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (is_string($value) && str_contains($value, ',')) {
|
|
foreach (explode(',', $value) as $segment) {
|
|
$appendDepartmentIds(trim($segment));
|
|
}
|
|
return;
|
|
}
|
|
|
|
$id = (int)$value;
|
|
if ($id > 0) {
|
|
$normalized[$id] = $id;
|
|
}
|
|
};
|
|
|
|
$appendDepartmentIds($department_ids);
|
|
|
|
return array_values($normalized);
|
|
}
|
|
|
|
/**
|
|
* @return array<int,array{product_id:int,slug:string,title:string}>
|
|
*/
|
|
private function getDailyReportProductDefinitions(): array
|
|
{
|
|
return [
|
|
['product_id' => 24, 'slug' => 'spot-free-lastbil', 'title' => 'Spot Free (Lastbil)'],
|
|
['product_id' => 25, 'slug' => 'faelg-flex', 'title' => 'Fælg flex pr. enhed'],
|
|
['product_id' => 27, 'slug' => 'extraordinary-10-min', 'title' => 'Ekstraordinær pr. 10 min inkl. kemi'],
|
|
['product_id' => 26, 'slug' => 'hoejglans', 'title' => 'Højglans - Voksforsegling pr. enhed'],
|
|
['product_id' => 21, 'slug' => 'undervognsskyl', 'title' => 'Undervognsskyl pr. enhed'],
|
|
['product_id' => 22, 'slug' => 'double-duty-kemi', 'title' => 'Tillæg for Specialsæbe - DD'],
|
|
];
|
|
}
|
|
|
|
/**
|
|
* @param array<int> $department_ids
|
|
* @return array<string,mixed>
|
|
* @throws Exception
|
|
*/
|
|
private function buildNightWashMetric(array $department_ids, string $date, string $date_to): array
|
|
{
|
|
return $this->outsideHoursStatisticsService()->toOverviewMetric(
|
|
$this->outsideHoursStatisticsService()->getSummary($date, $department_ids, $date_to)
|
|
);
|
|
|
|
foreach ($department_ids as $department_id) {
|
|
if (!isset($opening_hours_by_department_id[$department_id])) {
|
|
return $this->metricPayload(
|
|
null,
|
|
null,
|
|
'unavailable',
|
|
'Døgnvask kræver åbningstider for alle valgte afdelinger.'
|
|
);
|
|
}
|
|
}
|
|
|
|
$night_wash_count = 0;
|
|
foreach ($wash_transactions as $wash_transaction) {
|
|
$department_id = (int)($wash_transaction['department_id'] ?? 0);
|
|
$created_at = (string)($wash_transaction['created_at'] ?? '');
|
|
if ($created_at === '') {
|
|
continue;
|
|
}
|
|
|
|
$opening_hours = $opening_hours_by_department_id[$department_id] ?? null;
|
|
if (!is_array($opening_hours)) {
|
|
continue;
|
|
}
|
|
|
|
if ($this->isOutsideOpeningHours($created_at, $opening_hours)) {
|
|
$night_wash_count++;
|
|
}
|
|
}
|
|
|
|
return $this->metricPayload($night_wash_count);
|
|
}
|
|
|
|
/**
|
|
* @param array<int> $department_ids
|
|
* @return array<string,mixed>
|
|
*/
|
|
private function buildComplaintsMetric(array $department_ids, string $date, string $date_to): array
|
|
{
|
|
return $this->metricPayload(
|
|
$this->dailyReportComplaintsRepository()->countForDepartmentsInRange($department_ids, $date, $date_to)
|
|
);
|
|
}
|
|
|
|
/**
|
|
* @param array<int> $department_ids
|
|
* @return array<string,mixed>
|
|
* @throws Exception
|
|
*/
|
|
private function buildOvertimeMetric(array $department_ids, string $date, string $date_to): array
|
|
{
|
|
$departments = $this->fetchDepartmentsByIds($department_ids);
|
|
if (count($departments) !== count($department_ids)) {
|
|
return $this->metricPayload(
|
|
null,
|
|
null,
|
|
'unavailable',
|
|
'Overarbejde er ikke tilgængelig for alle valgte afdelinger.'
|
|
);
|
|
}
|
|
|
|
$workfeed_department_ids_by_department = $this->resolveWorkfeedDepartmentIdsByDepartmentId($departments);
|
|
if (count($workfeed_department_ids_by_department) !== count($department_ids)) {
|
|
return $this->metricPayload(
|
|
null,
|
|
null,
|
|
'unavailable',
|
|
'Overarbejde kræver Workfeed-kobling for alle valgte afdelinger.'
|
|
);
|
|
}
|
|
|
|
$range_start = new DateTime($date . ' 00:00:00');
|
|
$range_end_exclusive = new DateTime($date_to . ' 00:00:00');
|
|
$range_end_exclusive->add(new DateInterval('P1D'));
|
|
|
|
$query_start = clone $range_start;
|
|
$query_start->sub(new DateInterval('P1D'));
|
|
|
|
$overtime_hours = $this->sumOvertimeHoursForShifts(
|
|
$this->fetchWorkfeedShifts($query_start, $range_end_exclusive),
|
|
$workfeed_department_ids_by_department,
|
|
$range_start,
|
|
$range_end_exclusive
|
|
);
|
|
|
|
return $this->metricPayload($overtime_hours);
|
|
}
|
|
|
|
/**
|
|
* @param array<int|string,mixed> $metric
|
|
* @return array<string,mixed>
|
|
*/
|
|
private function metricPayload(mixed $value, mixed $out_of = null, string $state = 'ready', string $message = null): array
|
|
{
|
|
return [
|
|
'state' => $state,
|
|
'value' => $value,
|
|
'out_of' => $out_of,
|
|
'message' => $message,
|
|
];
|
|
}
|
|
|
|
/**
|
|
* @param array<int,array<string,mixed>> $opening_hours_by_department_id
|
|
* @return bool
|
|
*/
|
|
private function isOutsideOpeningHours(string $created_at, array $opening_hours): bool
|
|
{
|
|
$timestamp = strtotime($created_at);
|
|
if ($timestamp === false) {
|
|
return false;
|
|
}
|
|
|
|
$weekday = match ((int)date('N', $timestamp)) {
|
|
1 => 'monday',
|
|
2 => 'tuesday',
|
|
3 => 'wednesday',
|
|
4 => 'thursday',
|
|
5 => 'friday',
|
|
6 => 'saturday',
|
|
7 => 'sunday',
|
|
default => null,
|
|
};
|
|
|
|
if ($weekday === null) {
|
|
return false;
|
|
}
|
|
|
|
$opening_start = $opening_hours[$weekday . '_start'] ?? null;
|
|
$opening_end = $opening_hours[$weekday . '_end'] ?? null;
|
|
if (!is_string($opening_start) || trim($opening_start) === '' || !is_string($opening_end) || trim($opening_end) === '') {
|
|
return true;
|
|
}
|
|
|
|
$wash_time = date('H:i', $timestamp);
|
|
$opening_start_time = date('H:i', strtotime($opening_start));
|
|
$opening_end_time = date('H:i', strtotime($opening_end));
|
|
|
|
return !($wash_time >= $opening_start_time && $wash_time <= $opening_end_time);
|
|
}
|
|
|
|
/**
|
|
* @param array<int,mixed> $shifts
|
|
* @param array<int,string> $workfeed_department_ids_by_department
|
|
*/
|
|
private function sumOvertimeHoursForShifts(
|
|
array $shifts,
|
|
array $workfeed_department_ids_by_department,
|
|
DateTime $range_start,
|
|
DateTime $range_end_exclusive
|
|
): float {
|
|
$department_lookup = [];
|
|
foreach ($workfeed_department_ids_by_department as $workfeed_department_id) {
|
|
$normalized = trim((string)$workfeed_department_id);
|
|
if ($normalized !== '') {
|
|
$department_lookup[$normalized] = true;
|
|
}
|
|
}
|
|
|
|
if ($department_lookup === []) {
|
|
return 0.0;
|
|
}
|
|
|
|
$total_hours = 0.0;
|
|
foreach ($shifts as $shift) {
|
|
$shift_department_id = $this->extractWorkfeedDepartmentId($shift);
|
|
if ($shift_department_id === null || !isset($department_lookup[$shift_department_id])) {
|
|
continue;
|
|
}
|
|
|
|
$total_hours += $this->calculateShiftOvertimeHoursInRange(
|
|
$this->normalizeWorkfeedRecord($shift),
|
|
$range_start,
|
|
$range_end_exclusive
|
|
);
|
|
}
|
|
|
|
return round($total_hours, 2);
|
|
}
|
|
|
|
private function calculateShiftOvertimeHoursInRange(array $record, DateTime $range_start, DateTime $range_end_exclusive): float
|
|
{
|
|
return workfeed_shift_time_resolver::calculateOvertimeHoursInRange($record, $range_start, $range_end_exclusive);
|
|
}
|
|
|
|
/**
|
|
* @param array<int,mixed> $departments
|
|
* @return array<int,string>
|
|
*/
|
|
private function resolveWorkfeedDepartmentIdsByDepartmentId(array $departments): array
|
|
{
|
|
$resolved_ids = [];
|
|
$workfeed_departments = null;
|
|
|
|
foreach ($departments as $department) {
|
|
$department_id = $this->departmentIdFromValue($department);
|
|
if ($department_id < 1) {
|
|
continue;
|
|
}
|
|
|
|
$configured_id = $this->getConfiguredWorkfeedDepartmentId($department);
|
|
if ($configured_id !== null) {
|
|
$resolved_ids[$department_id] = $configured_id;
|
|
continue;
|
|
}
|
|
|
|
if ($workfeed_departments === null) {
|
|
$workfeed_departments = $this->fetchWorkfeedDepartments();
|
|
}
|
|
if ($workfeed_departments === []) {
|
|
continue;
|
|
}
|
|
|
|
$department_name = $this->departmentNameFromValue($department);
|
|
if ($department_name === '') {
|
|
continue;
|
|
}
|
|
|
|
$matched_id = $this->matchWorkfeedDepartmentIdByName($department_name, $workfeed_departments);
|
|
if ($matched_id !== null) {
|
|
$resolved_ids[$department_id] = $matched_id;
|
|
}
|
|
}
|
|
|
|
return $resolved_ids;
|
|
}
|
|
|
|
private function getConfiguredWorkfeedDepartmentId(mixed $department): ?string
|
|
{
|
|
foreach ([
|
|
'workfeed_department_id',
|
|
'workfeedDepartmentId',
|
|
'workfeed_departmentID',
|
|
'workfeed_department',
|
|
] as $key) {
|
|
$value = $this->departmentVariableValue($department, $key);
|
|
if (!is_string($value)) {
|
|
continue;
|
|
}
|
|
|
|
$trimmed = trim($value);
|
|
if ($trimmed !== '') {
|
|
return $trimmed;
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* @param array<int,mixed> $workfeed_departments
|
|
*/
|
|
private function matchWorkfeedDepartmentIdByName(string $department_name, array $workfeed_departments): ?string
|
|
{
|
|
$needle = $this->normalizeDepartmentName($department_name);
|
|
foreach ($workfeed_departments as $entry) {
|
|
$record = $this->normalizeWorkfeedRecord($entry);
|
|
$name = trim((string)($record['name'] ?? ''));
|
|
$id = trim((string)($record['id'] ?? ''));
|
|
if ($name === '' || $id === '') {
|
|
continue;
|
|
}
|
|
|
|
if ($this->normalizeDepartmentName($name) === $needle) {
|
|
return $id;
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
private function normalizeDepartmentName(string $name): string
|
|
{
|
|
$collapsed = preg_replace('/\s+/', ' ', trim($name));
|
|
|
|
return strtolower($collapsed ?? trim($name));
|
|
}
|
|
|
|
/**
|
|
* @return array<int,mixed>
|
|
*/
|
|
private function normalizeWorkfeedCollection(mixed $payload): array
|
|
{
|
|
if (is_array($payload)) {
|
|
return $payload;
|
|
}
|
|
|
|
if (!is_object($payload)) {
|
|
return [];
|
|
}
|
|
|
|
foreach (['data', 'items', 'results', 'shifts', 'departments'] as $key) {
|
|
if (!isset($payload->$key)) {
|
|
continue;
|
|
}
|
|
|
|
$value = $payload->$key;
|
|
if (is_array($value)) {
|
|
return $value;
|
|
}
|
|
if (is_object($value)) {
|
|
return array_values(get_object_vars($value));
|
|
}
|
|
}
|
|
|
|
return [];
|
|
}
|
|
|
|
/**
|
|
* @return array<string,mixed>
|
|
*/
|
|
private function normalizeWorkfeedRecord(mixed $record): array
|
|
{
|
|
if (is_array($record)) {
|
|
return $record;
|
|
}
|
|
if (is_object($record)) {
|
|
return get_object_vars($record);
|
|
}
|
|
|
|
return [];
|
|
}
|
|
|
|
private function extractWorkfeedDepartmentId(mixed $shift): ?string
|
|
{
|
|
$record = $this->normalizeWorkfeedRecord($shift);
|
|
|
|
$department_id = $record['departmentID'] ?? $record['departmentId'] ?? null;
|
|
if (($department_id === null || $department_id === '') && isset($record['department'])) {
|
|
$department = $this->normalizeWorkfeedRecord($record['department']);
|
|
$department_id = $department['id'] ?? $department['departmentID'] ?? $department['departmentId'] ?? null;
|
|
}
|
|
|
|
if ($department_id === null) {
|
|
return null;
|
|
}
|
|
|
|
$normalized = trim((string)$department_id);
|
|
return $normalized === '' ? null : $normalized;
|
|
}
|
|
|
|
private function parseDateTimeValue(mixed $value): ?DateTime
|
|
{
|
|
if (is_string($value)) {
|
|
$normalized = trim($value);
|
|
if ($normalized === '') {
|
|
return null;
|
|
}
|
|
|
|
try {
|
|
return new DateTime($normalized);
|
|
} catch (Exception) {
|
|
if (!is_numeric($normalized)) {
|
|
return null;
|
|
}
|
|
$value = (float)$normalized;
|
|
}
|
|
}
|
|
|
|
if (is_int($value) || is_float($value)) {
|
|
if (!is_finite((float)$value)) {
|
|
return null;
|
|
}
|
|
|
|
$timestamp = (float)$value;
|
|
if ($timestamp > 9999999999) {
|
|
$timestamp /= 1000;
|
|
}
|
|
|
|
try {
|
|
$date = new DateTime('@' . (string)(int)round($timestamp));
|
|
$date->setTimezone(new DateTimeZone('UTC'));
|
|
return $date;
|
|
} catch (Exception) {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
$record = $this->normalizeWorkfeedRecord($value);
|
|
foreach (['seconds', '_seconds', 'epochSeconds', 'timestamp'] as $key) {
|
|
if (!array_key_exists($key, $record)) {
|
|
continue;
|
|
}
|
|
|
|
$parsed = $this->parseDateTimeValue($record[$key]);
|
|
if ($parsed !== null) {
|
|
return $parsed;
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
private function getNestedRecordValue(array $record, string $path): mixed
|
|
{
|
|
$segments = explode('.', $path);
|
|
$current = $record;
|
|
|
|
foreach ($segments as $segment) {
|
|
if (is_array($current)) {
|
|
if (!array_key_exists($segment, $current)) {
|
|
return null;
|
|
}
|
|
$current = $current[$segment];
|
|
continue;
|
|
}
|
|
|
|
if (is_object($current)) {
|
|
if (!property_exists($current, $segment)) {
|
|
return null;
|
|
}
|
|
$current = $current->$segment;
|
|
continue;
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
return $current;
|
|
}
|
|
|
|
/**
|
|
* @param array<int,string> $paths
|
|
*/
|
|
private function firstShiftDateTimeFromPaths(array $record, array $paths): ?DateTime
|
|
{
|
|
foreach ($paths as $path) {
|
|
$parsed = $this->parseDateTimeValue($this->getNestedRecordValue($record, $path));
|
|
if ($parsed !== null) {
|
|
return $parsed;
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* @param array<int,string> $paths
|
|
*/
|
|
private function lastShiftDateTimeFromPaths(array $record, array $paths): ?DateTime
|
|
{
|
|
$latest = null;
|
|
foreach ($paths as $path) {
|
|
$parsed = $this->parseDateTimeValue($this->getNestedRecordValue($record, $path));
|
|
if ($parsed === null) {
|
|
continue;
|
|
}
|
|
|
|
if ($latest === null || $parsed->getTimestamp() > $latest->getTimestamp()) {
|
|
$latest = $parsed;
|
|
}
|
|
}
|
|
|
|
return $latest;
|
|
}
|
|
|
|
private function hasShiftApproval(array $record): bool
|
|
{
|
|
if (!array_key_exists('approval', $record)) {
|
|
return false;
|
|
}
|
|
|
|
$approval = $record['approval'];
|
|
if ($approval === null) {
|
|
return false;
|
|
}
|
|
if (is_array($approval)) {
|
|
return $approval !== [];
|
|
}
|
|
if (is_object($approval)) {
|
|
return get_object_vars($approval) !== [];
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
private function resolveEffectiveShiftEnd(array $record, DateTime $shift_start, DateTime $shift_end): DateTime
|
|
{
|
|
if ($this->hasShiftApproval($record)) {
|
|
return $shift_end;
|
|
}
|
|
|
|
$update_time = $this->parseDateTimeValue($record['updateTime'] ?? null);
|
|
if ($update_time === null) {
|
|
return $shift_end;
|
|
}
|
|
|
|
$shift_start_ts = $shift_start->getTimestamp();
|
|
$shift_end_ts = $shift_end->getTimestamp();
|
|
$update_ts = $update_time->getTimestamp();
|
|
|
|
if ($update_ts <= $shift_end_ts) {
|
|
return $shift_end;
|
|
}
|
|
|
|
$max_unapproved_extension_seconds = 6 * 3600;
|
|
if (($update_ts - $shift_end_ts) > $max_unapproved_extension_seconds) {
|
|
return $shift_end;
|
|
}
|
|
if (($update_ts - $shift_start_ts) > 24 * 3600) {
|
|
return $shift_end;
|
|
}
|
|
|
|
return $update_time;
|
|
}
|
|
|
|
/**
|
|
* @param array<int> $department_ids
|
|
* @return array<int,array<string,mixed>>
|
|
*/
|
|
protected function fetchOpeningHoursByDepartmentId(array $department_ids): array
|
|
{
|
|
global $db;
|
|
|
|
$normalized_department_ids = array_values(array_unique(array_map('intval', $department_ids)));
|
|
$normalized_department_ids = array_values(array_filter($normalized_department_ids, static fn(int $id): bool => $id > 0));
|
|
if ($normalized_department_ids === []) {
|
|
return [];
|
|
}
|
|
|
|
$department_ids_sql = implode(',', $normalized_department_ids);
|
|
$sql = "SELECT * FROM department_time_bookings_opening_hours WHERE department IN ($department_ids_sql)";
|
|
$result = $db->query($sql);
|
|
if (!is_object($result) || $result->num_rows === 0) {
|
|
return [];
|
|
}
|
|
|
|
$rows = [];
|
|
while ($row = $result->fetch_assoc()) {
|
|
$rows[(int)($row['department'] ?? 0)] = $row;
|
|
}
|
|
|
|
return $rows;
|
|
}
|
|
|
|
/**
|
|
* @param array<int> $department_ids
|
|
* @return array<int,mixed>
|
|
* @throws Exception
|
|
*/
|
|
protected function fetchDepartmentsByIds(array $department_ids): array
|
|
{
|
|
$departments = [];
|
|
foreach ($department_ids as $department_id) {
|
|
$department = (new departments_o())->select((int)$department_id);
|
|
if (!$department->exists()) {
|
|
continue;
|
|
}
|
|
$departments[] = $department;
|
|
}
|
|
|
|
return $departments;
|
|
}
|
|
|
|
/**
|
|
* @return array<int,mixed>
|
|
*/
|
|
protected function fetchWorkfeedDepartments(): array
|
|
{
|
|
try {
|
|
return $this->normalizeWorkfeedCollection((new workfeed())->listDepartments());
|
|
} catch (Exception) {
|
|
return [];
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @return array<int,mixed>
|
|
*/
|
|
protected function fetchWorkfeedShifts(DateTime $query_start, DateTime $range_end_exclusive): array
|
|
{
|
|
try {
|
|
return $this->normalizeWorkfeedCollection((new workfeed())->listShifts([
|
|
'startFrom' => $query_start->format(DateTime::ATOM),
|
|
'startTo' => $range_end_exclusive->format(DateTime::ATOM),
|
|
]));
|
|
} catch (Exception) {
|
|
return [];
|
|
}
|
|
}
|
|
|
|
protected function dailyReportRepository(): object
|
|
{
|
|
return new department_daily_reports_o();
|
|
}
|
|
|
|
protected function dailyReportComplaintsRepository(): object
|
|
{
|
|
return new department_daily_report_complaints_o();
|
|
}
|
|
|
|
protected function complaintCustomerSearchService(): economicCustomers
|
|
{
|
|
return new economicCustomers();
|
|
}
|
|
|
|
protected function outsideHoursStatisticsService(): department_outside_hours_statistics_service
|
|
{
|
|
return new department_outside_hours_statistics_service();
|
|
}
|
|
|
|
private function sanitizeComplaintCustomerLookupUpstreamErrorMessage(\Throwable $throwable): string
|
|
{
|
|
$message = trim($throwable->getMessage());
|
|
if ($message === '') {
|
|
return 'Unexpected e-conomic integration error.';
|
|
}
|
|
|
|
$message = preg_replace('/\s+/', ' ', $message);
|
|
if (!is_string($message)) {
|
|
return 'Unexpected e-conomic integration error.';
|
|
}
|
|
|
|
return substr($message, 0, 500);
|
|
}
|
|
|
|
private function departmentIdFromValue(mixed $department): int
|
|
{
|
|
if (is_array($department)) {
|
|
return (int)($department['id'] ?? 0);
|
|
}
|
|
if (is_object($department)) {
|
|
return (int)($department->id ?? 0);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
private function departmentNameFromValue(mixed $department): string
|
|
{
|
|
if (is_array($department)) {
|
|
return trim((string)($department['name'] ?? ''));
|
|
}
|
|
if (is_object($department) && isset($department->name)) {
|
|
$value = $department->name;
|
|
if (is_object($value) && method_exists($value, 'value')) {
|
|
return trim((string)$value->value());
|
|
}
|
|
|
|
return trim((string)$value);
|
|
}
|
|
|
|
return '';
|
|
}
|
|
|
|
private function departmentVariableValue(mixed $department, string $key): mixed
|
|
{
|
|
if (is_array($department)) {
|
|
return $department[$key] ?? null;
|
|
}
|
|
|
|
if (is_object($department) && isset($department->variables) && is_object($department->variables) && method_exists($department->variables, 'getVariable')) {
|
|
return $department->variables->getVariable($key);
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
}
|