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

@@ -11,8 +11,7 @@
<field name="arch" type="xml">
<form string="Family Law Case">
<header>
<field name="stage" widget="statusbar"
statusbar_visible="intake,preparation,filed,service_complete,discovery,mediation,hearing_scheduled,order_entered,closed"/>
<field name="stage_id" widget="statusbar" options="{'clickable': '1'}"/>
<button name="action_run_ai_analysis" string="Run AI Analysis"
type="object" class="oe_highlight"
groups="activeblue_familylaw.group_admin,activeblue_familylaw.group_paralegal"/>
@@ -301,7 +300,7 @@
<field name="court_case_number"/>
<field name="petitioner_id"/>
<field name="respondent_id"/>
<field name="stage"/>
<field name="stage_id"/>
<field name="filing_date"/>
<field name="next_deadline"/>
<field name="next_deadline_label" string="Next Deadline"/>
@@ -330,11 +329,11 @@
<filter string="Active Cases" name="active"
domain="[('active', '=', True)]"/>
<filter string="Intake" name="stage_intake"
domain="[('stage', '=', 'intake')]"/>
domain="[('stage_id.name', '=', 'Intake &amp; Qualification')]"/>
<filter string="Filed" name="stage_filed"
domain="[('stage', '=', 'filed')]"/>
domain="[('stage_id.name', '=', 'Filed — Awaiting Service')]"/>
<filter string="Discovery" name="stage_discovery"
domain="[('stage', '=', 'discovery')]"/>
domain="[('stage_id.name', '=', 'Discovery')]"/>
<separator/>
<filter string="Modification Cases" name="type_modification"
domain="[('case_type', '=', 'modification')]"/>
@@ -351,7 +350,7 @@
domain="[('threshold_met', '=', True)]"/>
<separator/>
<group expand="0" string="Group By">
<filter string="Stage" name="group_stage" context="{'group_by': 'stage'}"/>
<filter string="Stage" name="group_stage" context="{'group_by': 'stage_id'}"/>
<filter string="Case Type" name="group_type" context="{'group_by': 'case_type'}"/>
<filter string="Filing Date" name="group_date" context="{'group_by': 'filing_date:month'}"/>
</group>
@@ -359,13 +358,76 @@
</field>
</record>
<!-- ══════════════════════════════════════════════════════
KANBAN VIEW
══════════════════════════════════════════════════════ -->
<record id="view_fl_case_kanban" model="ir.ui.view">
<field name="name">fl.case.kanban</field>
<field name="model">fl.case</field>
<field name="arch" type="xml">
<kanban default_group_by="stage_id" on_create="quick_create"
quick_create_view="activeblue_familylaw.view_fl_case_form">
<field name="name"/>
<field name="case_type"/>
<field name="stage_id"/>
<field name="next_deadline"/>
<field name="next_deadline_label"/>
<field name="attorney_referral_flag"/>
<field name="domestic_violence_flag"/>
<field name="overdue_deadline_count"/>
<field name="petitioner_id"/>
<templates>
<t t-name="kanban-card">
<div class="oe_kanban_global_click">
<div class="o_kanban_record_top">
<div class="o_kanban_record_headings">
<strong class="o_kanban_record_title">
<field name="name"/>
</strong>
<span class="o_kanban_record_subtitle">
<field name="petitioner_id"/>
</span>
</div>
</div>
<div class="o_kanban_record_body">
<span class="badge rounded-pill text-bg-secondary">
<field name="case_type"/>
</span>
<t t-if="record.attorney_referral_flag.raw_value">
<span class="badge rounded-pill text-bg-danger ms-1">Atty Referral</span>
</t>
<t t-if="record.domestic_violence_flag.raw_value">
<span class="badge rounded-pill text-bg-warning ms-1">DV</span>
</t>
<t t-if="record.overdue_deadline_count.raw_value > 0">
<span class="badge rounded-pill text-bg-danger ms-1">
<t t-esc="record.overdue_deadline_count.raw_value"/> Overdue
</span>
</t>
</div>
<div class="o_kanban_record_bottom" t-if="record.next_deadline.raw_value">
<div class="oe_kanban_bottom_left text-muted small">
<i class="fa fa-calendar-o me-1"/>
<field name="next_deadline"/>
<t t-if="record.next_deadline_label.raw_value">
<field name="next_deadline_label"/>
</t>
</div>
</div>
</div>
</t>
</templates>
</kanban>
</field>
</record>
<!-- ══════════════════════════════════════════════════════
ACTIONS
══════════════════════════════════════════════════════ -->
<record id="action_fl_case_list" model="ir.actions.act_window">
<field name="name">Family Law Cases</field>
<field name="res_model">fl.case</field>
<field name="view_mode">tree,form</field>
<field name="view_mode">kanban,tree,form</field>
<field name="search_view_id" ref="view_fl_case_search"/>
<field name="context">{'search_default_active': 1}</field>
<field name="help" type="html">