Let vacation years go back to the child's birth year
Settings only offered vacation-day rows from the current year forward, while analytics ran its balance series from the child's birth year. Every year in the gap had no vacation_days row, so the series assumed a full statutory 25 earned with no use and banked earned - floor of it forward — phantom saved days the user had no row to correct. Settings now offers editable rows back to the birth year, and a past year with no row assumes only the floor was earned, so it banks nothing. A past year the user did enter is untouched by that rule. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -114,6 +114,18 @@ class ParentYearBalance:
|
||||
purely to the over-cap cause (e.g. earned 30, floor 20, used 15 →
|
||||
5 days over-cap lost) while the dashboard showed "Expiring 0" — that
|
||||
silent gap is why the breakdown exists now.
|
||||
- `earned` for a year with no `vacation_days` row depends on whether the
|
||||
year is in the past relative to `current_year` (the caller's "now").
|
||||
`year >= current_year` assumes the full statutory entitlement, same as
|
||||
always — unconfirmed (`is_assumed=True`), but a forward-looking guess.
|
||||
`year < current_year` instead assumes only the floor was earned, so
|
||||
`current_year_saveable` is `max(floor - floor, 0) = 0` and
|
||||
`carried_out = min(left, old_carry_forward)` — a past year with no row
|
||||
never phantom-banks days the user never told us they saved. Settings
|
||||
offers editable rows back to the child's birth year precisely so a
|
||||
genuine past figure can replace this assumption. A row the user DID
|
||||
enter for a past year is untouched by this rule: `earned` is always
|
||||
that row's `total_days`, regardless of year.
|
||||
|
||||
`saved_days_in` on a `vacation_days` row is normally only used to *seed*
|
||||
the bank for the first year of the series (year-earned = year − 1, since
|
||||
@@ -352,7 +364,7 @@ class AnalyticsService:
|
||||
vacation_use_by_year[d.date.year] = vacation_use_by_year.get(d.date.year, 0) + 1
|
||||
|
||||
vacation_balances = self._vacation_year_series(
|
||||
years, vacation_by_year, vacation_use_by_year, floor
|
||||
years, vacation_by_year, vacation_use_by_year, floor, date.today().year
|
||||
)
|
||||
|
||||
fp_rows = [
|
||||
@@ -387,9 +399,12 @@ class AnalyticsService:
|
||||
vacation_by_year: dict[int, VacationDays],
|
||||
use_by_year: dict[int, int],
|
||||
floor: int,
|
||||
current_year: int,
|
||||
) -> list[VacationYearBalance]:
|
||||
"""FIFO carry-forward bank simulation — see `ParentYearBalance`'s
|
||||
docstring for the full algorithm this implements."""
|
||||
docstring for the full algorithm this implements, including the
|
||||
past-vs-current-year `earned` rule (`current_year`) applied below for
|
||||
a year with no entered `vacation_days` row."""
|
||||
carry_years = settings.rules.vacation_carry_forward_years
|
||||
default_earned = settings.rules.statutory_vacation_days_per_year
|
||||
# (year_earned, remaining_count) entries, oldest first.
|
||||
@@ -398,7 +413,17 @@ class AnalyticsService:
|
||||
|
||||
for i, year in enumerate(years):
|
||||
row = vacation_by_year.get(year)
|
||||
earned = row.total_days if row is not None else default_earned
|
||||
if row is not None:
|
||||
earned = row.total_days
|
||||
elif year >= current_year:
|
||||
earned = default_earned
|
||||
else:
|
||||
# Past year, no entered row: assume only the floor was
|
||||
# earned/used, not the full statutory default — see
|
||||
# `ParentYearBalance`'s docstring. Keeps a phantom past year
|
||||
# from banking `default_earned - floor` days the user never
|
||||
# actually saved.
|
||||
earned = floor
|
||||
is_assumed = row is None
|
||||
|
||||
if i == 0:
|
||||
|
||||
Reference in New Issue
Block a user