diff --git a/app/Domain/Comments/Repositories/Comments.php b/app/Domain/Comments/Repositories/Comments.php index c4f868740..7d9d65b2d 100644 --- a/app/Domain/Comments/Repositories/Comments.php +++ b/app/Domain/Comments/Repositories/Comments.php @@ -261,5 +261,26 @@ public function editComment($text, $id): bool $stmn->closeCursor(); return $result; } + + public function getAllAccountComments(): array|false + { + $sql = "SELECT comment.id, + comment.module, + comment.text, + comment.date, + comment.moduleId, + comment.userId, + comment.commentParent, + comment.status + FROM zp_comment as comment"; + + $stmn = $this->db->database->prepare($sql); + + $stmn->execute(); + $values = $stmn->fetchAll(); + $stmn->closeCursor(); + + return $values; + } } } diff --git a/app/Domain/Comments/Services/Comments.php b/app/Domain/Comments/Services/Comments.php index b62ef433a..bee0836a7 100644 --- a/app/Domain/Comments/Services/Comments.php +++ b/app/Domain/Comments/Services/Comments.php @@ -139,6 +139,9 @@ public function deleteComment($commentId): bool return $this->commentRepository->deleteComment($commentId); } + public function pollComments(): array | false + { + return $this->commentRepository->getAllAccountComments(); + } } - } diff --git a/app/Domain/Goalcanvas/Repositories/Goalcanvas.php b/app/Domain/Goalcanvas/Repositories/Goalcanvas.php index 14403236b..3a5d839d0 100644 --- a/app/Domain/Goalcanvas/Repositories/Goalcanvas.php +++ b/app/Domain/Goalcanvas/Repositories/Goalcanvas.php @@ -61,11 +61,11 @@ class Goalcanvas extends Canvas * @var array */ protected array $dataLabels = [ - 1 => ['title' => 'label.what_are_you_measuring', 'field' => 'assumptions', 'type' => 'string', 'active' => true], - 2 => ['title' => 'label.current_value', 'field' => 'data', 'type' => 'int', 'active' => true], - 3 => ['title' => 'label.goal_value', 'field' => 'conclusion', 'type' => 'int', 'active' => true], + 1 => ['title' => 'label.what_are_you_measuring', 'field' => 'assumptions', 'type' => 'string', 'active' => true], + 2 => ['title' => 'label.current_value', 'field' => 'data', 'type' => 'int', 'active' => true], + 3 => ['title' => 'label.goal_value', 'field' => 'conclusion', 'type' => 'int', 'active' => true], - ]; + ]; /** @@ -159,5 +159,62 @@ public function getSingleCanvas($canvasId): false|array return $values; } + + /** + * Gets all goals related to a milestone + * + * @return array|false + */ + public function getAllAccountGoals(): false|array + { + $sql = "SELECT + zp_canvas_items.id, + zp_canvas_items.description, + zp_canvas_items.title, + zp_canvas_items.assumptions, + zp_canvas_items.data, + zp_canvas_items.conclusion, + zp_canvas_items.box, + zp_canvas_items.author, + zp_canvas_items.created, + zp_canvas_items.modified, + zp_canvas_items.canvasId, + zp_canvas_items.sortindex, + zp_canvas_items.status, + zp_canvas_items.relates, + zp_canvas_items.milestoneId, + zp_canvas_items.kpi, + zp_canvas_items.data1, + zp_canvas_items.data2, + zp_canvas_items.data3, + zp_canvas_items.data4, + zp_canvas_items.data5, + zp_canvas_items.startDate, + zp_canvas_items.endDate, + zp_canvas_items.setting, + zp_canvas_items.metricType, + zp_canvas_items.startValue, + zp_canvas_items.currentValue, + zp_canvas_items.endValue, + zp_canvas_items.impact, + zp_canvas_items.effort, + zp_canvas_items.probability, + zp_canvas_items.action, + zp_canvas_items.assignedTo, + zp_canvas_items.parent, + zp_canvas_items.tags + FROM + zp_canvas_items + + WHERE zp_canvas_items.box = 'goal' + + "; + + $stmn = $this->db->database->prepare($sql); + $stmn->execute(); + $values = $stmn->fetchAll(); + $stmn->closeCursor(); + return $values; + } } } diff --git a/app/Domain/Goalcanvas/Services/Goalcanvas.php b/app/Domain/Goalcanvas/Services/Goalcanvas.php index 4da3c9396..8a274c7bb 100644 --- a/app/Domain/Goalcanvas/Services/Goalcanvas.php +++ b/app/Domain/Goalcanvas/Services/Goalcanvas.php @@ -172,19 +172,35 @@ public function getGoalsByMilestone($milestoneId): array return $goals; } - public function updateGoalboard($values) { + public function updateGoalboard($values) + { return $this->goalRepository->updateCanvas($values); } - public function createGoalboard($values) { + public function createGoalboard($values) + { return $this->goalRepository->addCanvas($values); } - public function getSingleCanvas($id) { + public function getSingleCanvas($id) + { return $this->goalRepository->getSingleCanvas($id); } + public function pollGoals() + { + return $this->goalRepository->getAllAccountGoals(); + } - } + public function pollForUpdatedGoals(): array|false + { + $goals = $this->goalRepository->getAllAccountGoals(); + + foreach ($goals as $key => $goal) { + $goals[$key]['id'] = $goal['id'] . '-' . $goal['modified']; + } + return $goals; + } + } } diff --git a/app/Domain/Ideas/Repositories/Ideas.php b/app/Domain/Ideas/Repositories/Ideas.php index c75b969ea..f782e783e 100644 --- a/app/Domain/Ideas/Repositories/Ideas.php +++ b/app/Domain/Ideas/Repositories/Ideas.php @@ -626,5 +626,34 @@ public function bulkUpdateIdeaStatus($params): bool return true; } + + public function getAllIdeas(): array|false + { + $sql = "SELECT zp_canvas_items.id, + zp_canvas_items.description, + zp_canvas_items.assumptions, + zp_canvas_items.data, + zp_canvas_items.conclusion, + zp_canvas_items.box, + zp_canvas_items.author, + zp_canvas_items.created, + zp_canvas_items.modified, + zp_canvas_items.canvasId, + zp_canvas_items.sortindex, + zp_canvas_items.status, + zp_canvas_items.tags, + zp_canvas_items.milestoneId + FROM zp_canvas_items + LEFT JOIN zp_canvas AS canvasBoard ON zp_canvas_items.canvasId = canvasBoard.id + WHERE canvasBoard.type = 'idea'"; + + $stmn = $this->db->database->prepare($sql); + + $stmn->execute(); + $values = $stmn->fetchAll(PDO::FETCH_ASSOC); + $stmn->closeCursor(); + + return $values; + } } } diff --git a/app/Domain/Ideas/Services/Ideas.php b/app/Domain/Ideas/Services/Ideas.php new file mode 100644 index 000000000..898f9fcea --- /dev/null +++ b/app/Domain/Ideas/Services/Ideas.php @@ -0,0 +1,32 @@ +ideasRepository = $ideasRepository; + } + + public function pollForNewIdeas(): array + { + return $this->ideasRepository->getAllIdeas(); + } + + public function pollForUpdatedIdeas(): array + { + $ideas = $this->ideasRepository->getAllIdeas(); + + foreach ($ideas as $key => $idea) { + $ideas[$key]['id'] = $idea['id'] . '-' . $idea['modified']; + } + + return $ideas; + } + } +} diff --git a/app/Domain/Install/Repositories/Install.php b/app/Domain/Install/Repositories/Install.php index 3eed7d9f0..5c6f94a65 100644 --- a/app/Domain/Install/Repositories/Install.php +++ b/app/Domain/Install/Repositories/Install.php @@ -617,7 +617,7 @@ private function sqlPrep(): string ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; insert into `zp_tickets`(`id`,`projectId`,`headline`,`description`,`acceptanceCriteria`,`date`,`dateToFinish`,`priority`,`status`,`userId`,`os`,`browser`,`resolution`,`component`,`version`,`url`,`milestoneid`,`editFrom`,`editTo`,`editorId`,`planHours`,`hourRemaining`,`type`,`production`,`staging`,`storypoints`,`sprint`,`sortindex`,`kanbanSortIndex`) values - (9,3,'Getting Started with Leantime', '".$gettingStartedDescription."','','".date("Y-m-d")."','".date("Y-m-d")."',2,3,1,NULL,NULL,NULL,NULL,'',NULL,NULL,'1969-12-31 00:00:00','1969-12-31 00:00:00',1,0,0,'Story',0,0,0,0,NULL,NULL); + (9,3,'Getting Started with Leantime', '" . $gettingStartedDescription . "','','" . date("Y-m-d") . "','" . date("Y-m-d") . "',2,3,1,NULL,NULL,NULL,NULL,'',NULL,NULL,'1969-12-31 00:00:00','1969-12-31 00:00:00',1,0,0,'Story',0,0,0,0,NULL,NULL); CREATE TABLE `zp_timesheets` ( `id` int(255) NOT NULL AUTO_INCREMENT, @@ -1316,11 +1316,11 @@ private function update_sql_20111(): bool|array "UPDATE zp_projects SET menuType = '" . MenuRepository::DEFAULT_MENU . "'", "ALTER TABLE zp_canvas_items ADD relates VARCHAR(255) null", "UPDATE zp_canvas_items INNER JOIN zp_canvas ON zp_canvas.id = zp_canvas_items.id " . - "SET zp_canvas_items.status = 'draft' WHERE zp_canvas_items.status = 'danger' AND zp_canvas.type = 'leancanvas'", + "SET zp_canvas_items.status = 'draft' WHERE zp_canvas_items.status = 'danger' AND zp_canvas.type = 'leancanvas'", "UPDATE zp_canvas_items INNER JOIN zp_canvas ON zp_canvas.id = zp_canvas_items.id " . - "SET zp_canvas_items.status = 'valid' WHERE zp_canvas_items.status = 'sucess' AND zp_canvas.type = 'leancanvas'", + "SET zp_canvas_items.status = 'valid' WHERE zp_canvas_items.status = 'sucess' AND zp_canvas.type = 'leancanvas'", "UPDATE zp_canvas_items INNER JOIN zp_canvas ON zp_canvas.id = zp_canvas_items.id " . - "SET zp_canvas_items.status = 'invalid' WHERE zp_canvas_items.status = 'info' AND zp_canvas.type = 'leancanvas'", + "SET zp_canvas_items.status = 'invalid' WHERE zp_canvas_items.status = 'info' AND zp_canvas.type = 'leancanvas'", "UPDATE zp_canvas SET zp_canvas.type = 'retroscanvas' WHERE zp_canvas.type = 'retrospective'", ]; @@ -1909,7 +1909,8 @@ public function update_sql_20407(): bool|array } } - public function update_sql_30002(): bool|array { + public function update_sql_30002(): bool|array + { $errors = array(); @@ -1956,8 +1957,6 @@ public function update_sql_30003(): bool|array { return true; - } - } } diff --git a/app/Domain/Projects/Services/Projects.php b/app/Domain/Projects/Services/Projects.php index fabd8fe75..13b75585c 100644 --- a/app/Domain/Projects/Services/Projects.php +++ b/app/Domain/Projects/Services/Projects.php @@ -180,7 +180,7 @@ public function getProjectProgress($projectId): array } - $returnValue = array("percent" => $finalPercent, "estimatedCompletionDate" => $completionDate , "plannedCompletionDate" => ''); + $returnValue = array("percent" => $finalPercent, "estimatedCompletionDate" => $completionDate, "plannedCompletionDate" => ''); if ($numberOfClosedTickets < 10) { $returnValue['estimatedCompletionDate'] = " Complete more To-Dos to see that!"; } elseif ($finalPercent == 100) { @@ -614,7 +614,7 @@ public function setCurrentProject(): void //If last project setting is set use that $lastProject = $this->settingsRepo->getSetting("usersettings." . session("userdata.id") . ".lastProject"); if ( - ! empty($lastProject) + !empty($lastProject) && $this->changeCurrentSessionProject($lastProject) ) { return; @@ -1053,26 +1053,26 @@ public function duplicateProject(int $projectId, int $clientId, string $projectN //Ideas $this->duplicateCanvas( - repository:IdeaRepository::class, + repository: IdeaRepository::class, originalProjectId: $projectId, newProjectId: $newProjectId ); $this->duplicateCanvas( - repository:GoalcanvaRepository::class, + repository: GoalcanvaRepository::class, originalProjectId: $projectId, newProjectId: $newProjectId ); $this->duplicateCanvas( - repository:Wiki::class, + repository: Wiki::class, originalProjectId: $projectId, newProjectId: $newProjectId, canvasTypeName: "wiki" ); $this->duplicateCanvas( - repository:LeancanvaRepository::class, + repository: LeancanvaRepository::class, originalProjectId: $projectId, newProjectId: $newProjectId ); @@ -1241,19 +1241,19 @@ public function getProjectSetupChecklist($projectId): array "status" => "", "link" => BASE_URL . "/projects/showProject/" . session("currentProject") . "", "description" => "checklist.define.tasks.description", - ), + ), "defineTeam" => array( "title" => "label.defineTeam", "status" => "", "link" => BASE_URL . "/projects/showProject/" . session("currentProject") . "#team", "description" => "checklist.define.tasks.defineTeam", - ), + ), "createBlueprint" => array( "title" => "label.createBlueprint", "status" => "", "link" => BASE_URL . "/strategy/showBoards/", "description" => "checklist.define.tasks.createBlueprint", - ), + ), ), "status" => '', ), @@ -1266,7 +1266,7 @@ public function getProjectSetupChecklist($projectId): array "status" => "", "link" => BASE_URL . "/goalcanvas/dashboard", "description" => "checklist.goals.tasks.setGoals", - ), + ), ), "status" => '', ), @@ -1279,7 +1279,7 @@ public function getProjectSetupChecklist($projectId): array "status" => "", "link" => BASE_URL . "/tickets/roadmap", "description" => "checklist.timeline.tasks.createMilestones", - ), + ), ), "status" => '', @@ -1292,13 +1292,13 @@ public function getProjectSetupChecklist($projectId): array "title" => "label.createTasks", "status" => "", "link" => BASE_URL . "/tickets/showAll", "description" => "checklist.implementation.tasks.createTasks ", - ), + ), "finish80percent" => array( "title" => "label.finish80percent", "status" => "", "link" => BASE_URL . "/reports/show", "description" => "checklist.implementation.tasks.finish80percent", - ), + ), ), "status" => '', ), @@ -1344,7 +1344,7 @@ public function getProjectSetupChecklist($projectId): array } //Add overrides - if (! $stepsCompleted = $this->settingsRepo->getSetting("projectsettings.$projectId.stepsComplete")) { + if (!$stepsCompleted = $this->settingsRepo->getSetting("projectsettings.$projectId.stepsComplete")) { $stepsCompleted = []; } else { $stepsCompleted = unserialize($stepsCompleted); @@ -1390,7 +1390,7 @@ public function getProjectSetupChecklist($projectId): array // otherwise, set the step as completed $progressSteps[$name]['status'] = 'done'; if ( - ! in_array($previousValue['stepType'] ?? null, ['current', '']) + !in_array($previousValue['stepType'] ?? null, ['current', '']) || $name == array_key_first($progressSteps) ) { $progressSteps[$name]['stepType'] = 'complete'; @@ -1443,7 +1443,8 @@ public function updateProjectProgress($stepsComplete, $projectId): void * @param array $projects An array of project IDs to be assigned to the user. * @return bool Returns true if the project relations were successfully edited, false otherwise. */ - public function editUserProjectRelations($id, $projects): bool { + public function editUserProjectRelations($id, $projects): bool + { return $this->projectRepository->editUserProjectRelations($id, $projects); } @@ -1559,6 +1560,16 @@ public function getAll(bool $showClosedProjects = false): array { return $this->projectRepository->getAll($showClosedProjects); } - } + public function pollForUpdatedProjects(): array + { + $projects = $this->projectRepository->getAll(false); + + foreach ($projects as $key => $project) { + $projects[$key]['id'] = $project['id'] . '-' . $project['modified']; + } + + return $projects; + } + } } diff --git a/app/Domain/Tickets/Repositories/Tickets.php b/app/Domain/Tickets/Repositories/Tickets.php index 3e39f729a..1907e4f37 100644 --- a/app/Domain/Tickets/Repositories/Tickets.php +++ b/app/Domain/Tickets/Repositories/Tickets.php @@ -693,7 +693,6 @@ public function simpleTicketQuery(?int $userId, ?int $projectId): array|false $stmn->closeCursor(); return $values; - } public function getScheduledTasks(CarbonImmutable $dateFrom, CarbonImmutable $dateTo, ?int $userId = null) @@ -1792,6 +1791,7 @@ public function updateTicket(array $values, $id): bool description=:description, projectId=:projectId, status = :status, + date = :date, dateToFinish = :dateToFinish, sprint = :sprint, storypoints = :storypoints, @@ -1814,6 +1814,7 @@ public function updateTicket(array $values, $id): bool $stmn->bindValue(':description', $values['description'], PDO::PARAM_STR); $stmn->bindValue(':projectId', $values['projectId'], PDO::PARAM_STR); $stmn->bindValue(':status', $values['status'], PDO::PARAM_STR); + $stmn->bindValue(':date', $values['date'], PDO::PARAM_STR); $stmn->bindValue(':dateToFinish', $values['dateToFinish'], PDO::PARAM_STR); $stmn->bindValue(':sprint', $values['sprint'], PDO::PARAM_STR); $stmn->bindValue(':storypoints', $values['storypoints'], PDO::PARAM_STR); @@ -2064,5 +2065,4 @@ public function delMilestone($id): bool return true; } } - } diff --git a/app/Domain/Tickets/Services/Tickets.php b/app/Domain/Tickets/Services/Tickets.php index 469979abc..9dbdfba2c 100644 --- a/app/Domain/Tickets/Services/Tickets.php +++ b/app/Domain/Tickets/Services/Tickets.php @@ -399,8 +399,8 @@ public function simpleTicketCounter(?int $userId = null, ?int $project = null, s $projectStatusLabels[$ticket['projectId']][$ticket['status']]["statusType"] !== "DONE" ) ) { - $ticketCounter++; - continue; + $ticketCounter++; + continue; } if ( @@ -590,7 +590,7 @@ public function getAllGrouped($searchCriteria): array case "priority": case "storypoints": $ticketGroups = array_sort($ticketGroups, 'id'); - // no break + // no break default: $ticketGroups = array_sort($ticketGroups, 'label'); break; @@ -808,10 +808,10 @@ public function getOpenUserTicketsByPriority($userId, $projectId): array } else { // If the priority is not set, the label for priority not defined is used. if (empty($this->ticketRepository->priority[$row['priority']])) { - $label =$this->language->__("label.priority_not_defined"); + $label = $this->language->__("label.priority_not_defined"); } $tickets[$row['priority']] = array( - "labelName" =>$label, + "labelName" => $label, "tickets" => array($row), "groupValue" => $row['time'], ); @@ -1237,7 +1237,7 @@ public function addTicket($values) 'headline' => $values['headline'] ?? "", 'type' => $values['type'] ?? "task", 'description' => $values['description'] ?? "", - 'projectId' => $values['projectId'] ?? session("currentProject") , + 'projectId' => $values['projectId'] ?? session("currentProject"), 'editorId' => $values['editorId'] ?? "", 'userId' => session("userdata.id"), 'date' => gmdate("Y-m-d H:i:s"), @@ -1323,6 +1323,15 @@ public function addTicket($values) * - 'time*/ public function updateTicket($values): array|bool { + if (!isset($values["headline"])) { + $currentTicket = $this->getTicket($values['id']); + + if (!$currentTicket) { + return array("msg" => "This ticket id does not exist within your leantime account.", "type" => "error"); + } + + $values["headline"] = $currentTicket->headline; + } $values = array( 'id' => $values['id'], @@ -1354,34 +1363,30 @@ public function updateTicket($values): array|bool return array("msg" => "notifications.ticket_save_error_no_access", "type" => "error"); } - if ($values['headline'] === '') { - return array("msg" => "notifications.ticket_save_error_no_headline", "type" => "error"); - } else { - $values = $this->prepareTicketDates($values); + $values = $this->prepareTicketDates($values); - //Update Ticket - if ($this->ticketRepository->updateTicket($values, $values['id']) === true) { - $subject = sprintf($this->language->__("email_notifications.todo_update_subject"), $values['id'], $values['headline']); - $actual_link = BASE_URL . "/dashboard/home#/tickets/showTicket/" . $values['id']; - $message = sprintf($this->language->__("email_notifications.todo_update_message"), session("userdata.name"), $values['headline']); + //Update Ticket + if ($this->ticketRepository->updateTicket($values, $values['id']) === true) { + $subject = sprintf($this->language->__("email_notifications.todo_update_subject"), $values['id'], $values['headline']); + $actual_link = BASE_URL . "/dashboard/home#/tickets/showTicket/" . $values['id']; + $message = sprintf($this->language->__("email_notifications.todo_update_message"), session("userdata.name"), $values['headline']); - $notification = app()->make(NotificationModel::class); - $notification->url = array( - "url" => $actual_link, - "text" => $this->language->__("email_notifications.todo_update_cta"), - ); - $notification->entity = $values; - $notification->module = "tickets"; - $notification->projectId = session("currentProject"); - $notification->subject = $subject; - $notification->authorId = session("userdata.id"); - $notification->message = $message; + $notification = app()->make(NotificationModel::class); + $notification->url = array( + "url" => $actual_link, + "text" => $this->language->__("email_notifications.todo_update_cta"), + ); + $notification->entity = $values; + $notification->module = "tickets"; + $notification->projectId = session("currentProject"); + $notification->subject = $subject; + $notification->authorId = session("userdata.id"); + $notification->message = $message; - $this->projectService->notifyProjectUsers($notification); + $this->projectService->notifyProjectUsers($notification); - return true; - } + return true; } @@ -2059,6 +2064,53 @@ public function prepareTicketDates(&$values) return $values; } - } + public function pollForNewAccountMilestones(): array | false + { + return $this->ticketRepository->getAllBySearchCriteria( + ["type" => "milestone"], + 'date' + ); + } + + // since the date attribute of milestones gets updated when the milestone is updated we need to poll for updated milestones + // using that date attribute + public function pollForUpdatedAccountMilestones(): array|false + { + $milestones = $this->ticketRepository->getAllBySearchCriteria( + ["type" => "milestone"], + 'date' + ); + + foreach ($milestones as $key => $milestone) { + $milestones[$key]['id'] = $milestone['id'] . '-' . $milestone['date']; + } + + return $milestones; + } + + public function pollForNewAccountTodos(): array|false + { + return $this->ticketRepository->getAllBySearchCriteria( + ["excludeType" => "milestone"], + 'date' + ); + } + + // Since the date attribute of todos gets updated when the todo is updated we need to poll for updated todos + // using that date attribute + public function pollForUpdatedAccountTodos(): array|false + { + $todos = $this->ticketRepository->getAllBySearchCriteria( + ["excludeType" => "milestone"], + 'date' + ); + + foreach ($todos as $key => $todo) { + $todos[$key]['id'] = $todo['id'] . '-' . $todo['date']; + } + + return $todos; + } + } } diff --git a/app/Domain/Timesheets/Repositories/Timesheets.php b/app/Domain/Timesheets/Repositories/Timesheets.php index b33818c6c..020f59197 100644 --- a/app/Domain/Timesheets/Repositories/Timesheets.php +++ b/app/Domain/Timesheets/Repositories/Timesheets.php @@ -82,6 +82,7 @@ public function getAll(?int $id, ?string $kind, ?CarbonInterface $dateFrom, ?Car zp_tickets.headline, zp_tickets.planHours, zp_tickets.tags, + zp_tickets.modified, milestone.headline as milestone FROM zp_timesheets @@ -231,6 +232,7 @@ public function getWeeklyTimesheets(int $projectId, CarbonInterface $fromDate, i zp_timesheets.paid, zp_timesheets.paidDate, zp_timesheets.kind, + zp_timesheets.modified, zp_tickets.headline, zp_tickets.planHours, zp_projects.name, @@ -329,7 +331,8 @@ public function addTime(array $values): void invoicedCompDate, rate, paid, - paidDate + paidDate, + modified ) VALUES ( :userId, :ticket, @@ -343,7 +346,8 @@ public function addTime(array $values): void :invoicedCompDate, :rate, :paid, - :paidDate + :paidDate, + :modified ) ON DUPLICATE KEY UPDATE hours = hours + :hours, description = CONCAT(:date, '\n', :description, '\n', '--', '\n\n', description)"; @@ -367,6 +371,7 @@ public function addTime(array $values): void $call->bindValue(':hours', $values['hours']); $call->bindValue(':paid', $values['paid'] ?? ''); $call->bindValue(':paidDate', $values['paidDate'] ?? ''); + $call->bindValue(':modified', date("Y-m-d H:i:s"), PDO::PARAM_STR); $call->execute(); @@ -396,7 +401,8 @@ public function upsertTimesheetEntry(array $values): void invoicedCompDate, rate, paid, - paidDate + paidDate, + modified ) VALUES ( :userId, :ticket, @@ -409,7 +415,8 @@ public function upsertTimesheetEntry(array $values): void :invoicedCompDate, :rate, :paid, - :paidDate + :paidDate, + :modified ) ON DUPLICATE KEY UPDATE hours = :hours"; @@ -431,6 +438,7 @@ public function upsertTimesheetEntry(array $values): void $call->bindValue(':hours', $values['hours']); $call->bindValue(':paid', $values['paid'] ?? ''); $call->bindValue(':paidDate', $values['paidDate'] ?? ''); + $call->bindValue(':modified', date("Y-m-d H:i:s"), PDO::PARAM_STR); $call->execute(); @@ -460,7 +468,8 @@ public function getTimesheet(int $id): mixed zp_timesheets.invoicedEmplDate, zp_timesheets.invoicedCompDate, zp_timesheets.paid, - zp_timesheets.paidDate + zp_timesheets.paidDate, + zp_timesheets.modified FROM zp_timesheets LEFT JOIN zp_tickets ON zp_timesheets.ticketId = zp_tickets.id @@ -498,7 +507,8 @@ public function updateTime(array $values): void invoicedEmplDate =:invoicedEmplDate, invoicedCompDate =:invoicedCompDate, paid =:paid, - paidDate =:paidDate + paidDate =:paidDate, + modified =:modified WHERE id = :id"; @@ -517,6 +527,7 @@ public function updateTime(array $values): void $call->bindValue(':paid', $values['paid']); $call->bindValue(':paidDate', $values['paidDate']); $call->bindValue(':id', $values['id']); + $call->bindValue(':modified', date("Y-m-d H:i:s"), PDO::PARAM_STR); $call->execute(); @@ -537,7 +548,8 @@ public function updateHours(array $values): void $query = "UPDATE zp_timesheets SET - hours = :hours + hours = :hours, + modified =:modified WHERE userId = :userId AND ticketId = :ticketId @@ -555,6 +567,7 @@ public function updateHours(array $values): void $call->bindValue(':userId', $values['userId']); $call->bindValue(':ticketId', $values['ticket']); $call->bindValue(':kind', $values['kind']); + $call->bindValue(':modified', date("Y-m-d H:i:s"), PDO::PARAM_STR); $call->execute(); @@ -658,8 +671,8 @@ public function getLoggedHoursForTicket(int $ticketId): array } else { $utc = dtHelper()->dbNow()->format("Y-m-d"); $returnValues[$utc] = [ - 'utc' => $utc, - 'summe' => 0, + 'utc' => $utc, + 'summe' => 0, ]; } @@ -716,13 +729,15 @@ public function updateInvoices(array $invEmpl, array $invComp = [], array $paid foreach ($invEmpl as $row1) { $query = "UPDATE zp_timesheets SET invoicedEmpl = 1, - invoicedEmplDate = :date + invoicedEmplDate = :date, + modified = :modified WHERE id = :id "; $invEmplCall = $this->dbcall(func_get_args(), ['dbcall_key' => 'inv_empl']); $invEmplCall->prepare($query); $invEmplCall->bindValue(':id', $row1); $invEmplCall->bindValue(':date', Carbon::now(session("usersettings.timezone"))->setTimezone('UTC')); + $invEmplCall->bindValue(':modified', date('Y-m-d H:i:s'), PDO::PARAM_STR); $invEmplCall->execute(); unset($invEmplCall); @@ -731,13 +746,15 @@ public function updateInvoices(array $invEmpl, array $invComp = [], array $paid foreach ($invComp as $row2) { $query2 = "UPDATE zp_timesheets SET invoicedComp = 1, - invoicedCompDate = :date + invoicedCompDate = :date, + modified = :modified WHERE id = :id "; $invCompCall = $this->dbcall(func_get_args(), ['dbcall_key' => 'inv_comp']); $invCompCall->prepare($query2); $invCompCall->bindValue(':id', $row2); $invCompCall->bindValue(':date', Carbon::now(session("usersettings.timezone"))->setTimezone('UTC')); + $invCompCall->bindValue(':modified', date('Y-m-d H:i:s'), PDO::PARAM_STR); $invCompCall->execute(); unset($invCompCall); @@ -746,13 +763,15 @@ public function updateInvoices(array $invEmpl, array $invComp = [], array $paid foreach ($paid as $row3) { $query3 = "UPDATE zp_timesheets SET paid = 1, - paidDate = :date + paidDate = :date, + modified = :modified WHERE id = :id "; $paidCol = $this->dbcall(func_get_args(), ['dbcall_key' => 'paid']); $paidCol->prepare($query3); $paidCol->bindValue(':id', $row3); $paidCol->bindValue(':date', Carbon::now(session("usersettings.timezone"))->setTimezone('UTC')); + $paidCol->bindValue(':modified', date('Y-m-d H:i:s'), PDO::PARAM_STR); $paidCol->execute(); unset($paidCol); @@ -905,5 +924,55 @@ public function isClocked(int $id): false|array return $onTheClock; } + public function getAllAccountTimesheets(): array|false + { + $query = "SELECT + zp_timesheets.id, + zp_timesheets.userId, + zp_timesheets.ticketId, + zp_timesheets.workDate, + zp_timesheets.hours, + zp_timesheets.description, + zp_timesheets.kind, + zp_projects.name, + zp_projects.id AS projectId, + zp_clients.name AS clientName, + zp_clients.id AS clientId, + zp_timesheets.invoicedEmpl, + zp_timesheets.invoicedComp, + zp_timesheets.invoicedEmplDate, + zp_timesheets.invoicedCompDate, + zp_timesheets.paid, + zp_timesheets.paidDate, + zp_timesheets.modified, + zp_user.firstname, + zp_user.lastname, + zp_tickets.id as ticketId, + zp_tickets.headline, + zp_tickets.planHours, + zp_tickets.tags, + milestone.headline as milestone + FROM + zp_timesheets + LEFT JOIN zp_user ON zp_timesheets.userId = zp_user.id + LEFT JOIN zp_tickets ON zp_timesheets.ticketId = zp_tickets.id + LEFT JOIN zp_projects ON zp_tickets.projectId = zp_projects.id + LEFT JOIN zp_clients ON zp_projects.clientId = zp_clients.id + LEFT JOIN zp_tickets milestone ON zp_tickets.milestoneid = milestone.id"; + + $query .= " GROUP BY + zp_timesheets.id, + zp_timesheets.userId, + zp_timesheets.ticketId, + zp_timesheets.workDate, + zp_timesheets.hours, + zp_timesheets.description, + zp_timesheets.kind"; + + $call = $this->dbcall(func_get_args()); + + $call->prepare($query); + return $call->fetchAll(); + } } diff --git a/app/Domain/Timesheets/Services/Timesheets.php b/app/Domain/Timesheets/Services/Timesheets.php index 746d91391..fac241ccf 100644 --- a/app/Domain/Timesheets/Services/Timesheets.php +++ b/app/Domain/Timesheets/Services/Timesheets.php @@ -2,6 +2,7 @@ namespace Leantime\Domain\Timesheets\Services; +use Carbon\Carbon; use Carbon\CarbonInterface; use Illuminate\Contracts\Container\BindingResolutionException; use Leantime\Core\Support\FromFormat; @@ -452,4 +453,20 @@ public function getBookedHourTypes(): array { return $this->timesheetsRepo->kind; } + + public function pollForNewTimesheets(): array|false + { + return $this->timesheetsRepo->getAllAccountTimesheets(); + } + + public function pollForUpdatedTimesheets(): array|false + { + $timesheets = $this->timesheetsRepo->getAllAccountTimesheets(); + + foreach ($timesheets as $key => $timesheet) { + $timesheets[$key]['id'] = $timesheet['id'] . '-' . $timesheet['modified']; + } + + return $timesheets; + } }