Commit graph

24 commits

Author SHA1 Message Date
Claude
db0ae227f1
Admin settings as accordions; cashier mode tab for top-up/withdrawal
- Replace flat settings form with 6 collapsible accordion sections
  (General, Business Address, Branding, Transactions, Receipt Labels,
  Receipt Footers), each with its own Save button and feedback message
- Add segmented mode-tab control to cashier form so staff pick
  Top Up or Withdrawal before entering amounts; withdrawal panel
  hidden by default, resets to Top Up on cancel/clear
- Add toggleAcc(), setCashierMode() JS helpers
- Add accordion + mode-tab CSS styles

https://claude.ai/code/session_01JuRTR5Xjx8emQsyerBgGU7
2026-05-31 04:11:00 +00:00
Claude
ca344e3762
fix: remove all non-ASCII characters that cause Windows encoding issues
- PIN input: removed literal bullet placeholder (was the reported gibberish)
- Search placeholders: ... -> ... (ASCII)
- Custom... period option in statement: uses … entity
- app.js/common.js/style.css/bar.js/cashier.js: en/em dashes in
  comments -> ASCII hyphens; Unicode minus sign U+2212 -> plain hyphen
- main.py: arrows and dashes in comments -> ASCII
- bar.html/cashier.html: standalone page titles and placeholders fixed

Intentional: pound sign in currency defaults stays as UTF-8 (served
with Content-Type: text/html; charset=utf-8 and correct HTTP headers).

https://claude.ai/code/session_01JuRTR5Xjx8emQsyerBgGU7
2026-05-31 03:48:14 +00:00
Claude
8450da6176
feat: period filter on statement page; add last_* periods everywhere
Statement page gains a period selector (same options as cashier widget)
in the print controls bar. Changing period reloads the page; custom
shows date pickers. Balance column reflects actual account balance at
each transaction by computing an opening balance before the period.
Period label shown in statement header.

Cashier stats widget gains: All time, Last week, Last month, Last
quarter, Last year options. _period_bounds extended with all last_*
variants and returns None for 'all' (callers skip the WHERE clause).

https://claude.ai/code/session_01JuRTR5Xjx8emQsyerBgGU7
2026-05-31 03:41:31 +00:00
Claude
7dfe66f5a1
feat: cashier stat widgets — outstanding credit and period summary
Adds two widgets at the top of the Cashier view:
- Total outstanding credit across all member accounts
- Transaction summary (top-ups / withdrawals / charges / net) with
  period selector: today, this week, month, quarter, year, or custom
  date range. Backed by new GET /cashier/stats endpoint that respects
  the configured display timezone for period boundaries.

https://claude.ai/code/session_01JuRTR5Xjx8emQsyerBgGU7
2026-05-31 03:37:15 +00:00
Claude
005d0ccd80
feat: searchable datalist for IANA timezone selector
Populates a <datalist> from Intl.supportedValuesOf('timeZone') on
startup, giving the timezone input native browser search/filter with
no extra dependencies. Degrades gracefully to plain text on browsers
that don't support the Intl API.

https://claude.ai/code/session_01JuRTR5Xjx8emQsyerBgGU7
2026-05-31 03:32:02 +00:00
Claude
8d1c2e4eb5
feat: hamburger menu for mobile nav
On screens ≤640px the tab row collapses; a hamburger button (Material
Symbols menu/close icon) appears in the nav-right area. Tapping it
opens a fixed full-width dropdown below the header with full-height
tap targets and a left accent border on the active item. Tapping a tab
or anywhere outside closes the menu. Desktop layout is unchanged —
nav-tabs uses display:contents so the buttons remain direct flex
children of nav.

https://claude.ai/code/session_01JuRTR5Xjx8emQsyerBgGU7
2026-05-31 03:30:45 +00:00
Claude
7a1a1a88d9
style: GitHub underline tabs with Material Symbols icons
Replace button-border nav tabs with full-height underline tabs:
active tab gets a white 2px bottom border against the dark header.
Icons from Material Symbols Outlined (filled when active, outlined
when inactive): group→Members, universal_currency_alt→Cashier,
point_of_sale→Bar, settings_applications→Admin.

https://claude.ai/code/session_01JuRTR5Xjx8emQsyerBgGU7
2026-05-31 03:27:56 +00:00
Claude
1f8fe21eaf
feat: two-step bar charge flow with full-screen PIN overlay
Staff enter amount and note, click Charge; a full-screen patron-facing
screen appears showing the charge total, member name, and a large PIN
input that auto-focuses. PIN errors stay on the overlay; Cancel returns
to the amount form so staff can adjust before handing the device back.

https://claude.ai/code/session_01JuRTR5Xjx8emQsyerBgGU7
2026-05-30 17:38:18 +00:00
Claude
a5b4ed8389
style: redesign UI to GitHub-esque aesthetic
Replaces the dark-navy nav and blue-tinted palette with GitHub's
design language: #24292f header, canvas-subtle body (#f6f8fa),
d0d7de borders, GitHub's font stack, green primary buttons, blue
accents, uppercase muted table headers, and focus rings.

https://claude.ai/code/session_01JuRTR5Xjx8emQsyerBgGU7
2026-05-30 17:33:58 +00:00
Claude
7b4e33254c
feat: admin-configurable default paper size for receipts/statements
Adds a Paper Size setting (A4/A5) to the General section of Admin
settings. Receipts and statements pre-select the configured size and
apply the correct @page margins; staff can still override per-print.

https://claude.ai/code/session_01JuRTR5Xjx8emQsyerBgGU7
2026-05-30 17:29:59 +00:00
Claude
b1fcc3dbe9
Add configurable display timezone; default to server's local timezone
- _server_timezone(): detects IANA timezone from /etc/timezone or
  /etc/localtime symlink at startup; used as the CONFIG default
- CONFIG: new "timezone" key set to server's detected timezone
- AppSettingsUpdate: new optional timezone field
- _display_tz(), _fmt_dt(), _now_display() helpers: convert stored UTC
  datetimes to the configured timezone for display; falls back to server
  local if the setting is empty or the zone name is invalid
- receipt(): transaction timestamp uses _fmt_dt() instead of raw UTC slice
- statement(): row timestamps and "Generated" line use _fmt_dt()/_now_display()
- Admin settings: Timezone text input (IANA name) in General section
- app.js: loadAdminSettings/saveSettings handle timezone field

https://claude.ai/code/session_01JuRTR5Xjx8emQsyerBgGU7
2026-05-30 16:59:54 +00:00
Claude
09df5efb07
Redesign receipts/statement to match spec; add logo upload
Receipts:
- Font size raised to 11pt base (labels 9pt, amounts 13pt bold)
- Each field now shows LABEL (small, uppercase, gray) above VALUE —
  two cells per row in a 1fr/1fr CSS grid, matching the provided samples
- Business header: left column = address lines, right column = Tel/Email/Web
- Charge receipt: STAFF+TRANSACTION / CHARGE+TIME / AMOUNT+BALANCE
- Top-up/Withdrawal receipt: STAFF+TXN / TRANSFER_TYPE+TIME /
  AMOUNT+BALANCE / TRANSFER_TYPE+TRANSFER_REF
- Print button moved into the paper-size controls bar

Statement:
- Reduced from 9 to 7 columns: Date, Reference, Type, Venue, Staff,
  Amount (+/-), Balance — removes the separate Charge/Credit split
- Amount shown as "+ £X.XX" (green) or "- £X.XX" (red)
- Sub-row shows "Transfer type: X — Ref" for top-ups/withdrawals,
  or the note text for charges

Logo:
- New POST /admin/logo endpoint: accepts image upload, saves to
  static/logo.{ext}, auto-updates logo_url setting
- New logo_max_width / logo_max_height config fields (default 200×80px)
- Admin branding section: file upload input + max-width/height fields
- python-multipart added to requirements.txt (needed for file upload)

https://claude.ai/code/session_01JuRTR5Xjx8emQsyerBgGU7
2026-05-30 16:55:23 +00:00
Claude
79ae833fa9
Replace literal em-dashes with &mdash; HTML entities
Fixes garbled display ("â€"") on Windows where the UTF-8 bytes for U+2014
were being misread as Windows-1252. All em-dash occurrences in index.html,
app.js, and common.js are now expressed as HTML entities.

https://claude.ai/code/session_01JuRTR5Xjx8emQsyerBgGU7
2026-05-30 16:42:46 +00:00
Claude
8616ff1d49
Add business branding, transaction references, and redesigned receipts
- CONFIG: add business address/contact, logo URL+alignment, venue names,
  transaction ref prefix, transfer types list, and 14 localizable receipt
  labels plus per-receipt-type footer fields
- DB: add transfer_type and transfer_ref columns to ledger_entries
  (init_db + migrate_db); fix duplicate return in pos_user
- Pydantic: extend AppSettingsUpdate with all new settings;
  add transfer_type/transfer_ref to TopupRequest and WithdrawalRequest
- /topup and /withdrawal: persist transfer_type and transfer_ref
- /config: return transfer_types as parsed array for frontend dropdowns
- New helpers: _txn_ref(), _logo_html(), _biz_header_html()
- New RECEIPT_CSS with 2-column grid layout; receipt() fully redesigned
  with business header, auto-generated TXN reference, separate charge vs
  top-up/withdrawal layouts; statement() adds Reference column and
  transfer-detail sub-rows
- index.html: Transfer Type + Transfer Reference fields on cashier/
  withdrawal panels; admin settings expanded into organized sections
  (General, Business Address, Branding, Transactions, Receipt Labels,
  Receipt Footers)
- app.js: populateTransferTypes() called on startup and after settings
  save; doTopup/doWithdrawal send transfer fields; clearCashierSelection
  clears new fields; loadAdminSettings/saveSettings handle all new fields

https://claude.ai/code/session_01JuRTR5Xjx8emQsyerBgGU7
2026-05-30 15:44:27 +00:00
Claude
21b6791f4c
Move overdraft override to per-member setting on member record
Instead of a per-transaction checkbox at point of sale, the overdraft
override is now a persistent flag on each member, set via the Edit Member
modal by the appropriate role.

Schema:
- members.overdraft_override INTEGER DEFAULT NULL
  (NULL = follow global policy, 1 = explicitly allowed, 0 = explicitly blocked)
- migrate_db(): ALTER TABLE members ADD COLUMN ... for existing databases

Charge logic (combining global policy + member flag):
- never:          always block (member flag ignored)
- always:         always allow (member flag ignored)
- staff_override: allow only if member flag = 1
- admin_override: allow only if member flag = 1 (only admin can set it via UI)
- staff_block:    allow unless member flag = 0 (explicitly blocked)

Edit Member modal:
- Shows "Allow overdraft for this member" for staff_override / admin_override
  (admin_override hidden from non-admins)
- Shows "Block overdraft for this member" for staff_block
- Hidden for never / always policies
- Checkbox state pre-populated from current member.overdraft_override value

Removed the per-transaction barOverrideRow that was added in the previous
commit — it has been superseded by this per-member approach.

https://claude.ai/code/session_01JuRTR5Xjx8emQsyerBgGU7
2026-05-30 15:08:19 +00:00
Claude
7fa74963b0
Replace overdraft checkbox with 5-option policy dropdown
Policies:
- never            – not allowed (default)
- always           – allowed for all charges
- staff_override   – default no; staff sees checkbox to allow per charge
- admin_override   – default no; only admins see the allow-per-charge checkbox
- staff_block      – default yes; staff sees checkbox to block per charge

Backend:
- CONFIG: allow_negative_balance → overdraft_policy: "never"
- migrate_db(): converts old allow_negative_balance setting in app_settings
  to the equivalent overdraft_policy value on first startup after upgrade
- ChargeRequest: new optional overdraft_override: bool = False
- POST /charge: full policy logic; admin_override enforced server-side
  so a non-admin can't bypass it by sending override=true
- POST /admin/settings: validates policy value before saving

Frontend:
- Admin settings: checkbox replaced by <select> with five options
- Bar form: barOverrideRow (hidden by default); selectBarMember() shows
  it with the right label when policy is staff_override / admin_override
  (admin only) / staff_block; hidden for never and always
- clearBarSelection() resets the override checkbox and hides the row

https://claude.ai/code/session_01JuRTR5Xjx8emQsyerBgGU7
2026-05-30 14:39:43 +00:00
Claude
acd8ff3fd0
Add withdrawal feature to cashier tab
Cashiers can process credit withdrawals (cash-back) for members:
- Requires member PIN to authorize
- Always checks sufficient balance (overdraft not allowed for withdrawals)
- Appears as 'withdrawal' type in ledger, statement, and on receipt
- Receipt opens automatically, same as top-up/charge

Backend:
- migrate_db() now also recreates ledger_entries with type constraint
  extended to include 'withdrawal' (existing rows preserved)
- POST /withdrawal endpoint (cashier_user required)
- Receipt label map updated to include Withdrawal

Frontend:
- Cashier tab now shows two panels side-by-side: Top Up and Withdrawal
- Each panel has its own message area so they don't overwrite each other
- Withdrawal panel includes PIN field; top-up panel unchanged

https://claude.ai/code/session_01JuRTR5Xjx8emQsyerBgGU7
2026-05-30 14:31:16 +00:00
Claude
68a35e5bff
Split staff role into cashier/pos-staff; fix admin tab visibility
Roles:
- cashier: Members + Cashier (top-up) tabs; POST /topup enforced at API level
- pos-staff: Members + Bar (charge) tabs; POST /charge enforced at API level
- admin: all tabs including Admin panel

Changes:
- migrate_db() recreates staff_accounts with new CHECK constraint and
  converts any existing 'staff' rows to 'pos-staff' on startup
- cashier_user / pos_user FastAPI dependencies added; applied to /topup and /charge
- Role dropdowns in admin panel updated to the three new values
- startApp() hides irrelevant tabs per role on login
- doLogout() resets all tab visibility so the next login starts clean
- fmtRole() formats role names ('pos-staff' → 'POS Staff') in the accounts table

https://claude.ai/code/session_01JuRTR5Xjx8emQsyerBgGU7
2026-05-30 14:19:41 +00:00
Claude
803d157d25
Fix login overlay visible on reopen when session is still active
The login overlay lacked the `hidden` class by default, so on every page
load it was visible and blocking the app. On fresh load with a valid session,
boot() skips showLogin() (no submit handler added), leaving the overlay
covering the screen with a non-functional form. Adding `hidden` to the HTML
and explicitly hiding the overlay in startApp() fixes both paths.

https://claude.ai/code/session_01JuRTR5Xjx8emQsyerBgGU7
2026-05-30 13:42:41 +00:00
Claude
a5c9af1ca6
Add staff auth, admin area, currency decimal input
Auth system
- staff_accounts table: name, username, bcrypt password, role (staff|admin)
- Session tokens in memory (8-hour TTL), httpOnly cookie
- POST /auth/login, /auth/logout, GET /auth/me
- All API endpoints now require a valid session
- Default admin account seeded on first run (admin/admin), printed to console
- Staff name for transactions comes from the session, no more dropdown

Currency input fix
- Amount inputs are now decimal (step=0.01); users enter 1.00 not 100
- Frontend multiplies by cfg.currency_divisor before POSTing
- TopupRequest/ChargeRequest no longer include staff_name (from session)

Admin area (4th tab, admin role only)
- App Settings: club name, currency symbol, major/minor unit names,
  divisor, min/max topup, max charge, receipt footer, allow overdraft
- Settings persisted in app_settings DB table; merged with CONFIG defaults
  at startup and refreshed after each save
- Staff Accounts: list with edit modal (name, username, password, role,
  active flag) and delete; Add Account inline form
- /admin/settings GET/POST, /admin/staff-accounts CRUD
- /config endpoint exposes live settings to frontend on every page load

receipt_footer field rendered on both receipt and statement print views

https://claude.ai/code/session_01JuRTR5Xjx8emQsyerBgGU7
2026-05-30 09:19:07 +00:00
Claude
79d51973cd
Remove product search from bar tab
Removed product search field, results list, barProductLookup(),
and selectProduct() from the bar view in index.html, bar.html,
app.js, and bar.js. Backend /products endpoints are unchanged.

https://claude.ai/code/session_01JuRTR5Xjx8emQsyerBgGU7
2026-05-30 08:52:18 +00:00
Claude
6c155d00bb
Restore three-view SPA; add member edit and delete
- / now serves index.html (three-view SPA: Members, Cashier, Bar)
- /cashier and /bar remain as standalone pages (unchanged)
- Members view: Edit button on every row opens a modal to update
  name, member number, and optionally PIN. Delete button only appears
  when balance is exactly 0; confirmation dialog before deletion removes
  the member and their ledger entries.
- PUT /members/{id}: updates any combination of name/member_number/pin;
  guards against duplicate member numbers.
- DELETE /members/{id}: rejects with 400 if balance != 0, otherwise
  deletes ledger entries then member row.
- Modal styles added to style.css; app.js rebuilt as combined SPA script
  (loads common.js for shared helpers).

https://claude.ai/code/session_01JuRTR5Xjx8emQsyerBgGU7
2026-05-30 08:33:44 +00:00
Claude
34b3e88fe2
Fix 1-4: staff dropdown, split pages, print size toggle, receipts
Fix 1: Replace free-text staff name with a dropdown populated from staff.json
  via GET/POST/DELETE /staff endpoints. Staff management panel on cashier page
  (type name, Add button, chip list with × remove). Dropdown remembers last
  selection per session via sessionStorage.

Fix 2: Split single-page app into /cashier (register + top-up + member list +
  staff management) and /bar (charge only). Each page is its own HTML file
  with two plain <a> nav links; / redirects to /cashier. Shared helpers
  extracted to common.js; page logic in cashier.js and bar.js.

Fix 3: Statement view gains an A4/A5 radio toggle that rewrites a dynamic
  <style> @page rule before the browser print dialog opens. Defaults to A4.

Fix 4: POST /topup and POST /charge now return entry_id. Each successful
  transaction opens /receipt/{entry_id} in a new tab — server-rendered HTML
  showing member name/number, type, amount, balance-after (computed as running
  sum up to that entry), staff, note, timestamp. Same A4/A5 print toggle.

https://claude.ai/code/session_01JuRTR5Xjx8emQsyerBgGU7
2026-05-30 06:28:54 +00:00
Claude
fa4884bdb4
Add store-credit web app (FastAPI + SQLite)
- main.py: single-file backend with Member, LedgerEntry, Product models;
  endpoints for register, list/search members, topup (cashier), charge (bar,
  PIN-verified), transaction history, printable HTML statement, product CRUD.
  All monetary values stored as integers (pence). bcrypt PIN hashing.
  Admin-tunable CONFIG dict at top of file.
- static/index.html + style.css + app.js: three-view SPA (Members, Cashier,
  Bar) with member search, product search with member-price support, and
  XSS-safe rendering throughout.
- requirements.txt: fastapi, uvicorn, bcrypt only.

https://claude.ai/code/session_01JuRTR5Xjx8emQsyerBgGU7
2026-05-30 05:21:01 +00:00