From f28e8472c573ed1baad99ded63e40f3ffc6df943 Mon Sep 17 00:00:00 2001 From: "anatoly.shipitz" Date: Wed, 2 Jul 2025 16:15:11 +0200 Subject: [PATCH 1/5] Enhance weekly financial report queries with custom field IDs - Added `REPORT_FILTER_FIELD_ID` and `RELATED_PROJECT_FIELD_ID` constants in `weeklyFinancialReport.ts` for better report filtering and linking issues to billable projects. - Refactored `TARGET_UNITS_QUERY` in `queries.ts` to utilize the new constants, improving the SQL query structure and ensuring accurate data retrieval based on custom fields. These changes enhance the flexibility and accuracy of the weekly financial report generation process. --- .../main/src/configs/weeklyFinancialReport.ts | 6 + .../main/src/services/TargetUnit/queries.ts | 119 +++++++++++++----- 2 files changed, 93 insertions(+), 32 deletions(-) diff --git a/workers/main/src/configs/weeklyFinancialReport.ts b/workers/main/src/configs/weeklyFinancialReport.ts index 0c5ba49..6d02b13 100644 --- a/workers/main/src/configs/weeklyFinancialReport.ts +++ b/workers/main/src/configs/weeklyFinancialReport.ts @@ -5,3 +5,9 @@ export enum GroupNameEnum { export const HIGH_MARGINALITY_THRESHOLD = 55; export const MEDIUM_MARGINALITY_THRESHOLD = 45; + +// ID of the custom field in Redmine used for report filtering +export const REPORT_FILTER_FIELD_ID = 253; + +// ID of the custom field in Redmine used to link issue to Billable project +export const RELATED_PROJECT_FIELD_ID = 20; diff --git a/workers/main/src/services/TargetUnit/queries.ts b/workers/main/src/services/TargetUnit/queries.ts index ad9f2c6..e312c99 100644 --- a/workers/main/src/services/TargetUnit/queries.ts +++ b/workers/main/src/services/TargetUnit/queries.ts @@ -1,43 +1,98 @@ -export const TARGET_UNITS_QUERY = `SELECT - group_id, - group_name, - project_id, - project_name, - user_id, - username, - DATE_FORMAT(spent_on, '%Y-%m-%d') AS spent_on, - SUM(total_hours) AS total_hours - FROM ( - SELECT - g.id AS group_id, - g.lastname AS group_name, - p.id AS project_id, - p.name AS project_name, - te.user_id AS user_id, - CONCAT(u.firstname, ' ', u.lastname) AS username, - te.spent_on AS spent_on, - te.hours AS total_hours +import { + GroupNameEnum, + RELATED_PROJECT_FIELD_ID, + REPORT_FILTER_FIELD_ID, +} from '../../configs/weeklyFinancialReport'; + +const groupNamesList = Object.values(GroupNameEnum) + .map((name) => `'${name}'`) + .join(', '); + +const COMMON_SELECT = ` + SELECT + g.id AS group_id, + g.lastname AS group_name, + p.id AS project_id, + p.name AS project_name, + te.user_id AS user_id, + CONCAT(u.firstname, ' ', u.lastname) AS username, + te.spent_on AS spent_on, + cv.value AS filter_name, +`; + +const COMMON_FROM = ` FROM users AS g - JOIN custom_values as cv ON cv.customized_id = g.id - JOIN members AS m ON m.user_id = g.id - JOIN projects AS p ON p.id = m.project_id - JOIN time_entries te ON te.project_id = p.id - JOIN users AS u ON u.id = te.user_id + JOIN custom_values as cv ON cv.customized_id = g.id + JOIN members AS m ON m.user_id = g.id + JOIN projects AS p ON p.id = m.project_id +`; + +const COMMON_WHERE = ` WHERE g.type = 'Group' - AND cv.customized_type = 'Principal' and cv.value = ? + AND cv.customized_type = 'Principal' + AND cv.custom_field_id = ${REPORT_FILTER_FIELD_ID} + AND cv.value IN (${groupNamesList}) AND te.spent_on BETWEEN DATE_SUB(CURDATE(), INTERVAL WEEKDAY(CURDATE()) + 7 DAY) AND DATE_SUB(CURDATE(), INTERVAL WEEKDAY(CURDATE()) + 1 DAY) - ) t - GROUP BY +`; + +function buildDirectGroupTimeEntries() { + return ` + ${COMMON_SELECT} + te.hours AS project_hours, + 0 as deptech_hours + ${COMMON_FROM} + JOIN time_entries te ON te.project_id = p.id + JOIN users AS u ON u.id = te.user_id + ${COMMON_WHERE} + `; +} + +function buildRelatedProjectTimeEntries() { + return ` + ${COMMON_SELECT} + 0 as project_hours, + te.hours AS deptech_hours + ${COMMON_FROM} + JOIN custom_values cv2 ON cv2.value = CAST(p.id AS CHAR) + AND cv2.customized_type = 'Issue' + AND cv2.custom_field_id = ${RELATED_PROJECT_FIELD_ID} + AND cv2.value <> "" + JOIN time_entries te ON te.issue_id = cv2.customized_id + JOIN users AS u ON u.id = te.user_id + ${COMMON_WHERE} + `; +} + +export const TARGET_UNITS_QUERY = ` + SELECT group_id, group_name, project_id, project_name, user_id, username, - spent_on + DATE_FORMAT(spent_on, '%Y-%m-%d') AS spent_on, + SUM(project_hours) AS project_hours, + SUM(deptech_hours) AS deptech_hours, + SUM(project_hours) + SUM(deptech_hours) AS total_hours, + filter_name + FROM ( + ${buildDirectGroupTimeEntries()} + UNION ALL + ${buildRelatedProjectTimeEntries()} + ) t + GROUP BY + t.group_id, + t.group_name, + t.project_id, + t.project_name, + t.user_id, + t.username, + t.spent_on ORDER BY - group_name ASC, - project_name ASC, - username ASC, - spent_on ASC`; + t.group_name ASC, + t.project_name ASC, + t.username ASC, + t.spent_on ASC +`; From c7181f086f6b4ce15436080624a5d01d886c5184 Mon Sep 17 00:00:00 2001 From: "anatoly.shipitz" Date: Wed, 2 Jul 2025 16:39:29 +0200 Subject: [PATCH 2/5] Refactor SQL query in TargetUnit service for clarity - Updated the SQL query in `queries.ts` to replace the `custom_values` alias from `cv2` to `icv`, improving readability and consistency in the code. - Ensured that the join conditions remain intact while enhancing the clarity of the query structure. These changes contribute to better maintainability and understanding of the SQL logic within the TargetUnit service. --- workers/main/src/services/TargetUnit/queries.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/workers/main/src/services/TargetUnit/queries.ts b/workers/main/src/services/TargetUnit/queries.ts index e312c99..b11977a 100644 --- a/workers/main/src/services/TargetUnit/queries.ts +++ b/workers/main/src/services/TargetUnit/queries.ts @@ -54,11 +54,11 @@ function buildRelatedProjectTimeEntries() { 0 as project_hours, te.hours AS deptech_hours ${COMMON_FROM} - JOIN custom_values cv2 ON cv2.value = CAST(p.id AS CHAR) - AND cv2.customized_type = 'Issue' - AND cv2.custom_field_id = ${RELATED_PROJECT_FIELD_ID} - AND cv2.value <> "" - JOIN time_entries te ON te.issue_id = cv2.customized_id + JOIN custom_values icv ON icv.value = CAST(p.id AS CHAR) + AND icv.customized_type = 'Issue' + AND icv.custom_field_id = ${RELATED_PROJECT_FIELD_ID} + AND icv.value <> "" + JOIN time_entries te ON te.issue_id = icv.customized_id JOIN users AS u ON u.id = te.user_id ${COMMON_WHERE} `; From 5101599a597576ed3e40fc35bedf47d54f6033b1 Mon Sep 17 00:00:00 2001 From: "anatoly.shipitz" Date: Wed, 2 Jul 2025 16:40:45 +0200 Subject: [PATCH 3/5] Fix SQL query in TargetUnit service to handle single quotes correctly - Updated the mapping function in `queries.ts` to escape single quotes in group names, ensuring proper SQL syntax and preventing potential query errors. - This change enhances the robustness of the SQL query generation process within the TargetUnit service, contributing to better data integrity and reliability. --- workers/main/src/services/TargetUnit/queries.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/workers/main/src/services/TargetUnit/queries.ts b/workers/main/src/services/TargetUnit/queries.ts index b11977a..0579a89 100644 --- a/workers/main/src/services/TargetUnit/queries.ts +++ b/workers/main/src/services/TargetUnit/queries.ts @@ -5,7 +5,7 @@ import { } from '../../configs/weeklyFinancialReport'; const groupNamesList = Object.values(GroupNameEnum) - .map((name) => `'${name}'`) + .map((name) => `'${name.replace(/'/g, "''")}'`) .join(', '); const COMMON_SELECT = ` From be71added58c1e46708cc374e86d206cbdc5d498 Mon Sep 17 00:00:00 2001 From: "anatoly.shipitz" Date: Thu, 3 Jul 2025 10:50:09 +0200 Subject: [PATCH 4/5] Fix formatting issue in TargetUnit service SQL query - Adjusted the mapping function in `queries.ts` to ensure consistent formatting of group names by removing unnecessary whitespace. - This change maintains the integrity of the SQL query generation process and enhances code readability. --- workers/main/src/services/TargetUnit/queries.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/workers/main/src/services/TargetUnit/queries.ts b/workers/main/src/services/TargetUnit/queries.ts index 0579a89..87ff9cb 100644 --- a/workers/main/src/services/TargetUnit/queries.ts +++ b/workers/main/src/services/TargetUnit/queries.ts @@ -5,7 +5,7 @@ import { } from '../../configs/weeklyFinancialReport'; const groupNamesList = Object.values(GroupNameEnum) - .map((name) => `'${name.replace(/'/g, "''")}'`) + .map((name) => `'${name.replace(/'/g, "''")}'`) .join(', '); const COMMON_SELECT = ` From 696029323a85435bce1fda243405b496b454b065 Mon Sep 17 00:00:00 2001 From: "anatoly.shipitz" Date: Thu, 3 Jul 2025 11:02:30 +0200 Subject: [PATCH 5/5] Enhance TARGET_UNITS_QUERY to include filter_name field - Updated the SQL query in `queries.ts` to add the `filter_name` field, improving the data retrieval capabilities for target units. - This change enhances the flexibility of the query, allowing for more detailed filtering and reporting of target unit data. --- workers/main/src/services/TargetUnit/queries.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/workers/main/src/services/TargetUnit/queries.ts b/workers/main/src/services/TargetUnit/queries.ts index 87ff9cb..75f8df3 100644 --- a/workers/main/src/services/TargetUnit/queries.ts +++ b/workers/main/src/services/TargetUnit/queries.ts @@ -89,7 +89,8 @@ export const TARGET_UNITS_QUERY = ` t.project_name, t.user_id, t.username, - t.spent_on + t.spent_on, + t.filter_name ORDER BY t.group_name ASC, t.project_name ASC,