fix: restore Sep+Oct adult merge and stop auto-truncating period selector #28
@@ -1,5 +1,12 @@
|
||||
# Changelog
|
||||
|
||||
## 2026-05-08 11:11 CEST — fix: period selector showed only Dec 2025+ on adults
|
||||
|
||||
- Restored the `2025-09 → 2025-10` adult merge in `scripts/attendance.py` and `go/internal/services/membership/sources.go` (commented-out by `1257f0d`); the `2025-12 → 2026-01` mapping stays disabled per product decision (Dec and Jan are billed separately for adults).
|
||||
- Dropped the `defaultFrom = maxMonthIdx − 4` JS auto-default in `templates/adults.html` and `templates/juniors.html` (introduced by `7774301`); the From-selector now starts at the oldest available month so all non-future periods render on first load. Future-month removal is preserved.
|
||||
- `go/internal/services/membership/sources_test.go`: `TestLoadAdults` / `TestLoadAdults_Fee` now assert that Sep dates land in the merged `2025-10` bucket.
|
||||
- **Independent of these code changes**: the live adults attendance Google Sheet header had been pruned to start at `02.12.2025` (Sep/Oct/Nov 2025 columns deleted); restoring those columns from Sheets version history is required to actually see those periods on the dashboard.
|
||||
|
||||
## 2026-05-08 10:15 CEST — fix(go): adults template — use lifted CSS classes for visual parity
|
||||
|
||||
- Use existing `.balance-pos` / `.balance-neg` (drop invented `balance-cell` / `balance-negative`); Pay-All button now lives inside the balance cell with `position: relative` (matches Python; no separate trailing column).
|
||||
|
||||
142
docs/plans/2026-05-08-1052-period-selector-missing-old-months.md
Normal file
142
docs/plans/2026-05-08-1052-period-selector-missing-old-months.md
Normal file
@@ -0,0 +1,142 @@
|
||||
# Period selector missing older months — diagnosis
|
||||
|
||||
## Context
|
||||
|
||||
User reports the "From / To" period selector on the **adults** dashboard now
|
||||
shows **Dec 2025** as the oldest available period. The older production
|
||||
deployment shows Sep+Oct 2025, Nov 2025, Dec 2025+Jan 2026 (merged labels) —
|
||||
i.e. data going back to September 2025. The user wants to know what went
|
||||
sideways. Confirmed: the dropdown options on both Python and Go genuinely
|
||||
start at Dec 2025, not just the default selection — Sep/Oct/Nov 2025 are not
|
||||
in the list at all.
|
||||
|
||||
## Root cause — the live adults sheet header is missing those columns
|
||||
|
||||
The fresh cache files at `tmp/go/attendance_regular_cache.json` (raw rows from
|
||||
Google Sheets, modifiedTime `2026-05-06T22:30:02Z`, cached `2026-05-08T00:26`)
|
||||
contain the actual header row for the adults tab (gid=0):
|
||||
|
||||
```text
|
||||
['FUJ tréninky úterý 20:30-22:00', '', '', '02.12.2025', '09.12.2025',
|
||||
'16.12.2025', '06.01.2026', '13.01.2026', '20.01.2026', '27.01.2026',
|
||||
'03.02.2026', '10.02.2026', '17.02.2026', '24.02.2026', '03.03.2026',
|
||||
'10.03.2026', '17.03.2026', '23.03.2026', '31.03.2026', '13.04.2026',
|
||||
'20.04.2026', '27.04.2026', '04.05.2026', '', '', '']
|
||||
```
|
||||
|
||||
**The first date column in the live adults sheet is `02.12.2025` (Dec 2 2025).**
|
||||
There are no September, October, or November 2025 columns in the header at
|
||||
all. Both backends parse this faithfully (no slicing, no cutoff anywhere) and
|
||||
correctly produce `sortedMonths = ["2025-12", "2026-01", …, "2026-05"]`.
|
||||
|
||||
The juniors sheet (different tab, `JUNIOR_SHEET_GID`) is **fine** — its header
|
||||
still contains `['', 'tier', '', '15.09.2025', '13.10.2025', '20.10.2025',
|
||||
'03.11.2025', '24.11.2025', '10.11.2025', '17.11.2025', '01.12.2025', …]`. So
|
||||
the juniors page still shows Sep+Oct / Nov / Dec+Jan correctly.
|
||||
|
||||
So this is a **data issue in the adults attendance Google Sheet**: at some
|
||||
point between when production's cache was last warmed (showing Sep–Nov) and
|
||||
2026-05-06, somebody (or some action) removed the columns for September,
|
||||
October, and November 2025 from the adults tab header.
|
||||
|
||||
The code is doing exactly what it should. There is no parser regression.
|
||||
|
||||
## What to do
|
||||
|
||||
### 1. Restore the missing date columns in the adults attendance sheet
|
||||
|
||||
The fix lives in Google Sheets, not in the codebase. Options, in order of
|
||||
preference:
|
||||
|
||||
- **(a) Use Sheets version history.** File → Version history → See version
|
||||
history; find a version from before the columns were dropped (anything
|
||||
before about Mar 2026 should still have them). Copy the Sep/Oct/Nov 2025
|
||||
date column headers and the `TRUE/FALSE` cells underneath them back into
|
||||
the current sheet. Only restore the 11 missing date columns; do not
|
||||
full-revert (you'd lose every change since then).
|
||||
- **(b) Pull from the production server's cache.** The production deployment
|
||||
evidently still has the older cache, since its dashboard renders those
|
||||
months. SSH there, copy `tmp/attendance_regular_cache.json`, and you can
|
||||
reconstruct the per-member Sep/Oct/Nov attendance counts from the
|
||||
`data[*][2]` map (keys `"2025-09"`, `"2025-10"`, `"2025-11"`). Re-enter
|
||||
those into the sheet manually as date columns + `TRUE` cells — tedious but
|
||||
deterministic.
|
||||
- **(c) Accept the loss.** If the older columns aren't recoverable, the
|
||||
dashboard correctly reflects what the sheet contains; nothing more to do.
|
||||
|
||||
Which to pick depends on whether those months still need to be billed /
|
||||
reconciled.
|
||||
|
||||
### 2. Restore `ADULT_MERGED_MONTHS` (user confirmed this was unintentional)
|
||||
|
||||
Independent of the sheet issue: commit `1257f0d` (Mar 9 2026) commented out
|
||||
the adult merge mappings. Once the Sep/Oct/Nov columns are back in the sheet,
|
||||
the dashboard would still show them as separate periods instead of the
|
||||
production-style "Sep+Oct 2025" and "Dec 2025+Jan 2026" merged labels.
|
||||
|
||||
User confirmed this was unintentional. Two files to update:
|
||||
|
||||
- [scripts/attendance.py:32-35](scripts/attendance.py#L32-L35) — uncomment
|
||||
the two mappings:
|
||||
|
||||
```python
|
||||
ADULT_MERGED_MONTHS = {
|
||||
"2025-12": "2026-01", # keys are merged into values
|
||||
"2025-09": "2025-10",
|
||||
}
|
||||
```
|
||||
|
||||
- [go/internal/services/membership/sources.go:30](go/internal/services/membership/sources.go#L30) — mirror the same:
|
||||
|
||||
```go
|
||||
var AdultMergedMonths = map[string]string{
|
||||
"2025-12": "2026-01",
|
||||
"2025-09": "2025-10",
|
||||
}
|
||||
```
|
||||
|
||||
After this change, hit `POST /flush-cache` on each backend so the in-process
|
||||
post-processed adults cache is rebuilt with the new mapping.
|
||||
|
||||
### 3. (Optional, separate) Fix the JS auto-default that hides older months
|
||||
|
||||
This is **not** the cause of the user's current symptom (that's the sheet
|
||||
issue), but it will become a UX issue once Sep/Oct/Nov columns are restored:
|
||||
the Python frontend's `defaultFrom = Math.max(0, maxMonthIdx - 4)` will still
|
||||
default the From-selector to ~5 months before the latest column on every page
|
||||
load, hiding restored older months until the user manually picks them.
|
||||
|
||||
- [templates/adults.html:1047](templates/adults.html#L1047) — `var defaultFrom = Math.max(0, maxMonthIdx - 4);`
|
||||
- [templates/juniors.html:1028](templates/juniors.html#L1028) — same line.
|
||||
|
||||
Drop those four lines (`defaultFrom`, `fromSelect.value = defaultFrom`,
|
||||
`toSelect.value = maxMonthIdx`, `applyMonthFilter()`) so the page loads with
|
||||
all non-future months visible — matching the Go side, which only calls
|
||||
`hideFutureMonths()` and leaves From at its first option.
|
||||
|
||||
Recommend bundling this with step 2 since they touch related UI.
|
||||
|
||||
## Verification
|
||||
|
||||
1. **After step 1** — `POST /flush-cache` on Python and Go backends; reload
|
||||
`/adults` on each. Confirm the dropdown now lists Sep/Oct/Nov 2025.
|
||||
2. **After step 2** — reload `/adults`. Confirm the dropdown shows
|
||||
"Sep+Oct 2025" as a single period and "Dec 2025+Jan 2026" as a single
|
||||
period. (Still requires the sheet columns to exist.)
|
||||
3. **After step 3** — reload `/adults` and `/juniors` on Python. Confirm the
|
||||
table renders all non-future months on first load (Sep 2025 through the
|
||||
current month) instead of starting at Dec 2025.
|
||||
4. **Parity check** — `make parity` should report zero diffs between Python
|
||||
and Go on `/api/adults` and `/api/juniors`.
|
||||
|
||||
## Critical files referenced
|
||||
|
||||
- `tmp/go/attendance_regular_cache.json` — current adults sheet rows
|
||||
(evidence: header starts at `02.12.2025`).
|
||||
- `tmp/go/attendance_juniors_cache.json` — current juniors sheet rows
|
||||
(header still has `15.09.2025`).
|
||||
- [scripts/attendance.py](scripts/attendance.py) — `ADULT_MERGED_MONTHS`
|
||||
empty after `1257f0d`; `parse_dates` / `group_by_month` faithful.
|
||||
- [go/internal/services/membership/sources.go](go/internal/services/membership/sources.go) — Go counterpart, same shape.
|
||||
- [templates/adults.html](templates/adults.html), [templates/juniors.html](templates/juniors.html) — JS onload `defaultFrom = -4` issue (step 3).
|
||||
- [go/internal/web/static/js/filters.js](go/internal/web/static/js/filters.js) — Go filter UI (already correct, no changes).
|
||||
@@ -27,7 +27,9 @@ const (
|
||||
|
||||
// AdultMergedMonths mirrors ADULT_MERGED_MONTHS in scripts/attendance.py.
|
||||
// Source month → target month (source attendance accumulated into target).
|
||||
var AdultMergedMonths = map[string]string{}
|
||||
var AdultMergedMonths = map[string]string{
|
||||
"2025-09": "2025-10",
|
||||
}
|
||||
|
||||
// JuniorMergedMonths mirrors JUNIOR_MERGED_MONTHS in scripts/attendance.py.
|
||||
var JuniorMergedMonths = map[string]string{
|
||||
|
||||
@@ -46,8 +46,8 @@ func TestLoadAdults(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// adultMergedMonths is empty so 2025-09 stays as-is
|
||||
if len(months) != 1 || months[0] != "2025-09" {
|
||||
// AdultMergedMonths sends 2025-09 → 2025-10
|
||||
if len(months) != 1 || months[0] != "2025-10" {
|
||||
t.Errorf("unexpected months: %v", months)
|
||||
}
|
||||
if len(members) != 2 {
|
||||
@@ -55,7 +55,7 @@ func TestLoadAdults(t *testing.T) {
|
||||
}
|
||||
byName := map[string]int{}
|
||||
for _, m := range members {
|
||||
byName[m.Name] = m.Fees["2025-09"].Attendance
|
||||
byName[m.Name] = m.Fees["2025-10"].Attendance
|
||||
}
|
||||
if byName["Alice"] != 2 {
|
||||
t.Errorf("Alice: want 2 sessions, got %d", byName["Alice"])
|
||||
@@ -73,9 +73,9 @@ func TestLoadAdults_Fee(t *testing.T) {
|
||||
}
|
||||
byName := map[string]int{}
|
||||
for _, m := range members {
|
||||
byName[m.Name] = m.Fees["2025-09"].Expected
|
||||
byName[m.Name] = m.Fees["2025-10"].Expected
|
||||
}
|
||||
// 2 sessions in 2025-09 → AdultFeeMonthlyRate["2025-09"] = 750
|
||||
// 2 sessions land in merged 2025-10 → AdultFeeMonthlyRate["2025-10"] = 750
|
||||
if byName["Alice"] != 750 {
|
||||
t.Errorf("Alice fee: want 750, got %d", byName["Alice"])
|
||||
}
|
||||
|
||||
@@ -30,8 +30,7 @@ JUNIOR_MONTHLY_RATE = {
|
||||
"2026-03": 250 # reduced fee for March 2026
|
||||
}
|
||||
ADULT_MERGED_MONTHS = {
|
||||
#"2025-12": "2026-01", # keys are merged into values
|
||||
#"2025-09": "2025-10"
|
||||
"2025-09": "2025-10", # keys are merged into values
|
||||
}
|
||||
|
||||
JUNIOR_MERGED_MONTHS = {
|
||||
|
||||
@@ -1044,8 +1044,6 @@
|
||||
}
|
||||
});
|
||||
|
||||
var defaultFrom = Math.max(0, maxMonthIdx - 4);
|
||||
fromSelect.value = defaultFrom;
|
||||
toSelect.value = maxMonthIdx;
|
||||
applyMonthFilter();
|
||||
})();
|
||||
|
||||
@@ -1025,8 +1025,6 @@
|
||||
}
|
||||
});
|
||||
|
||||
var defaultFrom = Math.max(0, maxMonthIdx - 4);
|
||||
fromSelect.value = defaultFrom;
|
||||
toSelect.value = maxMonthIdx;
|
||||
applyMonthFilter();
|
||||
})();
|
||||
|
||||
Reference in New Issue
Block a user