full-stack pharmacy workflow built on NestJS + React.
This repo now includes an end-to-end inventory workflow after authentication:
- create categories, suppliers, and products
- receive stock into expiry-aware batches
- sell products through a minimal POS screen
- allocate stock with FEFO on the backend
- record stock changes through
stock_movements - process constrained returns against original sales
docker compose up --buildBackend:
cd backend
npm install
npm run start:devFrontend:
cd frontend
npm install
npm run devThe existing dev setup still works with DATABASE_SYNCHRONIZE=true, but a reviewable migration and CLI config are now included.
Run migrations from backend/:
npm run migration:runRevert the last migration:
npm run migration:revertShow pending/applied migrations:
npm run migration:showFor a migration-driven environment, set:
DATABASE_SYNCHRONIZE=falseBackend inventory e2e:
cd backend
npx jest test/e2e/inventory.e2e.spec.ts --runInBand --verboseFull backend e2e suite:
cd backend
npm run test:e2eFrontend production build:
cd frontend
npm run buildThe forgot/reset password flow now switches by environment:
- non-production defaults to OTP reset by email
- production defaults to reset-link emails
PASSWORD_RESET_MODEcan override the default when you need to test a specific flow
Required backend mail variables:
SMTP_HOST=smtp.gmail.com
SMTP_PORT=587
SMTP_USER=your-gmail-address@gmail.com
SMTP_PASS=your-gmail-app-password
MAIL_FROM=your-gmail-address@gmail.com
FRONTEND_RESET_PASSWORD_URL=http://localhost:5173/auth/reset-password
PASSWORD_RESET_MODE=
PASSWORD_RESET_TOKEN_TTL_MINUTES=60
PASSWORD_RESET_OTP_LENGTH=6
PASSWORD_RESET_OTP_TTL_MINUTES=10
PASSWORD_RESET_OTP_MAX_ATTEMPTS=5
PASSWORD_RESET_EXPOSE_OTP=trueNotes:
SMTP_PASSmust be a Gmail app password, not your regular Gmail password.MAIL_FROMshould match the Gmail sender you configured.- In development and test mode, the backend sends an OTP email and also returns the OTP in the API response when
PASSWORD_RESET_EXPOSE_OTP=true. - In production or
PASSWORD_RESET_MODE=link, the backend sends a tokenized reset link and never exposes the token in the API response. - Automated backend tests use an in-memory mail transport; real SMTP delivery is used when
MAIL_TRANSPORT=smtpor when running outside the test environment with SMTP variables configured.
Manual OTP flow check:
curl -X POST http://localhost:3000/api/auth/forgot-password ^
-H "Content-Type: application/json" ^
-d "{\"email\":\"user@example.com\"}"curl -X POST http://localhost:3000/api/auth/reset-password ^
-H "Content-Type: application/json" ^
-d "{\"email\":\"user@example.com\",\"token\":\"<otp-code>\",\"newPassword\":\"NewSecure1!\"}"Manual reset-link flow check:
curl -X POST http://localhost:3000/api/auth/forgot-password ^
-H "Content-Type: application/json" ^
-d "{\"email\":\"user@example.com\"}"curl -X POST http://localhost:3000/api/auth/reset-password ^
-H "Content-Type: application/json" ^
-d "{\"token\":\"<reset-token>\",\"newPassword\":\"NewSecure1!\"}"- Sign in with a staff account.
- Open
/inventory. - Create a category, supplier, and product.
- Receive stock with a batch number, expiry date, quantity, and unit cost.
- Add the product to the cart and checkout.
- Load the sale ID in the return panel and process a partial return.
Inventory endpoints are guarded for admin and employee roles. The repo still does not include a dedicated role-management UI, so for manual testing you need a staff account already present or promoted in the database.