From a6f8d31316cfa71f71b14168e52906b07fbcd1ff Mon Sep 17 00:00:00 2001 From: tocmo0nlord Date: Tue, 2 Jun 2026 04:02:20 +0000 Subject: [PATCH] Step 4: deadline engine (per-proceeding clocks, weekend roll, overdue cron, calendar mirror) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit familylaw.deadline — a procedural clock attached to a PROCEEDING: - Deterministic due_date: trigger_date + days, with Rule-2.514-style weekend roll-forward (_roll_forward); _holiday_dates() hook left empty by design (holidays are jurisdiction/year-specific — content-maintenance concern) - STANDARD_OFFSETS (answer 20 / disclosure 45 / discovery 30 / objection 10) as defaults; per-record `days` override. Flagged "verify current rule". - state pending/done/waived/overdue; is_overdue computed + searchable - _cron_flag_overdue daily ir.cron flips pending+past -> overdue, audited - calendar.event mirror auto-created/updated on date changes (allday) - proceeding.deadline_ids + action_seed_standard_deadlines (idempotent) Adds 'calendar' dependency, data/familylaw_cron.xml, deadline views + menu, Deadlines tab + Seed button on proceeding form, security rules. Tests (familylaw_step4): 20 tests with FIXED dates — 20/45/30-day math, weekend roll (Sat/Sun -> Mon), type-default vs explicit days, overdue detect, cron flag, is_overdue search, calendar mirror create+update, per-proceeding isolation, idempotent seeding. Co-Authored-By: Claude Opus 4.8 --- .../activeblue_familylaw/__manifest__.py | 8 +- .../data/familylaw_cron.xml | 13 + .../activeblue_familylaw/models/__init__.py | 1 + .../models/familylaw_deadline.py | 232 ++++++++++++++++++ .../models/familylaw_proceeding.py | 28 +++ .../security/ir.model.access.csv | 2 + .../activeblue_familylaw/tests/__init__.py | 1 + .../activeblue_familylaw/tests/test_step4.py | 181 ++++++++++++++ .../views/familylaw_deadline_views.xml | 112 +++++++++ .../views/familylaw_menus.xml | 7 + .../views/familylaw_proceeding_views.xml | 17 ++ 11 files changed, 600 insertions(+), 2 deletions(-) create mode 100644 activeblue_familylaw_handoff/activeblue_familylaw_build/activeblue_familylaw/data/familylaw_cron.xml create mode 100644 activeblue_familylaw_handoff/activeblue_familylaw_build/activeblue_familylaw/models/familylaw_deadline.py create mode 100644 activeblue_familylaw_handoff/activeblue_familylaw_build/activeblue_familylaw/tests/test_step4.py create mode 100644 activeblue_familylaw_handoff/activeblue_familylaw_build/activeblue_familylaw/views/familylaw_deadline_views.xml diff --git a/activeblue_familylaw_handoff/activeblue_familylaw_build/activeblue_familylaw/__manifest__.py b/activeblue_familylaw_handoff/activeblue_familylaw_build/activeblue_familylaw/__manifest__.py index 886cf7b..6ba512b 100644 --- a/activeblue_familylaw_handoff/activeblue_familylaw_build/activeblue_familylaw/__manifest__.py +++ b/activeblue_familylaw_handoff/activeblue_familylaw_build/activeblue_familylaw/__manifest__.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- { "name": "Active Blue Family Law", - "version": "18.0.3.0.0", + "version": "18.0.4.0.0", "category": "Services/Legal", "summary": "Florida family law case management (Miami-Dade / 11th Judicial Circuit)", "description": """ @@ -9,7 +9,8 @@ Active Blue Family Law ====================== Case-management platform for a Florida family-law practice, built in verifiable steps. Step 1: case spine. Step 2: parties/children/issues/proceedings, conflict -screening, intake. Step 3: documents + the attorney review gate (Gate 1). +screening, intake. Step 3: documents + review gate. Step 4: deadline engine +(per-proceeding clocks, weekend roll, overdue cron, calendar mirror). Each step adds one vertical, independently testable slice. See BUILD_PLAN.md. """, @@ -19,15 +20,18 @@ Each step adds one vertical, independently testable slice. See BUILD_PLAN.md. "depends": [ "base", "mail", + "calendar", ], "data": [ "security/familylaw_security.xml", "security/ir.model.access.csv", + "data/familylaw_cron.xml", "views/familylaw_party_views.xml", "views/familylaw_child_views.xml", "views/familylaw_issue_views.xml", "views/familylaw_proceeding_views.xml", "views/familylaw_document_views.xml", + "views/familylaw_deadline_views.xml", "views/familylaw_intake_views.xml", "views/familylaw_case_views.xml", "views/familylaw_menus.xml", diff --git a/activeblue_familylaw_handoff/activeblue_familylaw_build/activeblue_familylaw/data/familylaw_cron.xml b/activeblue_familylaw_handoff/activeblue_familylaw_build/activeblue_familylaw/data/familylaw_cron.xml new file mode 100644 index 0000000..296672b --- /dev/null +++ b/activeblue_familylaw_handoff/activeblue_familylaw_build/activeblue_familylaw/data/familylaw_cron.xml @@ -0,0 +1,13 @@ + + + + + Family Law: Flag Overdue Deadlines + + code + model._cron_flag_overdue() + 1 + days + + + diff --git a/activeblue_familylaw_handoff/activeblue_familylaw_build/activeblue_familylaw/models/__init__.py b/activeblue_familylaw_handoff/activeblue_familylaw_build/activeblue_familylaw/models/__init__.py index 409633c..f46b910 100644 --- a/activeblue_familylaw_handoff/activeblue_familylaw_build/activeblue_familylaw/models/__init__.py +++ b/activeblue_familylaw_handoff/activeblue_familylaw_build/activeblue_familylaw/models/__init__.py @@ -6,3 +6,4 @@ from . import familylaw_proceeding from . import familylaw_conflict from . import familylaw_intake from . import familylaw_document +from . import familylaw_deadline diff --git a/activeblue_familylaw_handoff/activeblue_familylaw_build/activeblue_familylaw/models/familylaw_deadline.py b/activeblue_familylaw_handoff/activeblue_familylaw_build/activeblue_familylaw/models/familylaw_deadline.py new file mode 100644 index 0000000..46aab42 --- /dev/null +++ b/activeblue_familylaw_handoff/activeblue_familylaw_build/activeblue_familylaw/models/familylaw_deadline.py @@ -0,0 +1,232 @@ +# -*- coding: utf-8 -*- +"""STEP 4 — Deadline engine. + +A family-law deadline is a statutory/procedural clock. It attaches to a +PROCEEDING (locked decision — so the 2024 dissolution's disclosure clock never +collides with a 2027 modification's), computes its due date deterministically with +Florida-style weekend roll-forward, mirrors itself onto the shared calendar, and is +flagged overdue by a daily cron. + +DATE-MATH NOTE (verify current rule at build time): + Florida Rule of Judicial Administration 2.514 computes day-periods by excluding + the triggering day, counting every following day, and — if the last day falls on a + Saturday, Sunday, or legal holiday — rolling forward to the next day that is none + of those. We implement the exclude-trigger + weekend roll here. LEGAL HOLIDAYS are + jurisdiction- and year-specific and are intentionally NOT hardcoded; see + _holiday_dates() for the hook. Standard day-counts (20/45/30/10) are common values + and MUST be confirmed against the current rule for each matter type. +""" + +from datetime import timedelta + +from odoo import api, fields, models, _ + +# Common Florida day-counts. VERIFY CURRENT RULE per matter type — do not treat as +# permanent. Used as defaults; the `days` field can override per record. +STANDARD_OFFSETS = { + "answer": 20, # Respondent's answer window (Rule 12.140 family practice) + "disclosure": 45, # Mandatory disclosure (Rule 12.285) + "discovery": 30, # Interrogatory / production responses (Rule 12.340/12.350) + "objection": 10, # Subpoena objection window (Step 8) +} + + +class FamilyLawDeadline(models.Model): + _name = "familylaw.deadline" + _description = "Procedural Deadline" + _inherit = ["mail.thread"] + _order = "due_date asc" + + name = fields.Char(required=True, tracking=True) + proceeding_id = fields.Many2one( + "familylaw.proceeding", + string="Proceeding", + required=True, + ondelete="cascade", + index=True, + tracking=True, + help="Deadlines attach to a proceeding, not directly to the case.", + ) + case_id = fields.Many2one( + "familylaw.case", + related="proceeding_id.case_id", + store=True, + index=True, + ) + deadline_type = fields.Selection( + selection=[ + ("answer", "Answer / Response"), + ("disclosure", "Mandatory Disclosure"), + ("discovery", "Discovery Response"), + ("objection", "Objection Window"), + ("hearing", "Hearing"), + ("custom", "Custom"), + ], + string="Type", + required=True, + default="custom", + tracking=True, + ) + trigger_date = fields.Date( + string="Trigger Date", + required=True, + tracking=True, + help="The event that starts the clock (e.g. date of service). Excluded from " + "the count per Rule 2.514.", + ) + days = fields.Integer( + string="Days", + required=True, + default=0, + tracking=True, + help="Number of days in the period. Defaults from the type but can be " + "overridden. Verify the current rule.", + ) + due_date = fields.Date( + string="Due Date", + compute="_compute_due_date", + store=True, + tracking=True, + ) + state = fields.Selection( + selection=[ + ("pending", "Pending"), + ("done", "Completed"), + ("waived", "Waived"), + ("overdue", "Overdue"), + ], + default="pending", + required=True, + copy=False, + tracking=True, + ) + is_overdue = fields.Boolean( + compute="_compute_is_overdue", + search="_search_is_overdue", + string="Overdue?", + ) + calendar_event_id = fields.Many2one( + "calendar.event", + string="Calendar Entry", + copy=False, + readonly=True, + ) + notes = fields.Text() + + # --- date math ---------------------------------------------------------- + def _holiday_dates(self): + """Hook for legal holidays. Intentionally empty — holidays are + jurisdiction/year specific and must be supplied by the content-maintenance + process (Requirements for Success #7). Return a set of date objects.""" + return set() + + def _roll_forward(self, due): + """Roll a due date off weekends (and holidays, when supplied).""" + holidays = self._holiday_dates() + while due.weekday() >= 5 or due in holidays: + due += timedelta(days=1) + return due + + @api.depends("trigger_date", "days") + def _compute_due_date(self): + for dl in self: + if not dl.trigger_date or not dl.days: + dl.due_date = dl.trigger_date or False + continue + raw = dl.trigger_date + timedelta(days=dl.days) + dl.due_date = dl._roll_forward(raw) + + @api.depends("due_date", "state") + def _compute_is_overdue(self): + today = fields.Date.context_today(self) + for dl in self: + dl.is_overdue = bool( + dl.due_date + and dl.state == "pending" + and dl.due_date < today + ) + + def _search_is_overdue(self, operator, value): + if operator not in ("=", "!="): + raise ValueError(_("Unsupported operator for is_overdue search.")) + today = fields.Date.context_today(self) + want_overdue = (operator == "=" and value) or (operator == "!=" and not value) + if want_overdue: + return [("state", "=", "pending"), ("due_date", "<", today)] + return ["|", ("state", "!=", "pending"), ("due_date", ">=", today)] + + # --- defaults from type ------------------------------------------------- + @api.onchange("deadline_type") + def _onchange_deadline_type(self): + if self.deadline_type in STANDARD_OFFSETS and not self.days: + self.days = STANDARD_OFFSETS[self.deadline_type] + + @api.model_create_multi + def create(self, vals_list): + for vals in vals_list: + if not vals.get("days") and vals.get("deadline_type") in STANDARD_OFFSETS: + vals["days"] = STANDARD_OFFSETS[vals["deadline_type"]] + records = super().create(vals_list) + records._sync_calendar_event() + return records + + def write(self, vals): + res = super().write(vals) + if {"name", "due_date", "trigger_date", "days", "state"} & set(vals): + self._sync_calendar_event() + return res + + # --- calendar mirror ---------------------------------------------------- + def _sync_calendar_event(self): + Cal = self.env["calendar.event"] + for dl in self: + if not dl.due_date: + continue + title = _("[Deadline] %(name)s — %(matter)s", + name=dl.name, matter=dl.case_id.name or "") + vals = { + "name": title, + "allday": True, + "start_date": dl.due_date, + "stop_date": dl.due_date, + } + if dl.calendar_event_id: + dl.calendar_event_id.write(vals) + else: + dl.calendar_event_id = Cal.create(vals).id + + # --- actions ------------------------------------------------------------ + def action_mark_done(self): + for dl in self: + dl.state = "done" + dl.message_post(body=_("Deadline completed.")) + return True + + def action_waive(self): + for dl in self: + dl.state = "waived" + dl.message_post(body=_("Deadline waived.")) + return True + + def action_reset_pending(self): + for dl in self: + dl.state = "pending" + dl.message_post(body=_("Deadline reset to pending.")) + return True + + # --- cron --------------------------------------------------------------- + @api.model + def _cron_flag_overdue(self): + """Daily: flag pending deadlines whose due date has passed.""" + today = fields.Date.context_today(self) + overdue = self.search([ + ("state", "=", "pending"), + ("due_date", "<", today), + ]) + for dl in overdue: + dl.state = "overdue" + dl.message_post( + body=_("Deadline is OVERDUE (due %s) — flagged by the deadline engine.") + % dl.due_date + ) + return len(overdue) diff --git a/activeblue_familylaw_handoff/activeblue_familylaw_build/activeblue_familylaw/models/familylaw_proceeding.py b/activeblue_familylaw_handoff/activeblue_familylaw_build/activeblue_familylaw/models/familylaw_proceeding.py index 91b6df0..27cfabe 100644 --- a/activeblue_familylaw_handoff/activeblue_familylaw_build/activeblue_familylaw/models/familylaw_proceeding.py +++ b/activeblue_familylaw_handoff/activeblue_familylaw_build/activeblue_familylaw/models/familylaw_proceeding.py @@ -57,6 +57,34 @@ class FamilyLawProceeding(models.Model): document_ids = fields.One2many( "familylaw.document", "proceeding_id", string="Documents", ) + deadline_ids = fields.One2many( + "familylaw.deadline", "proceeding_id", string="Deadlines", + ) + + def action_seed_standard_deadlines(self): + """Create the common procedural deadlines for this proceeding, dated from + today. The day-counts come from familylaw.deadline.STANDARD_OFFSETS and MUST + be confirmed against the current rule — see that module's date-math note.""" + Deadline = self.env["familylaw.deadline"] + today = fields.Date.context_today(self) + labels = { + "answer": "Answer / Response due", + "disclosure": "Mandatory Disclosure due (Rule 12.285)", + "discovery": "Discovery responses due", + } + for proc in self: + existing = set(proc.deadline_ids.mapped("deadline_type")) + for dtype, label in labels.items(): + if dtype in existing: + continue + Deadline.create({ + "proceeding_id": proc.id, + "name": label, + "deadline_type": dtype, + "trigger_date": today, + }) + proc.message_post(body="Standard deadlines seeded.") + return True def action_close_proceeding(self): for proc in self: diff --git a/activeblue_familylaw_handoff/activeblue_familylaw_build/activeblue_familylaw/security/ir.model.access.csv b/activeblue_familylaw_handoff/activeblue_familylaw_build/activeblue_familylaw/security/ir.model.access.csv index 9651086..515902f 100644 --- a/activeblue_familylaw_handoff/activeblue_familylaw_build/activeblue_familylaw/security/ir.model.access.csv +++ b/activeblue_familylaw_handoff/activeblue_familylaw_build/activeblue_familylaw/security/ir.model.access.csv @@ -14,3 +14,5 @@ access_familylaw_conflict_hit_attorney,familylaw.conflict.hit attorney,model_fam access_familylaw_intake_wizard_user,familylaw.intake.wizard user,model_familylaw_intake_wizard,base.group_user,1,1,1,1 access_familylaw_document_user,familylaw.document staff,model_familylaw_document,group_familylaw_user,1,1,1,0 access_familylaw_document_attorney,familylaw.document attorney,model_familylaw_document,group_familylaw_attorney,1,1,1,1 +access_familylaw_deadline_user,familylaw.deadline staff,model_familylaw_deadline,group_familylaw_user,1,1,1,0 +access_familylaw_deadline_attorney,familylaw.deadline attorney,model_familylaw_deadline,group_familylaw_attorney,1,1,1,1 diff --git a/activeblue_familylaw_handoff/activeblue_familylaw_build/activeblue_familylaw/tests/__init__.py b/activeblue_familylaw_handoff/activeblue_familylaw_build/activeblue_familylaw/tests/__init__.py index a5f4e29..afcb0b3 100644 --- a/activeblue_familylaw_handoff/activeblue_familylaw_build/activeblue_familylaw/tests/__init__.py +++ b/activeblue_familylaw_handoff/activeblue_familylaw_build/activeblue_familylaw/tests/__init__.py @@ -1,3 +1,4 @@ from . import test_case_lifecycle from . import test_step2 from . import test_step3 +from . import test_step4 diff --git a/activeblue_familylaw_handoff/activeblue_familylaw_build/activeblue_familylaw/tests/test_step4.py b/activeblue_familylaw_handoff/activeblue_familylaw_build/activeblue_familylaw/tests/test_step4.py new file mode 100644 index 0000000..7bf4eef --- /dev/null +++ b/activeblue_familylaw_handoff/activeblue_familylaw_build/activeblue_familylaw/tests/test_step4.py @@ -0,0 +1,181 @@ +# -*- coding: utf-8 -*- +"""STEP 4 tests — deadline engine. + +Run just this step: + odoo -d -u activeblue_familylaw --test-enable \ + --test-tags familylaw_step4 --stop-after-init + +Date math is tested with FIXED dates (never today()) for determinism. Overdue +behaviour is tested with dates safely in the past/future relative to any run date. +""" + +from datetime import date + +from odoo.tests.common import TransactionCase, tagged + + +@tagged("post_install", "-at_install", "familylaw", "familylaw_step4") +class TestStep4DeadlineEngine(TransactionCase): + + @classmethod + def setUpClass(cls): + super().setUpClass() + cls.partner = cls.env["res.partner"].create({"name": "Deadline Client"}) + cls.case = cls.env["familylaw.case"].create({ + "name": "Deadline Matter", + "client_id": cls.partner.id, + "case_type": "support_modification", + }) + cls.proc = cls.case.proceeding_ids[0] + cls.Deadline = cls.env["familylaw.deadline"] + + def _mk(self, **kw): + vals = { + "proceeding_id": self.proc.id, + "name": "Test Deadline", + "deadline_type": "custom", + "trigger_date": date(2025, 1, 1), + "days": 20, + } + vals.update(kw) + return self.Deadline.create(vals) + + # --- basic math (exclude trigger day, add N days) ----------------------- + def test_01_twenty_day_math(self): + # 2025-01-01 (Wed) + 20 days = 2025-01-21 (Tue), no weekend roll needed + dl = self._mk(trigger_date=date(2025, 1, 1), days=20) + self.assertEqual(dl.due_date, date(2025, 1, 21)) + + def test_02_forty_five_day_math(self): + # 2025-01-01 + 45 = 2025-02-15 (Sat) -> roll to 2025-02-17 (Mon) + dl = self._mk(trigger_date=date(2025, 1, 1), days=45) + self.assertEqual(dl.due_date, date(2025, 2, 17)) + + def test_03_thirty_day_math(self): + # 2025-01-01 + 30 = 2025-01-31 (Fri), no roll + dl = self._mk(trigger_date=date(2025, 1, 1), days=30) + self.assertEqual(dl.due_date, date(2025, 1, 31)) + + # --- weekend roll-forward ----------------------------------------------- + def test_04_saturday_rolls_to_monday(self): + # 2025-03-07 (Fri) + 1 = 2025-03-08 (Sat) -> 2025-03-10 (Mon) + dl = self._mk(trigger_date=date(2025, 3, 7), days=1) + self.assertEqual(dl.due_date, date(2025, 3, 10)) + + def test_05_sunday_rolls_to_monday(self): + # 2025-03-07 (Fri) + 2 = 2025-03-09 (Sun) -> 2025-03-10 (Mon) + dl = self._mk(trigger_date=date(2025, 3, 7), days=2) + self.assertEqual(dl.due_date, date(2025, 3, 10)) + + def test_06_weekday_no_roll(self): + # 2025-03-07 (Fri) + 4 = 2025-03-11 (Tue), no roll + dl = self._mk(trigger_date=date(2025, 3, 7), days=4) + self.assertEqual(dl.due_date, date(2025, 3, 11)) + + # --- standard offsets default from type --------------------------------- + def test_07_answer_default_days(self): + dl = self.Deadline.create({ + "proceeding_id": self.proc.id, + "name": "Answer due", + "deadline_type": "answer", + "trigger_date": date(2025, 1, 1), + }) + self.assertEqual(dl.days, 20) + + def test_08_disclosure_default_days(self): + dl = self.Deadline.create({ + "proceeding_id": self.proc.id, + "name": "Disclosure due", + "deadline_type": "disclosure", + "trigger_date": date(2025, 1, 1), + }) + self.assertEqual(dl.days, 45) + + def test_09_explicit_days_overrides_default(self): + dl = self.Deadline.create({ + "proceeding_id": self.proc.id, + "name": "Custom answer", + "deadline_type": "answer", + "trigger_date": date(2025, 1, 1), + "days": 5, + }) + self.assertEqual(dl.days, 5) + + # --- overdue detection -------------------------------------------------- + def test_10_past_deadline_is_overdue(self): + dl = self._mk(trigger_date=date(2000, 1, 1), days=20) + self.assertTrue(dl.is_overdue) + + def test_11_future_deadline_not_overdue(self): + dl = self._mk(trigger_date=date(2999, 1, 1), days=20) + self.assertFalse(dl.is_overdue) + + def test_12_done_deadline_not_overdue(self): + dl = self._mk(trigger_date=date(2000, 1, 1), days=20) + dl.action_mark_done() + self.assertFalse(dl.is_overdue) + self.assertEqual(dl.state, "done") + + def test_13_cron_flags_overdue(self): + dl = self._mk(trigger_date=date(2000, 1, 1), days=20) + self.assertEqual(dl.state, "pending") + self.Deadline._cron_flag_overdue() + self.assertEqual(dl.state, "overdue") + + def test_14_cron_ignores_future(self): + dl = self._mk(trigger_date=date(2999, 1, 1), days=20) + self.Deadline._cron_flag_overdue() + self.assertEqual(dl.state, "pending") + + def test_15_is_overdue_search(self): + past = self._mk(trigger_date=date(2000, 1, 1), days=20) + self._mk(trigger_date=date(2999, 1, 1), days=20) + found = self.Deadline.search([ + ("is_overdue", "=", True), + ("proceeding_id", "=", self.proc.id), + ]) + self.assertIn(past, found) + + # --- calendar mirror ---------------------------------------------------- + def test_16_calendar_event_created(self): + dl = self._mk() + self.assertTrue(dl.calendar_event_id) + self.assertEqual(dl.calendar_event_id.start_date, dl.due_date) + + def test_17_calendar_event_updates_on_date_change(self): + dl = self._mk(trigger_date=date(2025, 1, 1), days=20) + first = dl.calendar_event_id.start_date + dl.days = 30 + self.assertNotEqual(dl.calendar_event_id.start_date, first) + self.assertEqual(dl.calendar_event_id.start_date, dl.due_date) + + # --- per-proceeding isolation ------------------------------------------- + def test_18_deadlines_isolated_per_proceeding(self): + proc2 = self.env["familylaw.proceeding"].create({ + "case_id": self.case.id, + "name": "Second Proceeding", + "proceeding_type": "modification", + }) + d1 = self._mk() + d2 = self.Deadline.create({ + "proceeding_id": proc2.id, + "name": "Other deadline", + "deadline_type": "custom", + "trigger_date": date(2025, 1, 1), + "days": 10, + }) + self.assertIn(d1, self.proc.deadline_ids) + self.assertNotIn(d2, self.proc.deadline_ids) + self.assertIn(d2, proc2.deadline_ids) + + # --- seeding ------------------------------------------------------------ + def test_19_seed_standard_deadlines(self): + self.proc.action_seed_standard_deadlines() + types = set(self.proc.deadline_ids.mapped("deadline_type")) + self.assertEqual(types, {"answer", "disclosure", "discovery"}) + + def test_20_seed_is_idempotent(self): + self.proc.action_seed_standard_deadlines() + n1 = len(self.proc.deadline_ids) + self.proc.action_seed_standard_deadlines() + self.assertEqual(len(self.proc.deadline_ids), n1) diff --git a/activeblue_familylaw_handoff/activeblue_familylaw_build/activeblue_familylaw/views/familylaw_deadline_views.xml b/activeblue_familylaw_handoff/activeblue_familylaw_build/activeblue_familylaw/views/familylaw_deadline_views.xml new file mode 100644 index 0000000..8fcaa8b --- /dev/null +++ b/activeblue_familylaw_handoff/activeblue_familylaw_build/activeblue_familylaw/views/familylaw_deadline_views.xml @@ -0,0 +1,112 @@ + + + + + familylaw.deadline.list + familylaw.deadline + + + + + + + + + + + + + + + + + familylaw.deadline.form + familylaw.deadline + +
+
+
+ + +
+
+ + + + + + + + + + + + + + + + + +
+
+ + + +
+
+
+
+ + + familylaw.deadline.search + familylaw.deadline + + + + + + + + + + + + + + + + + + + + Deadlines + familylaw.deadline + list,form + + {'search_default_pending': 1} + + +
diff --git a/activeblue_familylaw_handoff/activeblue_familylaw_build/activeblue_familylaw/views/familylaw_menus.xml b/activeblue_familylaw_handoff/activeblue_familylaw_build/activeblue_familylaw/views/familylaw_menus.xml index e4e1ca1..cc1b6a7 100644 --- a/activeblue_familylaw_handoff/activeblue_familylaw_build/activeblue_familylaw/views/familylaw_menus.xml +++ b/activeblue_familylaw_handoff/activeblue_familylaw_build/activeblue_familylaw/views/familylaw_menus.xml @@ -27,6 +27,13 @@ action="action_familylaw_document" sequence="20"/> + + +
+