Compare commits

...

3 Commits

Author SHA1 Message Date
chaim 4c0586cce8 feat: remove DirectAccessReport, keep only LegalAid entity
Complete removal of the old DirectAccessReport entity and all related
files. LegalAid is now the sole entity for legal aid data management.

- Remove DirectAccessReport: controller, service, hooks, entity defs,
  scopes, clientDefs, layouts, i18n (all 3 locales)
- Remove directAccessReports link from Case
- Rewrite GenerateInitialReport tool to work directly with LegalAid
- Clean up Case i18n (remove old field translations)
- Bump version to 1.5.0

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 17:44:24 +00:00
chaim b3c219aa57 feat: add LegalAid entity — centralized legal aid data management
Replace scattered Case fields with a dedicated LegalAid entity that
holds all legal aid data (aid type, urgency, client contact, proceeding
details, legal analysis, recommendation, etc.). DirectAccessReport now
pulls from LegalAid for DOCX generation with automatic checkbox mapping.

- Add LegalAid entity with controller, layouts, i18n (he/en/fa)
- Remove cAidType, cUrgencyLevel, cAppointmentDate, cFilingDeadline,
  cLegalAidType from Case (migrated to LegalAid)
- Add legalAidType field to LegalAid (DirectAccess/Regular/Duty)
- Update DirectAccessReportService to use LegalAid entity
- Add update_legal_aid and get_legal_aid SmartAssistant tools
- Fix AfterInstall template entityType to LegalAid

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 15:27:23 +00:00
chaim 31e6359df4 fix: update direct access report template
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 10:41:55 +00:00
25 changed files with 316 additions and 490 deletions
@@ -4,6 +4,6 @@ namespace Espo\Modules\LegalAssistance\Controllers;
use Espo\Core\Controllers\Record; use Espo\Core\Controllers\Record;
class DirectAccessReport extends Record class LegalAid extends Record
{ {
} }
@@ -1,76 +0,0 @@
<?php
namespace Espo\Modules\LegalAssistance\Hooks\Case;
use Espo\ORM\Entity;
use Espo\ORM\EntityManager;
use Espo\Core\Utils\Log;
class CreateDirectAccessTasks
{
public function __construct(
private EntityManager $entityManager,
private Log $log
) {}
public function afterSave(Entity $entity, array $options): void
{
if (!empty($options['skipHooks'])) {
return;
}
if (!$entity->isAttributeChanged('cLegalAidType')) {
return;
}
$newValue = $entity->get('cLegalAidType');
$oldValue = $entity->getFetched('cLegalAidType');
if ($newValue !== 'DirectAccess' || $oldValue === 'DirectAccess') {
return;
}
$this->log->info("LegalAssistance: Creating direct access tasks for case {$entity->getId()}");
$assignedUserId = $entity->get('assignedUserId');
$now = new \DateTime('now', new \DateTimeZone('Asia/Jerusalem'));
$tasks = [
[
'name' => 'שיחת טלפון ראשונה עם הלקוח',
'dateEnd' => (clone $now)->modify('+2 days')->format('Y-m-d'),
'priority' => 'High',
'description' => 'יש ליצור קשר ראשוני עם הלקוח תוך 48 שעות ממועד קבלת המינוי.',
],
[
'name' => 'פגישה עם הלקוח',
'dateEnd' => (clone $now)->modify('+7 days')->format('Y-m-d'),
'priority' => 'Normal',
'description' => 'לקבוע ולקיים פגישה ראשונה עם הלקוח. לתעד נוכחים ותוכן הפגישה.',
],
[
'name' => 'שליחת דוח גישה ישירה',
'dateEnd' => (clone $now)->modify('+14 days')->format('Y-m-d'),
'priority' => 'High',
'description' => 'למלא ולשלוח דוח דיווח ראשוני לסיוע המשפטי. ניתן לבקש משירה: "תיצרי דוח גישה ישירה".',
],
];
foreach ($tasks as $taskData) {
$task = $this->entityManager->getNewEntity('Task');
$task->set([
'name' => $taskData['name'],
'status' => 'Not Started',
'priority' => $taskData['priority'],
'dateEnd' => $taskData['dateEnd'],
'description' => $taskData['description'],
'parentType' => 'Case',
'parentId' => $entity->getId(),
'assignedUserId' => $assignedUserId,
]);
$this->entityManager->saveEntity($task, ['skipHooks' => false]);
}
$this->log->info("LegalAssistance: Created 3 direct access tasks for case {$entity->getId()}");
}
}
@@ -5,36 +5,7 @@
"tabs": { "tabs": {
"LegalAssistance": "Legal Assistance" "LegalAssistance": "Legal Assistance"
}, },
"fields": {
"cLegalAidType": "Legal Aid Type",
"cAppointmentDate": "Appointment Date",
"cAidType": "Aid Type",
"cUrgencyLevel": "Urgency Level",
"cFilingDeadline": "Filing Deadline"
},
"options": {
"cLegalAidType": {
"": "",
"DirectAccess": "Direct Access",
"Regular": "Regular",
"Duty": "Duty"
},
"cAidType": {
"": "",
"הגשת ערעור": "Filing Appeal",
"יעוץ והדרכה": "Counseling & Guidance",
"סיוע נוסף ביטוח לאומי": "Additional NI Aid",
"סיוע נוסף תחום אחר": "Additional Aid Other",
"אי מתן סיוע": "No Aid Provided"
},
"cUrgencyLevel": {
"": "",
"רגיל": "Normal",
"דחוף": "Urgent",
"בהול": "Critical"
}
},
"links": { "links": {
"directAccessReports": "Direct Access Reports" "legalAids": "Legal Aid"
} }
} }
@@ -1,8 +1,8 @@
{ {
"scopeNames": { "scopeNames": {
"DirectAccessReport": "Direct Access Report" "LegalAid": "Legal Aid"
}, },
"scopeNamesPlural": { "scopeNamesPlural": {
"DirectAccessReport": "Direct Access Reports" "LegalAid": "Legal Aid Records"
} }
} }
@@ -1,20 +1,25 @@
{ {
"scopeName": "Direct Access Report", "scopeName": "Legal Aid",
"scopeNamesPlural": "Direct Access Reports", "scopeNamesPlural": "Legal Aid Records",
"labels": { "labels": {
"Create DirectAccessReport": "Create Direct Access Report" "Create LegalAid": "Create Legal Aid Record"
}, },
"fields": { "fields": {
"name": "Report Name", "name": "Name",
"number": "Number", "number": "Number",
"status": "Status", "status": "Status",
"legalAidNumber": "Legal Aid Number",
"legalAidType": "Legal Aid Type",
"reportDate": "Report Date",
"case": "Case", "case": "Case",
"contact": "Contact",
"contactName": "Client Name",
"aidType": "Aid Type", "aidType": "Aid Type",
"urgencyLevel": "Urgency Level", "urgencyLevel": "Urgency Level",
"appointmentDate": "Appointment Date", "appointmentDate": "Appointment Date",
"filingDeadline": "Filing Deadline", "filingDeadline": "Filing Deadline",
"additionalUrgency": "Additional Urgency", "additionalUrgency": "Additional Urgency",
"additionalDeadline": "Additional Deadline", "additionalDeadline": "Additional Deadlines",
"firstContactDate": "First Contact Date", "firstContactDate": "First Contact Date",
"contactDelayReason": "Contact Delay Reason", "contactDelayReason": "Contact Delay Reason",
"meetingDate": "Meeting Date", "meetingDate": "Meeting Date",
@@ -27,34 +32,34 @@
"proceedingSubject": "Proceeding Subject", "proceedingSubject": "Proceeding Subject",
"claimSummary": "Claim Summary", "claimSummary": "Claim Summary",
"defenseClaims": "Defense Claims", "defenseClaims": "Defense Claims",
"q1DocsReviewed": "Committee Reviewed All Documents?", "q1DocsReviewed": "Committee reviewed all documents?",
"q1DocsDetail": "Documents Detail", "q1DocsDetail": "Documents Detail",
"q2Reasoned": "Decision Reasoned?", "q2Reasoned": "Decision is reasoned?",
"q2ReasonDetail": "Missing Reasoning Detail", "q2ReasonDetail": "Missing Reasoning Detail",
"q3PanelMatch": "Panel Matches Impairments?", "q3PanelMatch": "Panel matches impairments?",
"q4PanelComposition": "Panel Composition", "q4PanelComposition": "Panel Composition",
"q5ComplaintsAddressed": "All Complaints Addressed?", "q5ComplaintsAddressed": "All complaints addressed?",
"q6ComplaintsDetail": "Missing Complaints Detail", "q6ComplaintsDetail": "Missing Complaints Detail",
"q7ClinicalExam": "Clinical Exam Performed?", "q7ClinicalExam": "Clinical exam performed?",
"q8ClinicalGap": "Clinical vs Documents Gap", "q8ClinicalGap": "Clinical Exam vs Documents Gap",
"q9MoharaApplied": "Mohara Doctrine Applied?", "q9MoharaApplied": "Mohara doctrine applied?",
"q9MoharaDetail": "Missing Mohara Data", "q9MoharaDetail": "Missing Mohara Data",
"q10RehabGap": "Rehab Officer vs Committee Gap?", "q10RehabGap": "Rehab Officer vs Committee gap?",
"q10RehabDetail": "Gap Detail", "q10RehabDetail": "Gap Detail",
"q11ScoreGap": "Score vs Condition Gap?", "q11ScoreGap": "Score vs condition gap?",
"q11ScoreDetail": "Score Gap Detail", "q11ScoreDetail": "Score Gap Detail",
"q12UniqueAspects": "Unique Aspects Addressed?", "q12UniqueAspects": "Unique aspects addressed?",
"q12UniqueDetail": "Unique Aspects Detail", "q12UniqueDetail": "Unique Aspects Detail",
"recommendationType": "Recommendation Type", "recommendationType": "Recommendation Type",
"recommendationDetail": "Recommendation Detail", "recommendationDetail": "Recommendation Detail",
"additionalAidNeeded": "Additional Aid Needed", "additionalAidNeeded": "Additional Aid Needed",
"additionalAidNIDetail": "Additional NI Aid Detail", "additionalAidNIDetail": "Additional Aid Detail (NI)",
"additionalAidOtherDetail": "Additional Other Aid Detail", "additionalAidOtherDetail": "Additional Aid Detail (Other)",
"additionalAidUrgency": "Additional Aid Urgency", "additionalAidUrgency": "Additional Aid Urgency",
"additionalAidDeadlines": "Additional Aid Deadlines", "additionalAidDeadlines": "Additional Aid Deadlines",
"willingToRepresent": "Willing to Represent?", "willingToRepresent": "Willing to Represent?",
"notes": "General Notes", "notes": "General Notes",
"assignedUser": "Assigned User", "assignedUser": "Assigned Lawyer",
"teams": "Teams", "teams": "Teams",
"createdAt": "Created At", "createdAt": "Created At",
"modifiedAt": "Modified At", "modifiedAt": "Modified At",
@@ -64,16 +69,21 @@
"options": { "options": {
"status": { "status": {
"Draft": "Draft", "Draft": "Draft",
"Submitted": "Submitted", "Active": "Active",
"Approved": "Approved", "Closed": "Closed"
"Rejected": "Rejected" },
"legalAidType": {
"": "",
"DirectAccess": "Direct Access",
"Regular": "Regular",
"Duty": "Duty"
}, },
"aidType": { "aidType": {
"": "", "": "",
"הגשת ערעור": "Filing Appeal", "הגשת ערעור": "Filing Appeal",
"יעוץ והדרכה": "Counseling & Guidance", "יעוץ והדרכה": "Counseling & Guidance",
"סיוע נוסף ביטוח לאומי": "Additional NI Aid", "סיוע נוסף ביטוח לאומי": "Additional NI Aid",
"סיוע נוסף תחום אחר": "Additional Aid Other", "סיוע נוסף תחום אחר": "Additional Aid (Other)",
"אי מתן סיוע": "No Aid Provided" "אי מתן סיוע": "No Aid Provided"
}, },
"urgencyLevel": { "urgencyLevel": {
@@ -91,7 +101,7 @@
"contactStatus": { "contactStatus": {
"": "", "": "",
"תקין": "Normal", "תקין": "Normal",
"לא תקין": "Not Normal" "לא תקין": "Abnormal"
}, },
"courtName": { "courtName": {
"": "", "": "",
@@ -129,8 +139,8 @@
"additionalAidNeeded": { "additionalAidNeeded": {
"": "", "": "",
"לא נחוץ": "Not Needed", "לא נחוץ": "Not Needed",
"נחוץ בתחום ביטוח לאומי": "Needed - NI Field", "נחוץ בתחום ביטוח לאומי": "Needed (NI Field)",
"נחוץ בתחום אחר": "Needed - Other Field" "נחוץ בתחום אחר": "Needed (Other Field)"
}, },
"additionalAidUrgency": { "additionalAidUrgency": {
"": "", "": "",
@@ -140,11 +150,12 @@
} }
}, },
"links": { "links": {
"case": "Case" "case": "Case",
"contact": "Contact"
}, },
"presetFilters": { "presetFilters": {
"draft": "Draft", "draft": "Drafts",
"submitted": "Submitted", "active": "Active",
"approved": "Approved" "closed": "Closed"
} }
} }
@@ -5,22 +5,7 @@
"tabs": { "tabs": {
"LegalAssistance": "סיוע משפטי" "LegalAssistance": "סיוע משפטי"
}, },
"fields": {
"cLegalAidType": "סוג מינוי סיוע משפטי",
"cAppointmentDate": "מועד קבלת המינוי",
"cAidType": "מהות הסיוע",
"cUrgencyLevel": "דחיפות",
"cFilingDeadline": "מועד אחרון להגשה"
},
"options": {
"cLegalAidType": {
"": "",
"DirectAccess": "גישה ישירה",
"Regular": "מינוי רגיל",
"Duty": "תורנות"
}
},
"links": { "links": {
"directAccessReports": "דוחות גישה ישירה" "legalAids": "סיוע משפטי"
} }
} }
@@ -1,8 +1,8 @@
{ {
"scopeNames": { "scopeNames": {
"DirectAccessReport": "דוח גישה ישירה" "LegalAid": "סיוע משפטי"
}, },
"scopeNamesPlural": { "scopeNamesPlural": {
"DirectAccessReport": "דוחות גישה ישירה" "LegalAid": "סיוע משפטי"
} }
} }
@@ -1,14 +1,19 @@
{ {
"scopeName": "דוח גישה ישירה", "scopeName": "סיוע משפטי",
"scopeNamesPlural": "דוחות גישה ישירה", "scopeNamesPlural": "סיוע משפטי",
"labels": { "labels": {
"Create DirectAccessReport": "צור דוח גישה ישירה" "Create LegalAid": "צור רשומת סיוע משפטי"
}, },
"fields": { "fields": {
"name": "שם הדוח", "name": "שם",
"number": "מספר", "number": "מספר",
"status": "סטטוס", "status": "סטטוס",
"legalAidNumber": "מספר תיק סיוע משפטי",
"legalAidType": "סוג מינוי",
"reportDate": "תאריך הדוח",
"case": "תיק", "case": "תיק",
"contact": "איש קשר",
"contactName": "שם הלקוח",
"aidType": "מהות הסיוע", "aidType": "מהות הסיוע",
"urgencyLevel": "דחיפות הליך", "urgencyLevel": "דחיפות הליך",
"appointmentDate": "מועד קבלת המינוי", "appointmentDate": "מועד קבלת המינוי",
@@ -54,7 +59,7 @@
"additionalAidDeadlines": "מועדים (סיוע נוסף)", "additionalAidDeadlines": "מועדים (סיוע נוסף)",
"willingToRepresent": "בתחום התמחות + מעוניין לייצג?", "willingToRepresent": "בתחום התמחות + מעוניין לייצג?",
"notes": "הערות כלליות", "notes": "הערות כלליות",
"assignedUser": "אחראי", "assignedUser": "עורך דין אחראי",
"teams": "צוותים", "teams": "צוותים",
"createdAt": "נוצר ב", "createdAt": "נוצר ב",
"modifiedAt": "עודכן ב", "modifiedAt": "עודכן ב",
@@ -64,9 +69,14 @@
"options": { "options": {
"status": { "status": {
"Draft": "טיוטה", "Draft": "טיוטה",
"Submitted": "נשלח", "Active": "פעיל",
"Approved": "אושר", "Closed": "סגור"
"Rejected": "נדחה" },
"legalAidType": {
"": "",
"DirectAccess": "גישה ישירה",
"Regular": "מינוי רגיל",
"Duty": "תורנות"
}, },
"aidType": { "aidType": {
"": "", "": "",
@@ -140,11 +150,12 @@
} }
}, },
"links": { "links": {
"case": "תיק" "case": "תיק",
"contact": "איש קשר"
}, },
"presetFilters": { "presetFilters": {
"draft": "טיוטות", "draft": "טיוטות",
"submitted": "נשלחו", "active": "פעילים",
"approved": "אושרו" "closed": "סגורים"
} }
} }
@@ -5,22 +5,7 @@
"tabs": { "tabs": {
"LegalAssistance": "סיוע משפטי" "LegalAssistance": "סיוע משפטי"
}, },
"fields": {
"cLegalAidType": "סוג מינוי סיוע משפטי",
"cAppointmentDate": "מועד קבלת המינוי",
"cAidType": "מהות הסיוע",
"cUrgencyLevel": "דחיפות",
"cFilingDeadline": "מועד אחרון להגשה"
},
"options": {
"cLegalAidType": {
"": "",
"DirectAccess": "גישה ישירה",
"Regular": "מינוי רגיל",
"Duty": "תורנות"
}
},
"links": { "links": {
"directAccessReports": "דוחות גישה ישירה" "legalAids": "סיוע משפטי"
} }
} }
@@ -1,8 +1,8 @@
{ {
"scopeNames": { "scopeNames": {
"DirectAccessReport": "דוח גישה ישירה" "LegalAid": "סיוע משפטי"
}, },
"scopeNamesPlural": { "scopeNamesPlural": {
"DirectAccessReport": "דוחות גישה ישירה" "LegalAid": "סיוע משפטי"
} }
} }
@@ -1,14 +1,19 @@
{ {
"scopeName": "דוח גישה ישירה", "scopeName": "סיוע משפטי",
"scopeNamesPlural": "דוחות גישה ישירה", "scopeNamesPlural": "סיוע משפטי",
"labels": { "labels": {
"Create DirectAccessReport": "צור דוח גישה ישירה" "Create LegalAid": "צור רשומת סיוע משפטי"
}, },
"fields": { "fields": {
"name": "שם הדוח", "name": "שם",
"number": "מספר", "number": "מספר",
"status": "סטטוס", "status": "סטטוס",
"legalAidNumber": "מספר תיק סיוע משפטי",
"legalAidType": "סוג מינוי",
"reportDate": "תאריך הדוח",
"case": "תיק", "case": "תיק",
"contact": "איש קשר",
"contactName": "שם הלקוח",
"aidType": "מהות הסיוע", "aidType": "מהות הסיוע",
"urgencyLevel": "דחיפות הליך", "urgencyLevel": "דחיפות הליך",
"appointmentDate": "מועד קבלת המינוי", "appointmentDate": "מועד קבלת המינוי",
@@ -54,7 +59,7 @@
"additionalAidDeadlines": "מועדים (סיוע נוסף)", "additionalAidDeadlines": "מועדים (סיוע נוסף)",
"willingToRepresent": "בתחום התמחות + מעוניין לייצג?", "willingToRepresent": "בתחום התמחות + מעוניין לייצג?",
"notes": "הערות כלליות", "notes": "הערות כלליות",
"assignedUser": "אחראי", "assignedUser": "עורך דין אחראי",
"teams": "צוותים", "teams": "צוותים",
"createdAt": "נוצר ב", "createdAt": "נוצר ב",
"modifiedAt": "עודכן ב", "modifiedAt": "עודכן ב",
@@ -64,9 +69,14 @@
"options": { "options": {
"status": { "status": {
"Draft": "טיוטה", "Draft": "טיוטה",
"Submitted": "נשלח", "Active": "פעיל",
"Approved": "אושר", "Closed": "סגור"
"Rejected": "נדחה" },
"legalAidType": {
"": "",
"DirectAccess": "גישה ישירה",
"Regular": "מינוי רגיל",
"Duty": "תורנות"
}, },
"aidType": { "aidType": {
"": "", "": "",
@@ -140,11 +150,12 @@
} }
}, },
"links": { "links": {
"case": "תיק" "case": "תיק",
"contact": "איש קשר"
}, },
"presetFilters": { "presetFilters": {
"draft": "טיוטות", "draft": "טיוטות",
"submitted": "נשלחו", "active": "פעילים",
"approved": "אושרו" "closed": "סגורים"
} }
} }
@@ -4,6 +4,9 @@
"rows": [ "rows": [
[{"name": "name"}, {"name": "number"}], [{"name": "name"}, {"name": "number"}],
[{"name": "case"}, {"name": "status"}], [{"name": "case"}, {"name": "status"}],
[{"name": "legalAidNumber"}, {"name": "legalAidType"}],
[{"name": "reportDate"}, false],
[{"name": "contact"}, {"name": "contactName"}],
[{"name": "assignedUser"}, {"name": "teams"}] [{"name": "assignedUser"}, {"name": "teams"}]
] ]
}, },
@@ -0,0 +1,11 @@
[
{"name": "number", "width": 8},
{"name": "name", "link": true},
{"name": "legalAidNumber", "width": 12},
{"name": "case", "width": 15},
{"name": "contact", "width": 15},
{"name": "status", "width": 10},
{"name": "aidType", "width": 12},
{"name": "urgencyLevel", "width": 8},
{"name": "assignedUser", "width": 12}
]
@@ -1,8 +1,8 @@
{ {
"controller": "controllers/record", "controller": "controllers/record",
"boolFilterList": ["onlyMy"], "boolFilterList": ["onlyMy"],
"iconClass": "fas fa-file-alt", "iconClass": "fas fa-balance-scale",
"color": "#6C3483", "color": "#2E86C1",
"kanbanViewMode": true, "kanbanViewMode": true,
"statusField": "status", "statusField": "status",
"filterList": [ "filterList": [
@@ -11,12 +11,12 @@
"style": "default" "style": "default"
}, },
{ {
"name": "submitted", "name": "active",
"style": "warning" "style": "success"
}, },
{ {
"name": "approved", "name": "closed",
"style": "success" "style": "info"
} }
], ],
"menu": { "menu": {
@@ -1,41 +1,8 @@
{ {
"fields": {
"cLegalAidType": {
"type": "enum",
"options": ["", "DirectAccess", "Regular", "Duty"],
"audited": true,
"displayAsLabel": true,
"style": {
"DirectAccess": "primary",
"Regular": "default",
"Duty": "info"
}
},
"cAppointmentDate": {
"type": "date"
},
"cAidType": {
"type": "enum",
"options": ["", "הגשת ערעור", "יעוץ והדרכה", "סיוע נוסף ביטוח לאומי", "סיוע נוסף תחום אחר", "אי מתן סיוע"]
},
"cUrgencyLevel": {
"type": "enum",
"options": ["", "רגיל", "דחוף", "בהול"],
"displayAsLabel": true,
"style": {
"רגיל": "default",
"דחוף": "warning",
"בהול": "danger"
}
},
"cFilingDeadline": {
"type": "date"
}
},
"links": { "links": {
"directAccessReports": { "legalAids": {
"type": "hasMany", "type": "hasMany",
"entity": "DirectAccessReport", "entity": "LegalAid",
"foreign": "case", "foreign": "case",
"layoutRelationshipsDisabled": false "layoutRelationshipsDisabled": false
} }
@@ -11,21 +11,45 @@
}, },
"status": { "status": {
"type": "enum", "type": "enum",
"options": ["Draft", "Submitted", "Approved", "Rejected"], "options": ["Draft", "Active", "Closed"],
"default": "Draft", "default": "Draft",
"audited": true, "audited": true,
"displayAsLabel": true, "displayAsLabel": true,
"style": { "style": {
"Draft": "default", "Draft": "default",
"Submitted": "warning", "Active": "success",
"Approved": "success", "Closed": "info"
"Rejected": "danger"
} }
}, },
"legalAidNumber": {
"type": "varchar",
"maxLength": 50
},
"legalAidType": {
"type": "enum",
"options": ["", "DirectAccess", "Regular", "Duty"],
"audited": true,
"displayAsLabel": true,
"style": {
"DirectAccess": "primary",
"Regular": "default",
"Duty": "info"
}
},
"reportDate": {
"type": "date"
},
"case": { "case": {
"type": "link", "type": "link",
"required": true "required": true
}, },
"contact": {
"type": "link"
},
"contactName": {
"type": "varchar",
"maxLength": 150
},
"aidType": { "aidType": {
"type": "enum", "type": "enum",
@@ -232,7 +256,11 @@
"case": { "case": {
"type": "belongsTo", "type": "belongsTo",
"entity": "Case", "entity": "Case",
"foreign": "directAccessReports" "foreign": "legalAids"
},
"contact": {
"type": "belongsTo",
"entity": "Contact"
}, },
"createdBy": { "createdBy": {
"type": "belongsTo", "type": "belongsTo",
@@ -256,12 +284,15 @@
"collection": { "collection": {
"orderBy": "createdAt", "orderBy": "createdAt",
"order": "desc", "order": "desc",
"textFilterFields": ["name", "number"] "textFilterFields": ["name", "number", "legalAidNumber"]
}, },
"indexes": { "indexes": {
"status": { "status": {
"columns": ["status", "deleted"] "columns": ["status", "deleted"]
}, },
"legalAidNumber": {
"columns": ["legalAidNumber", "deleted"]
},
"createdAt": { "createdAt": {
"columns": ["createdAt"] "columns": ["createdAt"]
} }
@@ -1,11 +0,0 @@
[
{
"rows": [
[{"name": "name"}],
[{"name": "case"}],
[{"name": "status"}],
[{"name": "aidType"}],
[{"name": "recommendationType"}]
]
}
]
@@ -1,10 +0,0 @@
[
{"name": "number", "width": 8},
{"name": "name", "link": true},
{"name": "case", "width": 20},
{"name": "status", "width": 12},
{"name": "aidType", "width": 15},
{"name": "recommendationType", "width": 15},
{"name": "createdAt", "width": 15},
{"name": "assignedUser", "width": 15}
]
@@ -2,14 +2,15 @@
"entity": true, "entity": true,
"object": true, "object": true,
"stream": true, "stream": true,
"tab": false, "tab": true,
"acl": true, "acl": true,
"aclPortal": false, "aclPortal": false,
"disabled": false, "disabled": false,
"module": "LegalAssistance", "module": "LegalAssistance",
"isCustom": false, "isCustom": true,
"layouts": true,
"notifications": true, "notifications": true,
"calendar": false, "calendar": false,
"activity": false, "activity": false,
"kanbanStatusIgnoreList": ["Approved", "Rejected"] "kanbanStatusIgnoreList": ["Closed"]
} }
@@ -1,214 +0,0 @@
<?php
namespace Espo\Modules\LegalAssistance\Services;
use Espo\Core\Exceptions\Error;
use Espo\Core\InjectableFactory;
use Espo\ORM\EntityManager;
use Espo\Core\Utils\Log;
use Espo\Core\Utils\DateTime as DateTimeUtil;
use Espo\Entities\User;
class DirectAccessReportService
{
public function __construct(
private EntityManager $entityManager,
private InjectableFactory $injectableFactory,
private Log $log,
private User $user,
private DateTimeUtil $dateTimeUtil
) {}
public function generateReport(string $caseId, string $userId, array $params): array
{
$case = $this->entityManager->getEntityById('Case', $caseId);
if (!$case) {
throw new Error("Case {$caseId} not found.");
}
$contact = null;
$contactId = $case->get('contactId');
if ($contactId) {
$contact = $this->entityManager->getEntityById('Contact', $contactId);
}
// Build report name
$contactName = $contact
? ($contact->get('firstName') . ' ' . $contact->get('lastName'))
: 'ללא איש קשר';
$reportName = "דוח גישה ישירה — {$contactName}";
// Extract legal analysis from nested object
$legalAnalysis = $params['legalAnalysis'] ?? [];
if (is_string($legalAnalysis)) {
$legalAnalysis = json_decode($legalAnalysis, true) ?? [];
}
// Create DirectAccessReport entity
$report = $this->entityManager->getNewEntity('DirectAccessReport');
$report->set([
'name' => $reportName,
'status' => 'Draft',
'caseId' => $caseId,
'assignedUserId' => $userId,
// Section ב' — מהות הסיוע
'aidType' => $params['aidType'] ?? null,
'urgencyLevel' => $params['urgencyLevel'] ?? null,
'appointmentDate' => $this->normalizeDate($params['appointmentDate'] ?? null),
'filingDeadline' => $this->normalizeDate($params['filingDeadline'] ?? null),
'additionalUrgency' => $params['additionalUrgency'] ?? null,
'additionalDeadline' => $params['additionalDeadline'] ?? null,
// Section ג' — קשר עם הלקוח
'firstContactDate' => $this->normalizeDate($params['firstContactDate'] ?? null),
'contactDelayReason' => $params['contactDelayReason'] ?? null,
'meetingDate' => $this->normalizeDate($params['meetingDate'] ?? null),
'meetingDelayReason' => $params['meetingDelayReason'] ?? null,
'meetingAttendees' => $params['meetingAttendees'] ?? null,
'contactStatus' => $params['contactStatus'] ?? null,
'contactStatusDetail' => $params['contactStatusDetail'] ?? null,
// Section ד' — פרטי ההליך
'proceedingNumber' => $params['proceedingNumber'] ?? $case->get('cCourtCaseNumber'),
'courtName' => $params['courtName'] ?? null,
'proceedingSubject' => $params['proceedingSubject'] ?? null,
'claimSummary' => $params['claimSummary'] ?? null,
'defenseClaims' => $params['defenseClaims'] ?? null,
// Section ה'-ז' — בחינת סיכוי משפטי
'q1DocsReviewed' => $legalAnalysis['q1DocsReviewed'] ?? false,
'q1DocsDetail' => $legalAnalysis['q1DocsDetail'] ?? null,
'q2Reasoned' => $legalAnalysis['q2Reasoned'] ?? false,
'q2ReasonDetail' => $legalAnalysis['q2ReasonDetail'] ?? null,
'q3PanelMatch' => $legalAnalysis['q3PanelMatch'] ?? false,
'q4PanelComposition' => $legalAnalysis['q4PanelComposition'] ?? null,
'q5ComplaintsAddressed' => $legalAnalysis['q5ComplaintsAddressed'] ?? false,
'q6ComplaintsDetail' => $legalAnalysis['q6ComplaintsDetail'] ?? null,
'q7ClinicalExam' => $legalAnalysis['q7ClinicalExam'] ?? false,
'q8ClinicalGap' => $legalAnalysis['q8ClinicalGap'] ?? null,
'q9MoharaApplied' => $legalAnalysis['q9MoharaApplied'] ?? false,
'q9MoharaDetail' => $legalAnalysis['q9MoharaDetail'] ?? null,
'q10RehabGap' => $legalAnalysis['q10RehabGap'] ?? false,
'q10RehabDetail' => $legalAnalysis['q10RehabDetail'] ?? null,
'q11ScoreGap' => $legalAnalysis['q11ScoreGap'] ?? false,
'q11ScoreDetail' => $legalAnalysis['q11ScoreDetail'] ?? null,
'q12UniqueAspects' => $legalAnalysis['q12UniqueAspects'] ?? false,
'q12UniqueDetail' => $legalAnalysis['q12UniqueDetail'] ?? null,
// Section ח' — המלצה
'recommendationType' => $params['recommendationType'] ?? null,
'recommendationDetail' => $params['recommendationDetail'] ?? null,
// Section ט' — מיצוי זכויות
'additionalAidNeeded' => $params['additionalAidNeeded'] ?? null,
'additionalAidNIDetail' => $params['additionalAidNIDetail'] ?? null,
'additionalAidOtherDetail' => $params['additionalAidOtherDetail'] ?? null,
'additionalAidUrgency' => $params['additionalAidUrgency'] ?? null,
'additionalAidDeadlines' => $params['additionalAidDeadlines'] ?? null,
'willingToRepresent' => $params['willingToRepresent'] ?? false,
// Section י' — הערות
'notes' => $params['notes'] ?? null,
]);
$this->entityManager->saveEntity($report);
$this->log->info("LegalAssistance: Created DirectAccessReport {$report->getId()} for case {$caseId}");
// Also update Case fields if provided
$caseUpdated = false;
if (!empty($params['appointmentDate']) && !$case->get('cAppointmentDate')) {
$case->set('cAppointmentDate', $this->normalizeDate($params['appointmentDate']));
$caseUpdated = true;
}
if (!empty($params['aidType']) && !$case->get('cAidType')) {
$case->set('cAidType', $params['aidType']);
$caseUpdated = true;
}
if (!empty($params['urgencyLevel']) && !$case->get('cUrgencyLevel')) {
$case->set('cUrgencyLevel', $params['urgencyLevel']);
$caseUpdated = true;
}
if (!empty($params['filingDeadline']) && !$case->get('cFilingDeadline')) {
$case->set('cFilingDeadline', $this->normalizeDate($params['filingDeadline']));
$caseUpdated = true;
}
if ($caseUpdated) {
$this->entityManager->saveEntity($case, ['skipHooks' => true]);
}
// Generate DOCX using template
$docxResult = $this->generateDocx($report, $case, $contact, $userId);
$msg = "✅ דוח גישה ישירה נוצר בהצלחה: \"{$reportName}\"";
if (!empty($docxResult['fileName'])) {
$msg .= "\n📄 מסמך DOCX נוצר: {$docxResult['fileName']}";
}
return [
'success' => true,
'message' => $msg,
'entityType' => 'DirectAccessReport',
'entityId' => $report->getId(),
];
}
private function generateDocx($report, $case, $contact, string $userId): array
{
// Try to use LocalDocumentTemplateService if available
try {
$templateServiceClass = 'Espo\\Modules\\LocalDocuments\\Services\\LocalDocumentTemplateService';
if (!class_exists($templateServiceClass)) {
$templateServiceClass = 'Espo\\Modules\\NetworkStorageIntegration\\Services\\DocumentTemplateService';
}
if (!class_exists($templateServiceClass)) {
$this->log->warning("LegalAssistance: No template service available for DOCX generation");
return ['success' => false, 'error' => 'No template service'];
}
// Find the template
$template = $this->entityManager
->getRDBRepository('DocumentTemplate')
->where(['name' => 'דוח גישה ישירה'])
->findOne();
if (!$template) {
$this->log->warning("LegalAssistance: Template 'דוח גישה ישירה' not found");
return ['success' => false, 'error' => 'Template not found'];
}
$templateService = $this->injectableFactory->create($templateServiceClass);
$result = $templateService->createFromTemplate(
$template->getId(),
'Case',
$case->getId(),
$report->get('name')
);
return $result;
} catch (\Throwable $e) {
$this->log->error("LegalAssistance: DOCX generation failed: " . $e->getMessage());
return ['success' => false, 'error' => $e->getMessage()];
}
}
private function normalizeDate(?string $value): ?string
{
if (!$value) return null;
// Handle DD/MM/YYYY format
if (preg_match('/^(\d{1,2})\/(\d{1,2})\/(\d{4})$/', $value, $m)) {
return "{$m[3]}-{$m[2]}-{$m[1]}";
}
// Handle YYYY-MM-DD format (already correct)
if (preg_match('/^\d{4}-\d{2}-\d{2}$/', $value)) {
return $value;
}
return $value;
}
}
@@ -6,7 +6,6 @@ use Espo\Core\Exceptions\Error;
use Espo\Core\InjectableFactory; use Espo\Core\InjectableFactory;
use Espo\ORM\EntityManager; use Espo\ORM\EntityManager;
use Espo\Core\Utils\Log; use Espo\Core\Utils\Log;
use Espo\Modules\LegalAssistance\Services\DirectAccessReportService;
class GenerateInitialReport class GenerateInitialReport
{ {
@@ -24,10 +23,128 @@ class GenerateInitialReport
$this->log->info("LegalAssistance: Generating initial report for case {$caseId}"); $this->log->info("LegalAssistance: Generating initial report for case {$caseId}");
$service = $this->injectableFactory->create(DirectAccessReportService::class); $case = $this->entityManager->getEntityById('Case', $caseId);
if (!$case) {
throw new Error("Case {$caseId} not found.");
}
$result = $service->generateReport($caseId, $userId, $params); // Get contact
$contact = null;
$contactId = $case->get('contactId');
if (!$contactId) {
$contactsIds = $case->getLinkMultipleIdList('contacts');
if (!empty($contactsIds)) {
$contactId = $contactsIds[0];
}
}
if ($contactId) {
$contact = $this->entityManager->getEntityById('Contact', $contactId);
}
return $result; $contactName = $contact
? trim($contact->get('firstName') . ' ' . $contact->get('lastName'))
: 'ללא איש קשר';
// Extract legal analysis from nested object
$legalAnalysis = $params['legalAnalysis'] ?? [];
if (is_string($legalAnalysis)) {
$legalAnalysis = json_decode($legalAnalysis, true) ?? [];
}
// Find or create LegalAid
$legalAid = $this->entityManager
->getRDBRepository('LegalAid')
->where(['caseId' => $caseId])
->findOne();
if (!$legalAid) {
$legalAid = $this->entityManager->getNewEntity('LegalAid');
}
$legalAid->set([
'name' => "סיוע משפטי — {$contactName}",
'status' => 'Active',
'caseId' => $caseId,
'contactId' => $contactId,
'contactName' => $contactName,
'assignedUserId' => $userId,
'legalAidNumber' => $case->get('cLegalAidNumber') ?? '',
'reportDate' => date('Y-m-d'),
'aidType' => $params['aidType'] ?? $legalAid->get('aidType'),
'urgencyLevel' => $params['urgencyLevel'] ?? $legalAid->get('urgencyLevel'),
'appointmentDate' => $this->normalizeDate($params['appointmentDate'] ?? null) ?? $legalAid->get('appointmentDate'),
'filingDeadline' => $this->normalizeDate($params['filingDeadline'] ?? null) ?? $legalAid->get('filingDeadline'),
'additionalUrgency' => $params['additionalUrgency'] ?? $legalAid->get('additionalUrgency'),
'additionalDeadline' => $params['additionalDeadline'] ?? $legalAid->get('additionalDeadline'),
'firstContactDate' => $this->normalizeDate($params['firstContactDate'] ?? null) ?? $legalAid->get('firstContactDate'),
'contactDelayReason' => $params['contactDelayReason'] ?? $legalAid->get('contactDelayReason'),
'meetingDate' => $this->normalizeDate($params['meetingDate'] ?? null) ?? $legalAid->get('meetingDate'),
'meetingDelayReason' => $params['meetingDelayReason'] ?? $legalAid->get('meetingDelayReason'),
'meetingAttendees' => $params['meetingAttendees'] ?? $legalAid->get('meetingAttendees'),
'contactStatus' => $params['contactStatus'] ?? $legalAid->get('contactStatus'),
'contactStatusDetail' => $params['contactStatusDetail'] ?? $legalAid->get('contactStatusDetail'),
'proceedingNumber' => $params['proceedingNumber'] ?? $legalAid->get('proceedingNumber') ?? $case->get('cCourtCaseNumber'),
'courtName' => $params['courtName'] ?? $legalAid->get('courtName'),
'proceedingSubject' => $params['proceedingSubject'] ?? $legalAid->get('proceedingSubject'),
'claimSummary' => $params['claimSummary'] ?? $legalAid->get('claimSummary'),
'defenseClaims' => $params['defenseClaims'] ?? $legalAid->get('defenseClaims'),
'q1DocsReviewed' => $legalAnalysis['q1DocsReviewed'] ?? $legalAid->get('q1DocsReviewed') ?? false,
'q1DocsDetail' => $legalAnalysis['q1DocsDetail'] ?? $legalAid->get('q1DocsDetail'),
'q2Reasoned' => $legalAnalysis['q2Reasoned'] ?? $legalAid->get('q2Reasoned') ?? false,
'q2ReasonDetail' => $legalAnalysis['q2ReasonDetail'] ?? $legalAid->get('q2ReasonDetail'),
'q3PanelMatch' => $legalAnalysis['q3PanelMatch'] ?? $legalAid->get('q3PanelMatch') ?? false,
'q4PanelComposition' => $legalAnalysis['q4PanelComposition'] ?? $legalAid->get('q4PanelComposition'),
'q5ComplaintsAddressed' => $legalAnalysis['q5ComplaintsAddressed'] ?? $legalAid->get('q5ComplaintsAddressed') ?? false,
'q6ComplaintsDetail' => $legalAnalysis['q6ComplaintsDetail'] ?? $legalAid->get('q6ComplaintsDetail'),
'q7ClinicalExam' => $legalAnalysis['q7ClinicalExam'] ?? $legalAid->get('q7ClinicalExam') ?? false,
'q8ClinicalGap' => $legalAnalysis['q8ClinicalGap'] ?? $legalAid->get('q8ClinicalGap'),
'q9MoharaApplied' => $legalAnalysis['q9MoharaApplied'] ?? $legalAid->get('q9MoharaApplied') ?? false,
'q9MoharaDetail' => $legalAnalysis['q9MoharaDetail'] ?? $legalAid->get('q9MoharaDetail'),
'q10RehabGap' => $legalAnalysis['q10RehabGap'] ?? $legalAid->get('q10RehabGap') ?? false,
'q10RehabDetail' => $legalAnalysis['q10RehabDetail'] ?? $legalAid->get('q10RehabDetail'),
'q11ScoreGap' => $legalAnalysis['q11ScoreGap'] ?? $legalAid->get('q11ScoreGap') ?? false,
'q11ScoreDetail' => $legalAnalysis['q11ScoreDetail'] ?? $legalAid->get('q11ScoreDetail'),
'q12UniqueAspects' => $legalAnalysis['q12UniqueAspects'] ?? $legalAid->get('q12UniqueAspects') ?? false,
'q12UniqueDetail' => $legalAnalysis['q12UniqueDetail'] ?? $legalAid->get('q12UniqueDetail'),
'recommendationType' => $params['recommendationType'] ?? $legalAid->get('recommendationType'),
'recommendationDetail' => $params['recommendationDetail'] ?? $legalAid->get('recommendationDetail'),
'additionalAidNeeded' => $params['additionalAidNeeded'] ?? $legalAid->get('additionalAidNeeded'),
'additionalAidNIDetail' => $params['additionalAidNIDetail'] ?? $legalAid->get('additionalAidNIDetail'),
'additionalAidOtherDetail' => $params['additionalAidOtherDetail'] ?? $legalAid->get('additionalAidOtherDetail'),
'additionalAidUrgency' => $params['additionalAidUrgency'] ?? $legalAid->get('additionalAidUrgency'),
'additionalAidDeadlines' => $params['additionalAidDeadlines'] ?? $legalAid->get('additionalAidDeadlines'),
'willingToRepresent' => $params['willingToRepresent'] ?? $legalAid->get('willingToRepresent') ?? false,
'notes' => $params['notes'] ?? $legalAid->get('notes'),
]);
$this->entityManager->saveEntity($legalAid);
$this->log->info("LegalAssistance: Saved LegalAid {$legalAid->getId()} for case {$caseId}");
return [
'success' => true,
'message' => "✅ רשומת סיוע משפטי נוצרה/עודכנה: \"{$contactName}\"",
'entityType' => 'LegalAid',
'entityId' => $legalAid->getId(),
];
}
private function normalizeDate(?string $value): ?string
{
if (!$value) return null;
if (preg_match('/^(\d{1,2})\/(\d{1,2})\/(\d{4})$/', $value, $m)) {
return "{$m[3]}-{$m[2]}-{$m[1]}";
}
if (preg_match('/^\d{4}-\d{2}-\d{2}$/', $value)) {
return $value;
}
return $value;
} }
} }
+3 -3
View File
@@ -1,11 +1,11 @@
{ {
"name": "LegalAssistance", "name": "LegalAssistance",
"version": "1.3.2", "version": "1.5.0",
"acceptableVersions": [">=8.0.0"], "acceptableVersions": [">=8.0.0"],
"php": [">=8.1"], "php": [">=8.1"],
"releaseDate": "2026-04-06", "releaseDate": "2026-04-07",
"author": "Marcus-Law", "author": "Marcus-Law",
"description": "סיוע משפטי — דוח גישה ישירה, משימות אוטומטיות, אינטגרציה עם שירה", "description": "סיוע משפטי — ישות LegalAid מרכזית, דוח גישה ישירה, משימות אוטומטיות, אינטגרציה עם שירה",
"scripts": { "scripts": {
"afterInstall": "scripts/AfterInstall.php", "afterInstall": "scripts/AfterInstall.php",
"afterUninstall": "scripts/AfterUninstall.php" "afterUninstall": "scripts/AfterUninstall.php"
+34 -1
View File
@@ -1,7 +1,7 @@
{ {
"tools": { "tools": {
"generate_initial_report": { "generate_initial_report": {
"description": "Generate Direct Access initial report (דוח דיווח ראשוני — גישה ישירה). Call ONLY after collecting ALL required data through conversation with the lawyer. This tool creates a DirectAccessReport entity and generates a DOCX document.", "description": "Generate Direct Access initial report (דוח דיווח ראשוני — גישה ישירה). Call ONLY after collecting ALL required data through conversation with the lawyer. This tool creates/updates a LegalAid entity and generates a DOCX document from it.",
"parameters": { "parameters": {
"type": "object", "type": "object",
"properties": { "properties": {
@@ -148,6 +148,39 @@
}, },
"required": ["aidType", "proceedingSubject", "recommendationType"] "required": ["aidType", "proceedingSubject", "recommendationType"]
} }
},
"update_legal_aid": {
"description": "Update fields on an existing LegalAid entity. Use this to fill or modify any field on the legal aid record. Pass only the fields you want to update — existing values are preserved for unspecified fields. You can update any combination of fields.",
"parameters": {
"type": "object",
"properties": {
"legalAidId": {
"type": "string",
"description": "ID of the LegalAid entity to update"
},
"fields": {
"type": "object",
"description": "Key-value pairs of fields to update. Any field from the LegalAid entity is valid: aidType, urgencyLevel, appointmentDate, filingDeadline, firstContactDate, contactDelayReason, meetingDate, meetingDelayReason, meetingAttendees, contactStatus, contactStatusDetail, proceedingNumber, courtName, proceedingSubject, claimSummary, defenseClaims, q1DocsReviewed, q1DocsDetail, q2Reasoned, q2ReasonDetail, q3PanelMatch, q4PanelComposition, q5ComplaintsAddressed, q6ComplaintsDetail, q7ClinicalExam, q8ClinicalGap, q9MoharaApplied, q9MoharaDetail, q10RehabGap, q10RehabDetail, q11ScoreGap, q11ScoreDetail, q12UniqueAspects, q12UniqueDetail, recommendationType, recommendationDetail, additionalAidNeeded, additionalAidNIDetail, additionalAidOtherDetail, additionalAidUrgency, additionalAidDeadlines, willingToRepresent, notes, status, contactName, legalAidNumber, reportDate, additionalUrgency, additionalDeadline"
}
},
"required": ["legalAidId", "fields"]
}
},
"get_legal_aid": {
"description": "Read an existing LegalAid entity and return all its field values. Use this to check what data has already been filled before updating.",
"parameters": {
"type": "object",
"properties": {
"legalAidId": {
"type": "string",
"description": "ID of the LegalAid entity to read"
},
"caseId": {
"type": "string",
"description": "Alternatively, find the LegalAid by Case ID"
}
}
}
} }
} }
} }
+1 -1
View File
@@ -153,7 +153,7 @@ class AfterInstall
$template = $entityManager->getNewEntity('DocumentTemplate'); $template = $entityManager->getNewEntity('DocumentTemplate');
$template->set([ $template->set([
'name' => 'דוח גישה ישירה', 'name' => 'דוח גישה ישירה',
'entityType' => 'Case', 'entityType' => 'LegalAid',
'fileId' => $attachment->getId(), 'fileId' => $attachment->getId(),
]); ]);
$entityManager->saveEntity($template); $entityManager->saveEntity($template);
Binary file not shown.