Migrate AI engine to Claude API; convert stage field to Many2one Kanban model

- Replace fl.case.stage Selection field with Many2one → fl.case.stage model,
  enabling Kanban grouping and dynamic stage management
- Add FlCaseStage model (sequence, fold, description) and fl_stage_data.xml
  with all 11 procedural stages seeded with noupdate=1
- Migrate fl_ai_engine.py from Ollama/llama3.1 to Claude API
  (claude-sonnet-4-20250514); key from ir.config_parameter fl_ai.claude_api_key
- Fix stale field references in _rule_based_tagging and _build_case_context:
  employment/income now read from party_ids, timesharing fields corrected
- Add _fallback_complexity() for graceful degradation when API unavailable
- Add Kanban view to fl_case_views.xml; update action view_mode to kanban,tree,form
- Add fl.case.stage ACL entries to ir.model.access.csv

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-28 17:45:26 +00:00
parent c937282091
commit b8ab8494c7
6 changed files with 313 additions and 163 deletions

View File

@@ -2,6 +2,20 @@ from odoo import _, api, fields, models
from dateutil.relativedelta import relativedelta
class FlCaseStage(models.Model):
_name = 'fl.case.stage'
_description = 'Family Law Case Stage'
_order = 'sequence, id'
name = fields.Char(string='Stage Name', required=True, translate=True)
sequence = fields.Integer(default=10)
fold = fields.Boolean(
string='Folded in Kanban',
help='Folded stages appear collapsed on the Kanban board'
)
description = fields.Text(string='Stage Description')
class FlCase(models.Model):
_name = 'fl.case'
_description = 'Florida Family Law Case'
@@ -61,19 +75,15 @@ class FlCase(models.Model):
# STAGE / STATUS
# ══════════════════════════════════════════════════════════════════════
stage = fields.Selection([
('intake', 'Intake & Qualification'),
('preparation', 'Document Preparation'),
('filed', 'Filed — Awaiting Service'),
('service_complete', 'Service Complete'),
('discovery', 'Discovery'),
('deposition', 'Deposition Stage'),
('mediation', 'Mediation'),
('hearing_scheduled', 'Hearing Scheduled'),
('order_entered', 'Order Entered'),
('closed', 'Closed'),
('referred_out', 'Referred to Attorney'),
], string='Stage', default='intake', tracking=True)
stage_id = fields.Many2one(
'fl.case.stage',
string='Stage',
group_expand='_read_group_stage_ids',
default=lambda self: self.env['fl.case.stage'].search(
[], order='sequence asc', limit=1
),
tracking=True,
)
active = fields.Boolean(default=True)
# ══════════════════════════════════════════════════════════════════════
@@ -460,6 +470,10 @@ class FlCase(models.Model):
# COMPUTED METHODS
# ══════════════════════════════════════════════════════════════════════
@api.model
def _read_group_stage_ids(self, stages, domain, *args):
return stages.search([])
@api.depends('respondent_attorney_id')
def _compute_respondent_has_counsel(self):
for rec in self:
@@ -1006,14 +1020,10 @@ class FlCase(models.Model):
)
def trigger_ai_analysis(self):
"""
Trigger AI case analysis via Ollama (fl.ai.engine).
Phase 5 — full implementation.
"""
self.ensure_one()
engine = self.env['fl.ai.engine']
self.message_post(
body='🤖 AI analysis started. This may take up to 3 minutes...',
body='🤖 AI analysis started. This may take up to 30 seconds...',
subtype_xmlid='mail.mt_note',
)
analysis = engine.analyze_case(self.id)