feat: implement organization working days and hours settings

- Added functionality to fetch and update organization working days and hours in the admin center.
- Introduced a form for saving working days and hours, with validation and error handling.
- Updated the reporting allocation logic to utilize organization-specific working hours for accurate calculations.
- Enhanced localization files to support new settings in English, Spanish, and Portuguese.
This commit is contained in:
chamikaJ
2025-05-19 16:07:35 +05:30
parent fc30c1854e
commit a8b20680e5
7 changed files with 157 additions and 21 deletions

View File

@@ -118,7 +118,7 @@ BEGIN
SELECT SUM(time_spent)
FROM task_work_log
WHERE task_id = t.id
), 0) as logged_minutes
), 0) / 60.0 as logged_minutes
FROM tasks t
WHERE t.id = _task_id
)

View File

@@ -415,15 +415,20 @@ export default class ReportingController extends WorklenzControllerBase {
@HandleExceptions()
public static async getMyTeams(req: IWorkLenzRequest, res: IWorkLenzResponse): Promise<IWorkLenzResponse> {
const selectedTeamId = req.user?.team_id;
if (!selectedTeamId) {
return res.status(400).send(new ServerResponse(false, "No selected team"));
}
const q = `SELECT team_id AS id, name
FROM team_members tm
LEFT JOIN teams ON teams.id = tm.team_id
WHERE tm.user_id = $1
AND tm.team_id = $2
AND role_id IN (SELECT id
FROM roles
WHERE (admin_role IS TRUE OR owner IS TRUE))
ORDER BY name;`;
const result = await db.query(q, [req.user?.id]);
const result = await db.query(q, [req.user?.id, selectedTeamId]);
result.rows.forEach((team: any) => team.selected = true);
return res.status(200).send(new ServerResponse(true, result.rows));
}

View File

@@ -445,27 +445,44 @@ export default class ReportingAllocationController extends ReportingControllerBa
}
}
// Count only weekdays (Mon-Fri) in the period
// Fetch organization_id from the selected team
const selectedTeamId = req.user?.team_id;
let organizationId: string | undefined = undefined;
if (selectedTeamId) {
const orgIdQuery = `SELECT organization_id FROM teams WHERE id = $1`;
const orgIdResult = await db.query(orgIdQuery, [selectedTeamId]);
organizationId = orgIdResult.rows[0]?.organization_id;
}
// Fetch organization working hours and working days
let orgWorkingHours = 8;
let orgWorkingDays: { [key: string]: boolean } = {
monday: true, tuesday: true, wednesday: true, thursday: true, friday: true, saturday: false, sunday: false
};
if (organizationId) {
const orgHoursQuery = `SELECT working_hours FROM organizations WHERE id = $1`;
const orgHoursResult = await db.query(orgHoursQuery, [organizationId]);
if (orgHoursResult.rows[0]?.working_hours) {
orgWorkingHours = orgHoursResult.rows[0].working_hours;
}
const orgDaysQuery = `SELECT monday, tuesday, wednesday, thursday, friday, saturday, sunday FROM organization_working_days WHERE organization_id = $1 ORDER BY created_at DESC LIMIT 1`;
const orgDaysResult = await db.query(orgDaysQuery, [organizationId]);
if (orgDaysResult.rows[0]) {
orgWorkingDays = orgDaysResult.rows[0];
}
}
// Count only organization working days in the period
let workingDays = 0;
let current = startDate.clone();
while (current.isSameOrBefore(endDate, 'day')) {
const day = current.isoWeekday();
if (day >= 1 && day <= 5) workingDays++;
const weekday = current.format('dddd').toLowerCase(); // e.g., 'monday'
if (orgWorkingDays[weekday]) workingDays++;
current.add(1, 'day');
}
// Get hours_per_day for all selected projects
const projectHoursQuery = `SELECT id, hours_per_day FROM projects WHERE id IN (${projectIds})`;
const projectHoursResult = await db.query(projectHoursQuery, []);
const projectHoursMap: Record<string, number> = {};
for (const row of projectHoursResult.rows) {
projectHoursMap[row.id] = row.hours_per_day || 8;
}
// Sum total working hours for all selected projects
let totalWorkingHours = 0;
for (const pid of Object.keys(projectHoursMap)) {
totalWorkingHours += workingDays * projectHoursMap[pid];
}
// Use organization working hours for total working hours
const totalWorkingHours = workingDays * orgWorkingHours;
const durationClause = this.getDateRangeClause(duration || DATE_RANGES.LAST_WEEK, date_range);
const archivedClause = archived