PharmaFlow - Frontend Overview & Local Setup
PharmaFlow is an intelligent pharmacy operations platform built for retail pharmacies. It aims to digitize and streamline day-to-day pharmacy workflows, including:
- Zero-entry invoice scanning - point the camera at a distributor's bill or upload a PDF; Google Cloud Vision + a format-agnostic parser extract line items into a review-ready draft receipt. Manual entry still available as a first-class fallback.
- Inventory management - per-product view with batch-level detail, server-side filters for low-stock / near-expiry / out-of-stock, and FEFO allocation on sales.
- Expiry management - near-expiry batches surface on the dashboard, inventory list, and returns flow.
- Sales & invoice generation - create customer invoices with line items drawn FEFO from inventory batches; print via a self-contained popup window. Business-profile completeness is enforced before an invoice can be generated (invoices need a legally-compliant header).
- Past Invoices with an OWNER-only Staff audit column - who rang each sale survives even if the employee is removed from the team.
- Returns to distributor - select batches, auto-reserve the stock, print a regulation-compliant debit note (with the pharmacy's GSTIN, drug licence, and bank details), and record pickup to post an expense credit.
- Expense tracking - log manual expenses; return credits surface automatically as negative-amount rows.
- Analytics (owner-only) - one-page snapshot of revenue, cost-of-goods (from FEFO allocations), gross/net profit, sales trend, top products, payment-mode split, expense breakdown, and an inventory-health tile. Period selector toggles 7/30/90/365-day rolling windows.
- Settings & Profile - business profile, bank details, distributor list (real API-backed CRUD; renames cascade to history via FK), profile-picture upload, self-service password change.
- Team management (owner-only
/teampage) - invite / promote / disable / soft-remove MANAGER + STAFF accounts with server-generated temp passwords returned once in a copy-banner. Re-inviting a removed email reactivates the original row so every audit trail remains consistent. - Role-based access control - OWNER / MANAGER / STAFF enforced in two layers:
<RoleRoute>wrappers in the frontend routes +@role_required()decorators on every mutating backend endpoint.
- backend/docs/settings-architecture.md - start here for the Settings page, RBAC matrix, team management, distributors, profile picture upload, password rotation. Ties together every subsystem added with the April 2026 settings work.
- backend/docs/inventory-architecture.md - end-to-end walkthrough of the inventory / receipts / returns / OCR subsystems.
- backend/docs/invoices-architecture.md - sales-invoice + FEFO allocation logic (including the completeness gate + creator audit fields).
- backend/docs/analytics-architecture.md - OWNER-only reporting page: KPI formulas (revenue, COGS, gross/net profit), period resolution, inventory-health buckets, and the multi-tenant scoping tests.
- backend/docs/dashboard-architecture.md - role-aware landing page: per-role widget matrix, pending-queue semantics (receipts/returns/expenses), and the rationale for STAFF seeing only their own sales.
- backend/docs/auth-architecture.md - authentication primitives + business membership model.
- backend/docs/api-spec.yaml - OpenAPI 3 spec for every live endpoint. Drop it into Swagger UI / Redoc to browse interactively.
frontend/
βββ index.html # App shell, loads /src/main.jsx
βββ package.json # Dependencies & scripts (named: pharmaflow-frontend)
βββ vite.config.js # Vite config - dev server on :5173, proxies /api to :5000
βββ .gitignore
βββ src/
βββ main.jsx # React entry point - wraps app in BrowserRouter + Toaster
βββ App.jsx # Root - applies ThemeProvider + AuthProvider
βββ routes/
β βββ AppRoutes.jsx # All routes with <RoleRoute allowed=[...]> per-page gating
βββ pages/
β βββ public/ # LandingPage, AboutUsPage, ContactUsPage
β βββ auth/ # LoginPage (also handles /signup)
β βββ dashboard/ # DashboardPage (with setup + temp-password banners)
β βββ inventory/ # InventoryListPage, InventoryDetailPage
β βββ sales/ # InvoiceGeneratorPage, PastInvoicesPage (OWNER/MANAGER)
β βββ receipts/ # DrugReceiptsPage, ScanReceiptPage, ReviewReceiptPage, ManualReceiptPage (OWNER/MANAGER)
β βββ returns/ # ReturnsManagementPage, ReturnReceiptPage (OWNER/MANAGER)
β βββ expenses/ # ExpensesPage (OWNER)
β βββ analytics/ # AnalyticsPage (OWNER)
β βββ team/ # TeamPage - invite/promote/remove staff (OWNER)
β βββ settings/ # SettingsPage - profile, password, business, bank, distributors
βββ layouts/ # PublicLayout, DashboardLayout, AuthLayout
βββ context/ # AuthContext, ThemeContext
βββ components/ # Reusable UI components (incl. settings/, expenses/, common/)
βββ services/ # api.js axios client + per-domain services
β # businessService, distributorService, teamService,
β # invoiceService, receiptService, returnService,
β # expenseService, inventoryService, analyticsService,
β # dashboardService
βββ data/ # Static seed data for dashboard placeholders
βββ styles/
βββ index.css # Global styles + .sr-only utility
βββ variables.css # CSS custom properties (light + dark themes)
| Concern | Detail |
|---|---|
| Framework | React 18 with JSX |
| Build Tool | Vite 5 |
| Routing | React Router DOM v6 |
| API Calls | Axios (proxied via Vite to backend at http://localhost:5000) |
| Animations | Framer Motion |
| Charts | Recharts |
| Notifications | react-hot-toast |
| Icons | Lucide React |
| Fuzzy Search | Fuse.js |
| File Upload | react-dropzone |
| Date Utils | date-fns |
| Path Alias | @ maps to ./src |
ProtectedRoute- redirects unauthenticated users to/login.RoleRoute allowed={[β¦]}- centralised role gate used by every role-scoped page inAppRoutes.jsx(e.g.<RoleRoute allowed={['OWNER','MANAGER']}>). See the full matrix inbackend/docs/settings-architecture.md.- Auth state is managed globally via
AuthContext;updateUser()is defensive about partial updates so a strayrole: nullcan't silently demote a user.
- Node.js β₯ 18 (recommended)
- npm
# 1. Clone the repository and switch to the correct branch
git clone https://github.com/siddharth7113/SE_project.git
cd SE_project
# 2. Navigate into the frontend directory
cd frontend
# 3. Copy the environment file and fill in your Google Client ID
cp .env.example .env
# Then open .env and set: VITE_GOOGLE_CLIENT_ID=your-google-client-id.apps.googleusercontent.com
# 4. Install dependencies
npm install
# 5. Start the development server
npm run devThe app will be available at http://localhost:5173.
| Command | Description |
|---|---|
npm run dev |
Start Vite dev server on port 5173 |
npm run build |
Build for production (outputs to dist/) |
npm run preview |
Preview the production build locally |
npm run lint |
Run ESLint across all .js and .jsx files |
The full REST API specification is defined in OpenAPI 3.0 (Swagger) format:
π backend/docs/api-spec.yaml
You can preview it by pasting the contents into Swagger Editor, or install the VS Code OpenAPI extension for inline preview.
The backend lives in backend/ and uses Flask, SQLAlchemy ORM, and PostgreSQL 16.
- Python β₯ 3.11
- uv (Python package manager) - install with
curl -LsSf https://astral.sh/uv/install.sh | sh - Docker & Docker Compose (for PostgreSQL)
# 1. Start PostgreSQL (creates both dev and test databases automatically)
cd backend
.\check-docker.ps1
docker compose up -d
# 2. Configure Environment Variables
# Copy the example env file. You MUST configure JWT_SECRET_KEY and GOOGLE_CLIENT_ID
# for full authentication functionality.
copy .env.example .env
notepad .env # Or use your preferred editor like VS Code: code .env
# 3. Create virtual environment & install dependencies
python -m venv .venv
.\.venv\Scripts\activate
.\.venv\Scripts\python.exe -m pip install -r requirements.txt
# 4. Run database migrations
$env:FLASK_APP="manage.py"
.\.venv\Scripts\python.exe -m flask db upgrade
# 5. Seed the database with sample data
.\.venv\Scripts\python.exe seed.py
# 6. Start the development server
.\.venv\Scripts\python.exe run.py# 1. Start PostgreSQL (creates both dev and test databases automatically)
cd backend
docker compose up -d
# 2. Configure Environment Variables
# Copy the example env file. You MUST configure JWT_SECRET_KEY and GOOGLE_CLIENT_ID
# for full authentication functionality.
cp .env.example .env
nano .env
# 3. Create virtual environment & install dependencies
uv venv .venv
source .venv/bin/activate
uv pip install -r requirements.txt
# 4. Run database migrations
FLASK_APP=manage.py flask db upgrade
# 5. Seed the database with sample data
python seed.py
# 6. Start the development server
python run.pyThe backend server will be available at http://localhost:5000.
cd backend
source .venv/bin/activate
python -m pytest tests/ -vcd backend
.\.venv\Scripts\python.exe -m pytest tests -vAll 250 tests should pass (including 39 auth tests, 14 distributors tests, 16 team tests, 15 business-profile tests, and 21 invoice tests). Tests run against a separate pharmaflow_test database. Each test truncates all tables at teardown to ensure isolation, so committed mutations in route handlers are fine.
After running seed.py you can log in as any of three users on the
same pharmacy to walk the role matrix:
| Role | Password | Sees | |
|---|---|---|---|
vipul@vipulmedical.com |
OWNER | password123 |
Everything (expenses, analytics, team, settings). |
dharansh@email.com |
MANAGER | password123 |
Add Inventory, Returns, Past Invoices, generate invoices. |
ramesh@email.com |
STAFF | password123 |
Dashboard, Inventory browse, Generate Invoice, Settings. |
The seeded business already has a complete profile + bank account so invoices and debit notes can be printed out of the box. Log in as an OWNER first and watch for the Management sidebar section; it narrows as you switch to MANAGER and disappears entirely for STAFF.
To use Google Sign-In on the frontend:
- Go to the Google Cloud Console
- Create a new project (or use an existing one)
- Navigate to APIs & Services > Credentials
- Create an OAuth 2.0 Client ID (Application type: Web application)
- Add
http://localhost:5173to Authorized JavaScript origins - Copy the Client ID and add it to both
.envfiles:backend/.envasGOOGLE_CLIENT_IDfrontend/.envasVITE_GOOGLE_CLIENT_ID
backend/
βββ app/
β βββ __init__.py # Flask application factory + /api/uploads static route
β βββ extensions.py # SQLAlchemy & Migrate instances
β βββ utils.py # Shared parse/coerce helpers (date, expiry, int, float)
β βββ auth/ # register/login/me/change-password/google + profile picture
β βββ business/ # /api/business profile + bank + completeness gate
β βββ distributors/ # /api/distributors CRUD (settings-editor backing)
β βββ team/ # /api/team OWNER-only member CRUD + reactivation
β βββ invoices/ # /api/invoices (412 completeness gate, createdBy audit)
β βββ inventory/ # /api/inventory server-side filters
β βββ receipts/ # /api/receipts (OCR + manual + approve)
β βββ returns/ # /api/returns (debit notes)
β βββ expense/ # /api/expenses + categories
β βββ services/ # ocr_service (Google Cloud Vision integration)
β βββ models/
β βββ __init__.py # Re-exports all model classes
β βββ user.py # User, UserPasswordAuth, UserOAuthAccount
β βββ business.py # Business, BusinessMembership, BusinessBankAccount
β βββ distributor.py # Distributor
β βββ customer.py # Customer
β βββ product.py # Product
β βββ inventory.py # InventoryBatch, InventoryMovement
β βββ purchase.py # PurchaseReceipt, PurchaseReceiptItem, ReceiptScan
β βββ sales.py # SalesInvoice, SalesInvoiceItem, BatchAllocation
β βββ expense.py # ExpenseCategory, Expense
β βββ returns.py # ReturnRequest, ReturnRequestItem
β βββ audit.py # AuditLog
β βββ contact.py # ContactMessage
βββ migrations/ # Alembic migration versions
βββ tests/ # 250 tests (pytest) - see "Running Tests" below
β βββ conftest.py # Fixtures (seed_user returns OWNER with complete business)
β βββ test_auth.py # 39 tests incl. profile picture, must-change-password
β βββ test_business.py # 15 tests incl. completeness gate
β βββ test_distributors.py # 14 tests incl. rename-cascade + live-batch guard
β βββ test_team.py # 16 tests incl. soft-remove reactivation + last-owner guards
β βββ test_invoices_inventory.py # 21 tests incl. 412 gate + creator audit
β βββ test_receipts_inventory.py # 36 tests
β βββ test_returns.py # 30 tests
β βββ test_expenses.py # 9 tests
β βββ test_ocr.py # 22 tests (parser)
β βββ test_models.py # 42 model tests
β βββ test_seed.py # 6 tests
βββ docs/
β βββ api-spec.yaml # OpenAPI 3 - 44 paths, 56 schemas
β βββ settings-architecture.md # RBAC + team + distributors + profile picture (NEW)
β βββ auth-architecture.md # Auth primitives (JWT, decorators, password rotation)
β βββ invoices-architecture.md # FEFO allocation + completeness gate
β βββ inventory-architecture.md # Stock / receipts / returns / OCR
βββ uploads/ # UPLOAD_DIR default - profiles/ + receipts/
βββ config.py # Dev / Testing / Production configs
βββ manage.py # Flask CLI entry point
βββ seed.py # Sample data seeder (idempotent)
βββ docker-compose.yml # PostgreSQL 16 container
βββ requirements.txt # Python dependencies
βββ .env # Local environment variables
βββ .env.example # Template for .env
The core architecture assumes a multi-tenant Business model where everything is scoped to a pharmacy. The diagram below illustrates the relationships between key entities:
erDiagram
users ||--o{ business_memberships : "has"
businesses ||--o{ business_memberships : "has"
businesses ||--o{ products : "manages"
businesses ||--o{ distributors : "partners with"
businesses ||--o{ customers : "serves"
businesses ||--o{ expenses : "incurs"
businesses ||--o{ expense_categories : "defines"
businesses ||--o{ sales_invoices : "issues"
businesses ||--o{ purchase_receipts : "receives"
businesses ||--o{ inventory_batches : "holds"
businesses ||--o{ return_requests : "creates"
businesses ||--o{ audit_logs : "tracks"
distributors ||--o{ purchase_receipts : "supplies"
distributors ||--o{ return_requests : "receives"
distributors ||--o{ inventory_batches : "original source"
customers ||--o{ sales_invoices : "pays"
expense_categories ||--o{ expenses : "categorizes"
purchase_receipts ||--o{ purchase_receipt_items : "contains"
purchase_receipts ||--o| receipt_scans : "generated from"
products ||--o{ purchase_receipt_items : "bought as"
products ||--o{ sales_invoice_items : "sold as"
products ||--o{ inventory_batches : "stocked as"
products ||--o{ return_request_items : "returned as"
purchase_receipt_items ||--o| inventory_batches : "creates"
inventory_batches ||--o{ inventory_movements : "tracks history"
inventory_batches ||--o{ sales_invoice_item_batch_allocations : "allocated to"
sales_invoices ||--o{ sales_invoice_items : "contains"
sales_invoice_items ||--o{ sales_invoice_item_batch_allocations : "fulfilled by"
return_requests ||--o{ return_request_items : "contains"
users ||--o{ audit_logs : "performs actions"
| Command | Description |
|---|---|
docker compose up -d |
Start PostgreSQL container |
docker compose down |
Stop PostgreSQL container |
FLASK_APP=manage.py flask db migrate -m "msg" |
Generate a new migration |
FLASK_APP=manage.py flask db upgrade |
Apply pending migrations |
python seed.py |
Seed the database with sample data |
python -m pytest tests/ -v |
Run all tests with verbose output |
python -m pytest tests/ -v --cov=app |
Run tests with coverage report |