A multi-store, Philippines/BIR-oriented pizza POS system: a WPF desktop client (offline-first, syncs to a shared backend) plus a Node/Express + Postgres backend. See ROADMAP.md for the full build history and design decisions, and backend/README.md for backend-specific details.
This doc is for testers and other developers who need to get the app running on their own machine — not for end users of a finished install.
backend/— Node/Express + TypeScript REST API in front of a Postgres database (a Supabase-hosted Postgres works as-is). Handles auth (RS256 JWT) and sync (pull/push) for every store-scoped table.desktop/— WPF (.NET 10) desktop client. Offline-first: keeps a local SQLite cache and syncs against the backend every ~20s, but the Cashier screen keeps working if the backend is unreachable.
Every terminal talks to the same backend, so multiple testers/machines can use the app against one shared backend at once (see the multi-terminal conflict handling in ROADMAP.md if you're curious how simultaneous edits are resolved).
| Component | Requirement |
|---|---|
| OS (desktop client) | Windows 10/11, 64-bit — the client is WPF and Windows-only |
| .NET SDK | .NET 10 SDK (not just the runtime — you're building from source, not running a published binary) |
| Node.js | 20.x or newer (backend only) |
| Database | A reachable Postgres instance — a free Supabase project works without changes |
| Git | Optional — only needed if you're pulling this project via a Git remote rather than a shared folder/zip |
The backend can run on any OS (Windows/macOS/Linux) — only the desktop client requires Windows.
Copy/clone the whole project folder (backend/ and desktop/ together) — the desktop client talks to a backend, so you need both unless someone's already hosting a backend for you to point at (see step 3 for that shortcut).
cd backend
npm install
cp .env.example .env # fill in DATABASE_URL — a Supabase connection string works as-is
npm run generate-keys # creates keys/private.pem + keys/public.pem for RS256 JWT signing
npm run migrate # applies migrations/, creating all tables
npm run devLeave this running — it serves on http://localhost:3000 by default (see backend/README.md for endpoint details). Confirm it's up with curl http://localhost:3000/health.
If someone else is already hosting a backend for you to test against (e.g., a shared dev/staging server), skip this whole section — you just need its base URL for step 3.
cd desktop
dotnet build SinagtalasPos.Desktop/SinagtalasPos.Desktop.csprojThe client points at http://localhost:3000 by default. If your backend is running somewhere else (a shared server, a different port), set an environment variable before launching:
# Windows PowerShell
$env:SINAGTALAS_API_BASE_URL = "http://your-backend-host:3000"
# Git Bash
export SINAGTALAS_API_BASE_URL="http://your-backend-host:3000"Then run it:
cd SinagtalasPos.Desktop
dotnet runThe first launch on a machine creates a fresh, empty local database at %LocalAppData%\SinagtalasPos\pos.db and pulls down store/product/staff data from the backend on first sync — no manual DB setup needed on the desktop side.
Use one of the seeded demo accounts (see DEMO_ACCOUNTS.md for the full list and what each role can see) — for example:
| Username | Password | Role |
|---|---|---|
demo_cashier |
CashierDemo123 |
Cashier |
demo_manager |
ManagerDemo123 |
Manager |
demo_admin |
AdminDemo123 |
Admin |
These are real accounts on whatever backend you're pointed at (not local-only fakes), so they only work once that backend has been migrated and seeded.
- Login → Open Shift. A fresh login always prompts to open a shift before reaching the Cashier screen, unless that staff member already has one active (e.g., resuming after closing the app mid-shift).
- End Shift signs you out. Ending a shift returns to the Sign-in screen rather than immediately prompting to open another one — this is the intended shared-terminal flow (cashier A ends and signs out, cashier B signs in and opens their own shift).
- Offline is expected to work. If you stop the backend while the desktop client is running, the Cashier screen keeps functioning — writes queue locally and drain automatically once the backend's reachable again.
- There's no installer/published build yet — testers run
dotnet runfrom source like a developer would. If you need a standalone.exeto hand someone without the .NET SDK, seedotnet publish(self-contained) — not currently scripted here. - The receipt printer defaults to a file sink in dev (no physical ESC/POS printer required to test checkout) — see the top status bar's printer indicator to confirm which mode is active.
- If you hit "can't reach server," check the backend terminal is still running — killing
node.exeprocesses broadly (e.g., during cleanup) takes the backend down along with anything else running on Node.