លំហសិក្សាកម្ពុជា
V1.0
Small retail shops such as mini-marts often record sales and stock manually or in disconnected spreadsheets, which makes it difficult to know current stock levels, track daily sales, or stop a cashier from selling an item that is already out of stock. SmartPOS is a web-based Point-of-Sale and Inventory Management System built with Python, Flask, and MySQL that lets store staff manage products, process sales at checkout, and view sales reports, all enforced by a server-side role-based permission system. The system applies Object-Oriented Programming through a layered architecture that separates domain models, business services, and database repositories. The delivered system covers everything proposed and extends it substantially: role-based authentication for four staff roles, full product and stock management with a stock ledger, a checkout workflow that supports cash, KHQR bank-QR, card, and split-tender payment and automatically updates inventory, purchase orders with a supplier workflow, staff shift and cash-drawer accountability, a team task board, analytics with CSV/Excel export, and automated Telegram alerts for store activity. The final output is a working Flask application backed by a 23-table MySQL database, verified by 49 automated test cases and a full manual walkthrough of every role and workflow.
Object
Oriented Programming II
Advanced OOP with Python
Python + Flask + MySQL
Final Report
SmartPOS:
A Web-Based Point-of-Sale and Inventory Management System
Demo: Clone from our github
https://github.com/Cchheanglim/smartpos_withpythonflask.git
|
Field |
Student Input |
|
Project Title |
SmartPOS: A
Web-Based Point-of-Sale and Inventory Management System |
|
Team Members |
Lim Meng Huy,
Heng Longmeng, Chhum Chheanglim, Kang Sok sambathratanak |
|
Course / Class |
Object Oriented Programming II |
|
Lecturer |
SEK SOCHEAT |
|
Semester / Year |
Semester 1,
Year 2 (2026–2027) |
|
Submission Type |
Final Report |
Table of Contents
Abstract / Summary.........................................................................................................
1. Problem Statement..................................................................................................... 1
2. Objectives (Achieved)............................................................................................... 1
3. Scope of the Delivered System.................................................................................. 2
4. Target Users and Roles.............................................................................................. 3
5. System Features (As Delivered)................................................................................ 4
6. OOP Design............................................................................................................... 5
7. System Architecture................................................................................................... 7
8. Database Design......................................................................................................... 8
9. Implementation........................................................................................................ 11
10. Testing and Results................................................................................................ 20
11. Changes from the Original
Proposal...................................................................... 21
12. Challenges and Resolutions................................................................................... 22
13. Conclusion and Future Work................................................................................. 22
Appendix...................................................................................................................... 23
14. References.............................................................................................................. 25
Abstract
Small retail shops such
as mini-marts often record sales and stock manually or in disconnected
spreadsheets, which makes it difficult to know current stock levels, track
daily sales, or stop a cashier from selling an item that is already out of
stock. SmartPOS is a web-based Point-of-Sale and Inventory Management System
built with Python, Flask, and MySQL that lets store staff manage products,
process sales at checkout, and view sales reports, all enforced by a
server-side role-based permission system. The system applies Object-Oriented
Programming through a layered architecture that separates domain models,
business services, and database repositories.
The delivered system
covers everything proposed and extends it substantially: role-based
authentication for four staff roles, full product and stock management with a
stock ledger, a checkout workflow that supports cash, KHQR bank-QR, card, and
split-tender payment and automatically updates inventory, purchase orders with
a supplier workflow, staff shift and cash-drawer accountability, a team task
board, analytics with CSV/Excel export, and automated Telegram alerts for store
activity. The final output is a working Flask application backed by a 23-table
MySQL database, verified by 49 automated test cases and a full manual
walkthrough of every role and workflow.
1. Problem Statement
Many small shops track
products and sales using paper receipts or basic spreadsheets managed by the
shop owner. Cashiers have no quick way to check whether an item is in stock
while serving a customer, and owners cannot see accurate, up-to-date stock levels
or daily sales totals without manually counting inventory. The current process
has no shared system connecting what is sold at checkout to what remains in
stock, so stock counts drift out of date and sales figures must be calculated
by hand. A web-based POS system was needed so that every sale automatically
updates stock and produces an accurate, real-time sales record — and, specific
to the local market, one that supports Cambodia's real payment mix of cash,
KHQR, and card, sometimes within the same transaction.
|
Item |
Team Answer |
|
Current
situation |
Sales and stock
counts are recorded manually on paper or in separate spreadsheets by shop
staff. |
|
Main pain point |
No connection
between a recorded sale and the actual stock count, so inventory numbers
become inaccurate over time. |
|
Evidence /
observation |
Team
observation of a small retail shop's checkout and stock-taking process, where
sales and inventory are tracked in separate, unlinked records. |
|
Need for
software |
A centralized
POS system that automatically deducts stock at the point of sale and gives
the owner an accurate, real-time sales and inventory report, in the
currencies and payment methods customers actually use. |
2. Objectives (Achieved)
General Objective: To
design and implement a web-based Point-of-Sale and Inventory Management System
using Python, Flask, and MySQL that links every sale directly to stock levels
through a clean, layered Object-Oriented design. This objective was fully met.
Specific Objectives
●
Build a secure authentication and role-based authorization system
for Super Admin, Admin, Inventory Manager, and Cashier roles, with individually
grantable permission overrides.
●
Design and implement product and stock management with full CRUD
functionality and a full stock movement ledger.
●
Implement a checkout/sales workflow that records a sale and
automatically deducts stock in real time, supporting cash (USD/KHR), KHQR,
card, and split-tender payment.
●
Design a normalized MySQL database supporting products, sales,
stock movements, purchase orders, staff shifts, and users — 23 tables in total.
●
Apply a layered OOP architecture (models, services, repositories)
and verify core business rules with automated test cases.
Objectives and Success
Criteria
|
Objective |
Measurable Success Criterion — Result |
|
Authentication
and role-based access |
Users log
in/log out; each of the 4 roles can only access the routes and actions
permitted to that role, enforced server-side. Achieved. |
|
Product and
stock management |
Staff can
create, view, update, and deactivate products; stock quantity updates
automatically after each sale, refund, and received purchase order. Achieved. |
|
Checkout /
sales workflow |
Cashier can
create a sale with multiple items across four payment methods; the system
rejects sales that exceed available stock. Achieved. |
|
Clean OOP
layers |
60+ domain
classes, 20 services, and 19 repositories, each with a single clear
responsibility — exceeds the original target of 5/3/3. |
|
MySQL database |
ERD, SQL
schema, constraints, and seed data covering users, products, sales, stock
movements, purchase orders, and shifts. Achieved. |
|
Testing |
49 automated
test cases plus a documented manual verification pass — exceeds the original
target of 10. |
3. Scope of the Delivered
System
Delivered
●
Four user roles: Super Admin, Admin, Inventory Manager, Cashier —
each with a server-enforced permission set, plus individually grantable
permission overrides per staff member.
●
Product, category, and supplier management, including a full
stock-movement ledger with reason codes (restock, damaged, breakage, count
adjustment, supplier return, internal use).
●
Checkout/sales workflow that records a sale and deducts stock
automatically, supporting cash (USD and KHR), KHQR bank-QR, card, and split
payment across more than one method in the same sale.
●
Purchase orders with a Draft → Ordered → Received/Cancelled
workflow that restocks inventory automatically on receipt.
●
Staff shift clock-in/out with cash-drawer reconciliation (Balanced
/ Overage / Shortage).
●
Loyalty customers with tiered discounts and a points program.
●
A team task board, analytics and reports with CSV/Excel export,
and automated Telegram alerts for store activity.
Excluded from Scope
●
A certified bank payment gateway — the KHQR flow produces a real,
scannable QR code for demonstration purposes but is not certified with any bank
and cannot move real money.
●
Barcode hardware scanning was extended beyond the original plan:
the delivered system supports live camera-based barcode scanning in addition to
manual/keyboard entry.
●
Multi-branch / multi-store support.
Assumptions
●
The system supports a single shop/branch with one shared product
catalog.
●
Users access the system through a standard web browser on desktop
or mobile.
4. Target Users and Roles
|
Role |
Description |
|
Super Admin |
Full system
access, including editing the Roles & Permissions matrix and the Super
Admin role itself. |
|
Admin |
Manages staff
accounts, products, stock, and views full sales reports, but cannot edit the
Super Admin role. |
|
Inventory
Manager |
Manages
products and stock levels, adjusts stock with a reason code, and manages
purchase orders, but cannot manage user accounts or view overall sales
figures. |
|
Cashier |
Processes
checkout transactions, manages their own shift and cash drawer, and views
current stock while selling. |
|
Loyalty
Customer (non-login) |
A record the
cashier can look up by phone number to apply a tier discount and award/redeem
points; does not hold a login account. |
|
Shop Owner
(stakeholder) |
Reviews sales
reports but does not use the system directly. |
Note: the original
proposal planned an Admin Assistant role and a Customer login account. During
implementation, the team consolidated Admin Assistant's intended duties into
the Admin/Super Admin split (a simpler, equally effective permission boundary),
and replaced a Customer login account with a Loyalty Customer record looked up
by phone since the core need was letting a cashier recognise a repeat customer
and apply their discount, not a public-facing customer portal. Both decisions
are discussed further in Section 12.
5. System Features (As
Delivered)
●
User authentication with salted password hashing and protected
sessions.
●
Role-based authorization for Super Admin, Admin, Inventory
Manager, and Cashier, plus individual permission overrides.
●
Product management module (create, read, update, deactivate) with
barcode support.
●
Stock/inventory tracking with automatic deduction on each sale and
a full audit ledger.
●
Checkout workflow supporting multiple items per sale, live camera
barcode scanning, loyalty discounts and points, and cash / KHQR / card / split
payment.
●
Product search and filter by name, SKU, or barcode.
●
Purchase orders with supplier selection and an automatic restock
on receipt.
●
Staff shift clock-in/out with cash-drawer reconciliation.
●
A team task board with priority and due dates.
●
Sales report and analytics page showing daily totals, best
sellers, and cashier productivity, with CSV/Excel export.
●
Automated Telegram alerts for new products, low stock, restocks,
purchase orders received, new sales, refunds, new staff, new roles, and
password resets.
6. OOP Design
Domain classes represent
the core concepts of the POS system. Every domain object (Product, Sale, Cart,
User, Role, and more) is implemented as a Python dataclass that protects its
own state — for example, a Product cannot be saved with a negative price.
Service classes coordinate business rules across repositories — most
importantly, ensuring a sale and its stock deduction happen together inside one
database transaction — and repository classes handle all MySQL access so that
no SQL logic appears in the routes.
Domain / Model Classes
(selected)
●
User (base identity fields) with role_name/role_id determining
permissions; Role and Permission for the RBAC matrix.
●
Product: sku, barcode, name, category, price, cost,
quantity_in_stock, low_stock_threshold.
●
Cart, CartLine, Discount, PricingSummary: the checkout pricing
engine.
●
Sale, SaleItem, PaymentMethod (an enum): a completed transaction
and its line items.
●
StockMovement, MovementReason (an enum with a direction rule per
member): the inventory ledger.
●
PurchaseOrder, POStatus (an enum-driven state machine): Draft →
Ordered → Received/Cancelled.
●
AttendanceSession, DrawerStatus (an enum): a staff shift and its
cash-drawer reconciliation.
Service Classes
(selected)
●
AuthService: handles login, logout, password verification, and
permission checks across all roles.
●
CheckoutService: validates stock availability, creates a Sale with
its SaleItems, and triggers stock deduction, all inside one transaction.
●
ProductService and PurchaseOrderService: manage product CRUD,
stock adjustments, and the purchase-order lifecycle.
●
ReportService: aggregates sales data into daily totals,
best-selling items, and cashier productivity.
Repository Classes
19 repository classes in
total (ProductRepository, SaleRepository, UserRepository, and so on), each
responsible only for MySQL queries for its entity. Every repository inherits
from one abstract base class, BaseRepository(ABC, Generic[T]), because every repository
genuinely performs the same basic operations — find, list, delete — making this
the project's one deliberate inheritance relationship.
Key Relationships
●
A Sale has many SaleItems; each SaleItem references one Product.
●
Each completed Sale generates one or more StockMovement records
reducing product quantity; each received PurchaseOrder generates StockMovement
records increasing it.
●
Every Service is built through composition — constructed from the
repositories and helpers it needs through its constructor — rather than through
inheritance from a shared base service.
Figure 6.1 — Class relationships (representative slice)
7. System Architecture
The system follows a
strict four-layer architecture so that each part has a single responsibility. A
request only ever flows downward through the layers, and a response only ever
flows back up — no layer is ever skipped.
Browser →
Flask Routes/Blueprints → Services
→ Repositories →
MySQL Database
●
Routes: receive HTTP requests (e.g. a checkout submission), call
exactly one service method, and return a rendered template or redirect. Routes
contain no business logic and no SQL.
●
Services: apply business rules — for example, CheckoutService
rejects a sale if the requested quantity exceeds available stock, and wraps a
sale and its stock deduction in one transaction.
●
Repositories: execute parameterized SQL queries against MySQL and
return domain objects built through each model's from_row() classmethod.
●
Templates: Jinja2 pages render product lists, the checkout screen,
and reports; no business logic is placed in templates.
Figure 7.1 — Four-layer request/response architecture
8. Database Design
The database is
normalized, with foreign keys enforcing valid relationships between users,
products, sales, sale items, stock movements, and every other entity. The final
schema has 23 tables in total — considerably more than the original plan — to
support purchase orders, staff shifts and cash drawers, held orders, tasks, and
an audit log alongside the originally planned core tables.
|
Table |
Key Fields / Relationship |
|
users |
id (PK), name,
email (unique), password_hash, role_id (FK), shift_id (FK), is_active |
|
roles /
permissions / role_permissions |
Role and
permission catalog with a junction table defining which permissions each role
holds |
|
products |
id (PK), sku,
barcode, name, category_id (FK), supplier_id (FK), price, cost,
quantity_in_stock |
|
stock_movements |
id (PK),
product_id (FK), change_amount, reason (ENUM), created_by (FK) |
|
sales /
sale_items |
sales: id (PK),
cashier_id (FK), customer_id (FK), attendance_id (FK), payment_method,
total_amount — sale_items: sale_id (FK), product_id (FK), quantity,
unit_price |
|
purchase_orders
/ purchase_order_items |
id (PK),
supplier_id (FK), status (ENUM: draft/ordered/received/cancelled) |
|
attendance /
cash_movements |
One row per
staff shift with opening_float, expected_cash, counted_cash, cash_difference;
cash_movements is an append-only per-shift cash log |
|
customers |
id (PK), phone
(unique), name, points, total_spent — the loyalty program |
All queries use
parameterized statements to prevent SQL injection, and no plain-text passwords
are stored. Guarded UPDATE statements (for example, WHERE quantity_in_stock +
%s >= 0) prevent stock from ever going negative even under concurrent sales,
without needing an application-level lock.
Figure 8.1 — Simplified entity-relationship diagram (core
tables)
9. Implementation
Package Structure
smartpos_minimartsystem/
├── app/
│ ├── __init__.py # create_app() application factory
│ ├── config.py # Config / TestConfig (reads .env)
│ ├── extensions.py # LoginManager, CSRFProtect, get_db() (PyMySQL),
transaction()
│ ├── models/ # POPO dataclasses with from_row() — no DB access (60+ classes)
│ │ ├── auth/ inventory/ sales/ staff/ reports/
│ ├── repositories/ # the ONLY place SQL is written (19 classes, one per
entity)
│ ├── services/ # business rules and workflows (20 classes)
│ ├── routes/ # thin Flask blueprints — 15 controllers
│ ├── forms/ # request parsing + server-side validation
│ ├── utils/ # decorators, password hasher, image storage, CSV/XLSX/barcode
│ ├── templates/ # 40 Jinja2 pages, violet/lime theme
│ └── static/ # css/style.css, logo, uploads/
├── sql/ # schema.sql (23 tables) and seed.sql (demo data)
├── tests/ # pytest suite with in-memory fake repositories
├── docs/ # architecture.md, database.md, testing.md, user_guide.md
├── scripts/ # reset_demo_passwords.py, backup_db.sh, restore_db.sh
├── .env / .env.example
├── run.py
├── requirements.txt
└── README.md
Security Measures
Implemented
●
Passwords are salted and hashed with Werkzeug's PBKDF2
implementation — never stored in plain text.
●
Every state-changing form carries a CSRF token, checked by
Flask-WTF.
●
All SQL is parameterized — user input is never concatenated into a
query string.
●
Server-side permission checks gate every sensitive route; the
interface hiding a button is a convenience, not the security boundary.
Screenshots
Screenshot 1 — Login page
Screenshot 2 — Dashboard
Screenshot 3 — Loyalty Customers —
register a member
Screenshot 4 — POS Checkout — product
grid, cart, and split payment
Screenshot 5 — KHQR payment pop-up
Screenshot 6 — Printable receipt
Screenshot 7 — Products & Stock
Screenshot 8 — Product categories
Screenshot 9 — Suppliers
Screenshot 10 — Team tasks
Screenshot 11 — Staff Management, with the
Telegram alert banner
Screenshot 12 — Roles & Permissions —
role management
Screenshot 13 — Roles & Permissions —
configuring a role's permissions
Screenshot 14 — Roles & Permissions —
bulk permission matrix
Screenshot 15 — Work shifts
Screenshot 16 — Cash drawer audit
Screenshot 17 — My Profile
10. Testing and Results
Testing approach: core
business rules (stock deduction accuracy, checkout totals across all four
payment methods, and role-based access) were verified through service-level
automated tests using in-memory fake repositories, plus documented manual test
cases covering every role and workflow end to end.
|
# |
Test Case |
Expected Result |
Result |
|
1 |
Create a
product with price = 0 |
Rejected:
'Selling price must be greater than zero.' |
Pass |
|
2 |
Apply a 60%
manual discount |
Rejected —
discount capped at 50% |
Pass |
|
3 |
Complete a cash
sale for more than the total |
Change
calculated correctly in USD or KHR |
Pass |
|
4 |
Split payment:
card + QR + cash together |
Sale recorded;
only the cash share reaches the drawer |
Pass |
|
5 |
Refund part of
a split-tender sale |
Only the
proportional cash share leaves the drawer |
Pass |
|
6 |
Deactivate a
staff member with an open shift |
Rejected until
the shift is closed |
Pass |
|
7 |
Receive the
same purchase order twice |
Second attempt
rejected — already processed |
Pass |
|
8 |
Edit the Super
Admin role's permissions as a plain Admin |
Rejected —
Super Admin only |
Pass |
|
9 |
Sell a product
down to 0 stock, then sell one more |
Second sale
rejected — insufficient stock |
Pass |
|
10 |
Log in with the
wrong password |
Generic
'Invalid email or password' (no account enumeration) |
Pass |
Total automated test
count: 49 test cases across model, service, and route-level test files. A full
manual walkthrough of every role and every workflow was also completed,
returning zero server errors on the final pass.
11. Changes from the
Original Proposal
Development naturally
surfaced a few places where the original plan was adjusted for a cleaner or
more realistic result:
|
Proposed |
Delivered Instead |
Reason |
|
5 roles
including Admin Assistant and Customer login |
4 roles (Super
Admin, Admin, Inventory Manager, Cashier) plus a non-login Loyalty Customer
record |
Admin
Assistant's intended duties fit naturally inside the Admin/Super Admin
permission split; a public customer login was not needed to meet the core
requirement of recognising a repeat customer at checkout. |
|
Single cash
payment path |
Cash (USD/KHR),
KHQR, card, and split payment across methods |
Reflects
Cambodia's real point-of-sale payment mix more accurately than the original
single-method plan. |
|
Manual barcode
entry only |
Manual entry
plus live camera barcode scanning |
A real camera
scanner was a natural, low-cost addition once the checkout flow supported
AJAX updates. |
|
Basic sales
report page |
Full analytics
dashboard, CSV/Excel export, and automated Telegram alerts |
Store owners
asked for visibility into activity without needing to log in and check
manually. |
12. Challenges and
Resolutions
|
Challenge |
Resolution |
|
Ensuring a sale
and its stock deduction always happen together, even under concurrent sales |
Wrapped sale
creation and stock deduction in a single service-level transaction, using a
guarded UPDATE so stock can never go negative |
|
Seed data
failed to import (foreign-key ordering error) |
Reordered the
seed script so parent tables (attendance) load before dependent ones (sales) |
|
A split-tender
refund initially removed too much cash from the drawer |
Refunds on a
split sale now return only the proportional cash share |
|
Some pop-up
dialogs were briefly hard to read |
Traced to a CSS
class-name mismatch and corrected it |
|
Uneven
contribution risk among 4 team members under time pressure |
Assigned clear
weekly tasks and tracked progress through Git commit history and short
check-ins |
13. Conclusion and Future
Work
SmartPOS delivers a
complete, working, role-based Point-of-Sale and Inventory Management System
with a strict four-layer OOP architecture, a 23-table normalized MySQL
database, dual-currency and split-tender checkout, live camera barcode
scanning, a full staff shift and cash-drawer accountability system, and
automated Telegram operational alerts verified by 49 automated tests plus a
full manual walkthrough of every workflow. Every objective set out in the
original proposal was met, and the delivered scope substantially exceeds the
original 5-week minimum plan.
Limitations
●
The KHQR payment flow produces a real, scannable QR code but is a
demonstration only — it is not certified with any bank and cannot move real
money.
●
The system is designed for a single store; multi-branch stock
transfer is not supported.
Future Work
●
Integrate a certified bank KHQR merchant API for real payment
confirmation.
●
Add new permission on the UI
●
Add multi-branch inventory with stock transfer between locations.
●
Add a native mobile companion app for stock counts and quick
lookups.
Appendix
A. SQL Scripts
·
The full schema (sql/schema.sql, 23 tables with constraints) and
seed data (sql/seed.sql) are included in the project submission alongside this
report.
B. Sample Data
·
The seed data includes 100 products across 6 categories, 3
suppliers, 4 staff roles, 5 staff accounts, 4 loyalty customers, and 60 sample
sales spread across the last 30 days.
C. User Manual
· A full user guide is
included in the project submission (docs/user_guide.md), covering sign-in,
running a shift, checkout, refunds, inventory, staff, roles, tasks, and
reports.
D. Team Contribution
|
Contributors |
Student In |
|
Chhum Chheanglim |
Database Design, UI/UX, Report |
|
Kang
Soksambathratanak |
Coding, Backend, API implementation into the project |
|
Heng Longmeng |
|
|
Lim Meng Huy |
System Demonstration, Coding |
14. References
●
Flask Documentation — https://flask.palletsprojects.com/
●
MySQL 8.0 Reference Manual — https://dev.mysql.com/doc/
●
Jinja2 Documentation — https://jinja.palletsprojects.com/
●
PyMySQL Documentation — https://pymysql.readthedocs.io/
●
Professor Instruction
មតិយោបល់ 2