fix: statement notes render above divider; add zebra stripes

Compute sub-row text before emitting the main <tr> so has-note class
can suppress its border-bottom. The sub-row then carries the divider,
placing notes visually above it. Alternate rows get row-even class
(light #f7f8fa background) so each transaction is easy to track across
columns regardless of whether it has a note.

https://claude.ai/code/session_01JuRTR5Xjx8emQsyerBgGU7
This commit is contained in:
Claude 2026-05-31 03:43:24 +00:00
parent 8450da6176
commit 59b49a3d3a
No known key found for this signature in database

51
main.py
View file

@ -979,7 +979,9 @@ RECEIPT_CSS = """
th.rnum,td.rnum{text-align:right;padding-right:0;} th.rnum,td.rnum{text-align:right;padding-right:0;}
.credit{color:#080;} .credit{color:#080;}
.debit{color:#c00;} .debit{color:#c00;}
.sub-row td{font-size:10pt;color:#555;padding-top:0;border-bottom:none;padding-left:88px;} .has-note td{border-bottom:none;}
.sub-row td{font-size:10pt;color:#555;padding-top:1px;padding-bottom:5px;border-bottom:1px solid #e0e0e0;padding-left:12px;}
.row-even td{background:#f7f8fa;}
.balance-box{margin-top:14px;text-align:right;font-size:11pt;font-weight:bold;} .balance-box{margin-top:14px;text-align:right;font-size:11pt;font-weight:bold;}
""" """
@ -1068,29 +1070,11 @@ def statement(member_id: int,
def fmt(p): return f"{sym}{p/div:.2f}" def fmt(p): return f"{sym}{p/div:.2f}"
rows_html, running = "", opening_bal rows_html, running = "", opening_bal
for r in rows: for idx, r in enumerate(rows):
txn_ref = _txn_ref(r["id"], s) txn_ref = _txn_ref(r["id"], s)
venue = bar_name if r["venue"] == "bar" else cashier_name venue = bar_name if r["venue"] == "bar" else cashier_name
if r["type"] == "topup":
running += r["amount"]
amt_html = f'<span class="credit">+ {fmt(r["amount"])}</span>'
type_lbl = "Top-up"
elif r["type"] == "withdrawal":
running -= r["amount"]
amt_html = f'<span class="debit">- {fmt(r["amount"])}</span>'
type_lbl = "Withdrawal"
else:
running -= r["amount"]
amt_html = f'<span class="debit">- {fmt(r["amount"])}</span>'
type_lbl = "Charge"
rows_html += ( # Build sub-row text FIRST so we know whether to add has-note to the main row
f"<tr><td>{_fmt_dt(r['created_at'], s)}</td><td>{txn_ref}</td>"
f"<td>{type_lbl}</td><td>{venue}</td><td>{r['staff_name']}</td>"
f"<td class='rnum'>{amt_html}</td><td class='rnum'>{fmt(running)}</td></tr>"
)
# Detail sub-row
sub = "" sub = ""
if r["type"] in ("topup", "withdrawal"): if r["type"] in ("topup", "withdrawal"):
tf_type = r["transfer_type"] or "" tf_type = r["transfer_type"] or ""
@ -1104,8 +1088,31 @@ def statement(member_id: int,
elif r["note"]: elif r["note"]:
sub = r["note"] sub = r["note"]
if r["type"] == "topup":
running += r["amount"]
amt_html = f'<span class="credit">+ {fmt(r["amount"])}</span>'
type_lbl = "Top-up"
elif r["type"] == "withdrawal":
running -= r["amount"]
amt_html = f'<span class="debit">- {fmt(r["amount"])}</span>'
type_lbl = "Withdrawal"
else:
running -= r["amount"]
amt_html = f'<span class="debit">- {fmt(r["amount"])}</span>'
type_lbl = "Charge"
stripe = "row-even" if idx % 2 == 0 else ""
classes = " ".join(c for c in [stripe, "has-note" if sub else ""] if c)
tr_cls = f' class="{classes}"' if classes else ""
rows_html += (
f"<tr{tr_cls}><td>{_fmt_dt(r['created_at'], s)}</td><td>{txn_ref}</td>"
f"<td>{type_lbl}</td><td>{venue}</td><td>{r['staff_name']}</td>"
f"<td class='rnum'>{amt_html}</td><td class='rnum'>{fmt(running)}</td></tr>"
)
if sub: if sub:
rows_html += f'<tr class="sub-row"><td colspan="7">{sub}</td></tr>' sub_cls = ("sub-row " + stripe).strip()
rows_html += f'<tr class="{sub_cls}"><td colspan="7">{sub}</td></tr>'
period_lbl = dict(_STMT_PERIODS).get(period, period) period_lbl = dict(_STMT_PERIODS).get(period, period)
if period == "custom" and bounds: if period == "custom" and bounds: