Skip to content
cover

Projects

Roach

Roach is a small web shop I can hand to someone who sells from a counter. They open it in a phone browser, photograph a product, stick a QR label on the shelf, and later scan that label to add stock or to bill a sale. The bill remembers the selling price and the cost, so the shop can see what it made. A new shop gets a month to try it. After that, the person running the server writes down that the cash was paid, and the shop opens for another month. If they do not pay, the door closes, and the shelves stay as they were.

React JS · Node JS · HTML · CSS · MongoDB · Docker

1.Overview

I kept picturing one person, one phone, and a rack of goods. They do not want a billing machine. They want to know what is left, what went out today, and whether the price they charge is above what they paid.

Roach is that notebook, moved onto a site. A shop registers with a name, an email, and a password. From then on the dashboard is their own: their products, their stock value, their empty shelves. They add an item with a name, a category, a selling price, a cost, a quantity, and a reorder level. Description, benefits, and use cases can be written in if they want a fuller card. The picture can be a file or a still from the camera they are already holding.

On the product page the site draws a QR code for that item. Scan brings the phone back to the same product. From there they either pour more units in, or they drop the item into a checkout with others and confirm the sale. The quantity falls, a movement is written, and an invoice opens with revenue, cost, and profit. If the count hits the reorder level, or hits zero, a bell lights up in the header. Read all, clear all, and open are right there. If email has been set up, the same sentence also goes to their inbox. If it has not, the bell is enough, and Forgot password and Report Bug say, plainly, that email services are not available.

The person who hosts Roach signs in as the operator. Their month never runs out. On the Admin page they see every shop, the trial end, and the paid-through date. They type the months they were paid for, an optional amount, and a note. Or they block the shop. Blocking is a locked door, not a deleted shop.

The figures on the home page, the “23K” and the “36K”, are lines on a poster. They are not counts from the database.

3.Architecture

One machine runs three pieces. The browser talks only to Nginx on port 8080. Nginx serves the React app and forwards /api and /uploads to Express. Express talks to MongoDB, which nobody outside the stack can reach, and to the folder where photos live. Mail is a side door, used only when the host, user, and password are all filled in.

flowchart TB
  shopUser[ShopUser]
  operator[Operator]
  browser[PhoneOrLaptopBrowser]
  nginx[Nginx_8080]
  paused[PausedPage]

  auth[SignInRegisterReset]
  gate[AccessGate]
  catalog[ProductCatalog]
  scan[ScanRestockCheckout]
  alerts[StockAlerts]
  reports[InvoicesAndReports]
  billing[OperatorBilling]

  users[(User)]
  tokens[(PasswordResetToken)]
  products[(Product)]
  moves[(StockMovement)]
  invoices[(Invoice)]
  notes[(Notification)]
  payments[(Payment)]
  files[(PhotosOnDiskOrCloudinary)]
  smtp[SMTP_when_configured]

  shopUser -->|"register, stock, scan, sell"| browser
  operator -->|"record cash month or block"| browser
  browser --> nginx
  nginx -->|"pages, /api, /uploads"| auth
  nginx --> gate

  auth -->|"session cookie, 1 day"| users
  auth -->|"30 day trial on register"| users
  auth -->|"reset hash, 30 minutes"| tokens
  auth -.->|"reset mail"| smtp

  gate -->|"trial, paid-through, or operator"| catalog
  gate --> scan
  gate --> reports
  gate -->|"access ended, data kept"| paused

  catalog -->|"name, price, cost, reorder level, benefits, use cases"| products
  catalog -->|"camera still or file"| files
  catalog -->|"quantity edit"| moves
  catalog --> alerts

  scan -->|"QR is origin plus product id"| products
  scan -->|"restock or checkout lines"| moves
  scan -->|"one invoice, profit is price minus cost"| invoices
  scan --> files
  scan --> alerts

  alerts -->|"low stock or out of stock, one unread of each type"| notes
  alerts -.->|"same text by email"| smtp
  notes -->|"bell, read all, clear all, open"| browser

  reports --> invoices
  reports --> moves

  billing -->|"paid-through after the later of today, current paid date, and trial end"| users
  billing --> payments
  billing -->|"block clears access, keeps records"| users


Sign-in is a cookie the browser cannot read, kept for a day. The trial clock and the paid-through date sit on the user. Every product, movement, invoice, and alert belongs to that user, so two shops on the same server never see each other’s shelves. The QR code is only an address back to the product. The server still checks who is signed in, and whether their month is open.

4.Implementation

The site is a React app: pages for home, register, login, the dashboard, the product form, the product card with its QR code, scan, the stock sheet, checkout, reports, the invoice, profile, Report Bug, the paused page, and Admin. The header carries the bell. The cart for a sale sits in the browser until the sale is confirmed.

The API is Express. A session cookie guards the door. A second check asks whether this account is the operator, still inside the 30 days, or paid through a future date. Products, stock, reports, alerts, and Report Bug all pass through that check. Logout and the profile still work when the month has ended, so the shop can read why the door is shut.

MongoDB holds users, products, stock movements, invoices, notifications, offline payments, and password-reset tokens. Quantity, price, cost, and reorder level are numbers. A sale writes the name, SKU, quantity, price, and cost onto the invoice at that moment, so a later price change does not rewrite yesterday’s bill. Photos land on disk under /uploads, unless Cloudinary’s three settings are present, in which case the file goes there instead.

The operator account is created on first start from ADMIN_EMAIL and ADMIN_PASSWORD in .env. Those two lines are the knobs another person turns before they run it. The sample in .env.example is admin@example.com and change-me-admin. After the account exists, a new password in .env does not overwrite the one already stored. They change it from Edit Profile, or they wipe the database volume and let startup create it again.

The whole thing comes up with docker compose up --build and goes quiet with docker compose down.


Repo: https://github.com/diwakar767/Roach

5.Results

Roach leaves a shop with a shelf it can trust. Every item has a name, a price, a cost, and a count. When something is sold, the count falls and a bill appears in the same moment, with the money taken and the money earned written on it. At the end of the day the shop can see how many units went out, what they were sold for, what they cost, and what remained. When a count slips to the reorder line, or to zero, the bell says so while there is still time to buy more.

The phone is enough to do that work. A photo from the camera becomes the product picture. A QR label on the box brings the same product back, so restocking and billing happen in the aisle instead of at a desk. Several items can go on one bill. The shop does not keep a second notebook to reconcile later.

For the person offering this to shops, the result is a service they can run themselves. One installation holds many shops, and each shop sees only its own goods and bills. A new shop works for a month. After that, a payment taken in person opens the next month, and the history of stock and sales stays in place even if the shop is paused. The price of the tool is whatever that person charges for the month. There is no card company sitting in the middle.

What the shop gets, then, is a closed loop: goods in, goods out, a bill, a profit, and a warning when the shelf is thin. What the operator gets is a simple way to keep that loop available, shop by shop, for a small monthly fee.

6.Learnings

A small shop does not experience “inventory software.” It experiences a question at the counter: how many are left, what do I charge, and did I make anything on this sale. Roach only became a product when those three questions were answered by the same action. A sale that does not move the count is a poster. A count that does not remember the cost cannot tell the shop if the price is worth keeping.

The phone changed the shape of the work. The update happens where the box is, not where a computer happens to sit. The label on the shelf is what joins the physical item to the record. Once that is true, billing and stock-taking are the same visit.

The monthly fee taught a quieter lesson. Shops will pay a person they can talk to, for a tool that stays out of the way, more readily than they will learn a payment gateway. The product’s job is to open and close access cleanly, and to treat a paused shop as a customer who may return. Their goods and their old bills are the reason they would come back.

The last lesson is about scope. A bill on the screen, a QR label the shop prints itself, and a bell for a thin shelf are enough to manage a small rack. GST layouts, a slip from a thermal printer, and the barcode already printed on a packet are the next counter the shop will ask for. They are growth of the same product, not a different