Commit Graph

66 Commits

Author SHA1 Message Date
type-two
ef4769784f feat: Woo customer sync (non-destructive, in refresh.sh) + AusPost rates mirror & quote calc
customer_woo_sync.py: WooCommerce online customers (wc_customer_lookup+billing) → RecordGod
customer CRM, links-by-email/refresh-by-wp_user_id/insert-new, never clobbers POS rows; added
as refresh.sh step 5 (after the --clean push). shipping_sync.py mirrors disc_post_flat_rates
(+zones) → post_flat_rate/post_zone. /sales/shipping/quote (units|weight_g → 280g/record →
≤5kg parcels → flat rate × parcels, Parcel Post + Express). POS Register '📦 post' quotes the
cart + adds a postage line. AusPost +5% lands 2026-07-01 — re-pull rates then.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-24 00:55:56 +10:00
type-two
4c152954b5 fix(shop): key YouTube dead-cache by (release_id, uri) — mirrored video table has no id col
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-23 23:47:42 +10:00
type-two
988e6437cb feat(shop): per-track Apple previews (lazy iTunes) + jog mini-player + dead-YouTube check
/shop/audio-tracks/{id}: resolves each track's preview from its Apple song id via iTunes lookup
(au), cached in disc_release_track.apple_preview. /shop/audio validates YouTube via oembed (200=live),
caches dead in disc_release_video.dead, returns one live id. release.html + kiosk.html get a per-track
▶ on each tracklist row feeding a mini-player with a jog/seek bar (falls back to a single Listen).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-23 23:44:17 +10:00
type-two
d6e1e15982 feat(shop): storefront release page gets ▶ Listen too (Apple preview / YouTube / Beatport)
Same audio affordance as the kiosk on release.html — Apple 30s preview (instant audio),
else YouTube embed, else Beatport/Bandcamp embed, + country-scoped Apple Music link.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-23 23:22:46 +10:00
type-two
aa56e874ad feat(shop): country-scoped Apple ids in audio — AU-preferred music.apple.com link + clean preview
audio_sync mirrors apple_id ({cc:album_id}) + apple_song_id (kept raw, country preserved).
/shop/audio parses it: AU>US>first → music.apple.com/{cc}/album/{id}, per-track preview array
→ first clean 30s url. Kiosk Listen uses apple_preview + an Apple Music ↗ link.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-23 23:17:06 +10:00
type-two
7e9d5028b0 fix(shop): apple_preview_url is a JSON array — return the first clean preview URL
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-23 23:12:39 +10:00
type-two
16c85184cd feat(shop): wire audio/video previews — kiosk Listen (Apple 30s / YouTube / Beatport)
audio_sync.py mirrors WowPlatter disc_release_audio (22.5k; apple_preview_url, beatport_embed,
bandcamp…) + disc_release_video (231k YouTube) into Postgres. /shop/audio/{id} returns playable
sources. Kiosk detail gets a ▶ Listen: Apple 30s preview (instant <audio>), else YouTube embed,
else Beatport/Bandcamp embed; stops on close/idle.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-23 23:10:02 +10:00
type-two
bab0826fea feat(shop): in-store kiosk page — touch browse, locate-in-store, scan-to-buy QR
site/kiosk.html: full-screen themed kiosk (idle attract cover-wall → tap → browse with
big search + genre chips + record grid → detail with tracklist, in-store locator (crate ·
rack) and a QR to buy on the customer's phone). 75s idle returns to attract. /shop/release
copies enriched with crate/rack location (public-safe). Route /kiosk. Better than WowPlatter's
kiosk: locate + phone-QR front and centre. Audio previews deferred (no internal audio yet).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-23 23:01:54 +10:00
type-two
d6c08f05ce feat(nav): rotate diagram button — 90° view rotation (rack + store), text stays upright
ST.viewRot quarter-turns: drawRack remaps cells (rcell), edge labels (BACK/FRONT/L/R) and
facing-arrow glyphs per rotation while keeping all text upright; drawStore rotates rack
positions+yaw around centre with a matching unrotPt() so rack drag-to-move stays correct.
🔄 rotate button in the toolbar. Lets the diagram match where you're physically standing.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-23 21:55:51 +10:00
type-two
e20be06bf0 feat(nav): Quick Insert + click-to-pick insert slot (insert-with-shift)
/nav/insert: drop one record (release id/sku/barcode) into a crate at a chosen slot,
shifting items at>=slot down by one. Scanner: click a contents row to set the insert
anchor (before/after toggle) — drives both the bulk 'Insert at picked slot' mode and a
new Quick Insert one-record panel. Picked row highlighted; anchor is per-crate.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-23 19:45:42 +10:00
type-two
a57b74700c feat(admin): System tab — read-only Postgres health (db size/version/uptime/conns/cache-hit, largest tables, data freshness)
Admin-only /admin/system; no maintenance buttons (PG autovacuums, infra owns backups).
The useful 10% of WowPlatter's System page; the WP cache/transients/WP-CLI/debug rest dropped.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-23 18:44:44 +10:00
type-two
6a95efb9bb fix(report): count paid+completed sales (migrated=paid), fix hour :00 bind-param, add format breakdown
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-23 18:26:57 +10:00
type-two
336d525585 feat(admin): comprehensive lazy dashboard + reports workbench (Chart.js)
Dashboard: fast KPI tiles (instant /stats) + lazy 30-day revenue line + stock-by-format
donut. Reports rebuilt into a parameterized workbench — date presets (7d/30d/90d/12mo/all
+ custom from/to) × dimension (trend/genre/style/format/condition/payment/weekday/hour +
top releases/artists/labels/customers) × metric (revenue/count), each chart fetched ON
DEMAND. New /admin/report/{kpis,series,breakdown,top,stock} (whitelisted dims, bound dates).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-23 18:22:17 +10:00
type-two
4ab9c09899 feat(pos): Square Terminal payments — push checkout to a paired terminal
app/square.py (Terminal Checkout API: device-code pairing, create/poll/cancel checkout;
creds in vault, device_id in sales_setting). /sales/terminal/{status,pair,pair/{id},
checkout,checkout/{id},checkout/{id}/cancel}. POS: 📟 Terminal payment method (shown when
paired) → pushes total to the terminal, polls, finalises the sale only on COMPLETED;
Settings → Pair a terminal (device-code flow). square_* added to admin Connections.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-23 17:57:26 +10:00
type-two
5673ee125d fix(sales): register GET /{sale_id} last so /settings /customers /past /discounts resolve
The 1-segment parametric route was swallowing the literal GET routes (int-parse 422,
silently masked before). Moved it after all literals.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-23 17:38:16 +10:00
type-two
bf08cd9b35 feat(pos): receipt logo + Monster Robot template (store name/address/footer)
receipts.py renders store_logo (img) above the title; store_logo added to sales
settings (get/save) + a Logo URL field in POS Settings. Logo hosted at
site/receipt-logo.webp (Monster Robot, from WowPlatter receipt_templates). Print
window now waits for window.onload so the logo prints. Deploy via /opt/recordgod/deploy.sh.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-23 17:33:08 +10:00
type-two
5351cfb9c2 feat(pos): rebuild Sales — 3 tabs, smooth checkout + tender/change, print+email receipts
7 tabs → Register / History / Settings. Register: search/scan + cart + checkout flow
(cash tender→change, split tenders, layby deposit, notes) → completes → receipt auto-pops.
Receipts now server-rendered (app/receipts.py, one template) with per-item discounts, GST
breakdown, split lines, tendered/change, store header/footer; Print opens a clean window
(no visibility hack); Email via app/mailer.py (stdlib SMTP, creds in vault, admin-only) with
a Settings 'send test' button. History merges past sales + laybys (reprint/collect). Settings
holds currency/GST/discount + receipt header/footer + promotions. sales gains amount_tendered
+ notes; SaleIn carries tendered/notes; /sales/{id}/receipt + /{id}/email + /email-test added.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-23 16:29:42 +10:00
type-two
b46930d468 feat(staff): time clock — clock on/off = shift = timecard
staff_shift table (one open shift per staff via partial unique index). /admin/clock
(status+today total), /clock/in (idempotent), /clock/out; /admin/timecards (admin)
= per-staff hours + recent shifts over a window. search.html top bar gets a live
clock widget (🟢 On 2h14m · Clock off · Log out; Clock on when off) ticking every 30s;
Log out warns if still clocked on. admin Staff tab gains a Timecards report.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-23 15:40:02 +10:00
type-two
5e2a9e7ab1 feat(auth): staff accounts + roles — operators run the store, admins keep the keys
Token resolver now checks the env owner-token (admin) then a staff table (name/token/
role). New require_admin gates /settings/* (API keys/connections) + staff CRUD; everything
operational (search/scan/inventory/labels/intake) stays on require_token so staff can do it.
admin.html: /admin/me drives ROLE; data-admin nav (Staff + Connections) hidden for staff +
go() blocks the views; new Staff tab to add operators, set role, reset/show token once,
deactivate. Tokens shown once on create.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-23 15:33:19 +10:00
type-two
13e5200b8d feat(nav): colour crates by type in the rack diagram (material_color)
/nav/rack returns each crate's virtual_crate_type.material_color + type name;
drawRack fills/strokes/arrows each crate in its real colour (Blue Crate #0000FF,
Wooden Rack Bin #BA906A, White Tub #FFFFFF, Black Tub #191970, Shelf #C0C0C0…)
instead of all-blue. Legend lists the crate types present on the rack.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-23 11:26:34 +10:00
type-two
cba339151f feat(nav): Crate Info panel — edit label_text + rotate crate.direction in /search
Per-crate panel under Crate Contents: rename (label_text) + a Facing segment
(↑back/↓front/←left/→right) that rotates the crate live — the map arrow flips on
click. CrateEditIn gained 'direction'. Restores the crate editing my tabbed rewrite
dropped.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-23 10:51:41 +10:00
type-two
6b679152bc feat(nav): reorganize targets = picked crates sorted (editable) + per-crate landing summary
Sources stay the pick-ordered crates; targets default to those same real crate IDs
sorted ascending (4,3,2,1 → pour A–Z into 1,2,3,4), shown in an editable field so you
can consolidate/spread. Plan now shows where items land per crate. Drops the Start+Count
inputs (which could target non-existent crate IDs and orphan items).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-23 10:15:50 +10:00
type-two
c4b544373d feat(nav): unified tabbed Search & Inventory workstation (WowPlatter parity)
One persistent central store/rack map with a top tab strip (Scanner / Returns /
Reorganize / Collections / Stock Finder) all driving the shared map, + Store/Rack/
Crate/Item view buttons and an omni scan bar (release/SKU/c12/r3). Reorganize: click
crates IN ORDER (numbered pick badges feed the A–Z distribution planner) + drag racks
to move them on the store map (persists pos_x/pos_z; RackEditIn gained position fields).
Collections edit highlights picked crates on the map. Returns = stub (manual path via
Scanner + Release Info for now).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-23 09:58:18 +10:00
type-two
48f8579bff feat(shop): P4 wantlist — public 'request a record' page + admin queue
Storefront /wantlist form (themed, email-keyed, guest-friendly) posts to
/shop/wantlist → wantlist table (lazy DDL). Release pages deep-link to it
(prefilled; 'not in stock — request this record' when no copies). Admin gets
a Wantlist tab: pending/found/fulfilled queue with one-click status actions.
Default storefront menu → Records + Wanted.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-23 01:29:00 +10:00
type-two
28a46615a5 feat(shop): entity browse pages — artist/label/genre/style storefront views
records.html reads /artist|label|genre|style/{val} from the path, sets the
filter + an entity header (artist/label name via /shop/{kind}/{id}). /shop/browse
gains artist/label EXISTS filters + primary artist_id; release page links
artist→/artist/{id} and genre/style pills→/{kind}/{name}.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-23 01:18:15 +10:00
type-two
386343cdd3 feat(builder): P1 page editor — live preview now renders a REAL release from the catalog (real covers/tracklist) so block-arranging is WYSIWYG; + View-live link
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-23 01:00:28 +10:00
type-two
fcc3d7b2ff feat(storefront): P0 — public Browse (faceted release catalog) + Release detail pages + /shop/{browse,facets,release} API; Woo-transacted
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-23 00:37:04 +10:00
type-two
0269decb87 feat(search): editable Rack Info (name) + Reorganize planner UI (scan source crates → filter → A-Z distribute into target crates → review → apply)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-22 23:58:10 +10:00
type-two
83466389af feat(scanner): scan-to-slot assigner (replace/append/prepend/insert + Test preview + recover-last) + editable crate info (name/label); hdr Content-Type fix
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-22 23:46:14 +10:00
type-two
19c6e03c70 feat(search): clickable crate contents → Release Info panel (cover/label/format/country/year/genre/style + all copies + locate); crate genre/style + last-scanned + compress
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-22 23:40:06 +10:00
type-two
d6e75667bf feat(customers): CRM — list with spend/orders/last + record page (overview stats, editable info, mailing opt-in, sales history)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-22 23:17:11 +10:00
type-two
38c6714184 feat(print): label print (artist/title/price/SKU/QR, Dymo 54x25mm) + optional RFID chip program (Chafon H102 via local :7790 daemon)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-22 22:55:31 +10:00
type-two
a817b9d163 feat(import): bulk stock-in — resolve Release ID/barcode/catno against disc_release → create inventory (UI + endpoint)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-22 22:48:43 +10:00
type-two
c5ce84de1c feat(inventory): management UI — row edit modal, add-item, delete, bulk-select actions, webp thumbs
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-22 22:45:37 +10:00
type-two
4ece321b76 feat(inventory): CRUD backend (add/edit/delete/bulk) — first cream from the WowPlatter audit; + migration audit doc
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-22 22:39:17 +10:00
type-two
0b679ba079 fix(images): /img/r serves local copy else 302 to discogs webp (server-side fetch is CF-blocked); drop unused pillow
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-22 21:48:08 +10:00
type-two
ddaa6ecedf feat(images): RecordGod WebP cover cache (/img/r/{release_id}) — same-VPS, configurable DISC_IMAGE_DIR, no DealGod dep
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-22 21:45:31 +10:00
type-two
af3a611667 feat(collections): interactive editor — click-map crate select, genre/style/price/year rules, live match stats, priority
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-22 21:33:15 +10:00
type-two
10af3464ac feat(disc): mirror WowPlatter Discogs metadata into recordgod (disc_sync.py) + locator matches on collection genre/style ids
- disc_sync.py: MariaDB clone -> recordgod disc_* (30.8k releases, genre/style/label/format/identifier/artist/track/master), keeping WowPlatter's own genre/style ids
- locator reads disc_release.genre_ids/style_ids + parses collection 'ID,0' id format
- images stay with DealGod; masters sparse (backfill from Ultra later)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-22 21:19:47 +10:00
type-two
a1b4538954 fix(collections): is_active is int not bool — cast in queries
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-22 20:58:50 +10:00
type-two
879a58992f feat(collections): Collections CRUD + fallback locator + Reorganize scan/apply/compress API + Collections UI
- collections_routes.py: /nav/collections CRUD over virtual_collection + /nav/locate (exact-then-rules)
- navigator_routes.py: /nav/reorg/{scan,apply,compress} (atomic re-file)
- disc_cache gets year/genre_ids/style_ids cols for rule matching (backfill TODO)
- /search: collections list + locator fallback shows the should-be-here zone for unfiled hits

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-22 20:56:34 +10:00
type-two
0767778931 fix(store): WowPlatter coordinate convention in webstore renderer
- racks/portals/camera corner-relative (worldX = -W/2 + x); attach_wall snap + clamp
- crates parent to their rack_level (slot-grid or local pos offset), facing composed on rack
- free crates / decals / lights stay centered
- fixes the corner pile-up; booth-room overhead render verified

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-22 19:57:17 +10:00
type-two
fb533a7701 fix(nav): store view shows functional racks only (rack_purpose<>prop), truncate labels
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-22 19:38:29 +10:00
type-two
53b1a64a48 feat(nav): light/dark switch every page + store→rack→crate navigator
- nav.js owns a light/dark toggle (persisted, default light) across all owner pages
- /nav API: spaces, store-layout, rack/{id}, crate/{id} over migrated virtual_* geometry
- /search rebuilt as Store→Rack→Crate drill-down (true rack rotation, per-level slot grid,
  crate facing arrows + BACK/FRONT/L/R, search→locate)
- facing convention (directionYaw, slot-grid, rotatedArrows, world composition) in RECORDGOD_NAVIGATOR_PLAN.md
- no-cache HTML routes so deploys show instantly

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-22 19:33:24 +10:00
type-two
180615c7e0 ui: light theme for Search + POS (shop-admin readability) 2026-06-22 19:01:07 +10:00
type-two
eb502efaf4 feat(pos): full Sales console + migrate customers/discounts/settings 2026-06-22 02:40:35 +10:00
type-two
584fd140ad feat: Search/locate workstation + Search & Sales in nav (cache-bust nav.js) 2026-06-22 01:57:54 +10:00
type-two
6dd893fd01 feat: owner login landing + Sales POS + Sales in nav 2026-06-22 01:49:00 +10:00
type-two
80ff669cac feat(pos): Sales tab — ring-up, discounts, payment plans, holds, locate 2026-06-22 01:43:14 +10:00
type-two
e8e526f48e feat: unified owner nav bar across admin/builder/dash 2026-06-22 01:22:25 +10:00