Files
famlaw/activeblue_familylaw/views/fl_case_views.xml
tocmo0nlord 7bc0cc8554 Add conflict-of-interest check engine; gate stage advancement at Intake
- New fl.conflict.check model: screens petitioner/respondent/party_ids names
  against parties on other open cases (exact partner match + difflib fuzzy
  match at 0.85 threshold); skips folded/closed stages
- Runs automatically as the first action in fl.case.create; logs conflicts to
  chatter with matched-case detail and never silently passes
- fl.case gains conflict_check_passed/conflict_check_id/conflict_check_ids;
  write() blocks advancing stage_id past Intake until the check passes
- Admin-only action_override requires a written justification, stamps user/date,
  and flips conflict_check_passed True with a chatter audit entry
- Add conflict check form/tree/search views, action, Cases sub-menu item,
  case form banner + Run Conflict Check button, and Kanban conflict badge
- ACL entries for fl.conflict.check (admin full, paralegal no-delete)
- Finish Claude migration cleanup in fl_analysis.py (model_used default,
  docstring/help text)
- Add .gitignore for Python artifacts

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-28 18:19:32 +00:00

477 lines
30 KiB
XML

<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data>
<!-- ══════════════════════════════════════════════════════
FORM VIEW
══════════════════════════════════════════════════════ -->
<record id="view_fl_case_form" model="ir.ui.view">
<field name="name">fl.case.form</field>
<field name="model">fl.case</field>
<field name="arch" type="xml">
<form string="Family Law Case">
<header>
<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"/>
<button name="action_open_support_calculator" string="Open Calculator"
type="object"
attrs="{'invisible': [('case_type', 'not in', ['modification','dissolution_children','paternity','custody_modification'])]}"/>
<button name="action_run_conflict_check" string="Run Conflict Check"
type="object"
groups="activeblue_familylaw.group_admin,activeblue_familylaw.group_paralegal"/>
</header>
<!-- Attorney Referral Banner -->
<div class="alert alert-danger" role="alert"
attrs="{'invisible': [('attorney_referral_flag', '=', False)]}">
<strong>⚠️ ATTORNEY REFERRAL RECOMMENDED</strong>
— This case has been flagged as complex or high-risk for pro se representation.
Strongly consider consulting with a family law attorney.
<br/>Legal Services of Greater Miami: (305) 576-0080
</div>
<!-- Conflict of Interest Banner -->
<div class="alert alert-danger" role="alert"
attrs="{'invisible': ['|', ('conflict_check_passed', '=', True), ('conflict_check_id', '=', False)]}">
<strong>🚩 CONFLICT OF INTEREST — UNRESOLVED</strong>
— A conflict check has flagged matching parties on other open cases.
This case cannot advance past Intake until the conflict is overridden by an administrator.
Open the
<button name="action_run_conflict_check" type="object"
class="btn btn-link p-0 align-baseline" string="conflict check"/>
for details.
</div>
<!-- DV Safety Note -->
<field name="dv_safety_note" widget="html"
attrs="{'invisible': [('domestic_violence_flag', '=', False)]}"/>
<sheet>
<div class="oe_button_box" name="button_box">
<button class="oe_stat_button" type="object"
name="action_view_tasks" icon="fa-tasks">
<field string="Tasks" name="task_count" widget="statinfo"/>
</button>
<button class="oe_stat_button" type="object"
name="action_open_support_calculator" icon="fa-calculator"
attrs="{'invisible': [('case_type', 'not in', ['modification','dissolution_children','paternity','custody_modification'])]}">
<span class="o_stat_text">Calculator</span>
</button>
</div>
<div class="oe_title">
<h1>
<field name="name" readonly="1"/>
</h1>
<h2>
<field name="case_type" options="{'no_create': True}"/>
</h2>
</div>
<group>
<group string="Case Identity">
<field name="court_case_number"
placeholder="e.g. 2025-DR-012345-01"/>
<field name="circuit"/>
<field name="county"/>
<field name="division"/>
<field name="judge_id"/>
</group>
<group string="Key Dates">
<field name="filing_date"/>
<field name="service_date"/>
<field name="last_order_date"
attrs="{'invisible': [('case_type', 'not in', ['modification','alimony_modification','custody_modification'])]}"/>
<field name="marriage_date"
attrs="{'invisible': [('case_type', 'not in', ['dissolution_children','dissolution_no_children','alimony_modification'])]}"/>
<field name="years_of_marriage"
attrs="{'invisible': [('case_type', 'not in', ['dissolution_children','dissolution_no_children','alimony_modification'])]}"/>
<field name="retroactivity_date" readonly="1"/>
</group>
</group>
<notebook>
<!-- TAB 1: Parties & Safety -->
<page string="Parties" name="parties">
<group>
<group string="Petitioner">
<field name="petitioner_id"/>
<field name="petitioner_attorney_id"
placeholder="Leave blank if pro se"/>
<field name="petitioner_fl_resident_since"/>
<field name="residency_warning" readonly="1"
attrs="{'invisible': [('petitioner_fl_resident_since', '=', False)]}"/>
</group>
<group string="Respondent">
<field name="respondent_id"/>
<field name="respondent_attorney_id"/>
<field name="respondent_has_counsel" readonly="1"/>
<field name="respondent_answered"/>
</group>
</group>
<group>
<group string="Safety Flags">
<field name="domestic_violence_flag"/>
<field name="dv_injunction_active"
attrs="{'invisible': [('domestic_violence_flag', '=', False)]}"/>
</group>
<group string="Conflict of Interest">
<field name="conflict_check_passed" readonly="1"/>
<field name="conflict_check_id" readonly="1"/>
</group>
</group>
<separator string="Party Details (Income, Employment, Service)"/>
<field name="party_ids">
<tree string="Parties" editable="bottom">
<field name="partner_id"/>
<field name="role"/>
<field name="employment_type"/>
<field name="gross_monthly_income"/>
<field name="net_monthly_income" readonly="1"/>
<field name="income_imputed"/>
</tree>
</field>
</page>
<!-- TAB 2: Children -->
<page string="Children" name="children"
attrs="{'invisible': [('case_type', 'in', ['dissolution_no_children','alimony_modification'])]}">
<group>
<field name="children_count" readonly="1"/>
<field name="has_minor_children" readonly="1"/>
</group>
<separator string="Parenting Class (FL 61.21)"
attrs="{'invisible': [('parenting_class_required', '=', False)]}"/>
<group attrs="{'invisible': [('parenting_class_required', '=', False)]}">
<group>
<field name="petitioner_parenting_class_done"/>
<field name="petitioner_parenting_class_date"
attrs="{'invisible': [('petitioner_parenting_class_done', '=', False)]}"/>
</group>
<group>
<field name="respondent_parenting_class_done"/>
<field name="respondent_parenting_class_date"
attrs="{'invisible': [('respondent_parenting_class_done', '=', False)]}"/>
</group>
</group>
<field name="parenting_class_warning" readonly="1"
attrs="{'invisible': [('parenting_class_required', '=', False)]}"/>
<separator string="Children on Case"/>
<field name="child_ids">
<tree string="Children" editable="bottom">
<field name="name"/>
<field name="date_of_birth"/>
<field name="age" readonly="1"/>
<field name="emancipation_date" readonly="1"/>
<field name="approaching_emancipation" readonly="1"/>
<field name="emancipated" readonly="1"/>
<field name="support_amount"/>
</tree>
</field>
</page>
<!-- TAB 3: Support & Threshold -->
<page string="Support Calculation" name="support"
attrs="{'invisible': [('case_type', 'not in', ['modification','dissolution_children','paternity','custody_modification'])]}">
<group string="Current Order">
<field name="current_order_amount"/>
<field name="current_order_per_child"/>
<field name="current_order_total" readonly="1"/>
</group>
<group string="FL 61.30 Calculation">
<field name="support_calc_id"/>
<field name="calculated_support" readonly="1"/>
<field name="support_difference" readonly="1"/>
<field name="support_difference_pct" readonly="1"
string="Difference %" widget="percentage"/>
</group>
<group string="Modification Threshold Test (FL 61.30(1)(b))"
attrs="{'invisible': [('case_type', '!=', 'modification')]}">
<field name="threshold_met" readonly="1"/>
<field name="substantial_change_basis"/>
<field name="substantial_change_detail"/>
</group>
<field name="threshold_result" widget="html" readonly="1"
attrs="{'invisible': [('case_type', '!=', 'modification')]}"/>
</page>
<!-- TAB 4: Timesharing -->
<page string="Timesharing" name="timesharing"
attrs="{'invisible': [('case_type', 'not in', ['modification','dissolution_children','paternity','custody_modification'])]}">
<group>
<group>
<field name="timesharing_changed"/>
<field name="petitioner_overnights"/>
<field name="respondent_overnights" readonly="1"/>
<field name="petitioner_timesharing_pct" readonly="1" string="Petitioner %"/>
<field name="substantial_timesharing_applies" readonly="1"/>
</group>
<group>
<field name="mediator_id"/>
</group>
</group>
</page>
<!-- TAB 5: Deadlines -->
<page string="Deadlines" name="deadlines">
<group>
<field name="next_deadline" readonly="1"/>
<field name="next_deadline_label" readonly="1" string="Next Deadline"/>
<field name="overdue_deadline_count" readonly="1"/>
<field name="discovery_cutoff_date" readonly="1"/>
</group>
<field name="deadline_ids">
<tree string="Deadlines" decoration-danger="is_overdue == True"
decoration-warning="days_until_due &lt;= 7 and days_until_due &gt;= 0"
decoration-muted="completed == True">
<field name="name"/>
<field name="deadline_type"/>
<field name="due_date"/>
<field name="days_until_due"/>
<field name="statute_reference"/>
<field name="completed"/>
<field name="is_overdue" readonly="1"/>
</tree>
</field>
</page>
<!-- TAB 6: Hearings, Discovery, Depositions -->
<page string="Hearings" name="hearings">
<field name="next_hearing_date" readonly="1"/>
<field name="hearing_ids">
<tree string="Hearings" editable="bottom">
<field name="name"/>
<field name="hearing_type"/>
<field name="hearing_date"/>
<field name="location"/>
<field name="state"/>
</tree>
</field>
</page>
<!-- TAB 7: AI Analysis -->
<page string="AI Analysis" name="ai">
<group>
<field name="attorney_referral_flag" readonly="1"/>
<field name="fee_waiver_eligible" readonly="1"/>
</group>
<separator string="AI Case Summary (English)"/>
<field name="ai_plain_english" readonly="1" nolabel="1"
placeholder="Run AI Analysis to generate summary"/>
<separator string="AI Case Summary (Español)"/>
<field name="ai_plain_english_es" readonly="1" nolabel="1"/>
<separator string="All Analyses"/>
<field name="analysis_ids">
<tree string="Analyses">
<field name="analysis_date"/>
<field name="model_used"/>
<field name="attorney_referral_flag"/>
<field name="confidence_level"/>
<field name="case_complexity"/>
<field name="state"/>
</tree>
</field>
</page>
<!-- TAB 8: Fee Waiver & Expenses -->
<page string="Fees &amp; Expenses" name="fees">
<group string="Fee Waiver (FL 57.082)">
<field name="fee_waiver_eligible" readonly="1"/>
<field name="fee_waiver_id"/>
<button name="action_create_fee_waiver"
string="Create Fee Waiver Application"
type="object" class="btn-secondary"
attrs="{'invisible': [('fee_waiver_id', '!=', False)]}"/>
</group>
<group string="Post-Order">
<field name="new_order_amount"/>
<field name="new_order_date"/>
<field name="income_withholding_id"/>
</group>
<separator string="Case Expenses"/>
<field name="total_expenses" readonly="1"/>
</page>
</notebook>
</sheet>
<div class="oe_chatter">
<field name="message_follower_ids"/>
<field name="activity_ids"/>
<field name="message_ids"/>
</div>
</form>
</field>
</record>
<!-- ══════════════════════════════════════════════════════
TREE VIEW
══════════════════════════════════════════════════════ -->
<record id="view_fl_case_tree" model="ir.ui.view">
<field name="name">fl.case.tree</field>
<field name="model">fl.case</field>
<field name="arch" type="xml">
<tree string="Family Law Cases"
decoration-danger="overdue_deadline_count &gt; 0"
decoration-warning="attorney_referral_flag == True">
<field name="name"/>
<field name="case_type"/>
<field name="court_case_number"/>
<field name="petitioner_id"/>
<field name="respondent_id"/>
<field name="stage_id"/>
<field name="filing_date"/>
<field name="next_deadline"/>
<field name="next_deadline_label" string="Next Deadline"/>
<field name="overdue_deadline_count" string="Overdue"/>
<field name="threshold_met"
attrs="{'invisible': [('case_type', '!=', 'modification')]}"/>
<field name="attorney_referral_flag" widget="boolean_toggle"/>
<field name="domestic_violence_flag" widget="boolean_toggle"/>
</tree>
</field>
</record>
<!-- ══════════════════════════════════════════════════════
SEARCH VIEW
══════════════════════════════════════════════════════ -->
<record id="view_fl_case_search" model="ir.ui.view">
<field name="name">fl.case.search</field>
<field name="model">fl.case</field>
<field name="arch" type="xml">
<search string="Search Cases">
<field name="name" string="Case Reference"/>
<field name="court_case_number"/>
<field name="petitioner_id"/>
<field name="respondent_id"/>
<separator/>
<filter string="Active Cases" name="active"
domain="[('active', '=', True)]"/>
<filter string="Intake" name="stage_intake"
domain="[('stage_id.name', '=', 'Intake &amp; Qualification')]"/>
<filter string="Filed" name="stage_filed"
domain="[('stage_id.name', '=', 'Filed — Awaiting Service')]"/>
<filter string="Discovery" name="stage_discovery"
domain="[('stage_id.name', '=', 'Discovery')]"/>
<separator/>
<filter string="Modification Cases" name="type_modification"
domain="[('case_type', '=', 'modification')]"/>
<filter string="Dissolution" name="type_dissolution"
domain="[('case_type', 'in', ['dissolution_children','dissolution_no_children'])]"/>
<separator/>
<filter string="Has Overdue Deadlines" name="overdue"
domain="[('overdue_deadline_count', '&gt;', 0)]"/>
<filter string="Attorney Referral Flagged" name="attorney_referral"
domain="[('attorney_referral_flag', '=', True)]"/>
<filter string="Domestic Violence" name="dv"
domain="[('domestic_violence_flag', '=', True)]"/>
<filter string="Threshold Met" name="threshold_met"
domain="[('threshold_met', '=', True)]"/>
<separator/>
<group expand="0" string="Group By">
<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>
</search>
</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="conflict_check_passed"/>
<field name="conflict_check_id"/>
<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>
<t t-if="record.conflict_check_id.raw_value &amp;&amp; !record.conflict_check_passed.raw_value">
<span class="badge rounded-pill text-bg-danger ms-1">Conflict</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">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">
<p class="o_view_nocontent_smiling_face">
No cases yet. Create your first case!
</p>
<p>Use the intake wizard or create a case directly.</p>
</field>
</record>
<!-- Intake Wizard Action -->
<record id="action_fl_intake_wizard" model="ir.actions.act_window">
<field name="name">New Case — Intake</field>
<field name="res_model">fl.intake.wizard</field>
<field name="view_mode">form</field>
<field name="target">new</field>
</record>
</data>
</odoo>