2569003f33
Tasks created with parentType='Contact' (e.g. preparation calls linked to a case's contact) were invisible to AlertCalculator, CaseContextBuilder, and OfficeContextBuilder — all of which only queried parentType='Case'. This caused false "no preparation task" alerts in Shira's daily standup even when preparation tasks existed, because they were linked to the case's Contact instead of the Case entity directly. Changes: - AlertCalculator: getSummary, findOverdueTasks, findUpcomingTasks, getWorkloadByUser now query parentType=['Case','Contact'] - findUpcomingHearingsWithoutPrep: new caseHasPreparation() helper checks Case tasks, Contact tasks, and NhActivity-linked tasks - CaseContextBuilder.getOpenTasks: OR query across Case + Contact IDs - OfficeContextBuilder.buildCaseDrillDown: same OR query pattern - All three classes gain a getCaseContactIds() helper Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
111 lines
4.4 KiB
PHP
111 lines
4.4 KiB
PHP
<?php
|
|
|
|
namespace Espo\Modules\SmartAssistant\Services;
|
|
|
|
use Espo\Core\Acl;
|
|
use Espo\ORM\EntityManager;
|
|
use Espo\Entities\User;
|
|
|
|
class OfficeContextBuilder
|
|
{
|
|
private EntityManager $entityManager;
|
|
private Acl $acl;
|
|
private AlertCalculator $alertCalculator;
|
|
|
|
public function __construct(EntityManager $entityManager, Acl $acl, AlertCalculator $alertCalculator)
|
|
{
|
|
$this->entityManager = $entityManager;
|
|
$this->acl = $acl;
|
|
$this->alertCalculator = $alertCalculator;
|
|
}
|
|
|
|
public function buildSummaryContext(User $user, array $thresholds = []): array
|
|
{
|
|
$summary = $this->alertCalculator->getSummary();
|
|
$alerts = array_slice($this->alertCalculator->calculateAlerts($thresholds), 0, 20);
|
|
|
|
return [
|
|
'mode' => 'office',
|
|
'summary' => $summary,
|
|
'alerts' => $alerts,
|
|
'casesByStatus' => $this->alertCalculator->getCasesByStatus(),
|
|
'workloadByUser' => $this->alertCalculator->getWorkloadByUser(),
|
|
'upcomingHearings' => $this->alertCalculator->getUpcomingHearings(7),
|
|
'currentUser' => ['id' => $user->getId(), 'name' => $user->get('name')],
|
|
'today' => date('Y-m-d'),
|
|
'dayOfWeek' => date('l'),
|
|
];
|
|
}
|
|
|
|
public function buildCaseDrillDown(string $caseId, User $user): ?array
|
|
{
|
|
$case = $this->entityManager->getEntityById('Case', $caseId);
|
|
if (!$case) return null;
|
|
|
|
$caseData = [
|
|
'id' => $case->get('id'), 'name' => $case->get('name'), 'status' => $case->get('status'),
|
|
'type' => $case->get('type'), 'assignedUser' => $case->get('assignedUserName'),
|
|
'court' => $case->get('cCourt'), 'courtCaseNumber' => $case->get('cCourtCaseNumber'),
|
|
'judge' => $case->get('cJudge'), 'nextHearing' => $case->get('cNextHearing'),
|
|
'lastActivityAt' => $case->get('cLastActivityAt'),
|
|
'lastActivityType' => $case->get('cLastActivityType'),
|
|
'lastActivityDescription' => $case->get('cLastActivityDescription'),
|
|
'createdAt' => $case->get('createdAt'),
|
|
];
|
|
|
|
$contacts = [];
|
|
foreach ($this->entityManager->getRDBRepository('Case')->getRelation($case, 'contacts')->limit(0, 20)->find() as $c) {
|
|
$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(['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')];
|
|
}
|
|
|
|
$notes = [];
|
|
foreach ($this->entityManager->getRDBRepository('Note')
|
|
->select(['id', 'post', 'createdAt', 'createdByName', 'type'])
|
|
->where(['parentId' => $caseId, 'parentType' => 'Case', 'type' => 'Post', 'deleted' => false])
|
|
->order('createdAt', 'DESC')->limit(0, 5)->find() as $n) {
|
|
$notes[] = ['text' => mb_substr($n->get('post') ?? '', 0, 200), 'createdAt' => $n->get('createdAt'), 'createdBy' => $n->get('createdByName')];
|
|
}
|
|
|
|
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;
|
|
}
|
|
}
|