Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

13 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

🧁 Bakery Inventory & Kiosk System

A self-service ordering kiosk + complete bakery management platform β€” built for real shop operations.


Live Kiosk

GitHub License: MIT


React Node.js Express MongoDB Cloudinary Vercel Render


πŸ“– About

The Bakery Inventory & Kiosk System is a production-ready, full-stack web application that digitalises both the front-of-house and back-office operations of a small-to-mid-sized bakery. On the customer side, a large-screen self-service kiosk β€” styled like the ordering terminals at fast food chains β€” lets walk-in customers browse the menu, build their cart, and walk away with a token number in seconds, no cashier needed. On the owner side, a secure, JWT-protected dashboard gives complete control over the business: a live three-stage order pipeline, daily stock management that auto-disables sold-out products, custom wedding-cake orders with advance-payment tracking, raw material purchase logging, full price history records, and profit reports that calculate net income by subtracting daily expenses from completed-order revenue. Every price change is stored as a historical record rather than overwriting the old one, and every order permanently captures the price at time of sale β€” so financial reports are always accurate no matter how many times prices change in the future.


πŸ“š Table of Contents


🎬 Demo

πŸ“Ί Video Walkthrough

πŸŽ₯ Demo Video


πŸ“Έ Screenshots

🧍 Customer Kiosk

Customer Menu Checkout Token Screen
Customer Menu Checkout Token Screen
Product grid, categories, cart Cart review with customer details Order confirmed with token number

πŸ” Auth & Order Management

Login Dashboard Orders
Login Dashboard Orders
Owner authentication Live order pipeline Full historical order list

πŸ“¦ Product Management

Products Add Product Deleted Products
Products Add Product Deleted Products
Active catalogue and stock controls Image crop and Cloudinary upload Restore soft-deleted items

πŸ₯¦ Raw Materials & Purchases

Raw Materials Add Raw Material Deleted Raw Materials
Raw Materials Add Raw Material Deleted Raw Materials
Ingredient catalogue Register new ingredient Restore deleted materials
Raw Purchases Add Raw Purchase
Raw Purchases Add Raw Purchase
Track purchases and status Log new ingredient purchase

πŸ‘₯ Customers, Reports & Custom Orders

Customers Customer Details Reports
Customers Customer Details Reports
Customers grouped by phone Full order history per customer Daily and range profit reports
Add Custom Order Orders Summary
Add Custom Order Orders Summary
Bespoke order and advance payment tracking Summary of received orders

πŸ— System Architecture

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                         CLIENT LAYER                             β”‚
β”‚                                                                  β”‚
β”‚   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   β”‚
β”‚   β”‚   Customer Kiosk      β”‚    β”‚      Owner Dashboard       β”‚   β”‚
β”‚   β”‚   /menu               β”‚    β”‚   /login  /dashboard       β”‚   β”‚
β”‚   β”‚   /checkout           β”‚    β”‚   /orders /products  etc.  β”‚   β”‚
β”‚   β”‚        β”‚    β”‚                            β”‚   β”‚
β”‚   β”‚   (Public Routes)     β”‚    β”‚   (JWT Protected Routes)   β”‚   β”‚
β”‚   β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜   β”‚
β”‚              β”‚                                β”‚                  β”‚
β”‚              β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                  β”‚
β”‚                              β”‚  React.js + React Router          β”‚
β”‚                              β”‚  Deployed on Vercel               β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                               β”‚ HTTPS REST API
                               β”‚ (REACT_APP_API_URL)
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                          API LAYER                               β”‚
β”‚                              β”‚                                   β”‚
β”‚               β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                    β”‚
β”‚               β”‚      Express.js Server      β”‚                    β”‚
β”‚               β”‚   Node.js  Β·  server.js     β”‚                    β”‚
β”‚               β”‚   Deployed on Render        β”‚                    β”‚
β”‚               β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                    β”‚
β”‚                              β”‚                                   β”‚
β”‚       β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”            β”‚
β”‚       β”‚                      β”‚                     β”‚            β”‚
β”‚  β”Œβ”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”   β”‚
β”‚  β”‚ JWT Auth  β”‚   β”‚   12 Controllers      β”‚  β”‚  Cloudinary  β”‚   β”‚
β”‚  β”‚Middleware β”‚   β”‚   (business logic)    β”‚  β”‚  Middleware  β”‚   β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜   β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜   β”‚
└─────────────────────────────-β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                               β”‚ Mongoose ODM
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                         DATA LAYER                               β”‚
β”‚                              β”‚                                   β”‚
β”‚               β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                    β”‚
β”‚               β”‚       MongoDB Atlas         β”‚                    β”‚
β”‚               β”‚                            β”‚                    β”‚
β”‚               β”‚  users Β· products          β”‚                    β”‚
β”‚               β”‚  orders Β· customorders     β”‚                    β”‚
β”‚               β”‚  stockentries              β”‚                    β”‚
β”‚               β”‚  rawmaterials Β· rawpurchasesβ”‚                   β”‚
β”‚               β”‚  productprices Β· categoriesβ”‚                    β”‚
β”‚               β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                    β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                  External: Cloudinary CDN
                  (image upload, optimisation, delivery)

πŸ”„ Application Workflows

1. Customer Order Flow

Customer arrives at kiosk screen  β†’  /menu
             β”‚
             β–Ό
  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
  β”‚   Browse Products    β”‚  ← GET /api/products  (active, non-deleted)
  β”‚   Filter by category β”‚    GET /api/stock/today (0-stock items hidden)
  β”‚   See images + price β”‚
  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
             β”‚  Taps "Add to Cart"
             β–Ό
  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
  β”‚   Cart Sidebar       β”‚  ← Adjust qty, see running total
  β”‚   Review items       β”‚
  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
             β”‚  Taps "Checkout"  β†’  /checkout
             β–Ό
  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
  β”‚  Enter Name +        β”‚  ← Required: customerName, customerPhone
  β”‚  Phone Number        β”‚
  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
             β”‚  Confirms order
             β–Ό
  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
  β”‚  POST /api/orders    β”‚  ← Backend saves order
  β”‚                      β”‚     Β· priceAtSale stored per line item
  β”‚                      β”‚     Β· Token number generated
  β”‚                      β”‚     Β· Customer phone saved for history
  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
             β”‚
             β–Ό
  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
  β”‚  Token Number Screen β”‚  ← 
  β”‚  shown on kiosk      β”‚     Customer waits, shows token at counter
  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

2. Owner Order Pipeline

New order arrives  β†’  Dashboard (/dashboard)
              β”‚
              β–Ό
      β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
      β”‚   PENDING     β”‚  ← Visible immediately after customer submits
      β”‚               β”‚     Shows: token#, customer name, items, total
      β””β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜
              β”‚  Owner clicks "Mark Done"
              β”‚  PATCH /api/orders/:id/done
              β–Ό
      β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
      β”‚     DONE      β”‚  ← Staff are actively preparing this order
      β”‚               β”‚     Visual signal to kitchen/counter
      β””β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜
              β”‚  Owner clicks "Complete"
              β”‚  PATCH /api/orders/:id/complete
              β–Ό
      β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
      β”‚   COMPLETED   β”‚  ← Order handed to customer
      β”‚               β”‚     βœ“ Stock auto-deducted per item qty
      β”‚               β”‚     βœ“ Revenue logged for profit reports
      β”‚               β”‚     βœ“ Customer order history updated
      β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

      At any stage β†’ "Cancel"  β†’  PATCH /api/orders/:id/cancel
                                   No stock deducted Β· No revenue logged

      Full history viewable on:  /orders  and  /orders-summary

3. Daily Stock Workflow

Every morning β€” owner opens Stock management
              β”‚
              β–Ό
  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
  β”‚  Enter qty for each     β”‚  ← POST /api/stock/update
  β”‚  product                β”‚     Creates fresh StockEntry docs
  β”‚  e.g. croissants: 20    β”‚     keyed by productId + today's date
  β”‚       cakes: 8          β”‚
  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
               β”‚
               β–Ό
  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
  β”‚  Kiosk reflects stock   β”‚  ← GET /api/stock/today
  β”‚  Products with qty = 0  β”‚     Zero-stock items hidden on /menu
  β”‚  are hidden from menu   β”‚
  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
               β”‚  Throughout the day, as orders complete...
               β–Ό
  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
  β”‚  Stock auto-decrements  β”‚  ← On PATCH .../complete
  β”‚  per completed order    β”‚     orderController deducts ordered qty
  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
               β”‚
               β–Ό
  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
  β”‚  Product hits 0 stock   β”‚  ← Automatically disabled on kiosk
  β”‚                         β”‚     Cannot be ordered until restocked
  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

4. Price Change Workflow

Owner sets a new price for a product
              β”‚
              β–Ό
  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
  β”‚  POST /api/prices                                β”‚
  β”‚  { productId, newPrice }                         β”‚
  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                     β”‚
                     β–Ό
  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
  β”‚  Backend closes current ProductPrice record      β”‚
  β”‚  Sets  toDate = today  on the existing record    β”‚
  β”‚                                                  β”‚
  β”‚  Creates NEW ProductPrice record:                β”‚
  β”‚  { price: newPrice, fromDate: today, toDate: null}β”‚
  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                     β”‚
                     β–Ό
  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
  β”‚  Future orders  β†’  use new price                 β”‚
  β”‚  Past orders    β†’  priceAtSale unchanged          β”‚
  β”‚  Full history   β†’  always queryable              β”‚
  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

5. Custom Order Workflow

Customer contacts owner (phone / walk-in) for a wedding cake, bulk order, etc.
              β”‚
              β–Ό
  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
  β”‚  Owner opens             β”‚
  β”‚  AddCustomOrder page     β”‚  ← POST /api/custom-orders
  β”‚                          β”‚     Stores: customerName, customerPhone,
  β”‚  Enters:                 β”‚     description, totalPrice,
  β”‚  Β· Customer details      β”‚     advancePaid, balanceDue,
  β”‚  Β· Order description     β”‚     status = PENDING
  β”‚  Β· Total price           β”‚
  β”‚  Β· Advance payment       β”‚
  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
               β”‚
               β–Ό
       β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
       β”‚   PENDING     β”‚  ← Logged, advance recorded, balance tracked
       β””β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜
               β”‚  PATCH /api/custom-orders/:id/done
               β–Ό
       β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
       β”‚     DONE      β”‚  ← Custom item is being prepared
       β””β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜
               β”‚  PATCH /api/custom-orders/:id/complete
               β–Ό
       β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
       β”‚   COMPLETED   β”‚  ← Delivered Β· Balance collected
       β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

6. Raw Material Purchase Workflow

Owner needs to buy ingredients / packaging supplies
              β”‚
              β–Ό
  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
  β”‚  Ensure material exists β”‚  ← POST /api/raw-materials  (if new)
  β”‚  in catalogue           β”‚     e.g. "Refined Flour", "Butter"
  β”‚  (RawMaterials page)    β”‚
  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
               β”‚
               β–Ό
  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
  β”‚  Log the purchase       β”‚  ← POST /api/raw-purchases
  β”‚  (AddRawPurchase page)  β”‚     Stores: material(ref), qty,
  β”‚                         β”‚     unitPrice, totalCost,
  β”‚                         β”‚     purchaseDate, status = PENDING
  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
               β”‚
               β–Ό
       β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
       β”‚   PENDING   β”‚  ← Ordered, not yet received
       β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”˜
              β”‚  Goods arrive  β†’  PATCH /api/raw-purchases/:id/done
              β–Ό
       β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
       β”‚    DONE     β”‚  ← Confirmed received
       β”‚             β”‚     βœ“ Cost recorded as expense for that date
       β”‚             β”‚     βœ“ Feeds into daily expense report
       β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

7. Reporting Workflow

Owner opens Reports page  β†’  /reports
         β”‚
         β”œβ”€β”€ Daily Profit Report ─────────────────────────────────────┐
         β”‚   GET /api/reports/profit?date=YYYY-MM-DD                  β”‚
         β”‚                                                             β”‚
         β”‚   Revenue  = SUM of priceAtSale Γ— qty  (completed orders)  β”‚
         β”‚   Expenses = SUM of totalCost  (confirmed raw purchases)   β”‚
         β”‚   ─────────────────────────────────────────────────────    β”‚
         β”‚   Net Profit = Revenue βˆ’ Expenses                          β”‚
         β”‚                                                             β”‚
         β”œβ”€β”€ Date Range Profit Report ─────────────────────────────────
         β”‚   GET /api/reports/profit/range?start=...&end=...          β”‚
         β”‚                                                             β”‚
         β”‚   Same calculation across every day in the range           β”‚
         β”‚   Useful for weekly / monthly / custom period reviews      β”‚
         β”‚                                                             β”‚
         └── Daily Expense Report β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
             GET /api/reports/expense?date=YYYY-MM-DD

             Full breakdown of every confirmed raw purchase for the day

✨ Features β€” All 18 Pages

🧍 Customer-Facing Pages (Public β€” No Login Required)

# Route What It Does
1 /menu Large-screen kiosk product grid. Shows all active products with Cloudinary images, current prices, and live stock status. Products with zero stock are hidden. Customers browse by category and add items to the cart.
2 /checkout Cart review screen with quantity controls and a live running total. Customer enters their name and phone number. On confirmation, the order is submitted to the backend with priceAtSale locked per item.
3 Token number receipt screen displayed after a successful order. The large token number tells the customer what to quote at the counter when their order is ready. Also used by the owner as a read-only order summary list.

πŸ” Auth

# Route What It Does
4 /login Owner login form. Sends credentials to POST /api/auth/login, receives a JWT token, stores it in localStorage, and redirects to the dashboard. All subsequent protected routes read this token from storage.

πŸ“‹ Order Management

# Route What It Does
5 /dashboard The owner's primary working view. Displays three live columns β€” Pending, Done, Completed β€” with action buttons on each order card. Completing an order triggers automatic stock deduction.
6 /orders Full searchable and filterable history of all orders across all statuses. Useful for resolving disputes and reviewing past activity.

πŸ“¦ Product Management

# Route What It Does
7 /products Active product catalogue. Each product card shows its image, name, category, current price, and enable/disable toggle. Soft-delete sends the product to the deleted list without breaking order history.
8 /add-product Form to add a new product. The owner fills in name, category, and price, then uploads an image which passes through the image cropper for consistent aspect-ratio cropping before being sent to Cloudinary.
9 /deleted-products Lists all soft-deleted products. Each entry has a Restore button that sets isDeleted: false and makes the product visible on the kiosk again. Order history referencing the product is always preserved.

πŸ₯¦ Raw Material Management

# Route What It Does
10 /raw-materials Full catalogue of all active raw materials used in the bakery. Shows name, unit of measure, and category. Links to add new entries and view deleted ones.
11 /add-raw-material Form to register a new raw material in the catalogue before purchases can be logged against it. Captures name, unit (kg, litres, etc.), and category.
12 /deleted-raw-materials Lists all soft-deleted raw materials with a restore button. Prevents orphaned purchase records by never hard-deleting materials that have purchase history.

πŸ›’ Raw Purchase Tracking

# Route What It Does
13 /raw-purchases All logged raw material purchases with Pending/Done status, quantities, unit price, total cost, and purchase date. Confirmed (Done) purchases feed the daily expense report.
14 /add-raw-purchase Form to log a new raw material purchase. The owner selects the material from the catalogue, enters quantity and unit price, and the total cost is calculated automatically.

πŸ‘₯ Customer Management

# Route What It Does
15 /customers All customers who have ever placed an order, grouped by phone number. Shows order count and total spend per customer. Useful for identifying loyal repeat customers.
16 /customers/:phone Full order history for one specific phone number β€” every order placed, with date, items ordered, and amount paid. Useful for resolving customer queries.

πŸ“ˆ Reports & Custom Orders

# Route What It Does
17 /reports Daily and date-range profit reports (revenue minus expenses) and daily expense breakdowns. Revenue figures use priceAtSale values from completed orders so historical reports are always accurate.
18 /add-custom-order Form to create a bespoke order (wedding cakes, bulk event orders). Records customer details, order description, total agreed price, advance payment received, and outstanding balance due.

πŸ›  Tech Stack

Technology Purpose Why Chosen
React.js 18 Frontend UI Component model cleanly separates kiosk and dashboard into independent trees; hooks make cart state and form logic simple
React Router v6 Client-side routing Declarative protected-route wrappers let /menu stay fully public while /dashboard and beyond require a valid JWT
Node.js 18 Backend runtime Non-blocking I/O handles simultaneous order POSTs from the kiosk without queuing or blocking the owner's dashboard requests
Express.js 4 HTTP server and API Minimal, unopinionated; thin route files delegate to controller files β€” concerns are cleanly separated throughout
MongoDB Atlas Primary database Document model accommodates the varied shapes of orders, custom orders, stock entries, and price history without schema migrations
Mongoose MongoDB ODM Schema validation, pre-save hooks (e.g. auto-disable on zero stock), and clean query syntax over the raw MongoDB driver
JWT Owner authentication Stateless token auth is ideal for a single-admin system; no session table needed, works seamlessly across Vercel + Render
Cloudinary Image storage & CDN Handles upload, optimisation, resizing, and global delivery β€” the Node server never serves a single static asset
ImageCropper In-browser image crop Ensures all product images are cropped to a consistent aspect ratio before upload, keeping the kiosk grid visually uniform
Vercel Frontend hosting Zero-config React deployment with automatic HTTPS and global edge CDN
Render Backend hosting Free-tier Node.js hosting with persistent environment variables and straightforward MongoDB Atlas connectivity

πŸ“ Project Structure

INVENTORYSYSTEM/
β”‚
β”œβ”€β”€ backend/
β”‚   β”‚
β”‚   β”œβ”€β”€ controllers/                          # Business logic β€” 12 files, one per domain
β”‚   β”‚   β”œβ”€β”€ authController.js                 # Validate credentials, issue JWT
β”‚   β”‚   β”œβ”€β”€ categoryController.js             # Product category CRUD
β”‚   β”‚   β”œβ”€β”€ customerController.js             # Aggregate orders grouped by phone number
β”‚   β”‚   β”œβ”€β”€ customOrderController.js          # Create + Pendingβ†’Doneβ†’Completed flow for custom orders
β”‚   β”‚   β”œβ”€β”€ expenseReportController.js        # Sum confirmed raw-purchase costs per date
β”‚   β”‚   β”œβ”€β”€ orderController.js                # Place order (priceAtSale), status transitions, stock deduction
β”‚   β”‚   β”œβ”€β”€ productController.js              # CRUD + Cloudinary upload + soft-delete + enable/disable
β”‚   β”‚   β”œβ”€β”€ productPriceController.js         # Close old price record, open new; current-price query
β”‚   β”‚   β”œβ”€β”€ profitReportController.js         # Revenue βˆ’ expenses for day or date range
β”‚   β”‚   β”œβ”€β”€ rawMaterialController.js          # Catalogue CRUD + soft-delete
β”‚   β”‚   β”œβ”€β”€ rawPurchaseController.js          # Log purchase, confirm receipt β†’ records as expense
β”‚   β”‚   └── stockController.js               # Daily stock entry, today's query, zero-stock disabling
β”‚   β”‚
β”‚   β”œβ”€β”€ models/                               # Mongoose schemas β€” shape of every MongoDB document
β”‚   β”‚   β”œβ”€β”€ Category.js                       # { name, description }
β”‚   β”‚   β”œβ”€β”€ CustomOrder.js                    # { customerName, phone, description, totalPrice, advancePaid, balanceDue, status }
β”‚   β”‚   β”œβ”€β”€ Order.js                          # { customerName, phone, tokenNumber, items[{product, name, qty, priceAtSale}], status }
β”‚   β”‚   β”œβ”€β”€ Product.js                        # { name, category, imageUrl, cloudinaryId, isEnabled, isDeleted }
β”‚   β”‚   β”œβ”€β”€ ProductPrice.js                   # { product, price, fromDate, toDate }  β€” append-only history
β”‚   β”‚   β”œβ”€β”€ RawMaterial.js                    # { name, unit, category, isDeleted }
β”‚   β”‚   β”œβ”€β”€ RawPurchase.js                    # { material(ref), qty, unitPrice, totalCost, purchaseDate, status }
β”‚   β”‚   β”œβ”€β”€ StockEntry.js                     # { product(ref), date, quantityAdded, quantityRemaining }
β”‚   β”‚   └── User.js                           # { email, password(hashed) }  β€” single owner account
β”‚   β”‚
β”‚   β”œβ”€β”€ routes/                               # Express route definitions β€” thin, delegates to controllers
β”‚   β”‚   β”œβ”€β”€ authRoutes.js                     # POST /api/auth/login
β”‚   β”‚   β”œβ”€β”€ categoryRoutes.js                 # CRUD /api/categories
β”‚   β”‚   β”œβ”€β”€ customerRoutes.js                 # GET /api/customers  GET /api/customers/:phone
β”‚   β”‚   β”œβ”€β”€ customOrderRoutes.js              # CRUD + status /api/custom-orders
β”‚   β”‚   β”œβ”€β”€ expenseReportRoutes.js            # GET /api/reports/expense
β”‚   β”‚   β”œβ”€β”€ orderRoutes.js                    # POST + GET + status PATCH /api/orders
β”‚   β”‚   β”œβ”€β”€ productPriceRoutes.js             # POST + GET /api/prices
β”‚   β”‚   β”œβ”€β”€ productRoutes.js                  # Full product management /api/products
β”‚   β”‚   β”œβ”€β”€ profitReportRoutes.js             # GET /api/reports/profit  /api/reports/profit/range
β”‚   β”‚   β”œβ”€β”€ rawMaterialRoutes.js              # CRUD /api/raw-materials
β”‚   β”‚   β”œβ”€β”€ rawPurchaseRoutes.js              # CRUD + confirm /api/raw-purchases
β”‚   β”‚   └── stockRoutes.js                    # GET today + POST update /api/stock
β”‚   β”‚
β”‚   β”œβ”€β”€ middleware/
β”‚   β”‚   β”œβ”€β”€ authMiddleware.js                 # Verify JWT on every protected route, attach owner to req
β”‚   β”‚   └── cloudinaryConfig.js              # Initialise Cloudinary SDK + multer/upload config
β”‚   β”‚
β”‚   β”œβ”€β”€ setup.js                              # One-time seed: creates default owner account
β”‚   β”œβ”€β”€ server.js                             # Entry point: connect MongoDB, mount routes, listen
β”‚   └── package.json
β”‚
β”œβ”€β”€ frontend/
β”‚   └── src/
β”‚       β”‚
β”‚       β”œβ”€β”€ pages/                            # 18 full-page route components
β”‚       β”‚   β”‚
β”‚       β”‚   β”‚   ── CUSTOMER KIOSK (PUBLIC) ──────────────────────────
β”‚       β”‚   β”œβ”€β”€ CustomerMenu.js               # Product grid kiosk β€” browse, filter by category, add to cart
β”‚       β”‚   β”œβ”€β”€ Checkout.js                   # Cart review + name/phone entry + submit order
β”‚       β”‚   β”œβ”€β”€ OrdersSummary.js              # Token number receipt (public) + order summary (owner)
β”‚       β”‚   β”‚
β”‚       β”‚   β”‚   ── AUTH ───────────────────────────────────────────────
β”‚       β”‚   β”œβ”€β”€ Login.js                      # Owner login form + JWT storage
β”‚       β”‚   β”‚
β”‚       β”‚   β”‚   ── ORDERS ─────────────────────────────────────────────
β”‚       β”‚   β”œβ”€β”€ Dashboard.js                  # Live pipeline: Pending β†’ Done β†’ Completed
β”‚       β”‚   β”œβ”€β”€ Orders.js                     # Full historical order list, all statuses, searchable
β”‚       β”‚   β”‚
β”‚       β”‚   β”‚   ── PRODUCTS ────────────────────────────────────────────
β”‚       β”‚   β”œβ”€β”€ Products.js                   # Active catalogue with enable/disable/soft-delete
β”‚       β”‚   β”œβ”€β”€ AddProduct.js                 # Add product form β€” ImageCropper β†’ Cloudinary upload
β”‚       β”‚   β”œβ”€β”€ DeletedProducts.js            # Soft-deleted products with restore
β”‚       β”‚   β”‚
β”‚       β”‚   β”‚   ── RAW MATERIALS ──────────────────────────────────────
β”‚       β”‚   β”œβ”€β”€ RawMaterials.js               # Active raw material catalogue
β”‚       β”‚   β”œβ”€β”€ AddRawMaterial.js             # Add new raw material to catalogue
β”‚       β”‚   β”œβ”€β”€ DeletedRawMaterials.js        # Soft-deleted materials with restore
β”‚       β”‚   β”‚
β”‚       β”‚   β”‚   ── RAW PURCHASES ──────────────────────────────────────
β”‚       β”‚   β”œβ”€β”€ RawPurchases.js               # All logged purchases with Pending/Done status
β”‚       β”‚   β”œβ”€β”€ AddRawPurchase.js             # Log a new raw material purchase
β”‚       β”‚   β”‚
β”‚       β”‚   β”‚   ── CUSTOMERS ─────────────────────────────────────────
β”‚       β”‚   β”œβ”€β”€ Customers.js                  # All customers grouped by phone, order count, spend
β”‚       β”‚   β”œβ”€β”€ CustomerDetails.js            # Full order history for one phone number
β”‚       β”‚   β”‚
β”‚       β”‚   β”‚   ── REPORTS & CUSTOM ORDERS ────────────────────────────
β”‚       β”‚   β”œβ”€β”€ Reports.js                    # Profit + expense reports, daily and date-range
β”‚       β”‚   └── AddCustomOrder.js             # Bespoke order with advance payment tracking
β”‚       β”‚
β”‚       β”œβ”€β”€ components/                       # Reusable UI components
β”‚       β”‚   β”œβ”€β”€ Header.js                     # Owner dashboard nav bar with active route highlighting
β”‚       β”‚   └── ImageCropper.js              # In-browser aspect-ratio crop before Cloudinary upload
β”‚       β”‚
β”‚       β”œβ”€β”€ App.js                            # Route table, JWT auth context, protected route logic
β”‚       └── index.js                          # React DOM render entry point
β”‚
β”œβ”€β”€ assets/
β”‚   └── screenshots/                          # Screenshot images (add yours here)
β”‚
└── .gitignore

πŸš€ Getting Started

Prerequisites

Requirement Version Where to Get
Node.js v18 or higher nodejs.org
npm v9 or higher Bundled with Node.js
MongoDB Atlas (free) or local mongodb.com/atlas
Cloudinary account Free tier cloudinary.com β€” need Cloud Name, API Key, API Secret

Backend Setup

# 1. Clone the repository
git clone https://github.com/iit2023271/INVENTORYSYSTEM.git
cd INVENTORYSYSTEM

# 2. Enter the backend directory
cd backend

# 3. Install dependencies
npm install

# 4. Create your environment file and fill in values (see below)
cp .env.example .env

# 5. Start the development server
npm run dev
# API running at http://localhost:5000

Backend .env

Create backend/.env β€” every variable is required.

# ── DATABASE ─────────────────────────────────────────────────────────
# MongoDB Atlas:  mongodb+srv://<user>:<pass>@cluster.mongodb.net/<db>
# Local:          mongodb://localhost:27017/bakery
MONGO_URI=mongodb+srv://your_user:your_password@cluster.mongodb.net/bakery

# ── AUTH ─────────────────────────────────────────────────────────────
# Long random string used to sign JWTs β€” never commit this
# Generate: node -e "console.log(require('crypto').randomBytes(64).toString('hex'))"
JWT_SECRET=your_super_secret_jwt_key_minimum_32_characters

# ── OWNER SETUP ──────────────────────────────────────────────────────
# Passphrase checked by setup.js when seeding the owner account
OWNER_SECRET=your_owner_setup_passphrase

# ── SERVER ───────────────────────────────────────────────────────────
PORT=5000

# ── CLOUDINARY ───────────────────────────────────────────────────────
# Found at cloudinary.com β†’ Dashboard β†’ Settings β†’ API Keys
CLOUDINARY_CLOUD_NAME=your_cloud_name
CLOUDINARY_API_KEY=your_api_key
CLOUDINARY_API_SECRET=your_api_secret

Frontend Setup

# From the project root
cd frontend

# Install dependencies
npm install

# Create environment file and fill in your backend URL
cp .env.example .env

# Start React dev server
npm start
# App opens at http://localhost:3000

Frontend .env

Create frontend/.env:

# ── API URL ───────────────────────────────────────────────────────────
# Local development:
REACT_APP_API_URL=http://localhost:5000

# Production (replace with your Render URL after deploying the backend):
# REACT_APP_API_URL=https://your-backend-name.onrender.com

First-Time Owner Account

The system enforces exactly one owner. Before logging into the dashboard, run the setup script once:

# From the backend/ directory
npm run setup

Default credentials created by the script:

Field Value
Email admin@shop.com
Password admin123

⚠️ Change the password immediately after first login. The script refuses to run a second time if an owner already exists β€” this prevents accidental overwrites in production.


Access URLs

Local:

Interface URL Auth
Customer Kiosk http://localhost:3000/menu ❌ None
Checkout http://localhost:3000/checkout ❌ None
Owner Login http://localhost:3000/login β€”
Owner Dashboard http://localhost:3000/dashboard βœ… JWT
Backend API http://localhost:5000 Varies

Production:

Interface URL
Customer Kiosk https://inventorysystem-silk.vercel.app/menu
Owner Dashboard https://inventorysystem-silk.vercel.app

πŸ“‘ API Reference

Protected routes require a valid JWT in the Authorization header:

Authorization: Bearer <token_from_login>

πŸ” Auth

Method Endpoint Description Auth
POST /api/auth/login Owner login β€” returns JWT token ❌

πŸ“‹ Orders

Method Endpoint Description Auth
POST /api/orders Place order from kiosk. Stores priceAtSale per item. Returns token number. ❌
GET /api/orders Get all orders (all statuses) βœ…
PATCH /api/orders/:id/done Move order Pending β†’ Done βœ…
PATCH /api/orders/:id/complete Complete order β€” deducts stock, records revenue βœ…
PATCH /api/orders/:id/cancel Cancel order at any stage βœ…

🧁 Products

Method Endpoint Description Auth
GET /api/products All active (non-deleted) products ❌
POST /api/products Add product with Cloudinary image upload βœ…
GET /api/products/deleted All soft-deleted products βœ…
PATCH /api/products/:id/restore Restore soft-deleted product βœ…
PATCH /api/products/:id/enable Enable product on kiosk βœ…
PATCH /api/products/:id/disable Disable product from kiosk βœ…
DELETE /api/products/:id Soft-delete product βœ…

πŸ“Š Stock

Method Endpoint Description Auth
GET /api/stock/today Today's stock quantities for all products ❌
POST /api/stock/update Set today's stock quantities (fresh daily entry) βœ…

πŸ’° Product Prices

Method Endpoint Description Auth
POST /api/prices Set new price β€” closes old record, opens new βœ…
GET /api/prices/current Current active price for all products βœ…
GET /api/prices/:productId/history Full price history for one product βœ…

πŸ“ˆ Reports

Method Endpoint Description Auth
GET /api/reports/profit Daily profit: ?date=YYYY-MM-DD βœ…
GET /api/reports/profit/range Date-range profit: ?start=...&end=... βœ…
GET /api/reports/expense Daily expenses: ?date=YYYY-MM-DD βœ…

πŸ‘₯ Customers

Method Endpoint Description Auth
GET /api/customers All customers grouped by phone with order history βœ…
GET /api/customers/:phone Full order history for one phone number βœ…

πŸŽ‚ Custom Orders

Method Endpoint Description Auth
POST /api/custom-orders Create custom order with advance payment βœ…
GET /api/custom-orders Get all custom orders βœ…
PATCH /api/custom-orders/:id/done Move to Done βœ…
PATCH /api/custom-orders/:id/complete Complete custom order βœ…
PATCH /api/custom-orders/:id/cancel Cancel custom order βœ…

πŸ₯¦ Raw Materials

Method Endpoint Description Auth
POST /api/raw-materials Add material to catalogue βœ…
GET /api/raw-materials All active raw materials βœ…
GET /api/raw-materials/deleted Soft-deleted materials βœ…
PATCH /api/raw-materials/:id/restore Restore deleted material βœ…
DELETE /api/raw-materials/:id Soft-delete material βœ…

πŸ›’ Raw Purchases

Method Endpoint Description Auth
POST /api/raw-purchases Log new purchase (status = Pending) βœ…
GET /api/raw-purchases All logged purchases βœ…
PATCH /api/raw-purchases/:id/done Confirm received β†’ records as expense βœ…


πŸ—„ Database Schema

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  User                                                β”‚
β”‚  _id Β· email Β· password(bcrypt) Β· createdAt          β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

                          
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  Product                                             β”‚
β”‚  _id Β· name Β· category(ref) Β· imageUrl              β”‚
β”‚  cloudinaryId Β· isEnabled Β· isDeleted Β· createdAt   β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
           β”‚ ref                      β”‚ ref
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  ProductPrice       β”‚  β”‚  StockEntry              β”‚
β”‚  _id                β”‚  β”‚  _id                     β”‚
β”‚  product(ref)       β”‚  β”‚  product(ref)            β”‚
β”‚  price              β”‚  β”‚  date                    β”‚
β”‚  fromDate           β”‚  β”‚  quantityAdded           β”‚
β”‚  toDate(null=active)β”‚  β”‚  quantityRemaining       β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  Order                                               β”‚
β”‚  _id Β· customerName Β· customerPhone Β· tokenNumber   β”‚
β”‚  status (pending/done/completed/cancelled)          β”‚
β”‚  items: [{ product(ref), name, qty, priceAtSale }]  β”‚
β”‚  totalAmount Β· createdAt                            β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  CustomOrder                                         β”‚
β”‚  _id Β· customerName Β· customerPhone Β· description   β”‚
β”‚  status Β· totalPrice Β· advancePaid Β· balanceDue     β”‚
β”‚  createdAt                                          β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  RawMaterial                                         β”‚
β”‚  _id Β· name Β· unit Β· category Β· isDeleted Β· createdAtβ”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                          β”‚ ref
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  RawPurchase                                         β”‚
β”‚  _id Β· material(ref) Β· quantity Β· unitPrice         β”‚
β”‚  totalCost Β· purchaseDate                           β”‚
β”‚  status (pending/done) Β· createdAt                  β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

🧠 Design Decisions

1. Single Owner Account β€” Enforced at Architecture Level

The system is designed for one bakery operator, not a multi-tenant SaaS product. Rather than building role management and team-access features that would triple complexity without any business value here, the architecture enforces a hard constraint: setup.js refuses to create a second owner account if one already exists, and the login endpoint only ever issues tokens against that single record. This keeps authentication down to a standard JWT flow with no refresh tokens, no session tables, and no permission checks beyond "is this a valid owner token." If the business grows to need a team, the only change required is adding a role field to the User schema and a single middleware check β€” the foundation is already correct.

2. Daily Stock Entries Instead of a Running Inventory Counter

Most inventory systems maintain a running counter and decrement it on every sale. A bakery does not work that way β€” croissants are baked fresh every morning in specific quantities, and yesterday's unsold stock is written off or repurposed, not carried forward. The system models this reality by requiring the owner to enter fresh quantities each morning. If the owner forgets to enter stock, no product is shown as available with a stale count from a previous day. This also makes the stock entry ritual meaningful β€” it is the owner's daily signal to the system about what is available today, not a ledger correction.

3. Price History as Immutable Append-Only Records

When a price changes, the system closes the current ProductPrice record by setting its toDate to today, then creates a new record with fromDate set to today and toDate as null (signalling "currently active"). The old record is never updated or deleted. This means the full pricing timeline for every product is always queryable β€” you can find exactly what a product cost on any date in the past. It provides an automatic audit trail, makes the priceAtSale pattern reliable (below), and costs virtually nothing in storage since price changes are infrequent relative to orders.

4. priceAtSale Stored Directly on Every Order Line Item

When an order is placed, the backend looks up the currently active price for each product and stores it directly on the order document as priceAtSale. This makes every order a self-contained financial record β€” it knows what was sold, how many units, and exactly what was charged at that moment in time. Without this, any profit calculation for a past period would silently recalculate using today's prices rather than the prices actually charged, making historical reports unreliable. This is standard practice in every serious commerce system and is just as important for a small bakery as it is for a large e-commerce platform.

5. Soft Delete for Products and Raw Materials

Deleting a product or raw material sets an isDeleted: true flag rather than removing the document. This is essential for referential integrity: every Order references product IDs in its line items, and every RawPurchase references a raw material ID. A hard delete would orphan those references and make historical records unreadable. Soft delete preserves the full history, allows the owner to restore an item deleted by mistake, and costs nothing in query performance because all normal listing queries simply filter on isDeleted: false. The dedicated Deleted Products and Deleted Raw Materials pages give the owner complete visibility and control.


6.Everything is Mobile Friendl

Everyting can be controlled in the mobile itself.so this is the biggest advantage. assuming owner doesnt know any technical knowledge, this website is made for them.

πŸ‘€ Author

SALADI VISHNU VARDHAN β€” Full-stack developer Β· IIIT ALLAHABAD

Built this system to solve a real operational problem: replacing the paper-and-shouting workflow of a busy bakery counter with a clean digital kiosk and owner dashboard β€” from customer token to end-of-day profit report.

GitHub Live Demo


πŸ“„ License

This project is licensed under the MIT License.

MIT License

Copyright (c) 2025 iit2023271

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

Made with β˜•, flour, and way too many console.log statements.

⭐ Star this repo if it helped you β€” it takes one click and means a lot.

πŸ”— Live Kiosk Β· πŸ”— Owner Dashboard Β· πŸ”— GitHub

About

🧁 Bakery POS & Inventory System β€” self-service kiosk for customers + powerful owner dashboard. Real-time stock, profit reports, price history, custom orders & more. MERN Stack.

Topics

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages