01. Problem Statement
Small electronics, hardware, and pharmacy shops in Kenya track inventory by memory or a paper notebook. There is no reliable trigger for reordering — staff usually notice a shelf is empty only after a customer has already walked away, and by the time someone remembers to call the supplier, the sale (and sometimes the next few) is already lost. The owner has no visibility into which items are moving fast, which are dead stock, or whether a supplier has even acknowledged a restock request.
02. Solution Overview
DukaSync is a lightweight Ecommerce-lite system. When a user orders something the quantity of the order is reduced from the product database that single action does double duty: When the product goes under the quantity threshold the supplier receives a restocking message via whatsapp as the main channel, closing the loop from "shelf is empty" to "supplier has been asked" without anyone needing to remember to make a call.
03. System Architecture
3.1 Stack, and why each piece was chosen
3.2 Data Model (ERD)
Entities: Shop, User, Product, StockLevel, Sale, Supplier, RestockOrder
3.3 Core Workflow: How status updates flow
Sale Logged
StockLevel decremented automatically
Threshold Check
current_quantity <= reorder_threshold?
Restock Order Triggered
If breached AND no open order exists: Create RestockOrder (status = Pending)
Supplier Notification Sent
Background task sends SMS/WhatsApp message (status = Sent)
Webhook Confirmation
Supplier replies with keyword (e.g. "YES 4521") → HMAC-verified → status → Confirmed
Fulfillment & Restock
Owner marks Fulfilled once physical stock arrives → StockLevel manually topped up
3.4 What Happens Outside the App
Physical delivery of goods, price negotiation, and any confirmation that doesn't come through the structured SMS keyword all happen outside DukaSync. The system is deliberately not trying to be the source of truth for the physical handoff—it flags and tracks the request, it doesn't replace the phone call for anything nuanced.
3.5 Security
Inbound supplier-confirmation webhook is HMAC-signed and checked against a per-supplier secret, so a third party can't spoof a "Confirmed" status on someone else's order. PINs are stored hashed (bcrypt), never in plaintext. Every order is tied to a user id.
04. Trade-off Log (One-Page)
1. No dedicated barcode scanning
Trade-off #1
What it is: Staff pick products from a searchable list or type the SKU manually; no camera-based reliable scan.
Why accepted: A camera-scanning integration was a multi-day hardware investment; manual/search entry gets staff logging sales on day one.
2. Stock concurrency race conditions
Trade-off #2
What it is: Two staff logging the last unit of the same product at nearly the same moment can both succeed stock can go negative.
Why accepted: Target shops run 1-2 tills; sequential sales are the overwhelming case, and row-locking wasn't worth the complexity for v1.
3. Supplier confirmation depends on rigid format SMS
Trade-off #3
What it is: If a supplier replies in free text instead of the expected keyword, DukaSync can't parse it.
Why accepted: A full NLP parser or dedicated app was out of scope for a one-week build; a fixed keyword was the fastest path.
4. No role permissions beyond PIN
Trade-off #4
What it is: Any staff PIN can do anything a staff account can do.
Why accepted: These shops run 2-5 person teams who trust each other.
05. Roadmap
Near-term: Camera-based barcode scan fast-path; concurrency-safe stock writes; minimal supplier web confirmation portal.
Mid-term: Multi-branch support for one owner with several shops; sales analytics (fast vs. slow movers, margin by product); customer history tied to phone number.
Long-term: A supplier-side marketplace to compare prices across multiple suppliers before a restock request goes out; financing/credit-line integration for restocking.
06. Cross-Examination Prep (State Evidence)
Q: Why FastAPI over Django or Node/Express?
State: We picked FastAPI.
Context: We needed async I/O so sale-logging never blocks on a third-party SMS call, plus Pydantic validation to catch inventory-math bugs.
Q: Why PostgreSQL instead of a NoSQL store like Firebase?
State: We chose PostgreSQL.
Context: Our core relationships are relational, and a stock decrement plus a threshold check need to happen atomically.
Q: What actually happens today if two staff sell the last unit at the same time?
State: Both sales succeed today, and stock can go negative.
Context: We accepted this because our target shops run 1-2 tills, making it a low-probability event.
Written by Rosalind Nduta