Skip to content

Repository files navigation

Kent Adisyon

A lightweight, web-based prepaid tab system for a single group. Load a prepaid balance, sell products against it, and watch the remaining balance, consumption, and profit update in real time. Built for a small hotel / guesthouse setting where one group runs a shared tab over a stay — group total only, no per-person tracking.

“Adisyon” is Turkish for a bar/restaurant tab or check.

Features

  • Sales (/) — Category-grouped product cards, a live cart, the remaining balance, and a low-balance warning. If a cart exceeds the balance it warns but still allows the sale (the balance is allowed to go negative).
  • Products (/products) — Add / edit products (name, category, cost, price, optional stock). Products are never hard-deleted — they're deactivated, so historical sales stay intact.
  • Balance (/balance) — Top up the prepaid balance (cumulative), browse top-up history, and configure settings (group size + low-balance threshold).
  • Stats (/stats) — Day / week / month / all-time ranges: revenue, cost, profit, per-person average, top products, category breakdown, a daily revenue trend, and voided sales.
  • Auth — A single shared password protects the whole app via a signed, HTTP-only cookie. A lightweight proxy guards every page and API route.

Tech stack

Next.js 16 (App Router) · TypeScript · Prisma 6 + PostgreSQL · Tailwind CSS v4 + shadcn/ui (Base UI primitives) · Zod. Fully responsive and mobile-first — a bottom-sheet cart on phones, a sticky sidebar on desktop.

How it works (design notes)

  • Money is never a float. All amounts are stored as Decimal(10,2) (Turkish Lira, ₺). Authoritative sums are computed in SQL and returned as exact decimal strings, never passing through a JavaScript float.
  • Derived values aren't stored. Remaining balance, revenue, and profit are always queried — never cached in a column that could drift.
  • Sales snapshot price & cost. Each sale copies the product's price and cost at the moment of sale, so changing a product's price later never rewrites the profit of past sales.
  • Stock is optional per product. Leave stock empty and it's untracked; otherwise it decrements on sale and is restored on void.

Local development

Requires Node 20+ and Docker.

# 1) Install dependencies
npm install

# 2) Start local PostgreSQL (host port 5434)
docker compose up -d

# 3) Environment variables
cp .env.example .env        # DATABASE_URL is ready; set APP_PASSWORD and AUTH_SECRET

# 4) Schema + sample data
npm run db:migrate          # apply migrations
npm run db:seed             # sample products + default settings

# 5) Run
npm run dev                 # http://localhost:3000  (password: .env > APP_PASSWORD)

Useful scripts

Script Description
npm run dev Development server
npm run build / npm start Production build / start
npm run db:migrate prisma migrate dev
npm run db:seed Load the sample data
npm run db:studio Browse the data in Prisma Studio
npm run db:reset Reset the DB + migrate + seed

Environment variables

Variable Description
DATABASE_URL PostgreSQL connection string
APP_PASSWORD The single shared password to sign in
AUTH_SECRET Long random string used to sign the session cookie

Deployment

Ship it as a Docker image — the repo includes a production Dockerfile. On every deploy it applies pending migrations (prisma migrate deploy) and then starts the server on port 3000.

It runs on any Docker host. The steps below are for Coolify, which is what it was built for:

  1. Database: create a PostgreSQL resource and copy its internal connection URL.
  2. Application: connect this repo and choose Build Pack: Dockerfile. (The bundled docker-compose.yml is for local development only — don't use it in production.)
  3. Environment variables:
    • DATABASE_URL — the Postgres internal URL
    • APP_PASSWORD — login password (pick a strong one)
    • AUTH_SECRET — long random string: openssl rand -base64 32
  4. Port & domain: port 3000 is auto-detected. Assign a domain; HTTPS is required in production because the session cookie is Secure.
  5. Health check (optional): path /api/health.
  6. Deploy. Migrations run automatically on first boot once the DB is reachable.
  7. Sample data (optional): migrations create the schema, but seeding does not run automatically in production. Run npm run db:seed once from the app shell for the demo products, or just add products in the UI.
  8. Sign in with APP_PASSWORD.

Run a single instance — multiple concurrent instances could race on migrations.

Notes

  • Currency & locale. Amounts are Turkish Lira (₺), formatted in en-US; dates use the Europe/Istanbul time zone. These are centralized in lib/money.ts, lib/datetime.ts, and lib/stats.ts if you want to change them.
  • Prisma is pinned to v6. v7 drops url = env() from schema.prisma and requires a prisma.config.ts + a driver adapter; unnecessary surface area for a tool this size, so the upgrade was deliberately skipped.

License

MIT © 2026 HFerrahoglu

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages