Files
fuj-management/go/internal/services/membership/format_fees.go
T
kacerrandClaude Opus 5.5 022facc957
Deploy to K8s / deploy (push) Successful in 20s
refactor: move Python implementation to legacy/, make Go primary
Go (go/) becomes the primary implementation. The original Python
version moves to legacy/ and is kept runnable; new features are still
built in both (see the Dual implementation rule in CLAUDE.md).

- git mv app.py, scripts/, templates/, tests/, pyproject.toml, uv.lock
  and build/ into legacy/
- Makefile: Python targets point at legacy/, venv in legacy/.venv,
  CREDENTIALS_PATH exported for the root .secret/ file
- CI and make image build the Python image with legacy/ as context;
  the Makefile is no longer copied into the image
- legacy/.dockerignore moved to the context root so it takes effect
- CLAUDE.md, docs/README.md, Go comments, fixture README, capture
  script and VS Code launch config updated to the new layout

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 11:33:57 +02:00

90 lines
2.0 KiB
Go

package membership
import (
"fmt"
"fuj-management/go/internal/domain/reconcile"
"io"
"strings"
"time"
)
// printFeesTable writes a fixed-width adult-fees table to w.
// Mirrors legacy/scripts/calculate_fees.py main().
//
// Verify with:
//
// PYTHONPATH=legacy/scripts:legacy python legacy/scripts/calculate_fees.py
func printFeesTable(w io.Writer, members []reconcile.Member, sortedMonths []string) {
type row struct {
name string
fees map[string]reconcile.FeeData
}
var adults []row
for _, m := range members {
if m.Tier == "A" {
adults = append(adults, row{name: m.Name, fees: m.Fees})
}
}
if len(adults) == 0 {
fmt.Fprintln(w, "No data.")
return
}
monthLabel := func(m string) string {
t, err := time.Parse("2006-01", m)
if err != nil {
return m
}
return t.Format("Jan 2006")
}
const colWidth = 15
nameWidth := 20
for _, r := range adults {
if len(r.name) > nameWidth {
nameWidth = len(r.name)
}
}
// separator length: nameWidth + N*(colWidth+3) where +3 is " | "
sepLen := nameWidth + len(sortedMonths)*(colWidth+3)
// Header row
fmt.Fprintf(w, "%-*s", nameWidth, "Member")
for _, m := range sortedMonths {
fmt.Fprintf(w, " | %*s", colWidth, monthLabel(m))
}
fmt.Fprintln(w)
fmt.Fprintln(w, strings.Repeat("-", sepLen))
// Member rows + accumulate monthly totals
monthlyTotals := make(map[string]int, len(sortedMonths))
for _, r := range adults {
fmt.Fprintf(w, "%-*s", nameWidth, r.name)
for _, m := range sortedMonths {
fd := r.fees[m]
monthlyTotals[m] += fd.Expected
var cell string
if fd.Attendance > 0 {
cell = fmt.Sprintf("%d CZK (%d)", fd.Expected, fd.Attendance)
} else {
cell = "-"
}
fmt.Fprintf(w, " | %*s", colWidth, cell)
}
fmt.Fprintln(w)
}
// Totals row
fmt.Fprintln(w, strings.Repeat("-", sepLen))
fmt.Fprintf(w, "%-*s", nameWidth, "TOTAL")
for _, m := range sortedMonths {
cell := fmt.Sprintf("%d CZK", monthlyTotals[m])
fmt.Fprintf(w, " | %*s", colWidth, cell)
}
fmt.Fprintln(w)
}