This repository has been archived on 2026-07-19. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
SmartAssistant/files/custom/Espo/Modules/SmartAssistant/Services/OfficeContextBuilder.php
T
chaim 2569003f33 fix: broaden task detection to include Contact-linked tasks
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>
2026-04-14 06:02:27 +00:00

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;
}
}