Align case stages to 5-stage spec and add Paralegal AI agent
Stage alignment: - Replace the 11 procedural stages with the spec's 5-stage machine (Intake/Active/Discovery/Pre-Trial/Closed); only Closed is folded - Make fl_stage_data.xml updatable (drop noupdate) so the rename applies on upgrade; keep fl_stage_intake/discovery/closed XML ids - Update case search filters to the new stage names (Intake/Active/Discovery/Pre-Trial) Issue-tag bug fix (fl_ai_engine): - Rule-based tagging and caselaw matching compared snake_case keys against the human-readable seeded tag names, so they never matched and issue_tag_ids was never populated. Add RULE_KEY_TO_TAG_NAME and translate keys to real names before all fl.issue.tag / fl.caselaw lookups Paralegal agent (fl.paralegal.agent, AbstractModel): - on_stage_change(): fast rule-based pass fired automatically on stage entry and case creation — generates the stage task batch (idempotent), recalculates filing/service deadlines, cross-references statutes by issue tag + case type, posts a chatter summary. No Claude call, so it never blocks the workflow - run_manual(): full pass adding a best-effort Claude procedural briefing with rule-based fallback; wired to a "Paralegal Review" button on the case form - AI audit-time logging is guarded behind a fl.timesheet existence check (model not built yet) - fl.case.write fires on_stage_change only when stage_id actually changes; create() generates the Intake batch Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -25,19 +25,22 @@ CASE_FIELD_RULES = [
|
||||
]
|
||||
|
||||
# ─────────────────────────────────────────────────────────────────────────────
|
||||
# Caselaw topic → issue tag matching
|
||||
# Internal rule key → seeded fl.issue.tag display name (data/fl_issue_tags.xml).
|
||||
# Rule keys are the snake_case identifiers used in CASE_FIELD_RULES and the
|
||||
# tagging logic; the actual tag records use human-readable names, so all DB
|
||||
# lookups must translate through this map.
|
||||
# ─────────────────────────────────────────────────────────────────────────────
|
||||
TAG_TO_CASELAW_DOMAINS = {
|
||||
'modification_threshold': [('issue_tag_ids.name', '=', 'modification_threshold')],
|
||||
'income_imputation': [('issue_tag_ids.name', '=', 'income_imputation')],
|
||||
'self_employment_income': [('issue_tag_ids.name', '=', 'self_employment_income')],
|
||||
'timesharing_deviation': [('issue_tag_ids.name', '=', 'timesharing_deviation')],
|
||||
'domestic_violence': [('issue_tag_ids.name', '=', 'domestic_violence')],
|
||||
'fee_waiver': [('issue_tag_ids.name', '=', 'fee_waiver')],
|
||||
'default_judgment': [('issue_tag_ids.name', '=', 'default_judgment')],
|
||||
'residency': [('issue_tag_ids.name', '=', 'residency')],
|
||||
'parenting_class': [('issue_tag_ids.name', '=', 'parenting_class')],
|
||||
'post_order': [('issue_tag_ids.name', '=', 'post_order')],
|
||||
RULE_KEY_TO_TAG_NAME = {
|
||||
'modification_threshold': 'Modification Threshold',
|
||||
'income_imputation': 'Income Imputation',
|
||||
'self_employment_income': 'Self-Employment Income',
|
||||
'timesharing_deviation': 'Timesharing Deviation',
|
||||
'domestic_violence': 'Domestic Violence',
|
||||
'fee_waiver': 'Fee Waiver / Indigent Status',
|
||||
'default_judgment': 'Default Judgment',
|
||||
'residency': 'Residency Requirement',
|
||||
'parenting_class': 'Parenting Class Required',
|
||||
'post_order': 'Post-Order / Income Withholding',
|
||||
}
|
||||
|
||||
MAX_CASELAW_IN_PROMPT = 8
|
||||
@@ -81,9 +84,13 @@ class FlAiEngine(models.AbstractModel):
|
||||
try:
|
||||
triggered_tags = self._rule_based_tagging(case)
|
||||
if triggered_tags:
|
||||
tag_names = [
|
||||
RULE_KEY_TO_TAG_NAME[k]
|
||||
for k in triggered_tags if k in RULE_KEY_TO_TAG_NAME
|
||||
]
|
||||
existing_tags = case.issue_tag_ids.mapped('name')
|
||||
new_tag_recs = self.env['fl.issue.tag'].search([
|
||||
('name', 'in', list(triggered_tags)),
|
||||
('name', 'in', tag_names),
|
||||
('name', 'not in', existing_tags),
|
||||
])
|
||||
if new_tag_recs:
|
||||
@@ -183,13 +190,14 @@ class FlAiEngine(models.AbstractModel):
|
||||
|
||||
matched_ids = set()
|
||||
for tag in triggered_tags:
|
||||
domain = TAG_TO_CASELAW_DOMAINS.get(tag, [])
|
||||
if domain:
|
||||
recs = self.env['fl.caselaw'].search(
|
||||
[('active', '=', True)] + domain,
|
||||
limit=6,
|
||||
)
|
||||
matched_ids.update(recs.ids)
|
||||
tag_name = RULE_KEY_TO_TAG_NAME.get(tag)
|
||||
if not tag_name:
|
||||
continue
|
||||
recs = self.env['fl.caselaw'].search([
|
||||
('active', '=', True),
|
||||
('issue_tag_ids.name', '=', tag_name),
|
||||
], limit=6)
|
||||
matched_ids.update(recs.ids)
|
||||
|
||||
if not matched_ids:
|
||||
return self.env['fl.caselaw'].browse()
|
||||
|
||||
Reference in New Issue
Block a user