Files
famlaw/activeblue_familylaw/models/fl_fee_waiver.py
Carlos Garcia 1d52d85a78 Phase 1: core models, security, seed data, and backend views
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>
2026-05-04 18:52:04 -04:00

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'