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>
This commit is contained in:
162
activeblue_familylaw/models/fl_fee_waiver.py
Normal file
162
activeblue_familylaw/models/fl_fee_waiver.py
Normal file
@@ -0,0 +1,162 @@
|
||||
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'
|
||||
Reference in New Issue
Block a user