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:
@@ -45,5 +45,18 @@
|
|||||||
<field name="user_id" ref="base.user_root"/>
|
<field name="user_id" ref="base.user_root"/>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
|
<!-- Daily: Check for overdue discovery responses and deemed admissions (FL 1.370) -->
|
||||||
|
<record id="cron_fl_discovery_overdue" model="ir.cron">
|
||||||
|
<field name="name">FL Family Law: Discovery Overdue Alerts (FL 1.370 / 1.380)</field>
|
||||||
|
<field name="model_id" ref="model_fl_discovery"/>
|
||||||
|
<field name="state">code</field>
|
||||||
|
<field name="code">model._cron_discovery_overdue_alerts()</field>
|
||||||
|
<field name="interval_number">1</field>
|
||||||
|
<field name="interval_type">days</field>
|
||||||
|
<field name="numbercall">-1</field>
|
||||||
|
<field name="active">True</field>
|
||||||
|
<field name="user_id" ref="base.user_root"/>
|
||||||
|
</record>
|
||||||
|
|
||||||
</data>
|
</data>
|
||||||
</odoo>
|
</odoo>
|
||||||
|
|||||||
@@ -1,37 +1,86 @@
|
|||||||
|
from datetime import timedelta
|
||||||
|
|
||||||
from odoo import api, fields, models
|
from odoo import api, fields, models
|
||||||
|
from dateutil.relativedelta import relativedelta
|
||||||
|
|
||||||
|
|
||||||
class FlDeposition(models.Model):
|
class FlDeposition(models.Model):
|
||||||
"""
|
"""
|
||||||
Phase 3 — Full implementation with notice validation, duces tecum, no-show workflow.
|
Phase 3 — Full implementation.
|
||||||
Phase 1: Stub with core fields.
|
FL 1.310: Notice of Taking Deposition
|
||||||
|
- Minimum 10 days notice required (FL 1.310(b))
|
||||||
|
- Maximum 7 hours per deponent per day (FL 1.310(d))
|
||||||
|
- Duces tecum: document production requirement
|
||||||
|
- No-show workflow: auto-trigger Motion to Compel (FL 1.380)
|
||||||
"""
|
"""
|
||||||
_name = 'fl.deposition'
|
_name = 'fl.deposition'
|
||||||
_description = 'Deposition Record'
|
_description = 'Deposition Record'
|
||||||
_inherit = ['mail.thread']
|
_inherit = ['mail.thread']
|
||||||
_order = 'scheduled_date asc'
|
_order = 'scheduled_date asc'
|
||||||
|
_rec_name = 'deponent_id'
|
||||||
|
|
||||||
case_id = fields.Many2one(
|
case_id = fields.Many2one(
|
||||||
'fl.case', required=True, ondelete='cascade', index=True
|
'fl.case', required=True, ondelete='cascade', index=True,
|
||||||
|
string='Case'
|
||||||
)
|
)
|
||||||
deponent_id = fields.Many2one(
|
deponent_id = fields.Many2one(
|
||||||
'res.partner', string='Deponent', required=True
|
'res.partner', string='Deponent', required=True
|
||||||
)
|
)
|
||||||
deponent_type = fields.Selection([
|
deponent_type = fields.Selection([
|
||||||
('opposing_party', 'Opposing Party'),
|
('opposing_party', 'Opposing Party'),
|
||||||
('employer', 'Employer'),
|
('employer', "Deponent's Employer"),
|
||||||
('accountant_cpa', 'Accountant / CPA'),
|
('accountant_cpa', 'Accountant / CPA'),
|
||||||
('business_partner', 'Business Partner'),
|
('business_partner', 'Business Partner'),
|
||||||
('vocational_expert', 'Vocational Expert'),
|
('vocational_expert', 'Vocational Expert'),
|
||||||
('witness', 'Witness'),
|
('witness', 'Witness'),
|
||||||
('other', 'Other'),
|
('other', 'Other'),
|
||||||
], string='Deponent Type', required=True)
|
], string='Deponent Type', required=True)
|
||||||
|
|
||||||
|
# ── Notice ────────────────────────────────────────────────────────────────
|
||||||
notice_date = fields.Date(
|
notice_date = fields.Date(
|
||||||
string='Notice Served Date',
|
string='Notice Served Date',
|
||||||
help='FL 1.310(b): Minimum 10 days notice required before deposition'
|
help='Date the Notice of Taking Deposition was served on the deponent. '
|
||||||
|
'FL 1.310(b): Minimum 10 days notice required.'
|
||||||
)
|
)
|
||||||
scheduled_date = fields.Datetime(string='Deposition Date / Time')
|
days_notice = fields.Integer(
|
||||||
location = fields.Char(string='Location / Zoom Link')
|
string='Days of Notice',
|
||||||
|
compute='_compute_notice_info',
|
||||||
|
help='Number of days between notice date and scheduled deposition'
|
||||||
|
)
|
||||||
|
notice_valid = fields.Boolean(
|
||||||
|
string='Notice Valid (≥10 days)',
|
||||||
|
compute='_compute_notice_info',
|
||||||
|
help='FL 1.310(b): At least 10 days notice required'
|
||||||
|
)
|
||||||
|
notice_warning = fields.Char(
|
||||||
|
string='Notice Warning',
|
||||||
|
compute='_compute_notice_info'
|
||||||
|
)
|
||||||
|
|
||||||
|
# ── Schedule ──────────────────────────────────────────────────────────────
|
||||||
|
scheduled_date = fields.Datetime(
|
||||||
|
string='Deposition Date / Time', tracking=True
|
||||||
|
)
|
||||||
|
location = fields.Char(
|
||||||
|
string='Location / Zoom Link',
|
||||||
|
help='Physical address or Zoom link. Include court reporter contact info.'
|
||||||
|
)
|
||||||
|
court_reporter = fields.Char(
|
||||||
|
string='Court Reporter',
|
||||||
|
help='Court reporter name and contact information'
|
||||||
|
)
|
||||||
|
max_duration_hours = fields.Float(
|
||||||
|
string='Max Duration (hours)',
|
||||||
|
default=7.0,
|
||||||
|
help='FL 1.310(d): Maximum 7 hours per deponent per day. '
|
||||||
|
'Parties may agree to extend.'
|
||||||
|
)
|
||||||
|
days_until_deposition = fields.Integer(
|
||||||
|
string='Days Until Deposition',
|
||||||
|
compute='_compute_days_until_deposition'
|
||||||
|
)
|
||||||
|
|
||||||
|
# ── State ─────────────────────────────────────────────────────────────────
|
||||||
state = fields.Selection([
|
state = fields.Selection([
|
||||||
('draft', 'Drafting Notice'),
|
('draft', 'Drafting Notice'),
|
||||||
('noticed', 'Notice Served'),
|
('noticed', 'Notice Served'),
|
||||||
@@ -41,15 +90,305 @@ class FlDeposition(models.Model):
|
|||||||
('cancelled', 'Cancelled'),
|
('cancelled', 'Cancelled'),
|
||||||
('rescheduled', 'Rescheduled'),
|
('rescheduled', 'Rescheduled'),
|
||||||
], string='Status', default='draft', tracking=True)
|
], string='Status', default='draft', tracking=True)
|
||||||
|
|
||||||
|
# ── Duces Tecum ───────────────────────────────────────────────────────────
|
||||||
duces_tecum = fields.Boolean(
|
duces_tecum = fields.Boolean(
|
||||||
string='Duces Tecum (Document Production)',
|
string='Duces Tecum (Bring Documents)',
|
||||||
help='Deponent is required to bring documents'
|
help='Require deponent to produce documents at deposition'
|
||||||
)
|
)
|
||||||
max_duration_hours = fields.Float(
|
duces_tecum_items = fields.Text(
|
||||||
default=7.0,
|
string='Documents Required (Duces Tecum)',
|
||||||
help='FL 1.310(d): Maximum 7 hours per deponent per day'
|
help=(
|
||||||
|
'List documents deponent must bring. Examples:\n'
|
||||||
|
'- Last 3 years federal tax returns\n'
|
||||||
|
'- Last 6 months paystubs / payroll records\n'
|
||||||
|
'- Bank statements (all accounts, last 12 months)\n'
|
||||||
|
'- Business financial statements (if self-employed)\n'
|
||||||
|
'- Corporate tax returns (if business owner)\n'
|
||||||
|
'- Quickbooks / accounting records'
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# ── Calendar ──────────────────────────────────────────────────────────────
|
||||||
|
calendar_event_id = fields.Many2one(
|
||||||
|
'calendar.event', string='Calendar Event', ondelete='set null'
|
||||||
|
)
|
||||||
|
|
||||||
|
# ── Results ───────────────────────────────────────────────────────────────
|
||||||
income_verified = fields.Boolean(string='Income Figures Verified')
|
income_verified = fields.Boolean(string='Income Figures Verified')
|
||||||
income_verified_amount = fields.Float(string='Verified Income Amount ($)')
|
income_verified_amount = fields.Float(
|
||||||
key_findings = fields.Text(string='Key Findings')
|
string='Verified Monthly Income ($)',
|
||||||
|
help='Net monthly income as established at deposition'
|
||||||
|
)
|
||||||
|
key_findings = fields.Text(
|
||||||
|
string='Key Findings / Testimony Summary',
|
||||||
|
help='Summarize key admissions, income figures, contradictions, '
|
||||||
|
'and relevant testimony obtained'
|
||||||
|
)
|
||||||
|
transcript_received = fields.Boolean(string='Transcript Received')
|
||||||
|
transcript_notes = fields.Text(string='Transcript Notes')
|
||||||
|
|
||||||
|
# ── No-Show / Compel ──────────────────────────────────────────────────────
|
||||||
|
motion_to_compel_filed = fields.Boolean(
|
||||||
|
string='Motion to Compel Filed (FL 1.380)'
|
||||||
|
)
|
||||||
|
motion_to_compel_date = fields.Date(string='Motion to Compel Date')
|
||||||
|
sanctions_requested = fields.Boolean(string='Sanctions Requested')
|
||||||
|
|
||||||
notes = fields.Text(string='Notes')
|
notes = fields.Text(string='Notes')
|
||||||
|
|
||||||
|
# ══════════════════════════════════════════════════════════════════════════
|
||||||
|
# COMPUTED FIELDS
|
||||||
|
# ══════════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
|
@api.depends('notice_date', 'scheduled_date')
|
||||||
|
def _compute_notice_info(self):
|
||||||
|
for rec in self:
|
||||||
|
if rec.notice_date and rec.scheduled_date:
|
||||||
|
days = (rec.scheduled_date.date() - rec.notice_date).days
|
||||||
|
rec.days_notice = days
|
||||||
|
rec.notice_valid = days >= 10
|
||||||
|
if days < 10:
|
||||||
|
rec.notice_warning = (
|
||||||
|
'⚠️ FL 1.310(b): Only {} days notice. '
|
||||||
|
'Minimum 10 days required. Deponent may object.'.format(days)
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
rec.notice_warning = (
|
||||||
|
'✅ FL 1.310(b): {} days notice — requirement met.'.format(days)
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
rec.days_notice = 0
|
||||||
|
rec.notice_valid = False
|
||||||
|
rec.notice_warning = '⚪ Set notice date and scheduled date to validate'
|
||||||
|
|
||||||
|
@api.depends('scheduled_date')
|
||||||
|
def _compute_days_until_deposition(self):
|
||||||
|
today = fields.Date.today()
|
||||||
|
for rec in self:
|
||||||
|
if rec.scheduled_date:
|
||||||
|
rec.days_until_deposition = (rec.scheduled_date.date() - today).days
|
||||||
|
else:
|
||||||
|
rec.days_until_deposition = 0
|
||||||
|
|
||||||
|
# ══════════════════════════════════════════════════════════════════════════
|
||||||
|
# CRUD — calendar event lifecycle
|
||||||
|
# ══════════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
|
@api.model_create_multi
|
||||||
|
def create(self, vals_list):
|
||||||
|
records = super().create(vals_list)
|
||||||
|
for rec in records:
|
||||||
|
if rec.scheduled_date:
|
||||||
|
rec.with_context(_no_dep_sync=True)._create_or_update_calendar_event()
|
||||||
|
return records
|
||||||
|
|
||||||
|
def write(self, vals):
|
||||||
|
result = super().write(vals)
|
||||||
|
if not self.env.context.get('_no_dep_sync'):
|
||||||
|
sync_fields = {'scheduled_date', 'state', 'location', 'deponent_id'}
|
||||||
|
if sync_fields.intersection(vals.keys()):
|
||||||
|
for rec in self:
|
||||||
|
rec.with_context(_no_dep_sync=True)._create_or_update_calendar_event()
|
||||||
|
return result
|
||||||
|
|
||||||
|
# ══════════════════════════════════════════════════════════════════════════
|
||||||
|
# CALENDAR INTEGRATION
|
||||||
|
# ══════════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
|
def _create_or_update_calendar_event(self):
|
||||||
|
"""Create or update a calendar.event for this deposition."""
|
||||||
|
if not self.scheduled_date or self.state in ('cancelled',):
|
||||||
|
if self.calendar_event_id:
|
||||||
|
self.calendar_event_id.write({'active': False})
|
||||||
|
return
|
||||||
|
|
||||||
|
partners = self.env['res.partner']
|
||||||
|
if self.case_id.petitioner_id:
|
||||||
|
partners |= self.case_id.petitioner_id
|
||||||
|
if self.env.user.partner_id:
|
||||||
|
partners |= self.env.user.partner_id
|
||||||
|
|
||||||
|
duration = min(self.max_duration_hours or 7.0, 7.0)
|
||||||
|
stop_dt = self.scheduled_date + timedelta(hours=duration)
|
||||||
|
|
||||||
|
type_label = dict(self._fields['deponent_type'].selection).get(
|
||||||
|
self.deponent_type, 'Deponent'
|
||||||
|
)
|
||||||
|
event_name = '[{}] Deposition — {} ({})'.format(
|
||||||
|
self.case_id.name,
|
||||||
|
self.deponent_id.name,
|
||||||
|
type_label,
|
||||||
|
)
|
||||||
|
description = (
|
||||||
|
'FL 1.310 Deposition\n'
|
||||||
|
'Case: {}\n'
|
||||||
|
'Deponent: {} ({})\n'
|
||||||
|
'Location: {}\n'
|
||||||
|
'Court Reporter: {}\n'
|
||||||
|
'Max Duration: {} hours\n'
|
||||||
|
'Duces Tecum: {}\n'
|
||||||
|
'Status: {}'
|
||||||
|
).format(
|
||||||
|
self.case_id.name,
|
||||||
|
self.deponent_id.name,
|
||||||
|
type_label,
|
||||||
|
self.location or 'TBD',
|
||||||
|
self.court_reporter or 'TBD',
|
||||||
|
duration,
|
||||||
|
'YES — document production required' if self.duces_tecum else 'No',
|
||||||
|
dict(self._fields['state'].selection).get(self.state, ''),
|
||||||
|
)
|
||||||
|
|
||||||
|
if self.calendar_event_id:
|
||||||
|
self.calendar_event_id.write({
|
||||||
|
'name': event_name,
|
||||||
|
'start': self.scheduled_date,
|
||||||
|
'stop': stop_dt,
|
||||||
|
'description': description,
|
||||||
|
'active': self.state not in ('cancelled',),
|
||||||
|
'show_as': 'busy',
|
||||||
|
})
|
||||||
|
else:
|
||||||
|
event = self.env['calendar.event'].sudo().create({
|
||||||
|
'name': event_name,
|
||||||
|
'start': self.scheduled_date,
|
||||||
|
'stop': stop_dt,
|
||||||
|
'description': description,
|
||||||
|
'partner_ids': [(6, 0, partners.ids)],
|
||||||
|
'show_as': 'busy',
|
||||||
|
'privacy': 'confidential',
|
||||||
|
})
|
||||||
|
self.write({'calendar_event_id': event.id})
|
||||||
|
|
||||||
|
# ══════════════════════════════════════════════════════════════════════════
|
||||||
|
# WORKFLOW ACTIONS
|
||||||
|
# ══════════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
|
def action_mark_noticed(self):
|
||||||
|
"""Notice of Taking Deposition has been served on deponent."""
|
||||||
|
if not self.notice_date:
|
||||||
|
self.notice_date = fields.Date.today()
|
||||||
|
self.write({'state': 'noticed'})
|
||||||
|
self.case_id.message_post(
|
||||||
|
body=(
|
||||||
|
'📋 <b>Deposition Notice Served</b><br/>'
|
||||||
|
'Deponent: {} ({})<br/>'
|
||||||
|
'Notice date: {}<br/>'
|
||||||
|
'Scheduled: {}<br/>'
|
||||||
|
'Notice valid: {} (FL 1.310(b): 10 days required)'
|
||||||
|
).format(
|
||||||
|
self.deponent_id.name,
|
||||||
|
dict(self._fields['deponent_type'].selection).get(self.deponent_type, ''),
|
||||||
|
self.notice_date,
|
||||||
|
self.scheduled_date,
|
||||||
|
'✅ Yes' if self.notice_valid else '⚠️ INSUFFICIENT NOTICE',
|
||||||
|
),
|
||||||
|
subtype_xmlid='mail.mt_note',
|
||||||
|
)
|
||||||
|
|
||||||
|
def action_confirm(self):
|
||||||
|
"""Deponent and court reporter confirmed."""
|
||||||
|
self.write({'state': 'confirmed'})
|
||||||
|
|
||||||
|
def action_mark_completed(self):
|
||||||
|
"""Deposition completed successfully."""
|
||||||
|
self.write({'state': 'completed'})
|
||||||
|
if self.calendar_event_id:
|
||||||
|
self.calendar_event_id.write({'active': False})
|
||||||
|
self.case_id.message_post(
|
||||||
|
body=(
|
||||||
|
'✅ <b>Deposition Completed</b><br/>'
|
||||||
|
'Deponent: {} ({})<br/>'
|
||||||
|
'Income verified: {} {}<br/>'
|
||||||
|
'<i>Update Key Findings with testimony summary.</i>'
|
||||||
|
).format(
|
||||||
|
self.deponent_id.name,
|
||||||
|
dict(self._fields['deponent_type'].selection).get(self.deponent_type, ''),
|
||||||
|
'✅ ${}' .format(self.income_verified_amount) if self.income_verified else '❌ Not yet',
|
||||||
|
'/month' if self.income_verified else '',
|
||||||
|
),
|
||||||
|
subtype_xmlid='mail.mt_note',
|
||||||
|
)
|
||||||
|
|
||||||
|
def action_no_show(self):
|
||||||
|
"""
|
||||||
|
Deponent did not appear at deposition.
|
||||||
|
FL 1.380: Motion to Compel / Motion for Sanctions.
|
||||||
|
"""
|
||||||
|
self.write({'state': 'no_show'})
|
||||||
|
|
||||||
|
# Create Motion to Compel deadline (20 days to file)
|
||||||
|
mtc_due = fields.Date.today() + relativedelta(days=20)
|
||||||
|
self.env['fl.deadline'].create({
|
||||||
|
'case_id': self.case_id.id,
|
||||||
|
'name': 'File Motion to Compel Deposition — {} No-Show (FL 1.380)'.format(
|
||||||
|
self.deponent_id.name
|
||||||
|
),
|
||||||
|
'deadline_type': 'compliance',
|
||||||
|
'due_date': mtc_due,
|
||||||
|
'anchor_date': fields.Date.today(),
|
||||||
|
'offset_days': 20,
|
||||||
|
'statute_reference': (
|
||||||
|
'FL 1.380 — Motion to Compel Attendance at Deposition. '
|
||||||
|
'May also request sanctions under FL 1.380(b)(2).'
|
||||||
|
),
|
||||||
|
})
|
||||||
|
|
||||||
|
# Create project task
|
||||||
|
if self.case_id.project_id:
|
||||||
|
self.env['project.task'].create({
|
||||||
|
'name': 'File Motion to Compel: {} Deposition No-Show'.format(
|
||||||
|
self.deponent_id.name
|
||||||
|
),
|
||||||
|
'project_id': self.case_id.project_id.id,
|
||||||
|
'description': (
|
||||||
|
'{} failed to appear at the scheduled deposition on {}.\n\n'
|
||||||
|
'Steps:\n'
|
||||||
|
'1. Prepare Motion to Compel Attendance (FL 1.380)\n'
|
||||||
|
'2. Include Certificate of Good Faith Effort to Resolve\n'
|
||||||
|
'3. Request sanctions under FL 1.380(b)(2) — costs and attorney fees\n'
|
||||||
|
'4. File with Miami-Dade Clerk and set hearing date\n\n'
|
||||||
|
'Deadline to file: {}'
|
||||||
|
).format(
|
||||||
|
self.deponent_id.name,
|
||||||
|
self.scheduled_date,
|
||||||
|
mtc_due,
|
||||||
|
),
|
||||||
|
'date_deadline': mtc_due,
|
||||||
|
})
|
||||||
|
|
||||||
|
self.case_id.message_post(
|
||||||
|
body=(
|
||||||
|
'<b>⚠️ DEPOSITION NO-SHOW — Motion to Compel Required</b><br/>'
|
||||||
|
'{} did not appear at the deposition scheduled for {}.<br/><br/>'
|
||||||
|
'<b>Action Required:</b> File a Motion to Compel Attendance (FL 1.380) '
|
||||||
|
'within 20 days ({}).<br/>'
|
||||||
|
'You may also request sanctions (attorney fees / costs) '
|
||||||
|
'under FL 1.380(b)(2).'
|
||||||
|
).format(
|
||||||
|
self.deponent_id.name,
|
||||||
|
self.scheduled_date,
|
||||||
|
mtc_due,
|
||||||
|
),
|
||||||
|
subtype_xmlid='mail.mt_note',
|
||||||
|
)
|
||||||
|
|
||||||
|
def action_cancel(self):
|
||||||
|
"""Cancel this deposition (e.g. case settled, rescheduled)."""
|
||||||
|
self.write({'state': 'cancelled'})
|
||||||
|
if self.calendar_event_id:
|
||||||
|
self.calendar_event_id.write({'active': False})
|
||||||
|
|
||||||
|
def action_reschedule(self):
|
||||||
|
"""Mark as rescheduled — user should create a new deposition record."""
|
||||||
|
self.write({'state': 'rescheduled'})
|
||||||
|
self.case_id.message_post(
|
||||||
|
body=(
|
||||||
|
'🔄 <b>Deposition Rescheduled</b><br/>'
|
||||||
|
'Deposition for {} has been marked rescheduled. '
|
||||||
|
'Create a new deposition record with the new date and '
|
||||||
|
'serve a new Notice of Taking Deposition.'
|
||||||
|
).format(self.deponent_id.name),
|
||||||
|
subtype_xmlid='mail.mt_note',
|
||||||
|
)
|
||||||
|
|||||||
@@ -5,14 +5,19 @@ from dateutil.relativedelta import relativedelta
|
|||||||
class FlDiscovery(models.Model):
|
class FlDiscovery(models.Model):
|
||||||
"""
|
"""
|
||||||
Phase 3 — Full implementation.
|
Phase 3 — Full implementation.
|
||||||
Phase 1: Stub with core fields.
|
Covers all Florida discovery methods with response tracking,
|
||||||
|
deficiency workflow, and Motion to Compel automation.
|
||||||
|
|
||||||
Covers all FL discovery methods:
|
Discovery types covered (FL Rules of Civil Procedure):
|
||||||
- Interrogatories (FL 1.340) — 30 days to respond
|
FL 1.340 — Interrogatories (30 days to respond)
|
||||||
- Request for Production (FL 1.350) — 30 days to respond
|
FL 1.350 — Request for Production (30 days to respond)
|
||||||
- Request for Admissions (FL 1.370) — 30 days to respond
|
FL 1.370 — Request for Admissions (30 days to respond; deemed admitted if no response)
|
||||||
- Subpoena — third party documents (FL 1.351)
|
FL 1.351 — Subpoena (Third-party document production)
|
||||||
- Depositions — tracked in fl.deposition
|
FL 1.310 — Depositions (tracked separately in fl.deposition)
|
||||||
|
FL 1.280 — General discovery scope
|
||||||
|
|
||||||
|
Motion to Compel: FL 1.380
|
||||||
|
Subpoena to Employer: FL 1.351 (key tool when respondent income unknown)
|
||||||
"""
|
"""
|
||||||
_name = 'fl.discovery'
|
_name = 'fl.discovery'
|
||||||
_description = 'Discovery Item'
|
_description = 'Discovery Item'
|
||||||
@@ -20,51 +25,419 @@ class FlDiscovery(models.Model):
|
|||||||
_order = 'served_date asc'
|
_order = 'served_date asc'
|
||||||
|
|
||||||
case_id = fields.Many2one(
|
case_id = fields.Many2one(
|
||||||
'fl.case', required=True, ondelete='cascade', index=True
|
'fl.case', required=True, ondelete='cascade', index=True,
|
||||||
|
string='Case'
|
||||||
)
|
)
|
||||||
discovery_type = fields.Selection([
|
discovery_type = fields.Selection([
|
||||||
('interrogatories', 'Interrogatories (FL 1.340)'),
|
('interrogatories', 'Interrogatories (FL 1.340)'),
|
||||||
('production', 'Request for Production (FL 1.350)'),
|
('production', 'Request for Production of Documents (FL 1.350)'),
|
||||||
('admissions', 'Request for Admissions (FL 1.370)'),
|
('admissions', 'Request for Admissions (FL 1.370)'),
|
||||||
('subpoena', 'Subpoena — Third Party (FL 1.351)'),
|
('subpoena', 'Subpoena — Third Party (FL 1.351)'),
|
||||||
('deposition', 'Deposition (FL 1.310)'),
|
('deposition', 'Deposition Notice (FL 1.310)'),
|
||||||
], string='Discovery Type', required=True)
|
], string='Discovery Type', required=True, tracking=True)
|
||||||
|
|
||||||
directed_to = fields.Selection([
|
directed_to = fields.Selection([
|
||||||
('petitioner', 'Petitioner'),
|
('petitioner', 'Petitioner'),
|
||||||
('respondent', 'Respondent'),
|
('respondent', 'Respondent'),
|
||||||
('third_party', 'Third Party'),
|
('third_party', 'Third Party'),
|
||||||
], string='Directed To', required=True)
|
], string='Directed To', required=True)
|
||||||
|
|
||||||
third_party_id = fields.Many2one(
|
third_party_id = fields.Many2one(
|
||||||
'res.partner', string='Third Party'
|
'res.partner', string='Third Party',
|
||||||
|
help='Employer, bank, or other third party for subpoena'
|
||||||
)
|
)
|
||||||
description = fields.Char(string='Description / Subject')
|
description = fields.Char(
|
||||||
served_date = fields.Date(string='Served Date')
|
string='Description / Subject',
|
||||||
|
help='e.g. "Interrogatories regarding income and employment" or '
|
||||||
|
'"Request for Production — bank statements 2022-2024"'
|
||||||
|
)
|
||||||
|
|
||||||
|
# ── Dates and Deadlines ───────────────────────────────────────────────────
|
||||||
|
served_date = fields.Date(string='Date Served', tracking=True)
|
||||||
response_due_date = fields.Date(
|
response_due_date = fields.Date(
|
||||||
string='Response Due Date',
|
string='Response Due Date',
|
||||||
compute='_compute_response_due', store=True,
|
compute='_compute_response_due',
|
||||||
help='FL 1.340/1.350/1.370: 30 days to respond'
|
store=True,
|
||||||
|
help='30 days from service date (FL 1.340/1.350/1.370)'
|
||||||
)
|
)
|
||||||
response_received_date = fields.Date(string='Response Received Date')
|
response_received_date = fields.Date(
|
||||||
response_complete = fields.Boolean(string='Response Complete')
|
string='Response Date Received', tracking=True
|
||||||
objections_raised = fields.Boolean(string='Objections Raised')
|
)
|
||||||
objection_detail = fields.Text(string='Objection Details')
|
|
||||||
deficiency_notice_sent = fields.Boolean(string='Deficiency Notice Sent')
|
# ── Tracking ──────────────────────────────────────────────────────────────
|
||||||
|
response_complete = fields.Boolean(
|
||||||
|
string='Response Complete / Adequate',
|
||||||
|
help='Mark True when you have received a complete, non-deficient response'
|
||||||
|
)
|
||||||
|
objections_raised = fields.Boolean(string='Objections Raised by Respondent')
|
||||||
|
objection_detail = fields.Text(
|
||||||
|
string='Objection Details',
|
||||||
|
help='Document specific objections raised: privilege, overbroad, '
|
||||||
|
'not reasonably calculated to lead to admissible evidence, etc.'
|
||||||
|
)
|
||||||
|
deficiency_notice_sent = fields.Boolean(
|
||||||
|
string='Deficiency Notice Sent',
|
||||||
|
help='A written notice identifying deficiencies was sent before Motion to Compel'
|
||||||
|
)
|
||||||
|
deficiency_notice_date = fields.Date(string='Deficiency Notice Date')
|
||||||
|
|
||||||
|
# ── Status ────────────────────────────────────────────────────────────────
|
||||||
state = fields.Selection([
|
state = fields.Selection([
|
||||||
('draft', 'Drafting'),
|
('draft', 'Drafting'),
|
||||||
('served', 'Served'),
|
('served', 'Served — Awaiting Response'),
|
||||||
('responded', 'Response Received'),
|
('responded', 'Response Received'),
|
||||||
('deficient', 'Response Deficient'),
|
('deficient', 'Response Deficient'),
|
||||||
('compelled', 'Motion to Compel Filed'),
|
('compelled', 'Motion to Compel Filed'),
|
||||||
('complete', 'Complete'),
|
('complete', 'Complete'),
|
||||||
], string='Status', default='draft', tracking=True)
|
], string='Status', default='draft', tracking=True)
|
||||||
|
|
||||||
|
# ── Computed Status Fields ────────────────────────────────────────────────
|
||||||
|
is_overdue = fields.Boolean(
|
||||||
|
string='Response Overdue',
|
||||||
|
compute='_compute_overdue_status',
|
||||||
|
store=True
|
||||||
|
)
|
||||||
|
days_until_response = fields.Integer(
|
||||||
|
string='Days Until Response Due',
|
||||||
|
compute='_compute_overdue_status'
|
||||||
|
)
|
||||||
|
admissions_deemed = fields.Boolean(
|
||||||
|
string='Admissions Deemed Admitted',
|
||||||
|
compute='_compute_admissions_deemed',
|
||||||
|
store=True,
|
||||||
|
help='FL 1.370: If no response to Request for Admissions within 30 days, '
|
||||||
|
'each matter is AUTOMATICALLY deemed admitted'
|
||||||
|
)
|
||||||
|
|
||||||
|
# ── Motion to Compel ──────────────────────────────────────────────────────
|
||||||
|
motion_to_compel_filed = fields.Boolean(
|
||||||
|
string='Motion to Compel Filed (FL 1.380)'
|
||||||
|
)
|
||||||
|
motion_to_compel_date = fields.Date(string='Motion to Compel Date')
|
||||||
|
sanctions_requested = fields.Boolean(
|
||||||
|
string='Attorney Fees / Sanctions Requested',
|
||||||
|
help='FL 1.380(a)(4): If motion is granted, court shall award expenses. '
|
||||||
|
'If motion is denied, court may award expenses to the opposing party.'
|
||||||
|
)
|
||||||
|
|
||||||
|
# ── Subpoena-Specific ─────────────────────────────────────────────────────
|
||||||
|
subpoena_employer_flag = fields.Boolean(
|
||||||
|
string='Employer Subpoena',
|
||||||
|
compute='_compute_subpoena_employer_flag',
|
||||||
|
help='True when this is a subpoena directed to an employer for income records'
|
||||||
|
)
|
||||||
|
|
||||||
notes = fields.Text(string='Notes')
|
notes = fields.Text(string='Notes')
|
||||||
|
|
||||||
|
# ══════════════════════════════════════════════════════════════════════════
|
||||||
|
# COMPUTED FIELDS
|
||||||
|
# ══════════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
@api.depends('served_date', 'discovery_type')
|
@api.depends('served_date', 'discovery_type')
|
||||||
def _compute_response_due(self):
|
def _compute_response_due(self):
|
||||||
for rec in self:
|
for rec in self:
|
||||||
if rec.served_date and rec.discovery_type != 'deposition':
|
if rec.served_date and rec.discovery_type in (
|
||||||
rec.response_due_date = (
|
'interrogatories', 'production', 'admissions'
|
||||||
rec.served_date + relativedelta(days=30)
|
):
|
||||||
)
|
rec.response_due_date = rec.served_date + relativedelta(days=30)
|
||||||
else:
|
else:
|
||||||
rec.response_due_date = False
|
rec.response_due_date = False
|
||||||
|
|
||||||
|
@api.depends('response_due_date', 'state', 'response_received_date')
|
||||||
|
def _compute_overdue_status(self):
|
||||||
|
today = fields.Date.today()
|
||||||
|
for rec in self:
|
||||||
|
terminal_states = ('responded', 'complete')
|
||||||
|
if rec.state in terminal_states or not rec.response_due_date:
|
||||||
|
rec.is_overdue = False
|
||||||
|
rec.days_until_response = 0
|
||||||
|
else:
|
||||||
|
rec.days_until_response = (rec.response_due_date - today).days
|
||||||
|
rec.is_overdue = rec.response_due_date < today
|
||||||
|
|
||||||
|
@api.depends('discovery_type', 'response_due_date', 'response_received_date', 'state')
|
||||||
|
def _compute_admissions_deemed(self):
|
||||||
|
"""
|
||||||
|
FL 1.370: Each matter in a Request for Admissions is AUTOMATICALLY
|
||||||
|
deemed admitted if no response within 30 days.
|
||||||
|
"""
|
||||||
|
today = fields.Date.today()
|
||||||
|
for rec in self:
|
||||||
|
if (
|
||||||
|
rec.discovery_type == 'admissions'
|
||||||
|
and rec.response_due_date
|
||||||
|
and rec.response_due_date < today
|
||||||
|
and not rec.response_received_date
|
||||||
|
and rec.state not in ('responded', 'complete')
|
||||||
|
):
|
||||||
|
rec.admissions_deemed = True
|
||||||
|
else:
|
||||||
|
rec.admissions_deemed = False
|
||||||
|
|
||||||
|
@api.depends('discovery_type', 'directed_to')
|
||||||
|
def _compute_subpoena_employer_flag(self):
|
||||||
|
for rec in self:
|
||||||
|
rec.subpoena_employer_flag = (
|
||||||
|
rec.discovery_type == 'subpoena'
|
||||||
|
and rec.third_party_id
|
||||||
|
)
|
||||||
|
|
||||||
|
# ══════════════════════════════════════════════════════════════════════════
|
||||||
|
# WORKFLOW ACTIONS
|
||||||
|
# ══════════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
|
def action_mark_served(self):
|
||||||
|
"""
|
||||||
|
Mark discovery as served.
|
||||||
|
Sets served_date to today and creates a deadline for the response.
|
||||||
|
"""
|
||||||
|
if not self.served_date:
|
||||||
|
self.served_date = fields.Date.today()
|
||||||
|
self.write({'state': 'served'})
|
||||||
|
|
||||||
|
# Create response deadline in fl.deadline
|
||||||
|
if self.response_due_date:
|
||||||
|
type_label = dict(self._fields['discovery_type'].selection).get(
|
||||||
|
self.discovery_type, 'Discovery'
|
||||||
|
)
|
||||||
|
to_label = dict(self._fields['directed_to'].selection).get(
|
||||||
|
self.directed_to, ''
|
||||||
|
)
|
||||||
|
deadline_name = 'Response Due: {} to {} — {}'.format(
|
||||||
|
type_label, to_label, self.description or ''
|
||||||
|
)
|
||||||
|
self.env['fl.deadline'].create({
|
||||||
|
'case_id': self.case_id.id,
|
||||||
|
'name': deadline_name[:200],
|
||||||
|
'deadline_type': 'compliance',
|
||||||
|
'due_date': self.response_due_date,
|
||||||
|
'anchor_date': self.served_date,
|
||||||
|
'offset_days': 30,
|
||||||
|
'statute_reference': self._get_statute_ref(),
|
||||||
|
'notes': (
|
||||||
|
'Discovery response deadline. If no response received, '
|
||||||
|
'send deficiency notice, then file Motion to Compel (FL 1.380).'
|
||||||
|
),
|
||||||
|
})
|
||||||
|
|
||||||
|
self.case_id.message_post(
|
||||||
|
body=(
|
||||||
|
'📬 <b>Discovery Served</b><br/>'
|
||||||
|
'Type: {}<br/>'
|
||||||
|
'Directed to: {}<br/>'
|
||||||
|
'Description: {}<br/>'
|
||||||
|
'Response due: {}'
|
||||||
|
).format(
|
||||||
|
dict(self._fields['discovery_type'].selection).get(self.discovery_type, ''),
|
||||||
|
dict(self._fields['directed_to'].selection).get(self.directed_to, ''),
|
||||||
|
self.description or 'N/A',
|
||||||
|
self.response_due_date or 'N/A',
|
||||||
|
),
|
||||||
|
subtype_xmlid='mail.mt_note',
|
||||||
|
)
|
||||||
|
|
||||||
|
def action_mark_responded(self):
|
||||||
|
"""Response received — set date if not already set."""
|
||||||
|
if not self.response_received_date:
|
||||||
|
self.response_received_date = fields.Date.today()
|
||||||
|
self.write({'state': 'responded'})
|
||||||
|
|
||||||
|
def action_flag_deficient(self):
|
||||||
|
"""
|
||||||
|
Response received but is deficient (incomplete, evasive, objectionable).
|
||||||
|
Creates a deadline to send deficiency notice.
|
||||||
|
"""
|
||||||
|
self.write({'state': 'deficient'})
|
||||||
|
|
||||||
|
# Create deadline: send deficiency notice within 10 days
|
||||||
|
notice_due = fields.Date.today() + relativedelta(days=10)
|
||||||
|
self.env['fl.deadline'].create({
|
||||||
|
'case_id': self.case_id.id,
|
||||||
|
'name': 'Send Deficiency Notice for {} (FL 1.380 prerequisite)'.format(
|
||||||
|
self.description or dict(self._fields['discovery_type'].selection).get(
|
||||||
|
self.discovery_type, 'Discovery'
|
||||||
|
)
|
||||||
|
),
|
||||||
|
'deadline_type': 'compliance',
|
||||||
|
'due_date': notice_due,
|
||||||
|
'statute_reference': (
|
||||||
|
'FL 1.380: Must demonstrate good-faith effort to resolve '
|
||||||
|
'before filing Motion to Compel. Send written deficiency notice.'
|
||||||
|
),
|
||||||
|
})
|
||||||
|
|
||||||
|
self.case_id.message_post(
|
||||||
|
body=(
|
||||||
|
'⚠️ <b>Discovery Response Deficient</b><br/>'
|
||||||
|
'{} response from {} is incomplete or evasive.<br/><br/>'
|
||||||
|
'<b>Next Steps:</b><br/>'
|
||||||
|
'1. Send written deficiency notice ({}) — required before Motion to Compel<br/>'
|
||||||
|
'2. Allow 10-14 days for supplemental response<br/>'
|
||||||
|
'3. If no adequate response: file Motion to Compel (FL 1.380)'
|
||||||
|
).format(
|
||||||
|
dict(self._fields['discovery_type'].selection).get(self.discovery_type, ''),
|
||||||
|
dict(self._fields['directed_to'].selection).get(self.directed_to, ''),
|
||||||
|
notice_due,
|
||||||
|
),
|
||||||
|
subtype_xmlid='mail.mt_note',
|
||||||
|
)
|
||||||
|
|
||||||
|
def action_file_motion_to_compel(self):
|
||||||
|
"""
|
||||||
|
File Motion to Compel (FL 1.380).
|
||||||
|
Creates deadline and project task. Updates state to 'compelled'.
|
||||||
|
"""
|
||||||
|
self.write({
|
||||||
|
'state': 'compelled',
|
||||||
|
'motion_to_compel_filed': True,
|
||||||
|
'motion_to_compel_date': fields.Date.today(),
|
||||||
|
})
|
||||||
|
|
||||||
|
# Deadline: file motion within 7 days
|
||||||
|
mtc_due = fields.Date.today() + relativedelta(days=7)
|
||||||
|
self.env['fl.deadline'].create({
|
||||||
|
'case_id': self.case_id.id,
|
||||||
|
'name': 'File Motion to Compel Discovery — {} (FL 1.380)'.format(
|
||||||
|
self.description or dict(self._fields['discovery_type'].selection).get(
|
||||||
|
self.discovery_type, 'Discovery'
|
||||||
|
)
|
||||||
|
),
|
||||||
|
'deadline_type': 'compliance',
|
||||||
|
'due_date': mtc_due,
|
||||||
|
'statute_reference': (
|
||||||
|
'FL 1.380 — Motion to Compel Discovery. '
|
||||||
|
'Include: certificate of good faith, deficiency notice copy, '
|
||||||
|
'request for sanctions under FL 1.380(a)(4).'
|
||||||
|
),
|
||||||
|
})
|
||||||
|
|
||||||
|
if self.case_id.project_id:
|
||||||
|
self.env['project.task'].create({
|
||||||
|
'name': 'FILE: Motion to Compel — {} (FL 1.380)'.format(
|
||||||
|
self.description or dict(self._fields['discovery_type'].selection).get(
|
||||||
|
self.discovery_type, 'Discovery'
|
||||||
|
)
|
||||||
|
),
|
||||||
|
'project_id': self.case_id.project_id.id,
|
||||||
|
'description': (
|
||||||
|
'File Motion to Compel for {} directed to {}.\n\n'
|
||||||
|
'Required elements:\n'
|
||||||
|
'1. Certificate of Good Faith (prior notice of deficiency)\n'
|
||||||
|
'2. Copy of original discovery request\n'
|
||||||
|
'3. Copy of deficiency notice sent\n'
|
||||||
|
'4. Request for sanctions (attorney fees) under FL 1.380(a)(4)\n\n'
|
||||||
|
'File with Miami-Dade Clerk and set hearing date.\n'
|
||||||
|
'Deadline: {}'
|
||||||
|
).format(
|
||||||
|
dict(self._fields['discovery_type'].selection).get(
|
||||||
|
self.discovery_type, 'discovery'
|
||||||
|
),
|
||||||
|
dict(self._fields['directed_to'].selection).get(
|
||||||
|
self.directed_to, ''
|
||||||
|
),
|
||||||
|
mtc_due,
|
||||||
|
),
|
||||||
|
'date_deadline': mtc_due,
|
||||||
|
})
|
||||||
|
|
||||||
|
self.case_id.message_post(
|
||||||
|
body=(
|
||||||
|
'⚖️ <b>Motion to Compel Filed (FL 1.380)</b><br/>'
|
||||||
|
'Discovery: {} — {}<br/>'
|
||||||
|
'Directed to: {}<br/>'
|
||||||
|
'Sanctions requested: {}'
|
||||||
|
).format(
|
||||||
|
dict(self._fields['discovery_type'].selection).get(self.discovery_type, ''),
|
||||||
|
self.description or 'N/A',
|
||||||
|
dict(self._fields['directed_to'].selection).get(self.directed_to, ''),
|
||||||
|
'✅ Yes' if self.sanctions_requested else 'No',
|
||||||
|
),
|
||||||
|
subtype_xmlid='mail.mt_note',
|
||||||
|
)
|
||||||
|
|
||||||
|
def action_mark_complete(self):
|
||||||
|
"""Mark discovery item fully resolved."""
|
||||||
|
self.write({'state': 'complete', 'response_complete': True})
|
||||||
|
|
||||||
|
# ══════════════════════════════════════════════════════════════════════════
|
||||||
|
# HELPERS
|
||||||
|
# ══════════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
|
def _get_statute_ref(self):
|
||||||
|
"""Return the applicable statute reference for this discovery type."""
|
||||||
|
refs = {
|
||||||
|
'interrogatories': 'FL 1.340 — Interrogatories (30 days to respond)',
|
||||||
|
'production': 'FL 1.350 — Request for Production (30 days to respond)',
|
||||||
|
'admissions': (
|
||||||
|
'FL 1.370 — Request for Admissions (30 days to respond). '
|
||||||
|
'WARNING: Unanswered admissions are DEEMED ADMITTED automatically.'
|
||||||
|
),
|
||||||
|
'subpoena': 'FL 1.351 — Subpoena for Documents from Third Party',
|
||||||
|
'deposition': 'FL 1.310 — Notice of Taking Deposition',
|
||||||
|
}
|
||||||
|
return refs.get(self.discovery_type, 'FL Rules of Civil Procedure')
|
||||||
|
|
||||||
|
# ══════════════════════════════════════════════════════════════════════════
|
||||||
|
# CRON: Daily Discovery Overdue Alerts
|
||||||
|
# ══════════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
|
def _cron_discovery_overdue_alerts(self):
|
||||||
|
"""
|
||||||
|
Run daily. Check for:
|
||||||
|
1. Overdue responses → post chatter alert
|
||||||
|
2. Deemed admissions (FL 1.370) → urgent chatter alert
|
||||||
|
"""
|
||||||
|
today = fields.Date.today()
|
||||||
|
|
||||||
|
# Overdue responses
|
||||||
|
overdue = self.search([
|
||||||
|
('state', 'in', ['served']),
|
||||||
|
('response_due_date', '<', today),
|
||||||
|
('is_overdue', '=', True),
|
||||||
|
])
|
||||||
|
for item in overdue:
|
||||||
|
days_overdue = (today - item.response_due_date).days
|
||||||
|
item.case_id.message_post(
|
||||||
|
body=(
|
||||||
|
'🔴 <b>Discovery Response OVERDUE ({} days)</b><br/>'
|
||||||
|
'Type: {}<br/>'
|
||||||
|
'Directed to: {}<br/>'
|
||||||
|
'Description: {}<br/>'
|
||||||
|
'Was due: {}<br/><br/>'
|
||||||
|
'<b>Next Step:</b> Send deficiency notice, then '
|
||||||
|
'consider Motion to Compel (FL 1.380).'
|
||||||
|
).format(
|
||||||
|
days_overdue,
|
||||||
|
dict(item._fields['discovery_type'].selection).get(item.discovery_type, ''),
|
||||||
|
dict(item._fields['directed_to'].selection).get(item.directed_to, ''),
|
||||||
|
item.description or 'N/A',
|
||||||
|
item.response_due_date,
|
||||||
|
),
|
||||||
|
subtype_xmlid='mail.mt_note',
|
||||||
|
)
|
||||||
|
# Auto-update to served state stays but mark overdue flag is computed
|
||||||
|
|
||||||
|
# Deemed admissions (FL 1.370) — extra urgent alert
|
||||||
|
deemed = self.search([
|
||||||
|
('discovery_type', '=', 'admissions'),
|
||||||
|
('admissions_deemed', '=', True),
|
||||||
|
('state', 'not in', ['responded', 'complete', 'compelled']),
|
||||||
|
])
|
||||||
|
for item in deemed:
|
||||||
|
item.case_id.message_post(
|
||||||
|
body=(
|
||||||
|
'<b>🚨 DEEMED ADMITTED — FL 1.370 CRITICAL ALERT</b><br/>'
|
||||||
|
'A Request for Admissions directed to <b>{}</b> received NO response '
|
||||||
|
'within 30 days.<br/>'
|
||||||
|
'<b>Under FL 1.370, each matter is now AUTOMATICALLY DEEMED ADMITTED</b> '
|
||||||
|
'and may be used against that party at hearing.<br/><br/>'
|
||||||
|
'If you are the <b>responding party</b>: immediately file a Motion to '
|
||||||
|
'Withdraw or Amend the Admissions (court has discretion to allow this '
|
||||||
|
'if no prejudice to the requesting party).<br/><br/>'
|
||||||
|
'If you are the <b>requesting party</b>: these deemed admissions are '
|
||||||
|
'now established as facts for trial purposes.'
|
||||||
|
).format(
|
||||||
|
dict(item._fields['directed_to'].selection).get(item.directed_to, '')
|
||||||
|
),
|
||||||
|
subtype_xmlid='mail.mt_note',
|
||||||
|
)
|
||||||
|
|||||||
@@ -2,67 +2,234 @@
|
|||||||
<odoo>
|
<odoo>
|
||||||
<data>
|
<data>
|
||||||
|
|
||||||
|
<!-- ══════════════════════════════════════════════════════
|
||||||
|
TREE VIEW
|
||||||
|
══════════════════════════════════════════════════════ -->
|
||||||
<record id="view_fl_deposition_tree" model="ir.ui.view">
|
<record id="view_fl_deposition_tree" model="ir.ui.view">
|
||||||
<field name="name">fl.deposition.tree</field>
|
<field name="name">fl.deposition.tree</field>
|
||||||
<field name="model">fl.deposition</field>
|
<field name="model">fl.deposition</field>
|
||||||
<field name="arch" type="xml">
|
<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="case_id"/>
|
||||||
<field name="deponent_id"/>
|
<field name="deponent_id"/>
|
||||||
<field name="deponent_type"/>
|
<field name="deponent_type"/>
|
||||||
<field name="notice_date"/>
|
<field name="notice_date"/>
|
||||||
<field name="scheduled_date"/>
|
<field name="scheduled_date"/>
|
||||||
<field name="state"/>
|
<field name="days_until_deposition" string="Days Away"
|
||||||
<field name="duces_tecum"/>
|
attrs="{'invisible': [('state', 'in', ['completed', 'cancelled'])]}"/>
|
||||||
<field name="income_verified"/>
|
<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>
|
</tree>
|
||||||
</field>
|
</field>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
|
<!-- ══════════════════════════════════════════════════════
|
||||||
|
FORM VIEW
|
||||||
|
══════════════════════════════════════════════════════ -->
|
||||||
<record id="view_fl_deposition_form" model="ir.ui.view">
|
<record id="view_fl_deposition_form" model="ir.ui.view">
|
||||||
<field name="name">fl.deposition.form</field>
|
<field name="name">fl.deposition.form</field>
|
||||||
<field name="model">fl.deposition</field>
|
<field name="model">fl.deposition</field>
|
||||||
<field name="arch" type="xml">
|
<field name="arch" type="xml">
|
||||||
<form string="Deposition">
|
<form string="Deposition">
|
||||||
<header>
|
<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>
|
</header>
|
||||||
<sheet>
|
<sheet>
|
||||||
|
<!-- FL 1.310 Notice Alert -->
|
||||||
<div class="alert alert-info" role="alert">
|
<div class="alert alert-info" role="alert">
|
||||||
<strong>FL 1.310(b):</strong> Minimum <strong>10 days notice</strong>
|
<strong>FL 1.310 Requirements:</strong>
|
||||||
required before deposition.
|
<span class="ml-2">Minimum <strong>10 days notice</strong> (b) | Maximum <strong>7 hours/day</strong> per deponent (d)</span>
|
||||||
FL 1.310(d): Maximum <strong>7 hours</strong> per deponent per day.
|
|
||||||
</div>
|
</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>
|
<group string="Deponent">
|
||||||
<field name="case_id"/>
|
<field name="case_id"/>
|
||||||
<field name="deponent_id"/>
|
<field name="deponent_id"/>
|
||||||
<field name="deponent_type"/>
|
<field name="deponent_type"/>
|
||||||
|
</group>
|
||||||
|
<group string="Schedule (FL 1.310)">
|
||||||
<field name="notice_date"/>
|
<field name="notice_date"/>
|
||||||
<field name="scheduled_date"/>
|
<field name="scheduled_date"/>
|
||||||
</group>
|
<field name="days_notice" readonly="1"/>
|
||||||
<group>
|
<field name="days_until_deposition" readonly="1"/>
|
||||||
<field name="location"/>
|
|
||||||
<field name="max_duration_hours"/>
|
<field name="max_duration_hours"/>
|
||||||
<field name="duces_tecum"/>
|
|
||||||
</group>
|
</group>
|
||||||
</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: - Last 3 years federal/state tax returns - Last 6 months paystubs - All bank statements last 12 months - Business financial statements - Corporate tax returns - 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"/>
|
||||||
<field name="income_verified_amount"
|
<field name="income_verified_amount"
|
||||||
attrs="{'invisible': [('income_verified', '=', False)]}"/>
|
attrs="{'invisible': [('income_verified', '=', False)]}"/>
|
||||||
<field name="key_findings"/>
|
<field name="transcript_received"/>
|
||||||
</group>
|
</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>
|
</sheet>
|
||||||
|
<div class="oe_chatter">
|
||||||
|
<field name="message_follower_ids"/>
|
||||||
|
<field name="message_ids"/>
|
||||||
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</field>
|
</field>
|
||||||
</record>
|
</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">
|
<record id="action_fl_deposition_list" model="ir.actions.act_window">
|
||||||
<field name="name">Depositions</field>
|
<field name="name">Depositions</field>
|
||||||
<field name="res_model">fl.deposition</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>
|
</record>
|
||||||
|
|
||||||
</data>
|
</data>
|
||||||
|
|||||||
@@ -2,65 +2,242 @@
|
|||||||
<odoo>
|
<odoo>
|
||||||
<data>
|
<data>
|
||||||
|
|
||||||
|
<!-- ══════════════════════════════════════════════════════
|
||||||
|
TREE VIEW
|
||||||
|
══════════════════════════════════════════════════════ -->
|
||||||
<record id="view_fl_discovery_tree" model="ir.ui.view">
|
<record id="view_fl_discovery_tree" model="ir.ui.view">
|
||||||
<field name="name">fl.discovery.tree</field>
|
<field name="name">fl.discovery.tree</field>
|
||||||
<field name="model">fl.discovery</field>
|
<field name="model">fl.discovery</field>
|
||||||
<field name="arch" type="xml">
|
<field name="arch" type="xml">
|
||||||
<tree string="Discovery" decoration-danger="state == 'deficient'"
|
<tree string="Discovery"
|
||||||
decoration-warning="state == 'served'">
|
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="case_id"/>
|
||||||
<field name="discovery_type"/>
|
<field name="discovery_type"/>
|
||||||
<field name="directed_to"/>
|
<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="served_date"/>
|
||||||
<field name="response_due_date"/>
|
<field name="response_due_date"/>
|
||||||
<field name="response_complete"/>
|
<field name="days_until_response" string="Days Left"
|
||||||
<field name="state"/>
|
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>
|
</tree>
|
||||||
</field>
|
</field>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
|
<!-- ══════════════════════════════════════════════════════
|
||||||
|
FORM VIEW
|
||||||
|
══════════════════════════════════════════════════════ -->
|
||||||
<record id="view_fl_discovery_form" model="ir.ui.view">
|
<record id="view_fl_discovery_form" model="ir.ui.view">
|
||||||
<field name="name">fl.discovery.form</field>
|
<field name="name">fl.discovery.form</field>
|
||||||
<field name="model">fl.discovery</field>
|
<field name="model">fl.discovery</field>
|
||||||
<field name="arch" type="xml">
|
<field name="arch" type="xml">
|
||||||
<form string="Discovery Item">
|
<form string="Discovery Item">
|
||||||
<header>
|
<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>
|
</header>
|
||||||
<sheet>
|
<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>
|
<group string="Discovery Details">
|
||||||
<field name="case_id"/>
|
<field name="case_id"/>
|
||||||
<field name="discovery_type"/>
|
<field name="discovery_type"/>
|
||||||
<field name="directed_to"/>
|
<field name="directed_to"/>
|
||||||
<field name="third_party_id"
|
<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"/>
|
<field name="description"/>
|
||||||
</group>
|
</group>
|
||||||
<group>
|
<group string="Timeline">
|
||||||
<field name="served_date"/>
|
<field name="served_date"/>
|
||||||
<field name="response_due_date" readonly="1"/>
|
<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_received_date"/>
|
||||||
<field name="response_complete"/>
|
<field name="response_complete"/>
|
||||||
</group>
|
</group>
|
||||||
</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">
|
<group string="Objections / Deficiencies">
|
||||||
<field name="objections_raised"/>
|
<field name="objections_raised"/>
|
||||||
<field name="objection_detail"
|
<field name="objection_detail"
|
||||||
attrs="{'invisible': [('objections_raised', '=', False)]}"/>
|
attrs="{'invisible': [('objections_raised', '=', False)]}"/>
|
||||||
<field name="deficiency_notice_sent"/>
|
<field name="deficiency_notice_sent"/>
|
||||||
|
<field name="deficiency_notice_date"
|
||||||
|
attrs="{'invisible': [('deficiency_notice_sent', '=', False)]}"/>
|
||||||
</group>
|
</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>
|
</sheet>
|
||||||
|
<div class="oe_chatter">
|
||||||
|
<field name="message_follower_ids"/>
|
||||||
|
<field name="message_ids"/>
|
||||||
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</field>
|
</field>
|
||||||
</record>
|
</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">
|
<record id="action_fl_discovery_list" model="ir.actions.act_window">
|
||||||
<field name="name">Discovery</field>
|
<field name="name">Discovery</field>
|
||||||
<field name="res_model">fl.discovery</field>
|
<field name="res_model">fl.discovery</field>
|
||||||
<field name="view_mode">tree,form</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>
|
</record>
|
||||||
|
|
||||||
</data>
|
</data>
|
||||||
|
|||||||
Reference in New Issue
Block a user