Skip to content

[Invoices]: Recurring frequency parity — interval-based schedules (every N days/weeks/months/years) #642

Open

Description

@nielsdrost7

v1 supports 28 recurrence intervals; v2's RecurringFrequency enum (Modules/Invoices/Enums/RecurringFrequency.php) supports 5 (daily, weekly, monthly, quarterly, yearly). v1 users with "every 2 weeks", "every 2 months", "every 15 days", or "every 2 years" billing cannot express their schedules in v2, and v1 data cannot be migrated losslessly.

v1 spec: application/modules/invoices/models/Mdl_invoices_recurring.php:23-52 defines the interval codes — days 1,2,3,4,5,6,15,30; weeks 1-4 (7D/14D/21D/28D); months 1-11; years 1-5 — and application/helpers/date_helper.php:194 (increment_date()) applies them as DateInterval('P' . $code) (e.g. 14D, 3M, 1Y). Epic #52 mentions "Support custom frequencies: every X days/weeks/months" as a bullet but no implementable issue exists.

Part of Epic #52.

Specific

Replace the single coarse frequency value with an interval pair on recurring_invoices:

  • frequency_value unsignedTinyInt (>= 1)
  • frequency_unit string enum: day | week | month | year
    Next-run advancement becomes next_run_at->add(DateInterval("P{value}{D|W|M|Y}")), matching v1 increment_date() exactly. Keep RecurringFrequency as UI presets that map onto pairs (daily→1/day, weekly→1/week, monthly→1/month, quarterly→3/month, yearly→1/year) plus a "custom" option exposing the two fields; migration backfills existing rows via that map.

Measurable

  • Every v1 code from 1D to 5Y is representable and round-trips through create/edit UI.
  • Engine advancement for value=3, unit=month from 2026-01-31 matches PHP DateInterval math (documented edge: lands on 2026-05-01? No — v1 does single-step: Jan 31 + P3M = May 1st per PHP overflow; assert actual PHP behavior in a pinned test).
  • Existing seeded/test rows keep working after migration (backfill map covers all 5 legacy enum values).

Achievable

One migration + form change in Modules/Invoices/Filament/Company/Resources/RecurringInvoices/Schemas/RecurringInvoiceForm.php (Select preset + conditional value/unit inputs) + table column formatter ("Every 2 weeks") + engine increment helper. RecurringInvoicesTest gains cases for custom intervals.

Relevant

Fortnightly and bi-monthly billing are common real-world cadences; this is a hard v1→v2 migration blocker for affected users.

Time-Bound

One sprint; should land before or with the generation engine so the engine's increment helper is written once against the pair.

Arrange

  • Schedules: (14, day) a.k.a. fortnightly with next_run_at = today; (3, month) with next_run_at = today; legacy row created pre-migration with enum quarterly.

Act

  • Run the migration; run the generation engine.

Assert

  • Legacy quarterly row backfilled to frequency_value = 3, frequency_unit = month.
  • Fortnightly schedule's next_run_at advanced exactly 14 days; 3-month schedule advanced per PHP DateInterval('P3M') semantics (pinned test).
  • Form accepts value 1-99 and unit day/week/month/year; rejects 0 and negatives.
  • Table renders "Every 14 days" / "Quarterly" style labels for pairs matching presets.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    refinedRefined with SMART plan / test suggestions

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions