Private GitHub repo · public case study

Production-grade warehouse management, built from scratch.

Magazynio replaces a legacy ERP workflow with a focused Python application for products, invoices, stock movements, physical inventory counts, operational analytics, automated backups, and single-user Docker deployment.

dashboard.png
Magazynio dashboard with KPI cards, charts, recent invoices, and top products
product-form.png
Product edit form with price and stock charts
StackPython · Flask · SQLite
FrontendSPA tabs · Chart.js · barcode
DeployDocker · gunicorn · VPS
Quality25 test suites · in-memory DB
Real application screenshots. Every image is a genuine screenshot from the running application — no mockups, no prototypes.

Product proof

Actual screens from a working application.

Use the carousel to inspect every part of the system: dashboard KPIs, product analytics with price history, purchase intelligence, invoice management, printable layouts, periodic reports, and built-in help.

Capabilities

Built for day-to-day warehouse operations.

Every feature is designed around a real operational need — from barcode scanning at goods receipt to periodic stock reporting with turnover analytics.

01

Product catalog

Full catalog with barcode lookup (unique partial index), categories, VAT rates (0/5/8/23%), purchase and sell price tracking, margin calculation, supplier history per product, and active/inactive state management.

02

Invoice engine

Purchase and sale invoices with draft states, line-item auto-merge, supplier snapshotting (survives deletion), correction invoices linked to originals with full audit trail, and stock-impacting confirmation with safe rollback on delete.

03

Stock & inventory

Every stock change is an immutable movement — six types tracked, current stock is a rebuildable cache from source-of-truth movements. Physical inventory sessions reconcile counted vs. system stock with auto-adjustments. Legacy migration detection included.

04

Operational analytics

Dashboard KPIs, 12-month purchase trend, category breakdown, top products and suppliers. Per-product analytics include weighted average pricing, volatility classification, trend detection, and supplier ranking — all correction-aware.

05

Period stock reports

Configurable reports with start/end stock, period movements, turnover ratio, days-of-supply, and stock value. Twenty-two-column CSV export with UTF-8 BOM. Batch SQL optimization — 6 queries total, regardless of product count.

06

Security & testing

Single-user bcrypt auth with rate-limited login (5/min), CSP/HSTS security headers, and strict session protection. Twenty-five test files with in-memory SQLite, auto-logged-in client, and a separate Docker test container — never touching live data.

By the numbers

Technical specifications.

Concrete metrics that define the scope and quality of the project — no fluff, just what's been built.

14 Database tables Categories, suppliers, products, invoices, items, price_history, stock_movements, current_stock, inventories, inventory_items, drafts, settings, product_suppliers, active_sessions
40+ Route handlers Seven Flask blueprints — auth, main, products, suppliers, invoices, stock, inventory, plus a JSON API for autocomplete, dashboard stats, and i18n
7 Service modules Clean separation: product_service, invoice_service, stock_service, inventory_service, supplier_service, dashboard_service, draft_service — zero Flask imports
340+ Translation keys Full Polish and English i18n via custom JSON engine with parameter interpolation, runtime switching, and reactive UI via listener pattern
25 Test files In-memory SQLite per test, auto-logged-in client, comprehensive fixtures. Tests cover services, routes, and edge cases like correction chains
6 Movement types PURCHASE, SALE, CORRECTION, INVENTORY_SET, MANUAL_ADJ, MIGRATION — every quantity change recorded with type, reference, and quantity_after

Engineering

Clean architecture, real business behavior.

The system favors maintainability and testability: Flask factory pattern, isolated service modules, SQLAlchemy ORM models, inline SQLite migrations, JSON i18n, and a tab-based SPA-like frontend.

Flask app factory (create_app())
7 Blueprint routes
auth, main, products, suppliers, invoices, stock, inventory
7 Service modules
business logic, zero Flask imports
14 SQLAlchemy models
ORM with inline migrations
SQLite WAL + FKs
Concurrent-safe, integrity-enforced
JSON i18n engine
PL/EN, runtime switching, 340 keys
AJAX tab system
SPA-like, localStorage state, keyboard shortcuts
Rate limiting
200/min global, 5/min login
Dual Docker setup
prod + test containers, separate DBs
gunicorn · Nginx
2 workers, 120s timeout, non-root user
quick start
pip install -r requirements.txt
python src/app.py
production
gunicorn src.app:app
docker compose up -d --build
testing
pytest
# in-memory DB, isolated per test

Production and test run as separate Docker containers with their own databases. Development never touches live shop data. A sync script copies production data to the test environment when needed.

DevOps & security

Production-ready deployment.

The application runs as a Dockerized Flask service behind gunicorn with Nginx reverse proxy. Three compose files isolate production, testing, and the showcase site — each with its own database and configuration.

Backup & security
Authentication
Single-user bcrypt, no user table, strong session protection, HttpOnly cookies
Security
CSP, HSTS 1y, X-Frame-Options DENY, rate-limited login 5/min, 200/min global
Backups
In-app WAL-safe + Alpine container, sqlite3 .backup, rclone to Hetzner, 15-copy rotation
Docker infra
3 compose files for prod/test/showcase, health checks, 2 CPU / 2GB limits, non-root user
Frontend
Tab-based SPA, AJAX fragments, keyboard shortcuts, light/dark theme, barcode scanner
Proxy
Nginx reverse proxy, ProxyFix, gunicorn 2 workers, 120s timeout, separate networks
compose layout
docker-compose.yml          # production
docker-compose.test.yml     # testing
docker-compose.showcase.yml # showcase
production stack
gunicorn src.app:app \
  --workers 2 \
  --timeout 120 \
  --bind 0.0.0.0:5000

Non-root user (uid 1001) inside the container. Separate networks for internal and external traffic.