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>
163 lines
6.0 KiB
Python
163 lines
6.0 KiB
Python
from odoo import api, fields, models
|
|
|
|
|
|
class FlFeeWaiver(models.Model):
|
|
"""
|
|
FL Statute 57.082 — Determination of Civil Indigent Status.
|
|
Income threshold: < 200% of federal poverty level.
|
|
|
|
2025 Federal Poverty Level (HHS):
|
|
Base (1 person): $15,060/year
|
|
Per additional person: $5,380/year
|
|
|
|
Update FPL_BASE and FPL_PER_PERSON constants annually.
|
|
Source: https://aspe.hhs.gov/topics/poverty-economic-mobility/poverty-guidelines
|
|
"""
|
|
_name = 'fl.fee.waiver'
|
|
_description = 'Fee Waiver / Civil Indigent Status (FL 57.082)'
|
|
_inherit = ['mail.thread']
|
|
_order = 'create_date desc'
|
|
|
|
# 2025 FPL constants — UPDATE ANNUALLY
|
|
FPL_BASE_2025 = 15060 # 1-person household
|
|
FPL_PER_PERSON_2025 = 5380 # Each additional person
|
|
|
|
case_id = fields.Many2one(
|
|
'fl.case', string='Case',
|
|
ondelete='cascade', index=True
|
|
)
|
|
party_id = fields.Many2one(
|
|
'res.partner', string='Applicant',
|
|
required=True
|
|
)
|
|
|
|
# ── Income & Household ─────────────────────────────────────────────────
|
|
|
|
household_size = fields.Integer(
|
|
string='Household Size',
|
|
required=True,
|
|
default=1,
|
|
help='Include all persons in the household (applicant + dependents + spouse/partner)'
|
|
)
|
|
monthly_gross_income = fields.Float(
|
|
string='Monthly Gross Income ($)',
|
|
required=True,
|
|
help='Total gross monthly income from all sources'
|
|
)
|
|
annual_gross_income = fields.Float(
|
|
string='Annual Gross Income ($)',
|
|
compute='_compute_annual', store=True
|
|
)
|
|
|
|
# ── FPL Threshold Calculation ──────────────────────────────────────────
|
|
|
|
fpl_annual = fields.Float(
|
|
string='Federal Poverty Level — Annual ($)',
|
|
compute='_compute_threshold', store=True,
|
|
help='100% FPL for this household size'
|
|
)
|
|
fpl_200pct_threshold = fields.Float(
|
|
string='200% FPL Threshold ($)',
|
|
compute='_compute_threshold', store=True,
|
|
help='200% FPL — income must be below this to qualify'
|
|
)
|
|
|
|
eligible = fields.Boolean(
|
|
string='Fee Waiver Eligible',
|
|
compute='_compute_eligibility', store=True
|
|
)
|
|
eligibility_note = fields.Char(
|
|
string='Eligibility Result',
|
|
compute='_compute_eligibility'
|
|
)
|
|
|
|
# ── Miami-Dade Mediator Fee Waiver (FL 44.108) ─────────────────────────
|
|
|
|
mediator_fee_waiver_eligible = fields.Boolean(
|
|
string='Mediator Fee Waiver Eligible (FL 44.108)',
|
|
compute='_compute_mediator_waiver', store=True,
|
|
help='Miami-Dade County provides free mediators for qualifying income. '
|
|
'Same income threshold as civil indigent status (200% FPL).'
|
|
)
|
|
|
|
# ── Status ────────────────────────────────────────────────────────────
|
|
|
|
state = fields.Selection([
|
|
('draft', 'Draft'),
|
|
('submitted', 'Submitted to Clerk'),
|
|
('approved', 'Approved'),
|
|
('denied', 'Denied'),
|
|
], string='Status', default='draft', tracking=True)
|
|
|
|
application_date = fields.Date(
|
|
string='Application Date',
|
|
default=fields.Date.today
|
|
)
|
|
approval_date = fields.Date(string='Approval Date')
|
|
denial_reason = fields.Text(string='Denial Reason')
|
|
expiration_date = fields.Date(
|
|
string='Expiration Date',
|
|
help='Fee waiver approvals typically expire — check with clerk'
|
|
)
|
|
clerk_stamp_attachment_ids = fields.Many2many(
|
|
'ir.attachment',
|
|
'fl_fee_waiver_attachment_rel',
|
|
'waiver_id', 'attachment_id',
|
|
string='Clerk-Stamped Copy'
|
|
)
|
|
notes = fields.Text(string='Notes')
|
|
|
|
# ── Computed ──────────────────────────────────────────────────────────
|
|
|
|
@api.depends('monthly_gross_income')
|
|
def _compute_annual(self):
|
|
for rec in self:
|
|
rec.annual_gross_income = (rec.monthly_gross_income or 0.0) * 12
|
|
|
|
@api.depends('household_size')
|
|
def _compute_threshold(self):
|
|
for rec in self:
|
|
size = max(rec.household_size or 1, 1)
|
|
fpl = self.FPL_BASE_2025 + (size - 1) * self.FPL_PER_PERSON_2025
|
|
rec.fpl_annual = fpl
|
|
rec.fpl_200pct_threshold = fpl * 2
|
|
|
|
@api.depends('annual_gross_income', 'fpl_200pct_threshold')
|
|
def _compute_eligibility(self):
|
|
for rec in self:
|
|
annual = rec.annual_gross_income or 0.0
|
|
threshold = rec.fpl_200pct_threshold or 0.0
|
|
if annual and threshold:
|
|
rec.eligible = annual <= threshold
|
|
if rec.eligible:
|
|
rec.eligibility_note = (
|
|
f'✅ ELIGIBLE — Annual income ${annual:,.0f} '
|
|
f'is below 200% FPL (${threshold:,.0f})'
|
|
)
|
|
else:
|
|
excess = annual - threshold
|
|
rec.eligibility_note = (
|
|
f'❌ NOT ELIGIBLE — Annual income ${annual:,.0f} '
|
|
f'exceeds 200% FPL (${threshold:,.0f}) '
|
|
f'by ${excess:,.0f}'
|
|
)
|
|
else:
|
|
rec.eligible = False
|
|
rec.eligibility_note = 'Enter income and household size to check eligibility'
|
|
|
|
@api.depends('eligible')
|
|
def _compute_mediator_waiver(self):
|
|
# Miami-Dade FL 44.108 — same threshold as civil indigent status
|
|
for rec in self:
|
|
rec.mediator_fee_waiver_eligible = rec.eligible
|
|
|
|
def action_submit(self):
|
|
self.state = 'submitted'
|
|
|
|
def action_approve(self):
|
|
self.state = 'approved'
|
|
self.approval_date = fields.Date.today()
|
|
|
|
def action_deny(self):
|
|
self.state = 'denied'
|