feat(CPuls): Enhance CPuls entity with new fields, tooltips, and options; add localization for German and English
- Added new fields to CPuls entity including status, syncStatus, kiAnalyse, and others. - Implemented localization for CPuls in German (de_DE) and English (en_US). - Introduced new API actions for team activation and completion of CPuls. - Created hooks to update team statistics and manage document counts. - Added new entity definitions and metadata for CPulsTeamZuordnung and Team. - Implemented validation logic in formulas to prevent completion of unclean Puls. - Updated layouts for detail and list views of CPuls. - Enhanced user entity with absence tracking fields. - Added scopes for CPuls and CPulsTeamZuordnung.
This commit is contained in:
File diff suppressed because it is too large
Load Diff
121
custom/Espo/Custom/Api/CPuls/AbschliessenFuerTeam.php
Normal file
121
custom/Espo/Custom/Api/CPuls/AbschliessenFuerTeam.php
Normal file
@@ -0,0 +1,121 @@
|
|||||||
|
<?php
|
||||||
|
namespace Espo\Custom\Api\CPuls;
|
||||||
|
|
||||||
|
use Espo\Core\Api\Action;
|
||||||
|
use Espo\Core\Api\Request;
|
||||||
|
use Espo\Core\Api\Response;
|
||||||
|
use Espo\Core\Exceptions\BadRequest;
|
||||||
|
use Espo\Core\Exceptions\Forbidden;
|
||||||
|
use Espo\Core\Exceptions\NotFound;
|
||||||
|
|
||||||
|
class AbschliessenFuerTeam implements Action
|
||||||
|
{
|
||||||
|
public function __construct(
|
||||||
|
private \Espo\ORM\EntityManager $entityManager,
|
||||||
|
private \Espo\Core\Acl\Table $acl,
|
||||||
|
private \Espo\Entities\User $user,
|
||||||
|
private \Espo\Core\Utils\Log $log
|
||||||
|
) {}
|
||||||
|
|
||||||
|
public function process(Request $request): Response
|
||||||
|
{
|
||||||
|
$pulsId = $request->getRouteParam('id');
|
||||||
|
$data = $request->getParsedBody();
|
||||||
|
$teamId = $data->teamId ?? null;
|
||||||
|
|
||||||
|
if (!$pulsId || !$teamId) {
|
||||||
|
throw new BadRequest('pulsId oder teamId fehlt');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 1. Validierung: Ist User in diesem Team?
|
||||||
|
$userTeams = $this->user->getLinkMultipleIdList('teams');
|
||||||
|
|
||||||
|
if (!in_array($teamId, $userTeams)) {
|
||||||
|
throw new Forbidden('User nicht in angegebenem Team');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Lade Puls
|
||||||
|
$puls = $this->entityManager->getEntity('CPuls', $pulsId);
|
||||||
|
|
||||||
|
if (!$puls) {
|
||||||
|
throw new NotFound('Puls nicht gefunden');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Validierung: syncStatus = clean?
|
||||||
|
if ($puls->get('syncStatus') !== 'clean') {
|
||||||
|
throw new BadRequest('Puls hat neue Dokumente (unclean) - bitte warten Sie auf die KI-Analyse');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. Finde Zuordnung
|
||||||
|
$zuordnung = $this->entityManager
|
||||||
|
->getRDBRepository('CPulsTeamZuordnung')
|
||||||
|
->where([
|
||||||
|
'pulsId' => $pulsId,
|
||||||
|
'teamId' => $teamId,
|
||||||
|
'aktiv' => true
|
||||||
|
])
|
||||||
|
->findOne();
|
||||||
|
|
||||||
|
if (!$zuordnung) {
|
||||||
|
throw new NotFound('Team-Zuordnung nicht gefunden oder nicht aktiv');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 5. Bereits abgeschlossen?
|
||||||
|
if ($zuordnung->get('abgeschlossen')) {
|
||||||
|
return Response::json([
|
||||||
|
'success' => true,
|
||||||
|
'message' => 'Bereits abgeschlossen',
|
||||||
|
'alreadyCompleted' => true
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 6. Abschluss setzen
|
||||||
|
$zuordnung->set([
|
||||||
|
'abgeschlossen' => true,
|
||||||
|
'abgeschlossenAm' => date('Y-m-d H:i:s'),
|
||||||
|
'abgeschlossenVonId' => $this->user->getId()
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->entityManager->saveEntity($zuordnung);
|
||||||
|
|
||||||
|
// 6.5. FIRST-READ-CLOSES: Finalisiere Block bei erstem Abschluss
|
||||||
|
if (!$puls->get('finalisiert')) {
|
||||||
|
$puls->set([
|
||||||
|
'finalisiert' => true,
|
||||||
|
'finalisierungsGrund' => 'Erstes Team',
|
||||||
|
'finalisiertAm' => date('Y-m-d H:i:s'),
|
||||||
|
'finalisiertVonId' => $this->user->getId()
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->log->info("Block finalisiert durch erstes Team (Team {$teamId}, User {$this->user->getId()})");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 7. Prüfe: Alle Teams abgeschlossen?
|
||||||
|
$offeneTeams = $this->entityManager
|
||||||
|
->getRDBRepository('CPulsTeamZuordnung')
|
||||||
|
->where([
|
||||||
|
'pulsId' => $pulsId,
|
||||||
|
'aktiv' => true,
|
||||||
|
'abgeschlossen' => false
|
||||||
|
])
|
||||||
|
->count();
|
||||||
|
|
||||||
|
// 8. Update Puls-Status
|
||||||
|
if ($offeneTeams === 0) {
|
||||||
|
$puls->set('status', 'Abgeschlossen');
|
||||||
|
} else {
|
||||||
|
$puls->set('status', 'Teilweise abgeschlossen');
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->entityManager->saveEntity($puls);
|
||||||
|
|
||||||
|
$this->log->info("Team {$teamId} hat Puls {$pulsId} abgeschlossen");
|
||||||
|
|
||||||
|
return Response::json([
|
||||||
|
'success' => true,
|
||||||
|
'status' => $puls->get('status'),
|
||||||
|
'finalisiert' => $puls->get('finalisiert'),
|
||||||
|
'offeneTeams' => $offeneTeams
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
99
custom/Espo/Custom/Api/CPuls/AktiviereTeams.php
Normal file
99
custom/Espo/Custom/Api/CPuls/AktiviereTeams.php
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
<?php
|
||||||
|
namespace Espo\Custom\Api\CPuls;
|
||||||
|
|
||||||
|
use Espo\Core\Api\Action;
|
||||||
|
use Espo\Core\Api\Request;
|
||||||
|
use Espo\Core\Api\Response;
|
||||||
|
use Espo\Core\Exceptions\BadRequest;
|
||||||
|
use Espo\Core\Exceptions\NotFound;
|
||||||
|
|
||||||
|
class AktiviereTeams implements Action
|
||||||
|
{
|
||||||
|
public function __construct(
|
||||||
|
private \Espo\ORM\EntityManager $entityManager,
|
||||||
|
private \Espo\Core\Utils\Log $log
|
||||||
|
) {}
|
||||||
|
|
||||||
|
public function process(Request $request): Response
|
||||||
|
{
|
||||||
|
$id = $request->getRouteParam('id');
|
||||||
|
|
||||||
|
if (!$id) {
|
||||||
|
throw new BadRequest('ID fehlt');
|
||||||
|
}
|
||||||
|
|
||||||
|
$puls = $this->entityManager->getEntity('CPuls', $id);
|
||||||
|
|
||||||
|
if (!$puls) {
|
||||||
|
throw new NotFound('Puls nicht gefunden');
|
||||||
|
}
|
||||||
|
|
||||||
|
$data = $request->getParsedBody();
|
||||||
|
|
||||||
|
// 1. Update Puls
|
||||||
|
$puls->set([
|
||||||
|
'kiAnalyse' => $data->kiAnalyse ?? null,
|
||||||
|
'zusammenfassung' => $data->zusammenfassung ?? null,
|
||||||
|
'status' => $data->status ?? 'Bereit',
|
||||||
|
'syncStatus' => $data->syncStatus ?? 'clean'
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->entityManager->saveEntity($puls);
|
||||||
|
|
||||||
|
// 2. Lösche alte Zuordnungen (soft delete - setze inaktiv)
|
||||||
|
$this->entityManager
|
||||||
|
->getQueryBuilder()
|
||||||
|
->update()
|
||||||
|
->in('CPulsTeamZuordnung')
|
||||||
|
->set(['aktiv' => false])
|
||||||
|
->where(['pulsId' => $id])
|
||||||
|
->execute();
|
||||||
|
|
||||||
|
// 3. Erstelle neue Zuordnungen
|
||||||
|
if (isset($data->teams) && is_array($data->teams)) {
|
||||||
|
foreach ($data->teams as $teamData) {
|
||||||
|
$teamId = $teamData->teamId ?? null;
|
||||||
|
|
||||||
|
if (!$teamId) {
|
||||||
|
$this->log->warning("Team-ID fehlt in teams-Array");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prüfe ob bereits existiert
|
||||||
|
$existing = $this->entityManager
|
||||||
|
->getRDBRepository('CPulsTeamZuordnung')
|
||||||
|
->where([
|
||||||
|
'pulsId' => $id,
|
||||||
|
'teamId' => $teamId
|
||||||
|
])
|
||||||
|
->findOne();
|
||||||
|
|
||||||
|
if ($existing) {
|
||||||
|
// Reaktiviere
|
||||||
|
$existing->set([
|
||||||
|
'aktiv' => true,
|
||||||
|
'abgeschlossen' => false,
|
||||||
|
'prioritaet' => $teamData->prioritaet ?? 'Normal'
|
||||||
|
]);
|
||||||
|
$this->entityManager->saveEntity($existing);
|
||||||
|
} else {
|
||||||
|
// Erstelle neu
|
||||||
|
$zuordnung = $this->entityManager->createEntity('CPulsTeamZuordnung', [
|
||||||
|
'pulsId' => $id,
|
||||||
|
'teamId' => $teamId,
|
||||||
|
'aktiv' => true,
|
||||||
|
'abgeschlossen' => false,
|
||||||
|
'prioritaet' => $teamData->prioritaet ?? 'Normal'
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->log->info("Teams aktiviert für Puls {$id}");
|
||||||
|
|
||||||
|
return Response::json([
|
||||||
|
'success' => true,
|
||||||
|
'pulsId' => $id
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
46
custom/Espo/Custom/Hooks/CPuls/UpdateTeamStats.php
Normal file
46
custom/Espo/Custom/Hooks/CPuls/UpdateTeamStats.php
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
<?php
|
||||||
|
namespace Espo\Custom\Hooks\CPuls;
|
||||||
|
|
||||||
|
use Espo\ORM\Entity;
|
||||||
|
use Espo\Core\Hook\Hook\BeforeSave;
|
||||||
|
|
||||||
|
class UpdateTeamStats implements BeforeSave
|
||||||
|
{
|
||||||
|
public function __construct(
|
||||||
|
private \Espo\ORM\EntityManager $entityManager
|
||||||
|
) {}
|
||||||
|
|
||||||
|
public function beforeSave(Entity $entity, array $options): void
|
||||||
|
{
|
||||||
|
// Zähle Dokumente
|
||||||
|
if ($entity->isNew() || $entity->isAttributeChanged('id')) {
|
||||||
|
$dokumenteCount = $this->entityManager
|
||||||
|
->getRDBRepository('CDokumente')
|
||||||
|
->where(['pulsId' => $entity->getId()])
|
||||||
|
->count();
|
||||||
|
|
||||||
|
$entity->set('anzahlDokumente', $dokumenteCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Zähle Team-Zuordnungen
|
||||||
|
$zuordnungen = $this->entityManager
|
||||||
|
->getRDBRepository('CPulsTeamZuordnung')
|
||||||
|
->where(['pulsId' => $entity->getId()])
|
||||||
|
->find();
|
||||||
|
|
||||||
|
$aktiv = 0;
|
||||||
|
$abgeschlossen = 0;
|
||||||
|
|
||||||
|
foreach ($zuordnungen as $z) {
|
||||||
|
if ($z->get('aktiv')) {
|
||||||
|
$aktiv++;
|
||||||
|
if ($z->get('abgeschlossen')) {
|
||||||
|
$abgeschlossen++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$entity->set('anzahlTeamsAktiv', $aktiv);
|
||||||
|
$entity->set('anzahlTeamsAbgeschlossen', $abgeschlossen);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,9 +1,58 @@
|
|||||||
{
|
{
|
||||||
"links": {
|
|
||||||
"calls": "Anrufe",
|
|
||||||
"tasks": "Aufgaben"
|
|
||||||
},
|
|
||||||
"labels": {
|
"labels": {
|
||||||
"Create CPuls": "Puls erstellen"
|
"Create CPuls": "Puls erstellen",
|
||||||
|
"CPuls": "Puls",
|
||||||
|
"cPuls": "Pulse"
|
||||||
|
},
|
||||||
|
"fields": {
|
||||||
|
"name": "Bezeichnung",
|
||||||
|
"status": "Status",
|
||||||
|
"syncStatus": "Synchronisations-Status",
|
||||||
|
"kiAnalyse": "KI-Analyse",
|
||||||
|
"zusammenfassung": "Zusammenfassung",
|
||||||
|
"anzahlDokumente": "Anzahl Dokumente",
|
||||||
|
"anzahlTeamsAktiv": "Teams (aktiv)",
|
||||||
|
"anzahlTeamsAbgeschlossen": "Teams (abgeschlossen)",
|
||||||
|
"finalisiert": "Finalisiert",
|
||||||
|
"finalisierungsGrund": "Finalisierungsgrund",
|
||||||
|
"finalisiertAm": "Finalisiert am",
|
||||||
|
"finalisiertVon": "Finalisiert von",
|
||||||
|
"parent": "Vorgang",
|
||||||
|
"dokumente": "Dokumente",
|
||||||
|
"teamZuordnungen": "Team-Zuordnungen"
|
||||||
|
},
|
||||||
|
"links": {
|
||||||
|
"parent": "Vorgang",
|
||||||
|
"dokumente": "Dokumente",
|
||||||
|
"teamZuordnungen": "Team-Zuordnungen"
|
||||||
|
},
|
||||||
|
"tooltips": {
|
||||||
|
"syncStatus": "clean = KI-Analyse aktuell | unclean = Neue Dokumente, Analyse ausstehend",
|
||||||
|
"kiAnalyse": "Automatisch generierte Zusammenfassung durch KI-Middleware",
|
||||||
|
"zusammenfassung": "Kurze Zusammenfassung für Listen-Ansicht",
|
||||||
|
"finalisiert": "Block wurde geschlossen - neue Dokumente erzeugen automatisch einen neuen Block (First-Read-Closes Prinzip)",
|
||||||
|
"finalisierungsGrund": "Grund der Finalisierung: Erstes Team = Team hat abgeschlossen | Manuell = Admin-Aktion | Automatisch = System-Regel"
|
||||||
|
},
|
||||||
|
"options": {
|
||||||
|
"status": {
|
||||||
|
"Neu": "Neu",
|
||||||
|
"In Verarbeitung": "In Verarbeitung",
|
||||||
|
"Bereit": "Bereit",
|
||||||
|
"In Review": "In Review",
|
||||||
|
"Teilweise abgeschlossen": "Teilweise abgeschlossen",
|
||||||
|
"Abgeschlossen": "Abgeschlossen"
|
||||||
|
},
|
||||||
|
"syncStatus": {
|
||||||
|
"clean": "Aktuell",
|
||||||
|
"unclean": "Ausstehend"
|
||||||
|
},
|
||||||
|
"finalisierungsGrund": {
|
||||||
|
"Erstes Team": "Erstes Team",
|
||||||
|
"Manuell": "Manuell",
|
||||||
|
"Automatisch": "Automatisch"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"presetFilters": {
|
||||||
|
"meineOffenen": "Meine offenen Pulse"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
{
|
||||||
|
"labels": {
|
||||||
|
"Create CPulsTeamZuordnung": "Team-Zuordnung erstellen",
|
||||||
|
"CPulsTeamZuordnung": "Puls Team-Zuordnung",
|
||||||
|
"cPulsTeamZuordnung": "Puls Team-Zuordnungen"
|
||||||
|
},
|
||||||
|
"fields": {
|
||||||
|
"name": "Name",
|
||||||
|
"puls": "Puls",
|
||||||
|
"team": "Team",
|
||||||
|
"aktiv": "Aktiv",
|
||||||
|
"abgeschlossen": "Abgeschlossen",
|
||||||
|
"abgeschlossenAm": "Abgeschlossen am",
|
||||||
|
"abgeschlossenVon": "Abgeschlossen von",
|
||||||
|
"prioritaet": "Priorität"
|
||||||
|
},
|
||||||
|
"links": {
|
||||||
|
"puls": "Puls",
|
||||||
|
"team": "Team",
|
||||||
|
"abgeschlossenVon": "Abgeschlossen von"
|
||||||
|
},
|
||||||
|
"tooltips": {
|
||||||
|
"aktiv": "Ist diese Team-Zuordnung aktiv? Inaktive werden nicht angezeigt."
|
||||||
|
},
|
||||||
|
"options": {
|
||||||
|
"prioritaet": {
|
||||||
|
"Niedrig": "Niedrig",
|
||||||
|
"Normal": "Normal",
|
||||||
|
"Hoch": "Hoch"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,12 +1,58 @@
|
|||||||
{
|
{
|
||||||
"fields": {
|
"labels": {
|
||||||
|
"Create CPuls": "Create Pulse",
|
||||||
|
"CPuls": "Pulse",
|
||||||
|
"cPuls": "Pulses"
|
||||||
|
},
|
||||||
|
"fields": {
|
||||||
|
"name": "Name",
|
||||||
|
"status": "Status",
|
||||||
|
"syncStatus": "Sync Status",
|
||||||
|
"kiAnalyse": "AI Analysis",
|
||||||
|
"zusammenfassung": "Summary",
|
||||||
|
"anzahlDokumente": "Number of Documents",
|
||||||
|
"anzahlTeamsAktiv": "Teams (active)",
|
||||||
|
"anzahlTeamsAbgeschlossen": "Teams (completed)",
|
||||||
|
"finalisiert": "Finalized",
|
||||||
|
"finalisierungsGrund": "Finalization Reason",
|
||||||
|
"finalisiertAm": "Finalized At",
|
||||||
|
"finalisiertVon": "Finalized By",
|
||||||
|
"parent": "Parent Record",
|
||||||
|
"dokumente": "Documents",
|
||||||
|
"teamZuordnungen": "Team Assignments"
|
||||||
|
},
|
||||||
|
"links": {
|
||||||
|
"parent": "Parent Record",
|
||||||
|
"dokumente": "Documents",
|
||||||
|
"teamZuordnungen": "Team Assignments"
|
||||||
|
},
|
||||||
|
"tooltips": {
|
||||||
|
"syncStatus": "clean = AI analysis up-to-date | unclean = New documents, analysis pending",
|
||||||
|
"kiAnalyse": "Automatically generated summary by AI middleware",
|
||||||
|
"zusammenfassung": "Short summary for list views",
|
||||||
|
"finalisiert": "Block has been closed - new documents will automatically create a new block (First-Read-Closes principle)",
|
||||||
|
"finalisierungsGrund": "Reason for finalization: First Team = Team completed | Manual = Admin action | Automatic = System rule"
|
||||||
|
},
|
||||||
|
"options": {
|
||||||
|
"status": {
|
||||||
|
"Neu": "New",
|
||||||
|
"In Verarbeitung": "Processing",
|
||||||
|
"Bereit": "Ready",
|
||||||
|
"In Review": "In Review",
|
||||||
|
"Teilweise abgeschlossen": "Partially Completed",
|
||||||
|
"Abgeschlossen": "Completed"
|
||||||
},
|
},
|
||||||
"links": {
|
"syncStatus": {
|
||||||
"meetings": "Meetings",
|
"clean": "Up-to-date",
|
||||||
"calls": "Calls",
|
"unclean": "Pending"
|
||||||
"tasks": "Tasks"
|
|
||||||
},
|
},
|
||||||
"labels": {
|
"finalisierungsGrund": {
|
||||||
"Create CPuls": "Create Puls"
|
"Erstes Team": "First Team",
|
||||||
|
"Manuell": "Manual",
|
||||||
|
"Automatisch": "Automatic"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"presetFilters": {
|
||||||
|
"meineOffenen": "My Open Pulses"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,17 @@
|
|||||||
|
{
|
||||||
|
"teamZuordnungen": {
|
||||||
|
"index": 0,
|
||||||
|
"sticked": true,
|
||||||
|
"style": "info",
|
||||||
|
"label": "Team-Zuordnungen"
|
||||||
|
},
|
||||||
|
"dokumente": {
|
||||||
|
"index": 1,
|
||||||
|
"sticked": false,
|
||||||
|
"label": "Dokumente"
|
||||||
|
},
|
||||||
|
"stream": {
|
||||||
|
"index": 2,
|
||||||
|
"sticked": false
|
||||||
|
}
|
||||||
|
}
|
||||||
47
custom/Espo/Custom/Resources/layouts/CPuls/detail.json
Normal file
47
custom/Espo/Custom/Resources/layouts/CPuls/detail.json
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"label": "Übersicht",
|
||||||
|
"rows": [
|
||||||
|
[
|
||||||
|
{"name": "name"},
|
||||||
|
{"name": "status"}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{"name": "syncStatus"},
|
||||||
|
{"name": "parent"}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{"name": "anzahlDokumente"},
|
||||||
|
{"name": "anzahlTeamsAktiv"}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{"name": "finalisiert"},
|
||||||
|
{"name": "finalisierungsGrund"}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{"name": "zusammenfassung", "span": 2}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "KI-Analyse",
|
||||||
|
"rows": [
|
||||||
|
[
|
||||||
|
{"name": "kiAnalyse", "span": 2}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "System",
|
||||||
|
"rows": [
|
||||||
|
[
|
||||||
|
{"name": "createdAt"},
|
||||||
|
{"name": "modifiedAt"}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{"name": "createdBy"},
|
||||||
|
{"name": "modifiedBy"}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
8
custom/Espo/Custom/Resources/layouts/CPuls/list.json
Normal file
8
custom/Espo/Custom/Resources/layouts/CPuls/list.json
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
[
|
||||||
|
{"name": "name", "width": 30},
|
||||||
|
{"name": "status", "width": 15},
|
||||||
|
{"name": "syncStatus", "width": 10},
|
||||||
|
{"name": "parent", "width": 20},
|
||||||
|
{"name": "anzahlDokumente", "width": 10},
|
||||||
|
{"name": "createdAt", "width": 15}
|
||||||
|
]
|
||||||
14
custom/Espo/Custom/Resources/metadata/app/api.json
Normal file
14
custom/Espo/Custom/Resources/metadata/app/api.json
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"routes": [
|
||||||
|
{
|
||||||
|
"route": "/CPuls/:id/aktiviere-teams",
|
||||||
|
"method": "put",
|
||||||
|
"actionClassName": "Espo\\Custom\\Api\\CPuls\\AktiviereTeams"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"route": "/CPuls/:id/abschliessen-fuer-team",
|
||||||
|
"method": "post",
|
||||||
|
"actionClassName": "Espo\\Custom\\Api\\CPuls\\AbschliessenFuerTeam"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -1,37 +1,18 @@
|
|||||||
{
|
{
|
||||||
"controller": "controllers/record",
|
"controller": "controllers/record",
|
||||||
"boolFilterList": [
|
"iconClass": "fas fa-heartbeat",
|
||||||
"onlyMy"
|
"color": "#e74c3c",
|
||||||
],
|
"filterList": [
|
||||||
"sidePanels": {
|
"meineOffenen",
|
||||||
"detail": [
|
{
|
||||||
{
|
"name": "bereit"
|
||||||
"name": "activities",
|
|
||||||
"reference": "activities"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "history",
|
|
||||||
"reference": "history"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "tasks",
|
|
||||||
"reference": "tasks"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
},
|
||||||
"bottomPanels": {
|
{
|
||||||
"detail": [
|
"name": "inReview"
|
||||||
{
|
}
|
||||||
"name": "activities",
|
],
|
||||||
"reference": "activities",
|
"boolFilterList": [
|
||||||
"disabled": true
|
"onlyMy"
|
||||||
},
|
],
|
||||||
{
|
"defaultFilterPreset": "meineOffenen"
|
||||||
"name": "history",
|
|
||||||
"reference": "history",
|
|
||||||
"disabled": true
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"iconClass": "fas fa-heart-pulse"
|
|
||||||
}
|
}
|
||||||
@@ -105,6 +105,11 @@
|
|||||||
"default": "pending_sync",
|
"default": "pending_sync",
|
||||||
"tooltip": true,
|
"tooltip": true,
|
||||||
"isCustom": true
|
"isCustom": true
|
||||||
|
},
|
||||||
|
"puls": {
|
||||||
|
"type": "link",
|
||||||
|
"entity": "CPuls",
|
||||||
|
"isCustom": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"links": {
|
"links": {
|
||||||
@@ -138,6 +143,12 @@
|
|||||||
"skipOrmDefs": true,
|
"skipOrmDefs": true,
|
||||||
"utility": true
|
"utility": true
|
||||||
},
|
},
|
||||||
|
"puls": {
|
||||||
|
"type": "belongsTo",
|
||||||
|
"entity": "CPuls",
|
||||||
|
"foreign": "dokumente",
|
||||||
|
"isCustom": true
|
||||||
|
},
|
||||||
"contactsvmhdokumente": {
|
"contactsvmhdokumente": {
|
||||||
"type": "hasMany",
|
"type": "hasMany",
|
||||||
"relationName": "cDokumenteContact",
|
"relationName": "cDokumenteContact",
|
||||||
|
|||||||
@@ -3,10 +3,97 @@
|
|||||||
"name": {
|
"name": {
|
||||||
"type": "varchar",
|
"type": "varchar",
|
||||||
"required": true,
|
"required": true,
|
||||||
"pattern": "$noBadCharacters"
|
"maxLength": 255,
|
||||||
|
"trim": true,
|
||||||
|
"isCustom": true
|
||||||
},
|
},
|
||||||
"description": {
|
"status": {
|
||||||
"type": "text"
|
"type": "enum",
|
||||||
|
"options": [
|
||||||
|
"Neu",
|
||||||
|
"In Verarbeitung",
|
||||||
|
"Bereit",
|
||||||
|
"In Review",
|
||||||
|
"Teilweise abgeschlossen",
|
||||||
|
"Abgeschlossen"
|
||||||
|
],
|
||||||
|
"default": "Neu",
|
||||||
|
"required": true,
|
||||||
|
"isCustom": true,
|
||||||
|
"style": {
|
||||||
|
"Neu": "default",
|
||||||
|
"In Verarbeitung": "primary",
|
||||||
|
"Bereit": "success",
|
||||||
|
"In Review": "warning",
|
||||||
|
"Teilweise abgeschlossen": "info",
|
||||||
|
"Abgeschlossen": "success"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"syncStatus": {
|
||||||
|
"type": "enum",
|
||||||
|
"options": ["clean", "unclean"],
|
||||||
|
"default": "unclean",
|
||||||
|
"required": true,
|
||||||
|
"isCustom": true,
|
||||||
|
"tooltip": true
|
||||||
|
},
|
||||||
|
"kiAnalyse": {
|
||||||
|
"type": "text",
|
||||||
|
"isCustom": true,
|
||||||
|
"tooltip": true
|
||||||
|
},
|
||||||
|
"zusammenfassung": {
|
||||||
|
"type": "varchar",
|
||||||
|
"maxLength": 500,
|
||||||
|
"isCustom": true,
|
||||||
|
"tooltip": true
|
||||||
|
},
|
||||||
|
"anzahlDokumente": {
|
||||||
|
"type": "int",
|
||||||
|
"readOnly": true,
|
||||||
|
"notStorable": false,
|
||||||
|
"isCustom": true
|
||||||
|
},
|
||||||
|
"anzahlTeamsAktiv": {
|
||||||
|
"type": "int",
|
||||||
|
"readOnly": true,
|
||||||
|
"notStorable": false,
|
||||||
|
"isCustom": true
|
||||||
|
},
|
||||||
|
"anzahlTeamsAbgeschlossen": {
|
||||||
|
"type": "int",
|
||||||
|
"readOnly": true,
|
||||||
|
"notStorable": false,
|
||||||
|
"isCustom": true
|
||||||
|
},
|
||||||
|
"finalisiert": {
|
||||||
|
"type": "bool",
|
||||||
|
"default": false,
|
||||||
|
"readOnly": true,
|
||||||
|
"isCustom": true,
|
||||||
|
"tooltip": true
|
||||||
|
},
|
||||||
|
"finalisierungsGrund": {
|
||||||
|
"type": "enum",
|
||||||
|
"options": [
|
||||||
|
"Erstes Team",
|
||||||
|
"Manuell",
|
||||||
|
"Automatisch"
|
||||||
|
],
|
||||||
|
"readOnly": true,
|
||||||
|
"isCustom": true,
|
||||||
|
"tooltip": true
|
||||||
|
},
|
||||||
|
"finalisiertAm": {
|
||||||
|
"type": "datetime",
|
||||||
|
"readOnly": true,
|
||||||
|
"isCustom": true
|
||||||
|
},
|
||||||
|
"finalisiertVon": {
|
||||||
|
"type": "link",
|
||||||
|
"entity": "User",
|
||||||
|
"readOnly": true,
|
||||||
|
"isCustom": true
|
||||||
},
|
},
|
||||||
"createdAt": {
|
"createdAt": {
|
||||||
"type": "datetime",
|
"type": "datetime",
|
||||||
@@ -18,25 +105,43 @@
|
|||||||
},
|
},
|
||||||
"createdBy": {
|
"createdBy": {
|
||||||
"type": "link",
|
"type": "link",
|
||||||
"readOnly": true,
|
"entity": "User",
|
||||||
"view": "views/fields/user"
|
"readOnly": true
|
||||||
},
|
},
|
||||||
"modifiedBy": {
|
"modifiedBy": {
|
||||||
"type": "link",
|
"type": "link",
|
||||||
"readOnly": true,
|
"entity": "User",
|
||||||
"view": "views/fields/user"
|
"readOnly": true
|
||||||
},
|
},
|
||||||
"assignedUser": {
|
"assignedUser": {
|
||||||
"type": "link",
|
"type": "link",
|
||||||
"required": false,
|
"entity": "User",
|
||||||
"view": "views/fields/assigned-user"
|
"isCustom": true
|
||||||
},
|
},
|
||||||
"teams": {
|
"teams": {
|
||||||
"type": "linkMultiple",
|
"type": "linkMultiple",
|
||||||
"view": "views/fields/teams"
|
"isCustom": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"links": {
|
"links": {
|
||||||
|
"parent": {
|
||||||
|
"type": "belongsToParent",
|
||||||
|
"entityList": [
|
||||||
|
"CVmhRumungsklage",
|
||||||
|
"CMietinkasso",
|
||||||
|
"CKuendigung"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"dokumente": {
|
||||||
|
"type": "hasMany",
|
||||||
|
"entity": "CDokumente",
|
||||||
|
"foreign": "puls"
|
||||||
|
},
|
||||||
|
"teamZuordnungen": {
|
||||||
|
"type": "hasMany",
|
||||||
|
"entity": "CPulsTeamZuordnung",
|
||||||
|
"foreign": "puls"
|
||||||
|
},
|
||||||
"createdBy": {
|
"createdBy": {
|
||||||
"type": "belongsTo",
|
"type": "belongsTo",
|
||||||
"entity": "User"
|
"entity": "User"
|
||||||
@@ -49,62 +154,37 @@
|
|||||||
"type": "belongsTo",
|
"type": "belongsTo",
|
||||||
"entity": "User"
|
"entity": "User"
|
||||||
},
|
},
|
||||||
|
"finalisiertVon": {
|
||||||
|
"type": "belongsTo",
|
||||||
|
"entity": "User"
|
||||||
|
},
|
||||||
"teams": {
|
"teams": {
|
||||||
"type": "hasMany",
|
"type": "hasMany",
|
||||||
"entity": "Team",
|
"entity": "Team",
|
||||||
"relationName": "entityTeam",
|
"relationName": "EntityTeam",
|
||||||
"layoutRelationshipsDisabled": true
|
|
||||||
},
|
|
||||||
"meetings": {
|
|
||||||
"type": "hasMany",
|
|
||||||
"entity": "Meeting",
|
|
||||||
"foreign": "parent"
|
|
||||||
},
|
|
||||||
"calls": {
|
|
||||||
"type": "hasMany",
|
|
||||||
"entity": "Call",
|
|
||||||
"foreign": "parent"
|
|
||||||
},
|
|
||||||
"tasks": {
|
|
||||||
"type": "hasChildren",
|
|
||||||
"entity": "Task",
|
|
||||||
"foreign": "parent"
|
|
||||||
},
|
|
||||||
"emails": {
|
|
||||||
"type": "hasChildren",
|
|
||||||
"entity": "Email",
|
|
||||||
"foreign": "parent",
|
|
||||||
"layoutRelationshipsDisabled": true
|
"layoutRelationshipsDisabled": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"collection": {
|
"collection": {
|
||||||
"orderBy": "createdAt",
|
"orderBy": "createdAt",
|
||||||
"order": "desc"
|
"order": "desc",
|
||||||
|
"textFilterFields": ["name", "zusammenfassung"]
|
||||||
},
|
},
|
||||||
"indexes": {
|
"indexes": {
|
||||||
"name": {
|
"parent": {
|
||||||
"columns": [
|
"columns": ["parentType", "parentId"]
|
||||||
"name",
|
|
||||||
"deleted"
|
|
||||||
]
|
|
||||||
},
|
},
|
||||||
"assignedUser": {
|
"status": {
|
||||||
"columns": [
|
"columns": ["status"]
|
||||||
"assignedUserId",
|
},
|
||||||
"deleted"
|
"syncStatus": {
|
||||||
]
|
"columns": ["syncStatus"]
|
||||||
|
},
|
||||||
|
"finalisiert": {
|
||||||
|
"columns": ["finalisiert"]
|
||||||
},
|
},
|
||||||
"createdAt": {
|
"createdAt": {
|
||||||
"columns": [
|
"columns": ["createdAt"]
|
||||||
"createdAt"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"createdAtId": {
|
|
||||||
"unique": true,
|
|
||||||
"columns": [
|
|
||||||
"createdAt",
|
|
||||||
"id"
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,100 @@
|
|||||||
|
{
|
||||||
|
"fields": {
|
||||||
|
"name": {
|
||||||
|
"type": "varchar",
|
||||||
|
"notStorable": true,
|
||||||
|
"select": {
|
||||||
|
"select": "CONCAT:(team.name, ' - ', puls.name)"
|
||||||
|
},
|
||||||
|
"orderBy": {
|
||||||
|
"order": [
|
||||||
|
["team.name", "{direction}"]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"puls": {
|
||||||
|
"type": "link",
|
||||||
|
"entity": "CPuls",
|
||||||
|
"required": true,
|
||||||
|
"isCustom": true
|
||||||
|
},
|
||||||
|
"team": {
|
||||||
|
"type": "link",
|
||||||
|
"entity": "Team",
|
||||||
|
"required": true,
|
||||||
|
"isCustom": true
|
||||||
|
},
|
||||||
|
"aktiv": {
|
||||||
|
"type": "bool",
|
||||||
|
"default": true,
|
||||||
|
"isCustom": true,
|
||||||
|
"tooltip": true
|
||||||
|
},
|
||||||
|
"abgeschlossen": {
|
||||||
|
"type": "bool",
|
||||||
|
"default": false,
|
||||||
|
"isCustom": true
|
||||||
|
},
|
||||||
|
"abgeschlossenAm": {
|
||||||
|
"type": "datetime",
|
||||||
|
"readOnly": true,
|
||||||
|
"isCustom": true
|
||||||
|
},
|
||||||
|
"abgeschlossenVon": {
|
||||||
|
"type": "link",
|
||||||
|
"entity": "User",
|
||||||
|
"readOnly": true,
|
||||||
|
"isCustom": true
|
||||||
|
},
|
||||||
|
"prioritaet": {
|
||||||
|
"type": "enum",
|
||||||
|
"options": ["Niedrig", "Normal", "Hoch"],
|
||||||
|
"default": "Normal",
|
||||||
|
"isCustom": true,
|
||||||
|
"style": {
|
||||||
|
"Niedrig": "default",
|
||||||
|
"Normal": "primary",
|
||||||
|
"Hoch": "danger"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"createdAt": {
|
||||||
|
"type": "datetime",
|
||||||
|
"readOnly": true
|
||||||
|
},
|
||||||
|
"modifiedAt": {
|
||||||
|
"type": "datetime",
|
||||||
|
"readOnly": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"links": {
|
||||||
|
"puls": {
|
||||||
|
"type": "belongsTo",
|
||||||
|
"entity": "CPuls",
|
||||||
|
"foreign": "teamZuordnungen"
|
||||||
|
},
|
||||||
|
"team": {
|
||||||
|
"type": "belongsTo",
|
||||||
|
"entity": "Team"
|
||||||
|
},
|
||||||
|
"abgeschlossenVon": {
|
||||||
|
"type": "belongsTo",
|
||||||
|
"entity": "User"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"collection": {
|
||||||
|
"orderBy": "createdAt",
|
||||||
|
"order": "desc"
|
||||||
|
},
|
||||||
|
"indexes": {
|
||||||
|
"pulsTeam": {
|
||||||
|
"columns": ["pulsId", "teamId"],
|
||||||
|
"unique": true
|
||||||
|
},
|
||||||
|
"aktiv": {
|
||||||
|
"columns": ["aktiv"]
|
||||||
|
},
|
||||||
|
"abgeschlossen": {
|
||||||
|
"columns": ["abgeschlossen"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
16
custom/Espo/Custom/Resources/metadata/entityDefs/Team.json
Normal file
16
custom/Espo/Custom/Resources/metadata/entityDefs/Team.json
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
{
|
||||||
|
"fields": {
|
||||||
|
"teamKategorie": {
|
||||||
|
"type": "enum",
|
||||||
|
"options": [
|
||||||
|
"Anwalt",
|
||||||
|
"Mandatsbetreuung",
|
||||||
|
"Zwangsvollstreckung",
|
||||||
|
"Sonstiges"
|
||||||
|
],
|
||||||
|
"default": "Sonstiges",
|
||||||
|
"isCustom": true,
|
||||||
|
"tooltip": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,6 +2,23 @@
|
|||||||
"fields": {
|
"fields": {
|
||||||
"cCallQueues": {
|
"cCallQueues": {
|
||||||
"type": "linkOne"
|
"type": "linkOne"
|
||||||
|
},
|
||||||
|
"abwesend": {
|
||||||
|
"type": "bool",
|
||||||
|
"default": false,
|
||||||
|
"isCustom": true,
|
||||||
|
"tooltip": true
|
||||||
|
},
|
||||||
|
"abwesendBis": {
|
||||||
|
"type": "date",
|
||||||
|
"isCustom": true,
|
||||||
|
"tooltip": true
|
||||||
|
},
|
||||||
|
"vertretung": {
|
||||||
|
"type": "link",
|
||||||
|
"entity": "User",
|
||||||
|
"isCustom": true,
|
||||||
|
"tooltip": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"links": {
|
"links": {
|
||||||
@@ -10,6 +27,11 @@
|
|||||||
"foreign": "user",
|
"foreign": "user",
|
||||||
"entity": "CCallQueues",
|
"entity": "CCallQueues",
|
||||||
"isCustom": true
|
"isCustom": true
|
||||||
|
},
|
||||||
|
"vertretung": {
|
||||||
|
"type": "belongsTo",
|
||||||
|
"entity": "User",
|
||||||
|
"isCustom": true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
3
custom/Espo/Custom/Resources/metadata/formula/CPuls.json
Normal file
3
custom/Espo/Custom/Resources/metadata/formula/CPuls.json
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"beforeSaveApiScript": "// Verhindere Abschluss bei unclean Status\nif (\n (status == 'Abgeschlossen' || entity\\isAttributeChanged('status'))\n && syncStatus == 'unclean'\n) {\n recordService\\throwBadRequest('Puls kann nicht abgeschlossen werden: Neue Dokumente vorhanden (Status: unclean). Bitte warten Sie auf die KI-Analyse.');\n}\n\n// Verhindere Änderungen an finalisiertem Puls\nif (\n finalisiert == true\n && entity\\isAttributeChanged('finalisiert') == false\n && (entity\\isAttributeChanged('status') || entity\\isAttributeChanged('syncStatus'))\n) {\n recordService\\throwBadRequest('Puls ist finalisiert. Neue Dokumente erzeugen automatisch einen neuen Block.');\n}"
|
||||||
|
}
|
||||||
@@ -2,22 +2,16 @@
|
|||||||
"entity": true,
|
"entity": true,
|
||||||
"layouts": true,
|
"layouts": true,
|
||||||
"tab": true,
|
"tab": true,
|
||||||
"acl": true,
|
"acl": "recordAllTeamOwnNo",
|
||||||
"aclPortal": true,
|
"aclPortal": false,
|
||||||
"aclPortalLevelList": [
|
|
||||||
"all",
|
|
||||||
"account",
|
|
||||||
"contact",
|
|
||||||
"own",
|
|
||||||
"no"
|
|
||||||
],
|
|
||||||
"customizable": true,
|
"customizable": true,
|
||||||
"importable": true,
|
"importable": false,
|
||||||
"notifications": true,
|
"notifications": true,
|
||||||
"stream": true,
|
"stream": true,
|
||||||
"disabled": false,
|
"disabled": false,
|
||||||
"type": "BasePlus",
|
"type": "Base",
|
||||||
"module": "Custom",
|
"module": "Custom",
|
||||||
"object": true,
|
"object": true,
|
||||||
"isCustom": true
|
"isCustom": true,
|
||||||
|
"calendar": false
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"entity": true,
|
||||||
|
"tab": false,
|
||||||
|
"acl": "recordAllTeamNo",
|
||||||
|
"aclPortal": false,
|
||||||
|
"customizable": true,
|
||||||
|
"stream": false,
|
||||||
|
"disabled": false,
|
||||||
|
"type": "Base",
|
||||||
|
"module": "Custom",
|
||||||
|
"object": true,
|
||||||
|
"isCustom": true
|
||||||
|
}
|
||||||
@@ -358,7 +358,7 @@ return [
|
|||||||
0 => 'youtube.com',
|
0 => 'youtube.com',
|
||||||
1 => 'google.com'
|
1 => 'google.com'
|
||||||
],
|
],
|
||||||
'microtime' => 1770972794.710849,
|
'microtime' => 1770973606.273594,
|
||||||
'siteUrl' => 'https://crm.bitbylaw.com',
|
'siteUrl' => 'https://crm.bitbylaw.com',
|
||||||
'fullTextSearchMinLength' => 4,
|
'fullTextSearchMinLength' => 4,
|
||||||
'webSocketUrl' => 'ws://api.bitbylaw.com:5000/espocrm/ws',
|
'webSocketUrl' => 'ws://api.bitbylaw.com:5000/espocrm/ws',
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
return [
|
return [
|
||||||
'cacheTimestamp' => 1770972795,
|
'cacheTimestamp' => 1770973606,
|
||||||
'microtimeState' => 1770972795.029599,
|
'microtimeState' => 1770973606.458758,
|
||||||
'currencyRates' => [
|
'currencyRates' => [
|
||||||
'EUR' => 1.0
|
'EUR' => 1.0
|
||||||
],
|
],
|
||||||
|
|||||||
Reference in New Issue
Block a user