diff --git a/system/modules/task/models/TaskService.php b/system/modules/task/models/TaskService.php index 4d30666a5..dd5e0895c 100755 --- a/system/modules/task/models/TaskService.php +++ b/system/modules/task/models/TaskService.php @@ -1116,4 +1116,46 @@ public function navList(): array new MenuLinkStruct("Task Groups", "task-group/viewtaskgrouptypes"), ]; } + + public function getTotalTimeByTimeType($task_id) { + $all_tasks = TaskService::getInstance($this->w)->getTasks(); + foreach ($all_tasks as $task) { + if ($task->id == $task_id) { + $current_task = $task; + } + } + $all_timelogs_for_task = TimelogService::getInstance($this->w)->getTimelogsForObject($current_task); + + $config_var = Config::get('task.TaskType_' . $current_task->task_type); + if (!array_key_exists('time-types', $config_var)) { + return; + } + $timelog_types = $config_var['time-types']; + + $time_totals_for_time_types = []; + foreach ($timelog_types as $timelog_type) { + $total_time_for_type = 0; + foreach ($all_timelogs_for_task as $timelog) { + if ($timelog->time_type == $timelog_type) { + $total_time_for_type += $timelog->getDuration(); + } + $total_time_fmtd = floor($total_time_for_type / 3600) . gmdate(":i:s", $total_time_for_type) . ' '; + } + $time_totals_for_time_types[$timelog_type] = $total_time_fmtd; + } + return $time_totals_for_time_types; + } + + public function getTotalTimeByBillable($task_id) { + $time_totals = $this->getTotalTimeByTimeType($task_id); + $time_totals_in_seconds = []; + foreach ($time_totals as $time_total) { + $time_totals_in_seconds[] = strtotime("1970-01-01 $time_total UTC"); + } + $billable_time_in_seconds = array_sum(array_merge(array_slice($time_totals_in_seconds, 0, 3), [$time_totals_in_seconds[4]])); + $billable_time = floor($billable_time_in_seconds / 3600) . gmdate(":i:s", $billable_time_in_seconds); + $nonbillable_time = floor($time_totals_in_seconds[3] / 3600) . gmdate(":i:s", $time_totals_in_seconds[3]); + return ['Billable' => substr($billable_time, 0, -3), 'Non-Billable' => substr($nonbillable_time, 0, -3)]; + } + } diff --git a/system/modules/task/models/TaskTime.php b/system/modules/task/models/TaskTime.php index f2b6985ae..e1b759e7d 100755 --- a/system/modules/task/models/TaskTime.php +++ b/system/modules/task/models/TaskTime.php @@ -28,5 +28,4 @@ public function getComment() { public function getTask() { return $this->getObject("Task", $this->task_id); } - } diff --git a/system/modules/timelog/partials/actions/listtimelog.php b/system/modules/timelog/partials/actions/listtimelog.php index 3b84abc37..4f6d18a49 100755 --- a/system/modules/timelog/partials/actions/listtimelog.php +++ b/system/modules/timelog/partials/actions/listtimelog.php @@ -11,10 +11,21 @@ function listtimelog(\Web $w, $params) { return $carry += $timelog->getDuration(); }); } + + $all_tasks = \TaskService::getInstance($w)->getTasks(); + foreach ($all_tasks as $task) { + if ($task->id == $params['object_id']) { + $current_task = $task; + } + } + $task_types_with_time_types = get_task_types_with_time_types($w); + if (in_array($current_task->task_type, $task_types_with_time_types) && sizeof($timelogs) > 0) { + $w->ctx("billable_hours", \TaskService::getInstance($w)->getTotalTimeByBillable($params['object_id'])); + } $w->ctx("total", !empty($total) ? $total : 0); $w->ctx("class", $params['object_class']); $w->ctx("id", $params['object_id']); $w->ctx("redirect", !empty($params['redirect']) ? $params['redirect'] : ""); $w->ctx("timelogs", $timelogs); -} \ No newline at end of file +} diff --git a/system/modules/timelog/partials/templates/listtimelog.tpl.php b/system/modules/timelog/partials/templates/listtimelog.tpl.php index 4a8528c93..6f5a0d44d 100755 --- a/system/modules/timelog/partials/templates/listtimelog.tpl.php +++ b/system/modules/timelog/partials/templates/listtimelog.tpl.php @@ -1,7 +1,30 @@ -

- getFormatPeriod($total); ?> -

+ +
+ +

+ getFormatPeriod($total); ?> +

+ +
+ +

+ Non-Billable: ' . $billable_hours['Non-Billable']; + } + ?> +

+
+ +

+ getFormatPeriod($total); ?> +

+ diff --git a/system/modules/timelog/tests/acceptance/playwright/timelog.utils.ts b/system/modules/timelog/tests/acceptance/playwright/timelog.utils.ts index 3153feb07..bf86ef7dd 100644 --- a/system/modules/timelog/tests/acceptance/playwright/timelog.utils.ts +++ b/system/modules/timelog/tests/acceptance/playwright/timelog.utils.ts @@ -30,7 +30,7 @@ export class TimelogHelper { await expect(page.getByText(timelog)).toBeVisible(); } - static async createTimelog(page: Page, isMobile: boolean, timelog: string, taskName: string, taskID: string, date: DateTime, start_time: string, end_time: string, check_duplicate: boolean = false) + static async createTimelog(page: Page, isMobile: boolean, timelog: string, taskName: string, taskID: string, date: DateTime, start_time: string, end_time: string, check_duplicate: boolean = false, time_type?: string) { if(page.url() != HOST + "/task/edit/" + taskID + "#details") { if(page.url() != HOST + "/task/tasklist") @@ -58,6 +58,9 @@ export class TimelogHelper { await page.locator("#time_start").fill(start_time); await page.locator("#time_end").fill(end_time); await page.getByLabel("Description", {exact: true}).fill(timelog); + if (time_type) { + await page.getByRole("combobox", {name: 'Task time' }).selectOption({ value: time_type }); + } await page.locator("#timelog_edit_form").getByRole("button", { name: "Save" }).click(); // if(await page.$("#saved_record_id") != null) diff --git a/system/modules/timelog/timelog.hooks.php b/system/modules/timelog/timelog.hooks.php index 55e347841..d93d0c0f9 100755 --- a/system/modules/timelog/timelog.hooks.php +++ b/system/modules/timelog/timelog.hooks.php @@ -17,3 +17,32 @@ function timelog_core_dbobject_after_delete($w, $obj) } } } + +// find the task types which have time types +function get_task_types_with_time_types($w) +{ + $taskgroup_types = TaskService::getInstance($w)->getAllTaskGroupTypes(); + + $task_types = []; + foreach ($taskgroup_types as $taskgroup_type) { + $config_var = Config::get("task." . $taskgroup_type[1]); + if (!$config_var || !array_key_exists('tasktypes', $config_var)) { + continue; + } + + $task_types_for_taskgroup_type = $config_var['tasktypes']; + foreach (array_keys($task_types_for_taskgroup_type) as $task_type_for_taskgroup_type) { + $task_types[] = 'TaskType_' . $task_type_for_taskgroup_type; + } + } + + $task_types_with_time_types = []; + foreach ($task_types as $task_type) { + $config_var = Config::get("task." . $task_type); + if ($config_var && array_key_exists('time-types', $config_var)) { + $task_types_with_time_types[] = substr($task_type, 9); + } + } + + return $task_types_with_time_types; +}