Add weekly department results route, enhance date handling in invoicing and statistics logic, and improve Slack notification configuration

This commit is contained in:
Jeppe Bundgaard
2026-03-11 10:52:49 +01:00
parent 4945abfd8e
commit 15a7a7b8a5
4 changed files with 122 additions and 14 deletions
+54 -13
View File
@@ -446,22 +446,39 @@ class departments_o extends db
* Send department period statistics to Slack
* @param string $start_date (YYYY-MM-DD)
* @param string $end_date (YYYY-MM-DD)
* @return void
* @param int[] $product_ids The product ids to include in the statistics (e.g. [25, [23, 24], 22, 27, 21, 26])
* @param bool $return_as_array Whether to return the results as an array or not
* @return string[]
* @throws Exception If the department is not selected
*/
public function sendPeriodStatisticsToSlack(string $start_date, string $end_date): void
public function sendPeriodStatisticsToSlack(string $start_date, string $end_date, array $product_ids = [
25,
[23, 24], // Used to merge two products into one percentage (Spot Free)
22,
27,
21,
26
], bool $return_as_array = false): array
{
self::requireSelected();
// Ensure only one message is sent a week using redis cache
$cacheKey = "department_{$this->id}_weekly_statistics_sent";
// Get time until next monday at 00:00:00
$nextMonday = strtotime('next monday');
$cacheDuration = $nextMonday - time();
$lastSent = redis->get($cacheKey);
// If null or more than a week has passed since the last message, send a new message
$sendNow = match (true) {
$lastSent === null => true,
default => (time() - $lastSent) >= $cacheDuration
};
if (!$sendNow) {
return $return_as_array ? ["A weekly statistics message has already been sent for this department."] : ["A weekly statistics message has already been sent for this department."];
}
$this->cache($cacheKey, $cacheDuration);
$this->setCachedExpiration($cacheKey, $cacheDuration);
// Configuration
$department_id = $this->id;
$product_ids = [
25,
[23, 24], // Used to merge two products into one percentage (Spot Free)
22,
27,
21,
26
];
$date_end = date('Y-m-d 23:59:59', strtotime($end_date)); // End date at 23:59:59
$date_start = date('Y-m-d 00:00:00', strtotime($start_date)); // Start date at 00:00:00
/**
@@ -487,6 +504,10 @@ class departments_o extends db
$wash_count = (new orders_o())->countWashesInDateRange($date_start, $date_end, $department_id);
$analytics = $this->analyzeAddonSalesData($product_ids, $date_start, $date_end, $department_id, $max_addons, $sold_addons, $percentages);
$tmp .= "Washes: $wash_count\n" . implode('', $analytics);
// If it should be returned as an array, return it as an array instead of sending it to Slack
if ($return_as_array) {
return explode("\n", trim($tmp));
}
// Check if there's a custom webhook for the department
$slack = new slack();
@@ -497,6 +518,8 @@ class departments_o extends db
// Set the custom webhook for the department
$slack->send_webhook_message($tmp, $this->slack_webhook->value());
}
return explode("\n", trim($tmp));
}
/**
@@ -686,7 +709,8 @@ class departments_o extends db
* @param string $date_end (YYYY-MM-DD)
* @param int[] $department_ids
* @param int[] $product_ids
* @return void
* @param bool $return_as_array Whether to return the results as an array or not
* @return null|array
* @throws Exception
*/
public function sendSlackInternalStatisticNotification(string $date_start, string $date_end, array $department_ids, array $product_ids = [
@@ -696,7 +720,7 @@ class departments_o extends db
27,
21,
26
]): void
], bool $return_as_array = false): null|array
{
if (empty($department_ids)) {
throw new Exception('No department ids provided for the Slack internal statistic notification.');
@@ -810,8 +834,25 @@ class departments_o extends db
}
$tmp .= "> - Total: " . number_format($total_percentage, 2) . "%\n";
}
$array_of_results = [
"daily_management" => [],
"departments" => []
];
// Add $tmp to the daily management message
$array_of_results['daily_management'][] = $tmp;
// Send the message to the internal Slack webhook
$slack = new slack();
$slack->send_webhook_message($tmp, (new departments_o())->select(10)->slack_webhook->value());
if (!$return_as_array) {
$slack->send_webhook_message($tmp, (new departments_o())->select(10)->slack_webhook->value());
}
// Send a department-specific message for each internal department with the percentage of addons sold
foreach ( $department_ids as $department_id ) {
$department = (new departments_o())->select($department_id);
$tmp_dept = $department->sendPeriodStatisticsToSlack($date_start, $date_end, $product_ids, $return_as_array);
$array_of_results['departments'][$department_id] = $tmp_dept;
}
return $array_of_results;
}
}
@@ -62,7 +62,11 @@ class InvoicingPeriodRoute
self::requireDateFormat($dateFrom, 'Y-m-d');
self::requireDateFormat($dateTo, 'Y-m-d');
// Add a day to the dateTo parameter to include the end date in the range
$dateTo = date('Y-m-d', strtotime($dateTo . ' +1 day'));
$dateFrom = date('Y-m-d 00:00:00', strtotime($dateFrom));
$dateTo = date('Y-m-d 23:59:59', strtotime($dateTo));
// Add date from and date to to the response meta
$response->add_meta('date_from', $dateFrom);
$response->add_meta('date_to', $dateTo);
// Get the invoicing period for the user
$response->success([...self::getInvoicingPeriod($dateFrom, $dateTo)]);
} else {
@@ -429,5 +429,66 @@ class departmentsRoute
'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'); TODO: enable permission check once the feature is fully implemented and tested
// Get the user object
//$user = (new authentication())->get_user();
$user = true;
// Check if the request was successful
if ($user) {
// Get the department results for the week
$departments_o = new departments_o();
$weeks = 5;
$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'
]
);
}
}
@@ -1000,11 +1000,13 @@ trait db_object_t
* @param int $seconds The number of seconds to set the cached object expiration time
* @param null $objectId
* @return void
* @throws Exception If the object is not selected, and no object id is provided, it throws an exception
*/
public function setCachedExpiration(string $key, int $seconds, $objectId = null): void
{
// If the object id is not set, use the object id
if (!$objectId) {
$this->requireSelected();
$objectId = $this->id;
}
// Set the expiration time for the cached data