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>
166 lines
6.3 KiB
Python
166 lines
6.3 KiB
Python
from odoo import api, fields, models
|
|
|
|
|
|
class FlIncomeWithholding(models.Model):
|
|
"""
|
|
FL 61.1301 — Income Deduction (Withholding) Order.
|
|
|
|
MANDATORY after every child support or alimony order unless:
|
|
- Good cause shown, OR
|
|
- Written agreement between parties for alternative payment method.
|
|
|
|
All payments route through Florida State Disbursement Unit (SDU).
|
|
Employer receives copy and deducts from obligor's paycheck.
|
|
"""
|
|
_name = 'fl.income.withholding'
|
|
_description = 'Income Withholding Order (FL 61.1301)'
|
|
_inherit = ['mail.thread']
|
|
_order = 'issued_date desc'
|
|
|
|
case_id = fields.Many2one(
|
|
'fl.case', string='Case',
|
|
ondelete='cascade', index=True
|
|
)
|
|
name = fields.Char(
|
|
string='Reference',
|
|
compute='_compute_name', store=True
|
|
)
|
|
|
|
# ── Parties ────────────────────────────────────────────────────────────
|
|
|
|
obligor_id = fields.Many2one(
|
|
'res.partner',
|
|
string='Obligor (Person Paying)',
|
|
required=True
|
|
)
|
|
obligee_id = fields.Many2one(
|
|
'res.partner',
|
|
string='Obligee (Person Receiving)',
|
|
required=True
|
|
)
|
|
employer_id = fields.Many2one(
|
|
'res.partner',
|
|
string="Obligor's Employer",
|
|
help='Employer who will receive and execute the withholding order'
|
|
)
|
|
employer_contact = fields.Char(
|
|
string='Employer HR / Payroll Contact'
|
|
)
|
|
|
|
# ── Amounts ────────────────────────────────────────────────────────────
|
|
|
|
monthly_support_amount = fields.Float(
|
|
string='Monthly Support Amount ($)',
|
|
required=True,
|
|
tracking=True
|
|
)
|
|
arrears_amount = fields.Float(
|
|
string='Arrears Amount ($)',
|
|
help='Total unpaid support owed prior to this order'
|
|
)
|
|
arrears_monthly_payment = fields.Float(
|
|
string='Monthly Arrears Payment ($)',
|
|
help='Additional monthly amount toward arrears payoff'
|
|
)
|
|
total_monthly_withholding = fields.Float(
|
|
string='Total Monthly Withholding ($)',
|
|
compute='_compute_total', store=True,
|
|
help='Support + arrears payment per month'
|
|
)
|
|
|
|
# ── Florida State Disbursement Unit ────────────────────────────────────
|
|
# ALL payments must go through FL SDU — never directly to the other party
|
|
|
|
fl_sdu_case_number = fields.Char(
|
|
string='FL SDU Case Number',
|
|
help='Florida State Disbursement Unit case number. '
|
|
'Obtain from DOR: https://www.floridarevenue.com/childsupport'
|
|
)
|
|
fl_sdu_address = fields.Char(
|
|
string='FL SDU Payment Address',
|
|
default='Florida State Disbursement Unit, PO Box 8500, Tallahassee, FL 32314-8500',
|
|
readonly=True
|
|
)
|
|
fl_sdu_website = fields.Char(
|
|
default='https://www.floridarevenue.com/childsupport',
|
|
readonly=True
|
|
)
|
|
|
|
# ── Dates & Status ────────────────────────────────────────────────────
|
|
|
|
issued_date = fields.Date(
|
|
string='Order Issued Date',
|
|
tracking=True
|
|
)
|
|
served_on_employer_date = fields.Date(
|
|
string='Served on Employer Date'
|
|
)
|
|
first_withholding_date = fields.Date(
|
|
string='First Withholding Payroll Date',
|
|
help='Date of first paycheck with withholding applied'
|
|
)
|
|
|
|
state = fields.Selection([
|
|
('draft', 'Draft'),
|
|
('issued', 'Issued by Court'),
|
|
('served', 'Served on Employer'),
|
|
('active', 'Active — Withholding'),
|
|
('modified', 'Modified'),
|
|
('terminated', 'Terminated'),
|
|
], string='Status', default='draft', tracking=True)
|
|
|
|
# ── Exceptions (rare) ─────────────────────────────────────────────────
|
|
|
|
good_cause_exception = fields.Boolean(
|
|
string='Good Cause Exception',
|
|
help='FL 61.1301(1)(a): Court may waive withholding for good cause shown. '
|
|
'Must be documented in court order. RARE.'
|
|
)
|
|
written_agreement = fields.Boolean(
|
|
string='Written Agreement Between Parties',
|
|
help='Parties may agree in writing to an alternative payment method. '
|
|
'Agreement must be filed with the court.'
|
|
)
|
|
exception_reason = fields.Text(
|
|
string='Exception Reason / Agreement Details'
|
|
)
|
|
|
|
# ── Notes ─────────────────────────────────────────────────────────────
|
|
|
|
notes = fields.Text(string='Notes')
|
|
|
|
# ── Computed ──────────────────────────────────────────────────────────
|
|
|
|
@api.depends('case_id', 'issued_date')
|
|
def _compute_name(self):
|
|
for rec in self:
|
|
case_ref = rec.case_id.name if rec.case_id else 'NEW'
|
|
date_str = str(rec.issued_date) if rec.issued_date else 'Draft'
|
|
rec.name = f'IWO — {case_ref} — {date_str}'
|
|
|
|
@api.depends('monthly_support_amount', 'arrears_monthly_payment')
|
|
def _compute_total(self):
|
|
for rec in self:
|
|
rec.total_monthly_withholding = (
|
|
(rec.monthly_support_amount or 0.0)
|
|
+ (rec.arrears_monthly_payment or 0.0)
|
|
)
|
|
|
|
# ── State Actions ─────────────────────────────────────────────────────
|
|
|
|
def action_issue(self):
|
|
self.state = 'issued'
|
|
self.issued_date = self.issued_date or fields.Date.today()
|
|
|
|
def action_serve(self):
|
|
self.state = 'served'
|
|
self.served_on_employer_date = (
|
|
self.served_on_employer_date or fields.Date.today()
|
|
)
|
|
|
|
def action_activate(self):
|
|
self.state = 'active'
|
|
|
|
def action_terminate(self):
|
|
self.state = 'terminated'
|