Implements full Phase 1 of the activeblue_familylaw Odoo 18 module: - 17 Python models (fl.case, fl.party, fl.child, fl.support.calculation, fl.fee.waiver, fl.income.withholding, fl.deadline, fl.hearing, fl.deposition, fl.discovery, fl.document, fl.caselaw, fl.analysis, fl.ai.engine, fl.argument, fl.statute, fl.issue.tag) + hr.expense extension - 3 wizard stubs (intake, analysis, generate-packet) - Security: 4 groups (admin/paralegal/portal-petitioner/portal-respondent) + record rules scoping portal users to their own cases - Seed data: issue tags, FL statutes, FL DCF support schedule, ir.sequence - 13 backend view XML files with FL 61.30 worksheet, fee waiver eligibility banner, DV safety resources, emancipation alerts - Static CSS/JS stubs for Phase 6 portal Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
167 lines
6.8 KiB
Python
167 lines
6.8 KiB
Python
from odoo import api, fields, models
|
|
from dateutil.relativedelta import relativedelta
|
|
|
|
|
|
class FlChild(models.Model):
|
|
_name = 'fl.child'
|
|
_description = 'Child on Case'
|
|
_order = 'date_of_birth'
|
|
|
|
case_id = fields.Many2one(
|
|
'fl.case', string='Case',
|
|
required=True, ondelete='cascade', index=True
|
|
)
|
|
name = fields.Char(string='Full Name', required=True)
|
|
date_of_birth = fields.Date(string='Date of Birth', required=True)
|
|
age = fields.Integer(
|
|
string='Age', compute='_compute_age', store=True
|
|
)
|
|
gender = fields.Selection([
|
|
('m', 'Male'),
|
|
('f', 'Female'),
|
|
('x', 'Non-binary / Other'),
|
|
], string='Gender')
|
|
school = fields.Char(string='School')
|
|
medical_provider = fields.Char(string='Primary Care Provider')
|
|
|
|
# ── Support Per Child ──────────────────────────────────────────────────
|
|
support_amount = fields.Float(
|
|
string='Support Amount This Child ($)',
|
|
help='Per-child support amount from the calculation'
|
|
)
|
|
health_insurance_premium = fields.Float(
|
|
string='Health Insurance Premium ($)',
|
|
help='Monthly cost of health insurance for this child only'
|
|
)
|
|
childcare_cost = fields.Float(
|
|
string='Work-Related Childcare ($)',
|
|
help='FL 61.30(7): Only work or job-search related childcare costs qualify'
|
|
)
|
|
extraordinary_expenses = fields.Float(
|
|
string='Extraordinary Expenses ($)',
|
|
help='FL 61.30(9): Medical or educational expenses beyond ordinary child-rearing costs'
|
|
)
|
|
|
|
# ── Emancipation Tracking ──────────────────────────────────────────────
|
|
emancipation_date = fields.Date(
|
|
string='Emancipation Date',
|
|
compute='_compute_emancipation',
|
|
store=True,
|
|
help='18th birthday (or high school graduation, whichever is later). '
|
|
'Support terminates on this date.'
|
|
)
|
|
approaching_emancipation = fields.Boolean(
|
|
string='Approaching Emancipation',
|
|
compute='_compute_approaching_emancipation',
|
|
store=True,
|
|
help='True when emancipation is within 90 days'
|
|
)
|
|
emancipation_alert_sent = fields.Boolean(
|
|
string='Emancipation Alert Sent',
|
|
default=False
|
|
)
|
|
emancipated = fields.Boolean(
|
|
string='Emancipated',
|
|
compute='_compute_emancipated',
|
|
store=True
|
|
)
|
|
days_until_emancipation = fields.Integer(
|
|
string='Days Until Emancipation',
|
|
compute='_compute_approaching_emancipation'
|
|
)
|
|
|
|
# ── Early Emancipation Factors ─────────────────────────────────────────
|
|
married = fields.Boolean(
|
|
string='Child is Married',
|
|
help='Marriage constitutes emancipation under FL law'
|
|
)
|
|
active_military = fields.Boolean(
|
|
string='Active Military Service',
|
|
help='Active duty military service constitutes emancipation under FL law'
|
|
)
|
|
declared_emancipated = fields.Boolean(
|
|
string='Court-Declared Emancipated',
|
|
help='Court has entered an order declaring this child emancipated'
|
|
)
|
|
emancipation_notes = fields.Text(
|
|
string='Emancipation Notes',
|
|
help='Document basis for any early emancipation determination'
|
|
)
|
|
|
|
# ── Computed ──────────────────────────────────────────────────────────
|
|
|
|
@api.depends('date_of_birth')
|
|
def _compute_age(self):
|
|
today = fields.Date.today()
|
|
for rec in self:
|
|
if rec.date_of_birth:
|
|
delta = relativedelta(today, rec.date_of_birth)
|
|
rec.age = delta.years
|
|
else:
|
|
rec.age = 0
|
|
|
|
@api.depends('date_of_birth')
|
|
def _compute_emancipation(self):
|
|
for rec in self:
|
|
if rec.date_of_birth:
|
|
rec.emancipation_date = rec.date_of_birth + relativedelta(years=18)
|
|
else:
|
|
rec.emancipation_date = False
|
|
|
|
@api.depends('emancipation_date', 'married', 'active_military', 'declared_emancipated')
|
|
def _compute_approaching_emancipation(self):
|
|
today = fields.Date.today()
|
|
for rec in self:
|
|
# Early emancipation factors
|
|
if rec.married or rec.active_military or rec.declared_emancipated:
|
|
rec.approaching_emancipation = False
|
|
rec.days_until_emancipation = 0
|
|
continue
|
|
if rec.emancipation_date:
|
|
days_remaining = (rec.emancipation_date - today).days
|
|
rec.days_until_emancipation = max(days_remaining, 0)
|
|
rec.approaching_emancipation = 0 < days_remaining <= 90
|
|
else:
|
|
rec.approaching_emancipation = False
|
|
rec.days_until_emancipation = 0
|
|
|
|
@api.depends('emancipation_date', 'married', 'active_military', 'declared_emancipated')
|
|
def _compute_emancipated(self):
|
|
today = fields.Date.today()
|
|
for rec in self:
|
|
if rec.married or rec.active_military or rec.declared_emancipated:
|
|
rec.emancipated = True
|
|
elif rec.emancipation_date:
|
|
rec.emancipated = today >= rec.emancipation_date
|
|
else:
|
|
rec.emancipated = False
|
|
|
|
# ── Cron ──────────────────────────────────────────────────────────────
|
|
|
|
def _cron_emancipation_alerts(self):
|
|
"""
|
|
Run daily via ir.cron.
|
|
Sends chatter alert on cases where a child is approaching emancipation
|
|
and no alert has been sent yet.
|
|
"""
|
|
approaching = self.search([
|
|
('approaching_emancipation', '=', True),
|
|
('emancipation_alert_sent', '=', False),
|
|
])
|
|
for child in approaching:
|
|
days = child.days_until_emancipation
|
|
child.case_id.message_post(
|
|
body=(
|
|
f'<strong>⚠️ EMANCIPATION ALERT</strong><br/>'
|
|
f'<b>{child.name}</b> will turn 18 on '
|
|
f'<b>{child.emancipation_date}</b> '
|
|
f'({days} days from today).<br/>'
|
|
f'Child support for this child terminates on that date.<br/>'
|
|
f'<b>Action required:</b> A Motion to Modify should be filed '
|
|
f'at least 60 days before emancipation to address the change '
|
|
f'in support obligation.'
|
|
),
|
|
subtype_xmlid='mail.mt_note',
|
|
)
|
|
child.emancipation_alert_sent = True
|