diff --git a/files/custom/Espo/Modules/SmartAssistant/Services/AlertCalculator.php b/files/custom/Espo/Modules/SmartAssistant/Services/AlertCalculator.php index 8122299..66a29b4 100644 --- a/files/custom/Espo/Modules/SmartAssistant/Services/AlertCalculator.php +++ b/files/custom/Espo/Modules/SmartAssistant/Services/AlertCalculator.php @@ -46,7 +46,7 @@ class AlertCalculator ->where(['status!=' => self::CLOSED_STATUSES, 'deleted' => false])->count(); $totalOverdueTasks = $this->entityManager->getRDBRepository('Task') - ->where(['status!=' => ['Completed', 'Canceled', 'Deferred'], 'dateEnd<' => $today, 'dateEnd!=' => null, 'deleted' => false, 'parentType' => 'Case'])->count(); + ->where(['status!=' => ['Completed', 'Canceled', 'Deferred'], 'dateEnd<' => $today, 'dateEnd!=' => null, 'deleted' => false, 'parentType' => ['Case', 'Contact']])->count(); $upcomingHearings = $this->entityManager->getRDBRepository('Case') ->where(['cNextHearing>=' => $today, 'cNextHearing<=' => $weekEnd, 'status!=' => self::CLOSED_STATUSES, 'deleted' => false])->count(); @@ -106,7 +106,7 @@ class AlertCalculator $tasks = $this->entityManager->getRDBRepository('Task') ->select(['id', 'name', 'dateEnd', 'status', 'parentId', 'parentType', 'parentName', 'assignedUserName']) - ->where(['status!=' => ['Completed', 'Canceled', 'Deferred'], 'dateEnd<' => $today, 'dateEnd!=' => null, 'deleted' => false, 'parentType' => 'Case']) + ->where(['status!=' => ['Completed', 'Canceled', 'Deferred'], 'dateEnd<' => $today, 'dateEnd!=' => null, 'deleted' => false, 'parentType' => ['Case', 'Contact']]) ->order('dateEnd', 'ASC')->limit(0, 50)->find(); foreach ($tasks as $task) { @@ -132,7 +132,7 @@ class AlertCalculator $recentTaskThreshold = date('Y-m-d H:i:s', strtotime('-7 days')); $cases = $this->entityManager->getRDBRepository('Case') - ->select(['id', 'name', 'cNextHearing', 'assignedUserName', 'cLastActivityAt']) + ->select(['id', 'name', 'cNextHearing', 'assignedUserName', 'cLastActivityAt', 'contactId']) ->where(['cNextHearing>=' => $today, 'cNextHearing<=' => $threshold, 'status!=' => self::CLOSED_STATUSES, 'deleted' => false]) ->order('cNextHearing', 'ASC')->find(); @@ -140,13 +140,7 @@ class AlertCalculator $lastActivity = $case->get('cLastActivityAt'); if ($lastActivity && $lastActivity >= $recentActivityThreshold) continue; - $openTaskCount = $this->entityManager->getRDBRepository('Task') - ->where(['parentId' => $case->get('id'), 'parentType' => 'Case', 'status!=' => ['Completed', 'Canceled', 'Deferred'], 'deleted' => false])->count(); - if ($openTaskCount > 0) continue; - - $recentCompleted = $this->entityManager->getRDBRepository('Task') - ->where(['parentId' => $case->get('id'), 'parentType' => 'Case', 'status' => 'Completed', 'modifiedAt>=' => $recentTaskThreshold, 'deleted' => false])->count(); - if ($recentCompleted > 0) continue; + if ($this->caseHasPreparation($case, $recentTaskThreshold)) continue; $daysUntil = (new \DateTime())->diff(new \DateTime($case->get('cNextHearing')))->days; $severity = ($daysUntil <= 1) ? 'critical' : 'warning'; @@ -162,6 +156,104 @@ class AlertCalculator return $alerts; } + /** + * Check if a case has any evidence of hearing preparation: + * 1. Tasks linked directly to the case (parentType='Case') + * 2. Tasks linked to the case's contacts (parentType='Contact') + * 3. Tasks linked through NhActivity records for this case + */ + private function caseHasPreparation(\Espo\ORM\Entity $case, string $recentTaskThreshold): bool + { + $caseId = $case->get('id'); + + // Collect all parentId+parentType pairs that relate to this case + $parentConditions = [ + ['parentType' => 'Case', 'parentId' => $caseId], + ]; + + // Also check tasks linked to the case's contacts + $contactIds = $this->getCaseContactIds($case); + if (!empty($contactIds)) { + $parentConditions[] = ['parentType' => 'Contact', 'parentId' => $contactIds]; + } + + // Check 1: Any open tasks linked to the case or its contacts + $openTaskCount = $this->entityManager->getRDBRepository('Task') + ->where([ + 'OR' => $parentConditions, + 'status!=' => ['Completed', 'Canceled', 'Deferred'], + 'deleted' => false, + ])->count(); + if ($openTaskCount > 0) return true; + + // Check 2: Any recently completed tasks linked to the case or its contacts + $recentCompleted = $this->entityManager->getRDBRepository('Task') + ->where([ + 'OR' => $parentConditions, + 'status' => 'Completed', + 'modifiedAt>=' => $recentTaskThreshold, + 'deleted' => false, + ])->count(); + if ($recentCompleted > 0) return true; + + // Check 3: Tasks linked through NhActivity records for this case + $nhActivityTaskCount = $this->entityManager->getRDBRepository('NhActivity') + ->where([ + 'caseId' => $caseId, + 'taskId!=' => null, + 'deleted' => false, + ])->count(); + + if ($nhActivityTaskCount > 0) { + $nhActivities = $this->entityManager->getRDBRepository('NhActivity') + ->select(['taskId']) + ->where([ + 'caseId' => $caseId, + 'taskId!=' => null, + 'deleted' => false, + ])->find(); + + $taskIds = []; + foreach ($nhActivities as $nha) { + $taskIds[] = $nha->get('taskId'); + } + + $activeNhTasks = $this->entityManager->getRDBRepository('Task') + ->where([ + 'id' => $taskIds, + 'status!=' => ['Canceled'], + 'deleted' => false, + ])->count(); + if ($activeNhTasks > 0) return true; + } + + return false; + } + + private function getCaseContactIds(\Espo\ORM\Entity $case): array + { + $contactIds = []; + + $primaryContactId = $case->get('contactId'); + if ($primaryContactId) { + $contactIds[] = $primaryContactId; + } + + $contacts = $this->entityManager->getRDBRepository('Case') + ->getRelation($case, 'contacts') + ->select(['id']) + ->find(); + + foreach ($contacts as $contact) { + $id = $contact->get('id'); + if (!in_array($id, $contactIds)) { + $contactIds[] = $id; + } + } + + return $contactIds; + } + private function findUnassignedNewCases(): array { $alerts = []; @@ -191,7 +283,7 @@ class AlertCalculator $tasks = $this->entityManager->getRDBRepository('Task') ->select(['id', 'name', 'dateEnd', 'parentId', 'parentType', 'parentName', 'assignedUserName']) - ->where(['status!=' => ['Completed', 'Canceled', 'Deferred'], 'dateEnd>=' => $today, 'dateEnd<=' => $threshold, 'deleted' => false, 'parentType' => 'Case']) + ->where(['status!=' => ['Completed', 'Canceled', 'Deferred'], 'dateEnd>=' => $today, 'dateEnd<=' => $threshold, 'deleted' => false, 'parentType' => ['Case', 'Contact']]) ->order('dateEnd', 'ASC')->limit(0, 30)->find(); foreach ($tasks as $task) { @@ -237,7 +329,7 @@ class AlertCalculator foreach (array_keys($userCounts) as $uid) { $userCounts[$uid]['openTasks'] = $this->entityManager->getRDBRepository('Task') - ->where(['assignedUserId' => $uid, 'status!=' => ['Completed', 'Canceled', 'Deferred'], 'deleted' => false, 'parentType' => 'Case'])->count(); + ->where(['assignedUserId' => $uid, 'status!=' => ['Completed', 'Canceled', 'Deferred'], 'deleted' => false, 'parentType' => ['Case', 'Contact']])->count(); } return array_values($userCounts); diff --git a/files/custom/Espo/Modules/SmartAssistant/Services/CaseContextBuilder.php b/files/custom/Espo/Modules/SmartAssistant/Services/CaseContextBuilder.php index efbd23b..fb0c05a 100644 --- a/files/custom/Espo/Modules/SmartAssistant/Services/CaseContextBuilder.php +++ b/files/custom/Espo/Modules/SmartAssistant/Services/CaseContextBuilder.php @@ -82,9 +82,18 @@ class CaseContextBuilder private function getOpenTasks(string $caseId): array { + $parentConditions = [ + ['parentType' => 'Case', 'parentId' => $caseId], + ]; + + $contactIds = $this->getCaseContactIds($caseId); + if (!empty($contactIds)) { + $parentConditions[] = ['parentType' => 'Contact', 'parentId' => $contactIds]; + } + $tasks = []; $collection = $this->entityManager->getRDBRepository('Task') - ->where(['parentType' => 'Case', 'parentId' => $caseId, 'status!=' => ['Completed', 'Canceled']]) + ->where(['OR' => $parentConditions, 'status!=' => ['Completed', 'Canceled']]) ->order('dateEnd', 'ASC')->limit(0, 10)->find(); foreach ($collection as $t) { @@ -97,6 +106,32 @@ class CaseContextBuilder return $tasks; } + private function getCaseContactIds(string $caseId): array + { + $case = $this->entityManager->getEntityById('Case', $caseId); + if (!$case) return []; + + $contactIds = []; + $primaryContactId = $case->get('contactId'); + if ($primaryContactId) { + $contactIds[] = $primaryContactId; + } + + $contacts = $this->entityManager->getRDBRepository('Case') + ->getRelation($case, 'contacts') + ->select(['id']) + ->find(); + + foreach ($contacts as $contact) { + $id = $contact->get('id'); + if (!in_array($id, $contactIds)) { + $contactIds[] = $id; + } + } + + return $contactIds; + } + private function getUpcomingMeetings(string $caseId): array { $meetings = []; diff --git a/files/custom/Espo/Modules/SmartAssistant/Services/OfficeContextBuilder.php b/files/custom/Espo/Modules/SmartAssistant/Services/OfficeContextBuilder.php index 8f46ec9..9ac11db 100644 --- a/files/custom/Espo/Modules/SmartAssistant/Services/OfficeContextBuilder.php +++ b/files/custom/Espo/Modules/SmartAssistant/Services/OfficeContextBuilder.php @@ -58,10 +58,18 @@ class OfficeContextBuilder $contacts[] = ['name' => $c->get('name'), 'phone' => $c->get('phoneNumber'), 'email' => $c->get('emailAddress')]; } + $parentConditions = [ + ['parentType' => 'Case', 'parentId' => $caseId], + ]; + $contactIds = $this->getCaseContactIds($case); + if (!empty($contactIds)) { + $parentConditions[] = ['parentType' => 'Contact', 'parentId' => $contactIds]; + } + $tasks = []; foreach ($this->entityManager->getRDBRepository('Task') ->select(['id', 'name', 'status', 'dateEnd', 'assignedUserName']) - ->where(['parentId' => $caseId, 'parentType' => 'Case', 'status!=' => ['Completed', 'Canceled'], 'deleted' => false]) + ->where(['OR' => $parentConditions, 'status!=' => ['Completed', 'Canceled'], 'deleted' => false]) ->order('dateEnd', 'ASC')->limit(0, 20)->find() as $t) { $tasks[] = ['name' => $t->get('name'), 'status' => $t->get('status'), 'dateEnd' => $t->get('dateEnd'), 'assignedUser' => $t->get('assignedUserName')]; } @@ -76,4 +84,27 @@ class OfficeContextBuilder return ['case' => $caseData, 'contacts' => $contacts, 'openTasks' => $tasks, 'recentNotes' => $notes]; } + + private function getCaseContactIds(\Espo\ORM\Entity $case): array + { + $contactIds = []; + $primaryContactId = $case->get('contactId'); + if ($primaryContactId) { + $contactIds[] = $primaryContactId; + } + + $contacts = $this->entityManager->getRDBRepository('Case') + ->getRelation($case, 'contacts') + ->select(['id']) + ->find(); + + foreach ($contacts as $contact) { + $id = $contact->get('id'); + if (!in_array($id, $contactIds)) { + $contactIds[] = $id; + } + } + + return $contactIds; + } }