Phase 3: Full Discovery + Deposition workflow

fl_deposition.py — Full implementation:
- Calendar event sync for all scheduled depositions
- Notice validation: FL 1.310(b) 10-day minimum; days_notice + notice_valid computed
- Duces tecum document list field with production instructions
- Workflow buttons: Mark Noticed, Confirm, Completed, No-Show, Reschedule, Cancel
- action_no_show: auto-creates Motion to Compel deadline (FL 1.380, 20 days),
  project task with step-by-step instructions, urgent chatter alert
- Court reporter field, transcript tracking, key findings summary

fl_discovery.py — Full implementation:
- action_mark_served: creates fl.deadline for 30-day response window
- action_flag_deficient: creates deficiency notice deadline (good-faith prerequisite)
- action_file_motion_to_compel: FL 1.380 deadline + project task with instructions
- admissions_deemed computed: FL 1.370 auto-deemed-admitted after 30 days (critical)
- _cron_discovery_overdue_alerts: daily check — overdue responses + deemed admissions
  with urgent chatter alerts distinguishing regular overdue vs. deemed-admitted
- sanctions_requested field for FL 1.380(a)(4) expense awards

Enhanced views:
- fl_deposition_views: calendar view, notice validation banners, no-show alert,
  duces tecum section, workflow status bar, results section
- fl_discovery_views: FL 1.370 deemed-admitted critical red banner,
  overdue response warning, subpoena info section, Motion to Compel section,
  tree with inline action buttons, full search with filter presets

ir.cron: added daily discovery overdue alert job to fl_deadline_rules.xml

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Carlos Garcia
2026-05-04 23:20:40 -04:00
parent 7c865c8c22
commit fa0905ddbb
5 changed files with 1136 additions and 67 deletions

View File

@@ -2,67 +2,234 @@
<odoo>
<data>
<!-- ══════════════════════════════════════════════════════
TREE VIEW
══════════════════════════════════════════════════════ -->
<record id="view_fl_deposition_tree" model="ir.ui.view">
<field name="name">fl.deposition.tree</field>
<field name="model">fl.deposition</field>
<field name="arch" type="xml">
<tree string="Depositions">
<tree string="Depositions"
decoration-danger="state == 'no_show'"
decoration-success="state == 'completed'"
decoration-muted="state in ('cancelled', 'rescheduled')"
decoration-warning="state == 'noticed' and not notice_valid">
<field name="case_id"/>
<field name="deponent_id"/>
<field name="deponent_type"/>
<field name="notice_date"/>
<field name="scheduled_date"/>
<field name="state"/>
<field name="duces_tecum"/>
<field name="income_verified"/>
<field name="days_until_deposition" string="Days Away"
attrs="{'invisible': [('state', 'in', ['completed', 'cancelled'])]}"/>
<field name="notice_valid" string="Notice OK" optional="show"/>
<field name="state" widget="badge"
decoration-success="state == 'completed'"
decoration-danger="state == 'no_show'"
decoration-warning="state in ('noticed', 'draft')"
decoration-info="state == 'confirmed'"/>
<field name="duces_tecum" optional="show"/>
<field name="income_verified" optional="show"/>
</tree>
</field>
</record>
<!-- ══════════════════════════════════════════════════════
FORM VIEW
══════════════════════════════════════════════════════ -->
<record id="view_fl_deposition_form" model="ir.ui.view">
<field name="name">fl.deposition.form</field>
<field name="model">fl.deposition</field>
<field name="arch" type="xml">
<form string="Deposition">
<header>
<field name="state" widget="statusbar"/>
<button name="action_mark_noticed" string="Mark Notice Served"
type="object" class="oe_highlight"
attrs="{'invisible': [('state', '!=', 'draft')]}"/>
<button name="action_confirm" string="Confirm"
type="object"
attrs="{'invisible': [('state', '!=', 'noticed')]}"/>
<button name="action_mark_completed" string="Mark Completed"
type="object" class="oe_highlight"
attrs="{'invisible': [('state', 'not in', ['confirmed', 'noticed'])]}"/>
<button name="action_no_show" string="Deponent No-Show"
type="object"
confirm="Mark this deponent as a no-show? This will trigger the Motion to Compel workflow."
attrs="{'invisible': [('state', 'not in', ['confirmed', 'noticed'])]}"/>
<button name="action_reschedule" string="Reschedule"
type="object"
attrs="{'invisible': [('state', 'not in', ['draft', 'noticed', 'confirmed'])]}"/>
<button name="action_cancel" string="Cancel"
type="object"
confirm="Cancel this deposition?"
attrs="{'invisible': [('state', 'in', ['completed', 'cancelled'])]}"/>
<field name="state" widget="statusbar"
statusbar_visible="draft,noticed,confirmed,completed"/>
</header>
<sheet>
<!-- FL 1.310 Notice Alert -->
<div class="alert alert-info" role="alert">
<strong>FL 1.310(b):</strong> Minimum <strong>10 days notice</strong>
required before deposition.
FL 1.310(d): Maximum <strong>7 hours</strong> per deponent per day.
<strong>FL 1.310 Requirements:</strong>
<span class="ml-2">Minimum <strong>10 days notice</strong> (b) | Maximum <strong>7 hours/day</strong> per deponent (d)</span>
</div>
<!-- Notice validity warning -->
<div class="alert alert-warning" role="alert"
attrs="{'invisible': ['|', ('notice_valid', '=', True), ('notice_date', '=', False), ('scheduled_date', '=', False)]}">
<strong>⚠️ INSUFFICIENT NOTICE</strong>
<field name="notice_warning" readonly="1" nolabel="1"/>
</div>
<div class="alert alert-success" role="alert"
attrs="{'invisible': ['|', ('notice_valid', '=', False), ('notice_date', '=', False)]}">
<field name="notice_warning" readonly="1" nolabel="1"/>
</div>
<!-- No-Show alert -->
<div class="alert alert-danger" role="alert"
attrs="{'invisible': [('state', '!=', 'no_show')]}">
<strong>⚠️ DEPONENT NO-SHOW</strong> — File Motion to Compel
Attendance (FL 1.380). Check the Deadlines tab for the deadline.
You may also request sanctions under FL 1.380(b)(2).
</div>
<!-- Completed summary -->
<div class="alert alert-success" role="alert"
attrs="{'invisible': [('state', '!=', 'completed')]}">
<strong>✅ Deposition Completed</strong>
<span attrs="{'invisible': [('income_verified', '=', False)]}">
— Income verified at
<field name="income_verified_amount" readonly="1" nolabel="1"/> /month
</span>
</div>
<group>
<group>
<group string="Deponent">
<field name="case_id"/>
<field name="deponent_id"/>
<field name="deponent_type"/>
</group>
<group string="Schedule (FL 1.310)">
<field name="notice_date"/>
<field name="scheduled_date"/>
</group>
<group>
<field name="location"/>
<field name="days_notice" readonly="1"/>
<field name="days_until_deposition" readonly="1"/>
<field name="max_duration_hours"/>
<field name="duces_tecum"/>
</group>
</group>
<group string="Results">
<group>
<group string="Location">
<field name="location"/>
<field name="court_reporter"/>
<field name="calendar_event_id" readonly="1"
attrs="{'invisible': [('calendar_event_id', '=', False)]}"/>
</group>
</group>
<!-- Duces Tecum -->
<group string="Duces Tecum — Document Production Requirement">
<field name="duces_tecum"/>
<field name="duces_tecum_items"
attrs="{'invisible': [('duces_tecum', '=', False)], 'required': [('duces_tecum', '=', True)]}"
placeholder="List documents deponent must bring to the deposition:&#10;- Last 3 years federal/state tax returns&#10;- Last 6 months paystubs&#10;- All bank statements last 12 months&#10;- Business financial statements&#10;- Corporate tax returns&#10;- Quickbooks / accounting records"/>
</group>
<!-- Results (shown when completed) -->
<group string="Results / Findings"
attrs="{'invisible': [('state', 'not in', ['completed', 'no_show'])]}">
<field name="income_verified"/>
<field name="income_verified_amount"
attrs="{'invisible': [('income_verified', '=', False)]}"/>
<field name="key_findings"/>
<field name="transcript_received"/>
</group>
<field name="notes"/>
<field name="key_findings"
attrs="{'invisible': [('state', 'not in', ['completed'])]}"
placeholder="Summarize key testimony: income figures, contradictions, admissions, document admissions..."/>
<!-- Motion to Compel (no-show only) -->
<group string="Motion to Compel (FL 1.380)"
attrs="{'invisible': [('state', '!=', 'no_show')]}">
<field name="motion_to_compel_filed"/>
<field name="motion_to_compel_date"
attrs="{'invisible': [('motion_to_compel_filed', '=', False)]}"/>
<field name="sanctions_requested"/>
</group>
<field name="notes" placeholder="Notes, preparation strategy, questions to ask..."/>
</sheet>
<div class="oe_chatter">
<field name="message_follower_ids"/>
<field name="message_ids"/>
</div>
</form>
</field>
</record>
<!-- ══════════════════════════════════════════════════════
CALENDAR VIEW
══════════════════════════════════════════════════════ -->
<record id="view_fl_deposition_calendar" model="ir.ui.view">
<field name="name">fl.deposition.calendar</field>
<field name="model">fl.deposition</field>
<field name="arch" type="xml">
<calendar string="Deposition Calendar"
date_start="scheduled_date"
date_stop="scheduled_date"
color="deponent_type"
mode="month"
quick_create="False">
<field name="deponent_id"/>
<field name="case_id"/>
<field name="deponent_type"/>
<field name="state"/>
<field name="location"/>
</calendar>
</field>
</record>
<!-- ══════════════════════════════════════════════════════
SEARCH VIEW
══════════════════════════════════════════════════════ -->
<record id="view_fl_deposition_search" model="ir.ui.view">
<field name="name">fl.deposition.search</field>
<field name="model">fl.deposition</field>
<field name="arch" type="xml">
<search string="Search Depositions">
<field name="deponent_id"/>
<field name="case_id"/>
<field name="deponent_type"/>
<filter string="Pending" name="filter_pending"
domain="[('state', 'in', ['draft', 'noticed', 'confirmed'])]"/>
<filter string="No-Show" name="filter_no_show"
domain="[('state', '=', 'no_show')]"/>
<filter string="Completed" name="filter_completed"
domain="[('state', '=', 'completed')]"/>
<filter string="Invalid Notice" name="filter_invalid"
domain="[('notice_valid', '=', False), ('notice_date', '!=', False)]"/>
<filter string="Duces Tecum" name="filter_duces"
domain="[('duces_tecum', '=', True)]"/>
<group string="Group By">
<filter string="Case" name="group_case"
context="{'group_by': 'case_id'}"/>
<filter string="Status" name="group_state"
context="{'group_by': 'state'}"/>
<filter string="Deponent Type" name="group_type"
context="{'group_by': 'deponent_type'}"/>
</group>
</search>
</field>
</record>
<!-- ══════════════════════════════════════════════════════
ACTIONS
══════════════════════════════════════════════════════ -->
<record id="action_fl_deposition_list" model="ir.actions.act_window">
<field name="name">Depositions</field>
<field name="res_model">fl.deposition</field>
<field name="view_mode">tree,form</field>
<field name="view_mode">tree,calendar,form</field>
<field name="search_view_id" ref="view_fl_deposition_search"/>
<field name="domain">[('state', 'not in', ['cancelled'])]</field>
<field name="context">{'search_default_filter_pending': 1}</field>
</record>
</data>

View File

@@ -2,65 +2,242 @@
<odoo>
<data>
<!-- ══════════════════════════════════════════════════════
TREE VIEW
══════════════════════════════════════════════════════ -->
<record id="view_fl_discovery_tree" model="ir.ui.view">
<field name="name">fl.discovery.tree</field>
<field name="model">fl.discovery</field>
<field name="arch" type="xml">
<tree string="Discovery" decoration-danger="state == 'deficient'"
decoration-warning="state == 'served'">
<tree string="Discovery"
decoration-danger="is_overdue == True or admissions_deemed == True"
decoration-warning="state == 'deficient'"
decoration-success="state == 'complete'"
decoration-muted="state == 'compelled'">
<field name="case_id"/>
<field name="discovery_type"/>
<field name="directed_to"/>
<field name="third_party_id"/>
<field name="third_party_id"
attrs="{'invisible': [('directed_to', '!=', 'third_party')]}"/>
<field name="description"/>
<field name="served_date"/>
<field name="response_due_date"/>
<field name="response_complete"/>
<field name="state"/>
<field name="days_until_response" string="Days Left"
attrs="{'invisible': [('state', 'in', ['draft', 'responded', 'complete'])]}"/>
<field name="is_overdue" string="Overdue"/>
<field name="admissions_deemed" string="Deemed Admitted" optional="show"/>
<field name="state" widget="badge"
decoration-danger="state in ('deficient', 'compelled')"
decoration-warning="state == 'served'"
decoration-success="state == 'complete'"
decoration-muted="state == 'draft'"/>
<button name="action_mark_served" string="Served" type="object"
icon="fa-paper-plane"
attrs="{'invisible': [('state', '!=', 'draft')]}"/>
<button name="action_mark_responded" string="Responded" type="object"
icon="fa-check"
attrs="{'invisible': [('state', '!=', 'served')]}"/>
<button name="action_file_motion_to_compel" string="Compel"
type="object" icon="fa-gavel"
attrs="{'invisible': [('state', 'not in', ['served', 'deficient'])]}"/>
</tree>
</field>
</record>
<!-- ══════════════════════════════════════════════════════
FORM VIEW
══════════════════════════════════════════════════════ -->
<record id="view_fl_discovery_form" model="ir.ui.view">
<field name="name">fl.discovery.form</field>
<field name="model">fl.discovery</field>
<field name="arch" type="xml">
<form string="Discovery Item">
<header>
<field name="state" widget="statusbar"/>
<button name="action_mark_served" string="Mark Served"
type="object" class="oe_highlight"
attrs="{'invisible': [('state', '!=', 'draft')]}"/>
<button name="action_mark_responded" string="Response Received"
type="object"
attrs="{'invisible': [('state', '!=', 'served')]}"/>
<button name="action_flag_deficient" string="Flag Deficient"
type="object"
attrs="{'invisible': [('state', '!=', 'responded')]}"/>
<button name="action_file_motion_to_compel"
string="File Motion to Compel (FL 1.380)"
type="object"
attrs="{'invisible': [('state', 'not in', ['served', 'deficient'])]}"/>
<button name="action_mark_complete" string="Mark Complete"
type="object"
attrs="{'invisible': [('state', 'in', ['draft', 'complete'])]}"/>
<field name="state" widget="statusbar"
statusbar_visible="draft,served,responded,complete"/>
</header>
<sheet>
<!-- Deemed Admitted Alert (FL 1.370) — CRITICAL -->
<div class="alert alert-danger" role="alert"
attrs="{'invisible': [('admissions_deemed', '=', False)]}">
<strong>🚨 FL 1.370 — ADMISSIONS DEEMED ADMITTED</strong><br/>
No response was filed within 30 days. Each matter in this
Request for Admissions is <strong>automatically deemed admitted</strong>
and may be used as established facts at hearing.
If you are the responding party, immediately file a
<strong>Motion to Withdraw or Amend Admissions</strong>.
</div>
<!-- Overdue response alert -->
<div class="alert alert-warning" role="alert"
attrs="{'invisible': ['|', ('is_overdue', '=', False), ('admissions_deemed', '=', True)]}">
<strong>⏰ Response OVERDUE</strong> — Was due
<field name="response_due_date" readonly="1" nolabel="1"/>.
Consider sending a deficiency notice, then filing
Motion to Compel (FL 1.380).
</div>
<!-- Complete badge -->
<div class="alert alert-success" role="alert"
attrs="{'invisible': [('state', '!=', 'complete')]}">
<strong>✅ Discovery Item Complete</strong>
</div>
<group>
<group>
<group string="Discovery Details">
<field name="case_id"/>
<field name="discovery_type"/>
<field name="directed_to"/>
<field name="third_party_id"
attrs="{'invisible': [('directed_to', '!=', 'third_party')]}"/>
attrs="{'invisible': [('directed_to', '!=', 'third_party')], 'required': [('directed_to', '=', 'third_party')]}"/>
<field name="description"/>
</group>
<group>
<group string="Timeline">
<field name="served_date"/>
<field name="response_due_date" readonly="1"/>
<field name="days_until_response" readonly="1"
attrs="{'invisible': [('state', 'in', ['draft', 'responded', 'complete'])]}"/>
<field name="is_overdue" readonly="1"/>
<field name="response_received_date"/>
<field name="response_complete"/>
</group>
</group>
<!-- Subpoena info (third party) -->
<group string="Subpoena Information (FL 1.351)"
attrs="{'invisible': [('discovery_type', '!=', 'subpoena')]}">
<div class="alert alert-info" role="alert">
<strong>FL 1.351 Subpoena for Production:</strong>
Third party must respond within the time stated in the subpoena
(typically 15-30 days). Third party may object within 14 days.
If opposing party's employer: excellent tool to verify income
when self-employment or cash income is suspected.
</div>
</group>
<!-- Admissions specific note -->
<group string="Admissions (FL 1.370) — WARNING"
attrs="{'invisible': [('discovery_type', '!=', 'admissions')]}">
<div class="alert alert-warning" role="alert">
<strong>FL 1.370 Critical Rule:</strong>
If the responding party <strong>fails to respond within 30 days</strong>,
each matter is <strong>automatically deemed admitted</strong> without
any court order. This is one of the most powerful discovery tools
in Florida family law — it can establish income, assets, and conduct
as admitted facts.
</div>
<field name="admissions_deemed" readonly="1"/>
</group>
<group string="Objections / Deficiencies">
<field name="objections_raised"/>
<field name="objection_detail"
attrs="{'invisible': [('objections_raised', '=', False)]}"/>
<field name="deficiency_notice_sent"/>
<field name="deficiency_notice_date"
attrs="{'invisible': [('deficiency_notice_sent', '=', False)]}"/>
</group>
<field name="notes"/>
<group string="Motion to Compel (FL 1.380)"
attrs="{'invisible': [('state', '!=', 'compelled')]}">
<field name="motion_to_compel_filed" readonly="1"/>
<field name="motion_to_compel_date" readonly="1"/>
<field name="sanctions_requested"/>
</group>
<field name="notes"
placeholder="Notes on what was requested, what was received, gaps, strategy..."/>
</sheet>
<div class="oe_chatter">
<field name="message_follower_ids"/>
<field name="message_ids"/>
</div>
</form>
</field>
</record>
<!-- ══════════════════════════════════════════════════════
SEARCH VIEW
══════════════════════════════════════════════════════ -->
<record id="view_fl_discovery_search" model="ir.ui.view">
<field name="name">fl.discovery.search</field>
<field name="model">fl.discovery</field>
<field name="arch" type="xml">
<search string="Search Discovery">
<field name="case_id"/>
<field name="discovery_type"/>
<field name="directed_to"/>
<field name="description"/>
<filter string="Overdue Responses" name="filter_overdue"
domain="[('is_overdue', '=', True)]"/>
<filter string="Deemed Admitted" name="filter_deemed"
domain="[('admissions_deemed', '=', True)]"/>
<filter string="Deficient" name="filter_deficient"
domain="[('state', '=', 'deficient')]"/>
<filter string="Motion to Compel Filed" name="filter_compelled"
domain="[('state', '=', 'compelled')]"/>
<filter string="Pending Response" name="filter_pending"
domain="[('state', 'in', ['served'])]"/>
<filter string="Complete" name="filter_complete"
domain="[('state', '=', 'complete')]"/>
<separator/>
<filter string="Interrogatories" name="type_interrog"
domain="[('discovery_type', '=', 'interrogatories')]"/>
<filter string="Production Requests" name="type_production"
domain="[('discovery_type', '=', 'production')]"/>
<filter string="Admissions" name="type_admissions"
domain="[('discovery_type', '=', 'admissions')]"/>
<filter string="Subpoenas" name="type_subpoena"
domain="[('discovery_type', '=', 'subpoena')]"/>
<group string="Group By">
<filter string="Case" name="group_case"
context="{'group_by': 'case_id'}"/>
<filter string="Type" name="group_type"
context="{'group_by': 'discovery_type'}"/>
<filter string="Directed To" name="group_directed"
context="{'group_by': 'directed_to'}"/>
<filter string="Status" name="group_state"
context="{'group_by': 'state'}"/>
</group>
</search>
</field>
</record>
<!-- ══════════════════════════════════════════════════════
ACTIONS
══════════════════════════════════════════════════════ -->
<record id="action_fl_discovery_list" model="ir.actions.act_window">
<field name="name">Discovery</field>
<field name="res_model">fl.discovery</field>
<field name="view_mode">tree,form</field>
<field name="search_view_id" ref="view_fl_discovery_search"/>
<field name="domain">[('state', 'not in', ['complete'])]</field>
<field name="context">{'search_default_group_case': 1}</field>
</record>
<!-- All discovery (admin view) -->
<record id="action_fl_discovery_all" model="ir.actions.act_window">
<field name="name">All Discovery</field>
<field name="res_model">fl.discovery</field>
<field name="view_mode">tree,form</field>
<field name="search_view_id" ref="view_fl_discovery_search"/>
</record>
</data>