Files
famlaw/activeblue_familylaw/models/fl_income_withholding.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

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'