A lightweight inventory management system for daily stock IN / OUT operations.
This project started as a small internal inventory tool for real operational use in a small business environment. The first working version used a Tkinter GUI with Excel-based storage.
It was later extended with FastAPI, MySQL, SQLAlchemy, Docker Compose, automated testing, GitHub Actions CI, and Docker Hub publishing to demonstrate a more maintainable and production-oriented backend workflow.
- Inventory IN / OUT operations
- Excel-based inventory storage as the stable baseline
- Tkinter GUI for daily operation
- FastAPI backend APIs
- MySQL integration with SQLAlchemy
- Service and repository layer separation
- Dependency injection
- Business-rule validation and error handling
- Docker Compose environment
- Automated tests with pytest
- Coverage enforcement in CI
- GitHub Actions CI/CD
- Docker Hub image publishing
- Safe sample inventory data
The project contains two related execution paths.
run_gui.py
↓
Tkinter GUI
↓
InventoryService
↓
ExcelRepository
↓
Excel File
HTTP Client / Swagger UI
↓
FastAPI Endpoint
↓
InventoryMySQLService
↓
MySQLRepository
↓
SQLAlchemy
↓
MySQL Database
The Excel-based GUI is retained as the stable original implementation. The FastAPI + MySQL path demonstrates how the same inventory business rules can be moved into a database-backed backend service.
Key project files and directories:
inventory-mysql/
├── api/
│ ├── __init__.py
│ └── fastapi_app.py
├── app/
│ ├── __init__.py
│ ├── main.py
│ ├── db.py
│ ├── mysql_models.py
│ ├── check_mysql_conn.py
│ ├── check_database.py
│ └── check_inventory_orm.py
├── config/
│ ├── __init__.py
│ └── constants.py
├── core/
│ ├── __init__.py
│ ├── exceptions.py
│ ├── inventory_service.py
│ ├── inventory_mysql_service.py
│ └── item.py
├── data/
│ └── sample_inventory.xlsx
├── repository/
│ ├── __init__.py
│ ├── excel_repository.py
│ └── mysql_repository.py
├── scripts/
│ └── create_tables.py
├── tests/
│ ├── conftest.py
│ ├── test_fastapi.py
│ ├── test_repository.py
│ ├── test_service.py
│ ├── test_inventory_mysql_api.py
│ └── test_inventory_mysql_service.py
├── ui/
│ ├── __init__.py
│ └── gui_app.py
├── .github/
│ └── workflows/
│ └── ci.yml
├── .env.example
├── docker-compose.yml
├── Dockerfile
├── pytest.ini
├── requirements.txt
├── run_api.py
├── run_gui.py
└── README.md
Generated cache files, local environment files, database data, test artifacts, and private Excel files are intentionally omitted from this structure.
- Python 3.10+
- pip
- MySQL 8.x for the database-backed API
- Docker
- Docker Compose
pip install -r requirements.txtCreate a local .env file based on .env.example:
cp .env.example .envExample host configuration:
DB_HOST=127.0.0.1
DB_PORT=3307
DB_USER=root
DB_PASSWORD=your_password
DB_NAME=inventory_dbThe real .env file is excluded by .gitignore and must not be committed.
When connecting from the host machine or WSL:
DB_HOST=127.0.0.1
DB_PORT=3307When the application connects to MySQL inside Docker Compose:
DB_HOST=mysql
DB_PORT=3306Example MySQL Workbench connection:
Host: 127.0.0.1
Port: 3307
User: root
Database: inventory_db
The GUI version is the original stable implementation and uses the Excel repository.
Run the GUI from the project root:
python3 run_gui.pyOn Windows, this can also be run as:
python run_gui.pyThe GUI entry script creates the Tkinter application and injects the Excel repository and inventory service.
The MySQL FastAPI server is not required when running this direct Excel-based GUI path.
Start the configured services:
docker compose up -dCheck service status:
docker compose psView service logs:
docker compose logsStop the services:
docker compose downRemove the services and the MySQL data volume:
docker compose down -vWarning:
docker compose down -vremoves the database volume and its stored data.
Start MySQL first:
docker compose up -dThen start the FastAPI server from the project root:
uvicorn app.main:app --reloadDefault API URL:
http://127.0.0.1:8000
Interactive Swagger documentation:
http://127.0.0.1:8000/docs
GET /Example response:
{
"status": "ok",
"message": "Inventory MySQL API is running"
}GET /item/{pid}Example:
curl http://127.0.0.1:8000/item/A001POST /inventory/inExample request:
curl -X POST http://127.0.0.1:8000/inventory/in \
-H "Content-Type: application/json" \
-d '{
"pid": "A001",
"name": "Mouse",
"qty": 5,
"receiver": "",
"shipper": "Vendor A"
}'POST /inventory/outExample request:
curl -X POST http://127.0.0.1:8000/inventory/out \
-H "Content-Type: application/json" \
-d '{
"pid": "A001",
"name": "Mouse",
"qty": 2,
"receiver": "Customer A",
"shipper": ""
}'Example successful response:
{
"status": "success",
"message": "Item found",
"item": {
"pid": "A001",
"name": "Mouse",
"current_qty": 10,
"buyer": "",
"shipper": ""
}
}Application-level errors are converted into appropriate HTTP responses.
Unexpected SQLAlchemy errors trigger a database rollback and return an HTTP 500 database error response.
The project includes manual scripts for verifying MySQL connectivity, table setup, and ORM behavior.
Run the MySQL connection check:
python3 app/check_mysql_conn.pyRun the database check:
python3 app/check_database.pyRun the ORM check:
python3 app/check_inventory_orm.pyTypical expected results:
Database connection successful
Table created or verified successfully
ORM operation completed successfully
These scripts are intended for manual integration verification and are separate from the normal unit-test suite.
Run the complete test suite:
pytest -qRun tests with coverage details:
pytest --cov=. --cov-report=term-missingLast verified local result for the FastAPI + MySQL extension:
58 passed, 1 skipped
Required test coverage of 80% reached
Total coverage: 85.12%
The test suite covers:
- Inventory business rules
- Inventory IN and OUT operations
- Invalid quantity handling
- Insufficient-stock handling
- Item-not-found behavior
- Repository behavior
- FastAPI request and response behavior
- Endpoint routing
- Service dependency wiring
- Application error handling
- Database error rollback behavior
API-layer tests use fake service objects where appropriate. This keeps the normal test suite fast and stable without requiring a live MySQL database for every test run.
The real MySQL path is verified separately through Docker Compose and the manual database-check scripts.
A safe sample Excel file is included for testing and demonstration:
data/sample_inventory.xlsx
The sample file contains demonstration data only and does not contain confidential business information.
Some Excel-based tests may modify the sample file locally. Restore it before committing when necessary:
git restore data/sample_inventory.xlsxReal operational Excel files are excluded from source control.
The project uses GitHub Actions for automated testing and Docker image publishing.
The CI workflow runs on the master branch and performs the following general steps:
1. Checkout source code
2. Set up Python 3.10 and Python 3.11
3. Install project dependencies
4. Run pytest
5. Enforce the coverage threshold
6. Build the Docker image after successful tests
7. Publish the image to Docker Hub when applicable
The configured minimum test coverage is:
80%
A failed test or failed coverage check prevents the deployment stage from continuing.
The published Docker image is available as:
alextwtpyeh/inventory-mysql
Pull the versioned release:
docker pull alextwtpyeh/inventory-mysql:v2.0.0Pull the latest image:
docker pull alextwtpyeh/inventory-mysql:latestBasic image verification:
docker run --rm alextwtpyeh/inventory-mysql:v2.0.0 python --versionRun the packaged test suite:
docker run --rm alextwtpyeh/inventory-mysql:v2.0.0 pytest -qOn a successful push to the master branch:
Source Push
↓
GitHub Actions
↓
pytest and Coverage Gate
↓
Docker Image Build
↓
Docker Hub Login
↓
Docker Image Push
Docker Hub credentials are not stored in the repository.
They are configured as GitHub repository secrets:
DOCKERHUB_USERNAMEDOCKERHUB_TOKEN
The GitHub Actions workflow references these secrets during the Docker Hub login step.
The project follows several basic source-control and deployment safety practices:
- Real runtime credentials are stored in
.env. .envis excluded by.gitignore..env.examplecontains only safe placeholder values.- Real operational Excel files are excluded from Git.
- Only safe sample inventory data is committed.
- Docker Hub credentials are stored as GitHub repository secrets.
- Docker images are published only after automated tests pass.
- The coverage threshold is enforced before deployment.
- Database connection settings are passed through environment variables.
- Private credentials are not hard-coded in the source code.
When the Excel-based version runs inside WSL while an Excel file is open in Windows Excel, the Linux process may not reliably detect the Windows file lock.
This is caused by differences between Windows and Linux file-lock behavior.
File-in-use detection works more reliably when the Excel-based application runs directly on Windows.
MySQL uses:
3306 inside the Docker network
3307 from the host machine
The FastAPI server normally uses:
8000
Current stable release:
v2.0.0
Completed components:
- Excel-based inventory tool
- Tkinter GUI
- Inventory IN / OUT business rules
- Service and repository separation
- FastAPI backend
- MySQL repository
- SQLAlchemy ORM integration
- FastAPI + MySQL API path
- Dependency injection
- Application error handling
- Unit and API testing
- Docker Compose environment
- Coverage enforcement
- GitHub Actions CI
- Docker Hub image publishing
- Versioned Docker image
- Safe environment-variable handling
- Safe sample-data handling
This project demonstrates the modernization of a small operational desktop tool into a structured backend system.
Main engineering concepts include:
- Layered architecture
- Service and repository separation
- Dependency injection
- Domain-oriented business rules
- REST API design
- Pydantic request validation
- SQLAlchemy ORM
- MySQL integration
- Transaction rollback on database errors
- Unit testing with pytest
- API testing with fake dependencies
- Docker Compose
- GitHub Actions CI/CD
- Coverage enforcement
- Docker Hub publishing
- Environment-variable security
- Sample-data isolation
The following items are future design considerations and are not presented as completed features.
Add pagination and filtering for larger inventory result sets to avoid loading excessive data into memory.
Introduce Redis for selected high-frequency read operations, with explicit TTL and cache invalidation rules.
Potential cache risks to consider include:
- Cache penetration
- Cache breakdown
- Cache avalanche
- Stale-data handling
Introduce Alembic for version-controlled SQLAlchemy schema migrations.
A migration workflow would make database changes repeatable across development, testing, and deployment environments.
Keep stock updates inside database transactions.
For concurrent updates to the same product, possible strategies include:
- Optimistic locking
- Pessimistic locking
- Appropriate transaction isolation levels
Maintain normalized data structures where practical and add indexes for frequently searched fields such as product ID.
Define database backup and restore procedures and establish recovery targets such as:
- Recovery Point Objective (RPO)
- Recovery Time Objective (RTO)
Database-engine REDO and UNDO logs support crash recovery, while application-level recovery requires tested backup and restore procedures.
For larger deployments, evaluate the trade-offs between:
- Vertical scaling
- Horizontal scaling
- Read replicas
- Connection pooling
- Stateless API deployment
No license specified.