Give recurring revenue a real schedule: frequency and a start date. - #3
Open
Herklos wants to merge 1 commit into
Open
Give recurring revenue a real schedule: frequency and a start date.#3Herklos wants to merge 1 commit into
Herklos wants to merge 1 commit into
Conversation
Deploying with
|
| Status | Name | Latest Commit | Updated (UTC) |
|---|---|---|---|
| ✅ Deployment successful! View logs |
chrono | cbdf094 | Jul 29 2026, 12:14 PM |
Adding a recurring revenue source only asked for a flat amount, which was
silently read as "per month, forever, starting now". A weekly retainer, a
quarterly licence or a contract beginning next quarter had no way to be
expressed.
A recurring source now carries a schedule in `content`:
{ "frequency": "daily|weekly|biweekly|monthly|quarterly|yearly",
"amount_cents": <what ONE occurrence is worth> }
anchored on revenue_sources.starts_on and bounded by ends_on. Both columns
already existed and recognize_project_revenue already filtered on them; only
the app never wrote them. No column is added.
The schedule is expanded per month, not below it. revenue_entries.period_month
is a hard monthly grain, and invoices, rem_months, rem_lines, referral_earnings
and company_fee_reserve_ledger all key off it, so a weekly source does not
write weekly rows: recognition counts the occurrences landing inside the month
and writes that month's single entry. Weekly 500 EUR from 2026-03-11 recognizes
1500 in March and 2500 in April. Daily counts calendar days, weekends included.
Legacy sources are untouched. No `frequency` key means a pre-frequency source:
the flat monthly_amount_cents, once a month, exactly as before. Those rows also
have starts_on = null, so nothing about them shifts.
The occurrence math is a deliberate TS/SQL mirror pair -- occurrencesInMonth in
revenue-source.lib.ts and recurring_occurrences_in_month in the migration -- the
same way project_cost_cumulative mirrors project-cost.lib.ts. Change both
together. Both fallbacks are pinned by a test on each side, including the
awkward one: a frequency with no anchor (a hand-edited or imported row) pays
one occurrence a month rather than silently paying zero.
A backdated start date has months to catch up on, and recognize_project_revenue
only ever handles the one month it is given, so recognize_project_revenue_range
loops them server-side: one round trip, one transaction. It inherits the manager
gate from the inner function, and its own guards raise slugs
(revenue-range-invalid, revenue-range-too-wide:<months>), never prose.
Two renames the change forced. monthlyRecurringAmount is gone: the name lies as
soon as an amount is per-week, and it is replaced by recurringSchedule (the
configured per-occurrence amount) and recurringRevenue(source, period) (what a
given month recognizes). The project detail total now sums the current month's
recognized amount, because adding a weekly 500 to a monthly 3000 was not a
meaningful number.
Covered by 32 pgTAP assertions and 30 vitest ones, asserted against the same
numbers on both sides of the mirror: per-frequency occurrence counting, leap and
short-month anchor clamping, end-date truncation, idempotent re-recognition,
retirement of months a shrunk schedule no longer covers, range back-fill amounts,
and the manager gate on the range wrapper.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Herklos
force-pushed
the
recurring-revenue-frequency
branch
from
July 29, 2026 12:12
9a6387e to
cbdf094
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No description provided.