diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index b4cce45..b3a4f64 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,100 +1,107 @@ -# Contributing +# Contributing to StockSense -Four people, seven hours, one `main`. These rules exist so nobody spends hour 6 resolving merge conflicts. +Thanks for helping. This guide covers the workflow we use and the rules that keep the stock numbers correct. -## 1. Golden rules -1. **Edit only files you own** ([ownership map](docs/TEAM_PLAN.md#file-ownership)). CODEOWNERS will auto-request the owner's review if you don't. -2. **Never commit to `main` directly.** Work on your branch, merge through a PR. -3. **Rebase often** (about every 45 minutes) and push small PRs early. -4. **The API contract is frozen** ([docs/API.md](docs/API.md)). Changing a field name means telling the team and updating the doc in the same PR. -5. **No stock arithmetic in the frontend.** Stock changes only through `POST /api/operations/{id}/validate`. -6. **Commit from your own GitHub account.** The judges look at who did what. +## Ground rules -## 2. Branches +1. **Stock changes only through operations.** Nothing in the frontend, a script or a new endpoint may write `stock_balances` or `stock_ledger` directly. All changes go through `backend/app/services/operation_service.py`, inside one transaction. See [Inventory rules](docs/INVENTORY_RULES.md). +2. **No stock arithmetic in the browser.** The frontend shows what the API returns. Display-only previews (such as a count difference) are fine; the server computes the real result. +3. **The API contract is documented.** If you add or rename a field or endpoint, update [docs/API.md](docs/API.md) in the same pull request. +4. **The database enforces the rules too.** When you add a model or a rule, add the matching constraint (CHECK, UNIQUE, FK) in a migration, not only a Python check. +5. **`main` is always deployable.** Every merge deploys to production automatically. -| Member | Branch | -|---|---| -| M1 | `feat/backend-core` (later `feat/inventory-engine`, `feat/ledger`, …) | -| M2 | `feat/frontend-shell` (later `feat/products-ui`, `feat/warehouses-ui`, …) | -| M3 | `feat/operations-ui` | -| M4 | `feat/auth-dashboard` (later `feat/move-history`, `feat/otp-reset`, …) | - -Short-lived is better: one branch per feature, merged within ~1 hour. +## Workflow -## 3. Daily loop +`main` is protected: changes land only through pull requests, and both CI workflows must pass (the required checks are `backend` and `check`, from `backend.yml` and `frontend.yml`). ```bash -# start a feature git switch main && git pull -git switch -c feat/ +git switch -c feat/ # or fix/, docs/, chore/, test/ -# ... work, commit small and often ... -git add # never `git add -A` blindly; check `git status` first -git commit -m "feat(products): add SKU search" +# work in small commits +git add # check `git status` first; never commit .env or *.db +git commit -m "feat(inventory): explain why a delivery is waiting" -# sync with main (every ~45 min and before opening a PR) -git fetch origin -git rebase origin/main # resolve conflicts (should be rare if you stay in your files) -git push --force-with-lease # safe force push of your own rebased branch - -# open PR → CI green → owner/teammate approves → "Rebase and merge" (or "Create a merge commit") +git fetch origin && git rebase origin/main # stay current before opening the PR +git push -u origin feat/ ``` -**Never "Squash and merge".** It collapses your commits into one authored by whoever clicks the button, which erases per-member history. +Then open a pull request using the template. Every pull request gets a Vercel preview link and runs both CI workflows. Merge with **"Create a merge commit"** (or rebase-and-merge), **never squash**: squashing hides who wrote what. + +Code owners (see [`.github/CODEOWNERS`](.github/CODEOWNERS)) are requested for review automatically when a pull request touches their area. -If a rebase goes wrong: `git rebase --abort` and ask for help. Don't force-push `main`, ever. +## Commit messages -## 4. Commit messages ([Conventional Commits](https://www.conventionalcommits.org/)) +We follow [Conventional Commits](https://www.conventionalcommits.org/): ``` -(): +(): -optional body: what and why +Optional body: what changed and why. ``` -| type | use for | +| Type | Use for | |---|---| -| `feat` | new user-visible behavior | -| `fix` | bug fix | -| `refactor` | code change without behavior change | -| `test` | tests only | -| `docs` | documentation only | -| `style` | formatting, no logic | -| `chore` | deps, config, tooling | -| `ci` | GitHub Actions | - -Scopes: `backend`, `db`, `inventory`, `operations`, `ledger`, `products`, `warehouses`, `auth`, `dashboard`, `ui`, `deploy`. - -Examples: `feat(inventory): validate deliveries with row locks`, `fix(auth): lock account after 5 failed logins`, `feat(ui): add status badges to receipts list`. - -## 5. Pull requests -- Use the PR template checklist. -- Keep a PR under ~300 changed lines when possible. -- UI PRs include a screenshot. -- Review within 10 minutes when tagged. A quick "LGTM + run it" beats a perfect review at hour 6. -- CI (lint + tests + build) must be green before merge. - -## 6. Code style - -### Python (backend) -- Python 3.12, type hints everywhere, **ruff** for lint + format (`ruff check . && ruff format .`). -- Layers: `routes → services → models` (see [ARCHITECTURE](docs/ARCHITECTURE.md#3-backend-layers)). Routes never touch the DB session for writes; services never raise `HTTPException`. -- Names: `snake_case` functions/vars, `PascalCase` classes, plural table names, `*_id` for FKs. -- Raise domain errors (`InsufficientStock`, `InvalidState`, …) from services; `errors.py` maps them to HTTP. -- Every service function that changes stock has a pytest. - -### TypeScript (frontend) -- Strict TS, **eslint** (Next.js config) + **prettier**. -- Components `PascalCase.tsx`; hooks `useThing.ts`; route folders kebab-case. -- API calls only via `lib/api.ts`; types only from `lib/types.ts` (they mirror `docs/API.md`, snake_case fields as-is, no renaming). -- Forms: react-hook-form + zod; show server `field_errors` on the matching fields. -- Use shadcn/ui primitives and Tailwind tokens. No ad-hoc colors (see UI conventions in ARCHITECTURE). - -## 7. Dependencies -All expected dependencies are added in the scaffold commits. Need a new one? Ask the file owner (M1 for `requirements.txt`, M2 for `package.json`). They add it in a tiny separate PR, so lockfile conflicts never happen in feature branches. - -## 8. Database changes -Only **Member 1** changes models or creates Alembic migrations (avoids "multiple heads"). Need a column? Ask M1. - -## 9. Secrets -Never commit `.env`, keys, passwords or database files. Copy `.env.example` → `.env` locally. If a secret is committed by mistake: tell the team, rotate it, then remove it from the repo. +| `feat` | New behaviour users can see | +| `fix` | A bug fix | +| `refactor` | Code change with no behaviour change | +| `test` | Tests only | +| `docs` | Documentation only | +| `chore` / `ci` | Tooling, dependencies, workflows | + +Common scopes: `inventory`, `operations`, `ledger`, `products`, `warehouses`, `auth`, `dashboard`, `landing`, `ui`, `db`, `deploy`. + +## Run the checks locally + +CI runs exactly these, so run them before pushing. + +**Backend** (`cd backend`, with PostgreSQL running via `docker compose up -d db`): + +```bash +ruff check . && ruff format --check . +alembic upgrade head && alembic check # models and migrations agree +pytest -q # uses a separate _test database +``` + +**Frontend** (`cd frontend`): + +```bash +npm run lint +npm test # unit tests +npm run build +npm run test:e2e # Playwright: desktop, tablet and mobile +``` + +## Code style + +**Python** +- Python 3.12, type hints everywhere, formatted and linted with ruff. +- Layers: routes (parse, authorize, call one service) → services (rules and transactions) → models. Routes never write to the database; services never raise `HTTPException`. +- Raise domain errors (`InsufficientStock`, `InvalidState`, `ValidationFailed`, …) from services; `app/errors.py` turns them into the standard error response `{code, message, details, field_errors}`. +- Every change to stock logic comes with a test that runs on real PostgreSQL. + +**TypeScript** +- Strict TypeScript, ESLint and Prettier. +- Call the backend only through `lib/api.ts`, with relative `/api/...` paths; types live in `lib/types.ts` and mirror the API exactly (snake_case). +- Forms use react-hook-form and zod, and show server `field_errors` next to the matching field. +- Use the shared UI components and Tailwind tokens; no one-off colours. +- Animations must respect `prefers-reduced-motion`, and every drag-and-drop action needs a keyboard or button alternative. + +## Database changes + +Add or change a model, then create a migration and review it by hand. Autogenerate does not detect CHECK constraints or triggers. + +```bash +alembic revision --autogenerate -m "add " +alembic upgrade head && alembic downgrade -1 && alembic upgrade head +``` + +Coordinate migrations in the team channel so two branches don't create competing heads. + +## Security + +Never commit secrets, `.env` files or database dumps. Configuration comes from environment variables (see `backend/.env.example` and `frontend/.env.example`). If a secret is committed by mistake, tell the team, rotate it immediately, and then remove it from the history. + +## Reporting a bug + +Open an issue with the steps to reproduce, what you expected and what happened. For API errors, include the `X-Request-ID` response header: it matches the backend log line for that request. diff --git a/README.md b/README.md index 1e87159..b4c8767 100644 --- a/README.md +++ b/README.md @@ -1,146 +1,238 @@ +
+ # StockSense -**An accuracy-first Inventory Management System.** It replaces paper registers and scattered Excel sheets with one real-time app, where every stock change is a validated operation with a permanent audit trail. +### Every movement. Accounted for. + +A modular, accuracy-first **Inventory Management System** that replaces registers and spreadsheets with one real-time app.
+Receipts, deliveries, transfers and physical counts are validated operations on an append-only PostgreSQL ledger that can prove every number. + +
+ +[![Live app](https://img.shields.io/badge/Live_app-stocksense--eosin.vercel.app-4338ca?style=for-the-badge&logo=vercel&logoColor=white)](https://stocksense-eosin.vercel.app) +[![API docs](https://img.shields.io/badge/API_docs-OpenAPI-009688?style=for-the-badge&logo=fastapi&logoColor=white)](https://stocksense-api-vs0b.onrender.com/docs) + +[![Backend CI](https://github.com/Harsha-code-per/StockSense/actions/workflows/backend.yml/badge.svg)](https://github.com/Harsha-code-per/StockSense/actions/workflows/backend.yml) +[![Frontend CI](https://github.com/Harsha-code-per/StockSense/actions/workflows/frontend.yml/badge.svg)](https://github.com/Harsha-code-per/StockSense/actions/workflows/frontend.yml) +![PostgreSQL](https://img.shields.io/badge/PostgreSQL-16-336791?logo=postgresql&logoColor=white) +![FastAPI](https://img.shields.io/badge/FastAPI-Python_3.12-009688?logo=fastapi&logoColor=white) +![Next.js](https://img.shields.io/badge/Next.js-16-000000?logo=nextdotjs&logoColor=white) +[![License: MIT](https://img.shields.io/badge/License-MIT-4338ca.svg)](LICENSE) -[![License: MIT](https://img.shields.io/badge/License-MIT-indigo.svg)](LICENSE) -![Next.js](https://img.shields.io/badge/Next.js-App%20Router-black) -![FastAPI](https://img.shields.io/badge/FastAPI-Python%203.12-009688) -![PostgreSQL](https://img.shields.io/badge/PostgreSQL-16-336791) +**Odoo Hackathon 2026** · Built by a team of four -> 🔗 **Live app:** https://stocksense-eosin.vercel.app (demo login `manager@stocksense.dev` / `Manager@123`) · **API docs:** https://stocksense-api-vs0b.onrender.com/docs -> _Free tier: the API sleeps after 15 min idle, so the first request can take ~50 s._ -> Built for the Odoo Hackathon (7-hour round). +[Overview](#overview) · [Live demo](#live-demo) · [Evaluation criteria](#how-stocksense-meets-the-evaluation-criteria) · [Database design](#database-design) · [Architecture](#architecture) · [Getting started](#getting-started) · [Team](#team) + +
--- -## The problem -Businesses track stock in registers, spreadsheets and people's heads. Numbers drift from reality, nobody can say *why* a quantity changed, and managers find out about stockouts too late. +## Overview + +Small and mid-size businesses still track stock in registers, Excel sheets and memory. Numbers drift, nobody can explain why a quantity changed, and a stock-out is found when a customer is already waiting. + +StockSense is built on one rule: -## Our approach -StockSense is built around one rule: +> **Stock only changes through validated operations.** Every receipt, delivery, internal transfer and physical count is applied in a single database transaction and recorded in an append-only ledger. -> **Stock only changes through validated operations: receipts, deliveries, internal transfers and adjustments. Each one is applied atomically and recorded in an append-only ledger.** +So the system can always answer *what do we have, where is it, what is coming in or going out, and why did this number change*. It can also **prove** it: a live integrity check confirms that every balance equals the sum of its recorded movements. -So at any moment the app can answer: *What do we have? Where is it? What is coming in or going out? Why did this number change?* +## Live demo -## Features -| | | +**App:** https://stocksense-eosin.vercel.app  ·  **API (OpenAPI):** https://stocksense-api-vs0b.onrender.com/docs + +| Role | Email | Password | +|---|---|---| +| Inventory manager | `manager@stocksense.dev` | `Manager@123` | +| Warehouse staff | `staff@stocksense.dev` | `Staff@123` | + +The live database holds two weeks of realistic activity: 3 warehouses, 9 locations, 20 products and about 130 validated operations. + +> [!NOTE] +> The API runs on a free tier and sleeps after 15 minutes idle. The first request can take about 50 seconds. + +**A two-minute tour** + +1. **Dashboard:** KPIs, 14-day activity chart, operation pipeline and the *Ledger reconciled* badge. Filter by document type, status, warehouse and category. +2. **Reorder** a low-stock item in one click: a draft receipt for the suggested quantity opens, ready to validate. +3. **Count next:** *Office Chair* is ranked high risk, with its reasons. Record a count and see the difference before it is posted. +4. **Operations board:** drag a card to *Ready* to check availability, or to *Done* to validate. The motor delivery is refused: there isn't enough stock. +5. **The Steel Rod story** from the problem statement: receive 100 kg, transfer 40, try to deliver 50 (refused), deliver 20, count 17, **77 kg**. **Move history** then explains every step, and exports to CSV. +6. Sign in as **staff** and try to validate an adjustment: only managers can. + +## Problem statement coverage + +| Module | Delivered | |---|---| -| 🔐 **Auth** | Signup / login, OTP-based password reset, account lockout after repeated failures, manager & staff roles | -| 📊 **Dashboard** | Products in stock, low / out-of-stock, pending receipts & deliveries, scheduled transfers; filters by document type, status, warehouse/location, category | -| 📦 **Products** | SKU, category, unit of measure, initial stock, stock per location, reordering rules (min/max) with suggested order quantity | -| 📥 **Receipts** | Incoming goods from vendors → stock increases on validation | -| 📤 **Deliveries** | Outgoing goods → availability check (Waiting/Ready) → stock decreases on validation | -| 🔁 **Internal transfers** | Rack → rack, warehouse → warehouse, total stock unchanged | -| 🧮 **Adjustments** | Enter the physical count; the system logs the difference | -| 📜 **Move history** | Append-only stock ledger: who, when, what, from where to where, balance after | -| 🏭 **Multi-warehouse** | Warehouses with locations (racks, floors, zones) | -| 🔎 **Search & alerts** | SKU/name search, smart filters, low-stock badges | -| ✅ **Integrity check** | Proves ledger totals equal current balances for every product-location ("Ledger reconciled ✓" on the dashboard) | -| 🎯 **Count next** | Ranks which product-locations to physically count first, with plain-language reasons (movements since last count, days since last count, past discrepancies). A transparent rule score, no black box | -| 📄 **CSV export** | Download the move history with the current filters | - -Odoo-style references (`WH/IN/0001`, `WH/OUT/0001`, `WH/INT/0001`, `WH/ADJ/0001`) and statuses (Draft → Waiting / Ready → Done, or Canceled). - -## Screenshots -| Dashboard | Move history (the full audit trail) | +| **Authentication** | Sign up, log in, logout; OTP password reset by email; redirect to the dashboard; manager and staff roles | +| **Dashboard** | Products in stock, low / out of stock, pending receipts, pending deliveries, scheduled transfers; filters by document type, status, warehouse or location, and category | +| **Products** | Name, SKU, category, unit of measure, optional initial stock; stock per location; categories; reordering rules (min / max) | +| **Receipts** | Supplier and products, quantities received, validate → stock increases | +| **Delivery orders** | Pick and pack (availability check: *Waiting* / *Ready*), validate → stock decreases; overselling is refused | +| **Internal transfers** | Warehouse to production floor, rack to rack, warehouse to warehouse; company total unchanged; every movement in the ledger | +| **Stock adjustments** | Select product and location, enter the counted quantity; the difference is posted and logged | +| **Move history** | Who, when, what, from where to where, balance after; filters and CSV export | +| **Settings** | Multiple warehouses, each with its own locations | +| **Profile** | My profile, logout | +| **Additional features** | Low-stock alerts, multi-warehouse support, SKU search and smart filters on every list | + +**Beyond the brief** + +| Feature | What it adds | |---|---| -| ![Dashboard](docs/screenshots/dashboard.png) | ![Move history](docs/screenshots/move-history.png) | -| **Physical count: difference shown before posting** | **Products with stock status and reorder suggestion** | -| ![Adjustment](docs/screenshots/adjustment.png) | ![Products](docs/screenshots/products.png) | +| **Count next** | Ranks which shelves to count first from movements since the last count, days since the last count and past discrepancies, with reasons in plain language. Rule-based and explainable, not a black box. | +| **One-click reorder** | Turns a low-stock alert into a draft receipt for the suggested quantity. | +| **Operations board** | Kanban of Draft → Waiting → Ready → Done. Drag to confirm or validate, with buttons for keyboard and touch. | +| **Activity charts** | Validated operations per day by type and the open pipeline by stage, with a table view. | +| **Integrity proof** | A live check that every balance equals the sum of its ledger movements. | +| **Idempotent validation** | Validating twice returns the first result and moves nothing. | +| **Cinematic landing page** | WebGL hero and a GSAP scroll story of the Steel Rod example, with full reduced-motion support. | -The move history above is the demo story from [docs/DEMO.md](docs/DEMO.md): receive 100 kg of steel, move 40 to the production floor, deliver 20, count 17 (−3), leaving **77 kg**, and every step explains itself. +## How StockSense meets the evaluation criteria -## Why it's trustworthy -- **Atomic operations:** one database transaction per validation. A transfer can never decrease the source without increasing the destination. -- **Concurrency-safe:** PostgreSQL row locks, so two people can't ship the same last unit. -- **Idempotent:** double-clicking *Validate* never moves stock twice. -- **Enforced by the database:** `CHECK (quantity >= 0)`, shape constraints per operation type, and a trigger that makes the ledger append-only. -- **Validated twice:** instant form feedback (zod) plus server validation (Pydantic) with field-level messages. +| Criterion | How we address it | +|---|---| +| **Database design** | 11 normalized tables in PostgreSQL. Business rules enforced by the database itself: `CHECK` constraints (no negative stock, valid statuses, which locations each operation type may use), unique references, foreign keys, indexes on every hot filter, and a trigger that makes the ledger append-only. [Details](#database-design) | +| **Real backend, no BaaS** | Our own FastAPI service, schema, migrations, authentication and transactions. PostgreSQL runs in Docker locally and on Neon (plain Postgres) in production. | +| **Minimal third-party APIs** | Own auth, own charts (hand-built SVG), own business logic. The only external service is an SMTP relay for OTP email, and it falls back to the console when not configured. | +| **Real-time, dynamic data** | Every screen reads live data from PostgreSQL through the API. No static JSON; mocks exist only inside tests. | +| **Robust input validation** | Three layers: instant form feedback in the browser (zod), server validation (Pydantic) with field-level messages mapped back onto the form, and database constraints as the last line of defence. | +| **Logic** | One `operation_service` is the only code allowed to change stock: status lifecycle, availability checks, atomic multi-line moves, running balances and Odoo-style references (`WH/IN/0001`). | +| **Modularity** | Thin routes → services that own every transaction → SQLAlchemy models. Frontend split into route groups, shared UI components, a typed API client and feature modules. | +| **Coding standards** | Type hints and strict TypeScript throughout; ruff, ESLint and Prettier enforced in CI; Conventional Commits. | +| **Security** | bcrypt passwords, httpOnly `Secure` session cookie, server-side role checks, hashed one-time codes with expiry and attempt limits, login lockout, no user enumeration, CORS limited to the app's own domain. | +| **Performance** | Server-side pagination on every list, indexed filters, aggregates computed in SQL, eager loading to avoid N+1 queries, pooled connections. | +| **Scalability** | Stateless API; locks live in the database, so they stay correct across multiple instances; locks are taken in a fixed order, so concurrent validations never deadlock. | +| **Usability & front-end design** | One consistent design system and status colour scheme, clear sidebar navigation, loading, empty and error states, toasts that state the stock effect, responsive on desktop, tablet and mobile. | +| **Debugging** | Every response carries an `X-Request-ID` that matches its log line; one standard error format `{code, message, details, field_errors}`; health and integrity endpoints. | +| **CI/CD** | GitHub Actions on every pull request (lint, migrations up / down / up, backend tests, frontend build, browser tests). Protected `main`; every merge deploys automatically to Vercel and Render. | +| **Git as a team sport** | All four members commit from their own accounts on short-lived feature branches merged through pull requests, with file ownership per member (CODEOWNERS) to avoid merge conflicts. | + +## Database design -## How we know it works -- **73 backend tests** on real PostgreSQL: the full demo story, every rule in the acceptance matrix, two users racing for the last units (exactly one wins), multi-line transfers that roll back completely, a ledger that rejects edits, and the auth lockout under parallel attempts. -- **54 browser tests** (desktop, tablet, mobile) for the frontend, plus unit tests for the API client and the login redirect guard. -- **CI on every pull request** (lint, tests, migrations up/down/up, production build, browser tests); `main` is protected and only accepts green PRs. Every merge auto-deploys. -- The live site was walked through end to end in a real browser after deployment. +```mermaid +erDiagram + USERS ||--o{ PASSWORD_RESETS : requests + USERS ||--o{ OPERATIONS : creates + CATEGORIES ||--o{ PRODUCTS : groups + WAREHOUSES ||--|{ LOCATIONS : contains + PRODUCTS ||--o{ STOCK_BALANCES : "stocked as" + LOCATIONS ||--o{ STOCK_BALANCES : holds + OPERATIONS ||--|{ OPERATION_LINES : has + PRODUCTS ||--o{ OPERATION_LINES : "moved in" + OPERATIONS ||--o{ STOCK_LEDGER : posts + PRODUCTS ||--o{ STOCK_LEDGER : records + LOCATIONS ||--o{ STOCK_LEDGER : records +``` -## Tech stack -| Layer | Technology | -|---|---| -| Frontend | Next.js (App Router), TypeScript, Tailwind CSS, shadcn/ui, react-hook-form, zod | -| Backend | FastAPI, Pydantic v2, SQLAlchemy 2, Alembic | -| Database | PostgreSQL 16 | -| Auth | Own JWT in httpOnly cookie, bcrypt, email OTP via SMTP | -| CI/CD | GitHub Actions → Vercel (frontend) + Render (backend) + Neon (Postgres), all on free tiers | +- **Balances plus a ledger.** `stock_balances` holds the current quantity per product and location; `stock_ledger` holds every signed movement with the balance after it. The integrity check proves that the two always agree. +- **Rules in the schema.** `CHECK (quantity >= 0)` on balances, one `CHECK` per operation type for its allowed source and destination, `status = 'done'` if and only if `validated_at` is set, exactly one of quantity or counted quantity per line, and format checks on SKUs and warehouse codes. +- **Append-only history.** A trigger rejects any `UPDATE` or `DELETE` on the ledger. Mistakes are corrected with a new adjustment, never by editing the past. +- **Exact quantities.** `NUMERIC(18,3)` for kilograms and litres, never floating point. +- **Sequential references.** A per-warehouse, per-type counter row, locked on use, generates `WH/IN/0001`, `WH/OUT/0001` and so on inside the same transaction. +- **Migrations.** Alembic, with constraints and the trigger written by hand, and CI applying, rolling back and re-applying every migration. -**No Backend-as-a-Service.** The schema, business logic, authentication and transactions are all ours. Inventory correctness *is* the problem, so we need full control of the database transaction. +Every table, column, constraint and index: [docs/DATA_MODEL.md](docs/DATA_MODEL.md) ## Architecture ```mermaid flowchart LR - U[Browser] --> N["Next.js (Vercel)
UI + /api proxy"] - N --> F["FastAPI (Render)
routes → services → ORM"] - F --> P[("PostgreSQL (Neon)
constraints · row locks · append-only ledger")] + U[Browser] --> N["Next.js on Vercel
UI + /api proxy"] + N --> F["FastAPI on Render
routes → services → ORM"] + F --> P[("PostgreSQL on Neon
constraints · row locks · append-only ledger")] + F -. OTP email .-> M[SMTP relay] ``` -Details: [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md) +- **Modular monolith.** One FastAPI service in clear layers; the service layer owns every transaction. +- **Same-origin by design.** The browser only talks to the Next.js app, which proxies `/api/*` to the backend, so the session cookie stays first-party. +- **All or nothing.** Each validation is one transaction with the affected rows locked, so a transfer can never take stock from one place without adding it to the other, and two users can never ship the same last unit. + +More: [Architecture](docs/ARCHITECTURE.md) · [Inventory rules](docs/INVENTORY_RULES.md) · [API contract](docs/API.md) + +## Tech stack + +| Layer | Technology | +|---|---| +| Frontend | Next.js 16 (App Router), React 19, TypeScript, Tailwind CSS 4, react-hook-form + zod | +| Motion & graphics | GSAP, Motion, a hand-written WebGL shader, hand-built SVG charts | +| Backend | FastAPI, Pydantic v2, SQLAlchemy 2, Alembic | +| Database | PostgreSQL 16 (Docker locally, Neon in production) | +| Auth | Own JWT session cookie, bcrypt, email one-time codes | +| Quality | pytest on real PostgreSQL, Playwright, ruff, ESLint, Prettier | +| Delivery | GitHub Actions, Vercel, Render, Neon (all free tiers) | + +## Testing and CI/CD -## Quick start +- **76 backend tests** on real PostgreSQL: the full Steel Rod story, every rule in the acceptance matrix, two users racing for the last units (exactly one wins), multi-line transfers that roll back completely, a ledger that rejects edits, and login lockout under parallel attempts. +- **96 browser tests** with Playwright across desktop, tablet and mobile, plus unit tests for the API client and the route guard. +- **On every pull request:** lint and format, migrations up / down / up, backend tests, frontend build and browser tests. Both checks must pass before anything reaches `main`, and every merge deploys automatically. -Prerequisites: Git, Docker, Python 3.12, Node 20+. +## Getting started + +Prerequisites: Git, Docker, Python 3.12 and Node 24 or newer. ```bash -git clone && cd StockSense -docker compose up -d db # PostgreSQL 16 +git clone https://github.com/Harsha-code-per/StockSense.git && cd StockSense +docker compose up -d db # PostgreSQL (DB_PORT=5433 if 5432 is taken) cd backend python -m venv .venv && source .venv/bin/activate pip install -r requirements-dev.txt cp .env.example .env -alembic upgrade head && python -m app.seed -uvicorn app.main:app --reload # http://localhost:8000/docs +alembic upgrade head +python -m app.seed_demo --yes # two weeks of demo data (or: python -m app.seed) +uvicorn app.main:app --reload # http://localhost:8000/docs -cd ../frontend # in a second terminal +cd ../frontend # in a second terminal npm install && cp .env.example .env.local -npm run dev # http://localhost:3000 +npm run dev # http://localhost:3000 ``` -**Demo login:** `manager@stocksense.dev` / `Manager@123` · `staff@stocksense.dev` / `Staff@123` +| Task | Command | +|---|---| +| Backend tests | `cd backend && pytest -q` | +| Backend lint | `ruff check . && ruff format --check .` | +| Frontend checks | `cd frontend && npm run lint && npm test && npm run build` | +| Browser tests | `npm run test:e2e` | -Full setup, environment variables and deployment: [docs/DEPLOYMENT.md](docs/DEPLOYMENT.md) +Environment variables and the free deployment setup: [docs/DEPLOYMENT.md](docs/DEPLOYMENT.md) + +## Project structure -## Repository structure ```text StockSense/ -├── backend/ FastAPI app, models, services, Alembic migrations, tests -├── frontend/ Next.js app (App Router) -├── docs/ Architecture, data model, API contract, rules, plans -├── .github/ CODEOWNERS, PR template, CI workflow -├── docker-compose.yml Local PostgreSQL -└── LICENSE +├── backend/ FastAPI: routes, services (stock engine), models, Alembic migrations, tests +├── frontend/ Next.js: landing, auth, dashboard, products, operations, board, history +├── docs/ Architecture, data model, API contract, inventory rules, deployment, demo +├── .github/ CI workflows, CODEOWNERS, pull request template +├── render.yaml Backend deployment as code +└── docker-compose.yml ``` ## Documentation -| Doc | What's inside | + +| Document | Contents | |---|---| -| [ARCHITECTURE](docs/ARCHITECTURE.md) | Layers, request flow, transactions & locking, security, frontend structure, UI conventions | -| [DATA_MODEL](docs/DATA_MODEL.md) | Every table, constraint, index and trigger; ER diagram | -| [API](docs/API.md) | Frozen REST contract with request/response examples and error codes | -| [INVENTORY_RULES](docs/INVENTORY_RULES.md) | State machine, stock formulas, invariants, validation rules, roles | -| [TEAM_PLAN](docs/TEAM_PLAN.md) | Who owns what, 7-hour timeline, checkpoints | -| [DEPLOYMENT](docs/DEPLOYMENT.md) | Local setup, CI, free deployment on Vercel + Render + Neon | -| [DEMO](docs/DEMO.md) | Demo script, seed data, acceptance tests, judge Q&A | -| [ROADMAP](docs/ROADMAP.md) | P0/P1/P2 scope, assumptions, open questions | -| [CONTRIBUTING](CONTRIBUTING.md) | Branching, commits, PRs, code style | +| [Architecture](docs/ARCHITECTURE.md) | Layers, request flow, transactions and locking, security, frontend structure | +| [Data model](docs/DATA_MODEL.md) | Every table, constraint, index and trigger, with an ER diagram | +| [API contract](docs/API.md) | All endpoints with examples and error codes | +| [Inventory rules](docs/INVENTORY_RULES.md) | Status lifecycle, stock arithmetic, invariants, validation, roles | +| [Deployment](docs/DEPLOYMENT.md) | Local setup, CI/CD, Vercel + Render + Neon | +| [Demo guide](docs/DEMO.md) | Demo script, demo data, acceptance tests | +| [Team plan](docs/TEAM_PLAN.md) | Roles, file ownership and how we worked | +| [Roadmap](docs/ROADMAP.md) | What shipped, assumptions, what comes next | +| [Contributing](CONTRIBUTING.md) | Branches, commits, pull requests, code style | ## Team -| Member | Role | GitHub | + +| Member | Focus | GitHub | |---|---|---| -| Member 1 | Backend, database & inventory engine | [@Harsha-code-per](https://github.com/Harsha-code-per) | -| Member 2 | Frontend shell, products & warehouses, deployment | [@loktrishal-05](https://github.com/loktrishal-05) | -| Member 3 | Operations UI (receipts, deliveries, transfers, adjustments) | [@Sanjjith27](https://github.com/Sanjjith27) | -| Member 4 | Auth, dashboard, move history, QA & demo | [@Cholan-kinnera](https://github.com/Cholan-kinnera) | +| **Harshavardhan K** | Backend, database and stock engine; deployment and CI; auth pages, dashboard insights and landing page | [@Harsha-code-per](https://github.com/Harsha-code-per) | +| **Loktrishal K** | Frontend foundation and design system; products, warehouses, motion and end-to-end tests | [@loktrishal-05](https://github.com/loktrishal-05) | +| **Sanjjith B** | Operations: receipts, deliveries, transfers and adjustments | [@Sanjjith27](https://github.com/Sanjjith27) | +| **Cholan Abhaynadh Kinnera** | Authentication and dashboard APIs; dashboard, move history and profile; QA | [@Cholan-kinnera](https://github.com/Cholan-kinnera) | ## License + [MIT](LICENSE) © 2026 StockSense Team diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md index 430c6e8..88ebc71 100644 --- a/docs/ARCHITECTURE.md +++ b/docs/ARCHITECTURE.md @@ -20,7 +20,7 @@ flowchart LR subgraph Neon["Neon (free)"] P[(PostgreSQL 16)] end - S[[Gmail SMTP
OTP email, optional]] + S[[Brevo SMTP relay
OTP email, optional]] U -- HTTPS --> N N -- "/api/* proxied (same origin)" --> F F -- SQLAlchemy / psycopg --> P @@ -139,7 +139,7 @@ sequenceDiagram - **Tailwind + shadcn/ui** for one consistent design system (buttons, inputs, dialogs, tables, badges, toasts via `sonner`). - **react-hook-form + zod** for every form: instant inline errors, then server `field_errors` mapped onto the same fields. - **Data fetching:** client components call `/api/...` through `lib/api.ts` (one `fetch` wrapper: JSON, credentials, error-shape parsing → throws `ApiError {code, message, field_errors}`). After a mutation: toast + refetch. -- **Route protection:** `src/middleware.ts` (named `proxy.ts` on Next.js 16) redirects to `/login` when the `ss_session` cookie is absent. The API still enforces auth on every call. +- **Route protection:** `src/proxy.ts` (Next.js 16's name for middleware) redirects to `/login` when the `ss_session` cookie is absent. The API still enforces auth on every call. ```text frontend/src/ @@ -157,7 +157,7 @@ frontend/src/ ├── components/operations/ # OperationForm, LineEditor, StatusBadge (M3) ├── lib/api.ts lib/types.ts (M2) ├── lib/operations.ts (M3) -└── middleware.ts (M4) +└── proxy.ts (M4) ``` ### UI conventions (consistency is judged) diff --git a/docs/DEMO.md b/docs/DEMO.md index d0430ee..12330b7 100644 --- a/docs/DEMO.md +++ b/docs/DEMO.md @@ -71,12 +71,12 @@ | Why PostgreSQL? | Row-level locks for concurrent validation, exact NUMERIC for kg/litres, CHECK constraints and a trigger that enforce rules even if app code is wrong. | | What stops double-counting on a double click? | Operation row lock + `done` guard → second call is a no-op (`already_done`). | | How do you know stock is right? | Every change is a ledger row; `/inventory/integrity` proves Σ ledger = balance for every product-location. | -| Where is the AI? | Not in the core, on purpose (per guidelines). Our optional extension is an explainable cycle-count priority score (movement frequency, days since count, past adjustments), shown with its reasons, not presented as magic. | +| Where is the AI? | Not in the core, on purpose (per guidelines). What we ship is the "Count next" list: a rule-based, explainable cycle-count priority (movements since the last count, days since the last count, past discrepancies), shown with its reasons, not presented as magic. | | How would this scale? | Stateless FastAPI instances behind one Postgres; locks live in the DB so they work across instances; indexed, paginated queries; path to read replicas. | | What would you add next? | Barcode/QR scanning, returns, lot/expiry tracking, role configuration, CSV import from existing spreadsheets. | ## Claims to avoid - "Real-time" beyond what we do (we do: every read reflects committed data immediately; no WebSockets). -- "AI-powered", unless the risk score is implemented, and then only "rule-based, explainable". +- "AI-powered". Describe Count next as "rule-based, explainable". - "Better than Odoo/ERPNext". Say "inspired by established ERP flows (Odoo-style references and states)". - Any accuracy-improvement percentage without a measured experiment. diff --git a/docs/ROADMAP.md b/docs/ROADMAP.md index db8ad3b..6c6855d 100644 --- a/docs/ROADMAP.md +++ b/docs/ROADMAP.md @@ -23,7 +23,7 @@ - [x] Ledger CSV export - [ ] Low-stock bell in header with count - [ ] "Late" operations (scheduled_date < today and not done) highlighted -- [ ] Suggested reorder quantity on low-stock list → one click creates a draft receipt +- [x] Suggested reorder quantity on low-stock list → one click creates a draft receipt - [ ] Keyboard shortcuts / quick SKU search in header ## P2: only if everything else is solid @@ -31,6 +31,13 @@ - [ ] QR/barcode scan of SKU and location in operation forms (browser camera) - [ ] Returns (reverse of a done delivery/receipt) +## Shipped beyond the plan +- [x] Operations board: Kanban of Draft / Waiting / Ready / Done, drag to confirm or validate, with button alternatives for keyboard and touch +- [x] Dashboard activity charts: validated operations per day by type (`GET /api/dashboard/activity`) and the open pipeline by stage +- [x] Cinematic landing page: WebGL hero and a GSAP scroll story of the Steel Rod example, with reduced-motion support +- [x] Full-viewport video backgrounds on login, signup and password reset +- [x] Rich demo dataset (`python -m app.seed_demo --yes`): two weeks of activity created through the real operation engine + ## Out of scope (say so if asked) Purchasing/sales modules, invoicing/accounting/GST, CRM, serial/lot/expiry tracking, unit conversions, reservations/backorders, wave picking, multi-step routes, RFID, demand forecasting, LLM chatbot, blockchain, offline mode, WebSockets. diff --git a/docs/TEAM_PLAN.md b/docs/TEAM_PLAN.md index f5710c8..ae5da77 100644 --- a/docs/TEAM_PLAN.md +++ b/docs/TEAM_PLAN.md @@ -12,8 +12,6 @@ | **Member 3**: [@Sanjjith27](https://github.com/Sanjjith27) | Operations UI (Receipts, Deliveries, Transfers, Adjustments) | Warehouse tasks are fast and hard to get wrong | | **Member 4**: [@Cholan-kinnera](https://github.com/Cholan-kinnera) | Auth + Dashboard + Move History + QA/Demo | Secure entry, clear overview, and it all works end to end | -> Teammates: replace your placeholder handle here, in `README.md` and in `.github/CODEOWNERS` as your **first commit**. - ## File ownership Only the owner edits these paths. Need a change elsewhere? Ask the owner or open a PR and tag them. @@ -36,7 +34,7 @@ Only the owner edits these paths. Need a change elsewhere? Ask the owner or open | `frontend/src/lib/{api,types}.ts` | M2 | | `frontend/src/app/(app)/{products,warehouses}/**` | M2 | | `frontend/src/app/(app)/operations/**`, `components/operations/**`, `lib/operations.ts` | M3 | -| `frontend/src/app/(auth)/**`, `(app)/{dashboard,history,profile}/**`, `src/middleware.ts` | M4 | +| `frontend/src/app/(auth)/**`, `(app)/{dashboard,history,profile}/**`, `src/proxy.ts` | M4 | | `docs/{ARCHITECTURE,DATA_MODEL,API,INVENTORY_RULES,DEPLOYMENT}.md` | M1 | | `docs/DEMO.md` | M4 | | `README.md`, `docs/{TEAM_PLAN,ROADMAP}.md` | M1 (anyone may PR) | @@ -66,7 +64,7 @@ These are the only files everyone would otherwise touch. They are **pre-populate |---|---|---|---|---| | **0:00–0:20** | Everyone: read docs, create branch, first commit = own handle in CODEOWNERS/README/TEAM_PLAN | ← | ← | ← | | **0:20–1:00** | FastAPI scaffold, config, DB, **all models + first migration (with CHECKs + trigger)**, all stub routers in `main.py`, `docker-compose.yml`, CI `backend.yml`. **Merge by 1:00** | `create-next-app`, Tailwind, shadcn, layout + full sidebar + all placeholder pages, `lib/api.ts`, `lib/types.ts`, CI `frontend.yml`. **Merge by 1:00** | `components/operations/`: StatusBadge, LineEditor, OperationForm (mock data) | Login / Signup / Forgot-password UI with zod validation; draft `auth_service` logic | -| **1:00–2:00** | products, categories, warehouses, locations, inventory endpoints + `seed.py` | Products list/create/edit, Warehouses + Locations pages (wired) | Receipts + Deliveries list & form pages (mock) | Auth endpoints wired to users table, cookie, `middleware.ts`, profile + logout | +| **1:00–2:00** | products, categories, warehouses, locations, inventory endpoints + `seed.py` | Products list/create/edit, Warehouses + Locations pages (wired) | Receipts + Deliveries list & form pages (mock) | Auth endpoints wired to users table, cookie, `proxy.ts`, profile + logout | | **2:00–3:00** | `operation_service`: create / confirm / validate for receipt + delivery, ledger writes, sequence numbers, **pytest** | Product detail (stock by location, recent moves), stock badges | Wire receipts + deliveries to API (create → confirm → validate, available qty) | Dashboard API (KPI aggregates) + Dashboard page | | **3:00–3:30** | transfer + adjustment + cancel, `/inventory/integrity`, Steel Rod scenario test | UI consistency pass across pages | Transfers + Adjustments (system/counted/difference preview) | Move History page with filters; start end-to-end testing | | **3:30 ✅ CHECKPOINT** | **Full lifecycle works in the browser: create product → receive 100 → transfer 40 → deliver 20 → count 17 → total 77, ledger correct.** If not: everybody stops features and fixes this. | | | | diff --git a/docs/screenshots/adjustment.png b/docs/screenshots/adjustment.png deleted file mode 100644 index f0cc63e..0000000 Binary files a/docs/screenshots/adjustment.png and /dev/null differ diff --git a/docs/screenshots/dashboard.png b/docs/screenshots/dashboard.png deleted file mode 100644 index 70e8fb7..0000000 Binary files a/docs/screenshots/dashboard.png and /dev/null differ diff --git a/docs/screenshots/login.png b/docs/screenshots/login.png deleted file mode 100644 index 0707bf2..0000000 Binary files a/docs/screenshots/login.png and /dev/null differ diff --git a/docs/screenshots/move-history.png b/docs/screenshots/move-history.png deleted file mode 100644 index 71e5bb0..0000000 Binary files a/docs/screenshots/move-history.png and /dev/null differ diff --git a/docs/screenshots/products.png b/docs/screenshots/products.png deleted file mode 100644 index 39435db..0000000 Binary files a/docs/screenshots/products.png and /dev/null differ diff --git a/frontend/README.md b/frontend/README.md index 8f616c0..e0e0085 100644 --- a/frontend/README.md +++ b/frontend/README.md @@ -39,7 +39,7 @@ src/ ├── components/operations/ OperationForm, LineEditor, StatusBadge M3 ├── lib/api.ts · lib/types.ts M2 ├── lib/operations.ts M3 -└── middleware.ts route protection (proxy.ts on Next 16) M4 +└── proxy.ts route protection (redirects to /login) M4 ``` ## Rules @@ -63,13 +63,12 @@ shared page, loading/error, and form conventions. New shadcn components must imp `cn` from `@/lib/utils`; reuse that utility rather than installing another package. Browser tests use contract-shaped responses only inside `tests/`. They cover desktop, -tablet, and mobile layouts; the production app always calls the real API. Live login -and complete inventory lifecycle verification require Members 3 and 4's implementations. +tablet, and mobile layouts; the production app always calls the real API. ## Frontend deployment -Import this repository into Vercel with Root Directory `frontend` and the Next.js -preset. Set the server-only `BACKEND_URL` to the team's deployed backend origin; -do not prefix it with `NEXT_PUBLIC_`. Vercel runs `npm run build`. All browser requests -continue to use `/api/*` through the Next.js rewrite. Deployment account configuration -and the final backend URL must be supplied by the team. +Live at https://stocksense-eosin.vercel.app. Vercel builds from Root Directory `frontend` +with the Next.js preset; merges to `main` deploy to production and every pull request gets a +preview URL. The server-only `BACKEND_URL` (no `NEXT_PUBLIC_` prefix) points at the Render +API, and all browser requests go through the `/api/*` rewrite. Full steps: +[docs/DEPLOYMENT.md](../docs/DEPLOYMENT.md). diff --git a/frontend/screenshots/products-desktop.png b/frontend/screenshots/products-desktop.png deleted file mode 100644 index 7b2de30..0000000 Binary files a/frontend/screenshots/products-desktop.png and /dev/null differ diff --git a/frontend/screenshots/shell-desktop.png b/frontend/screenshots/shell-desktop.png deleted file mode 100644 index 1028990..0000000 Binary files a/frontend/screenshots/shell-desktop.png and /dev/null differ