Cedar Steel Inventory
LiveOffline-first operations for a steel trading business
My role
Designed and built the desktop application end to end as a freelance engagement, from the operational workflows and data model to the Rust services, React interface, reporting, backup, and release pipeline.
Desktop UI
Business layer
Data & delivery
Overview
Cedar Steel Inventory is an offline-first desktop application built for Cedar Steel Trading, a steel trading company in Ghana. It brings the company’s catalog, supplier-specific stock, purchasing, purchase returns, sales, expenses, payments, statements, reports, printing, and backups into one locally operated system.
The problem
A steel trader needs more than a generic quantity table: equivalent products may come from different suppliers at different costs, every purchase or sale must reconcile stock and account balances, partial payments and returns must remain traceable, and day-to-day operations must continue without depending on an internet connection.
Context
Freelance client project for Cedar Steel Trading in Ghana. The product is designed for a single local administrator and a local SQLite database, with a universal macOS release channel rather than a cloud-hosted, multi-user architecture.
Requirements
Functional
- Manage steel specifications, categories, supplier-specific product variants, locations, prices, minimum stock, and opening quantities.
- Record purchases, sales, expenses, customer receipts, supplier disbursements, and partial invoice or expense payments.
- Handle partial and full purchase returns with printing, editing, cancellation, restoration, and revision history.
- Produce stock, movement, profit, debt, settlement, inventory-value, and best-selling-product reports with printable and exportable views.
- Create automatic and manual backups, restore a selected SQLite backup, and support application updates from an installed desktop build.
Non-functional
- Core business workflows must remain available without an application server, cloud account, or active internet connection.
- Money must remain exact across invoices, payments, tax, discounts, shipping allocation, returns, and profit calculations.
- Mutations spanning stock, accounting, price history, and audit records must either commit completely or roll back completely.
- Historical business records must remain auditable when operational records are archived, cancelled, restored, or revised.
- Backup and restore must protect the active database and provide a recoverable path before replacement.
System architecture
The React interface calls a typed domain facade, which normalizes arguments and invokes authenticated Tauri commands. Those commands stay thin and delegate validation, accounting, inventory, reporting, and printing to Rust services. The services execute transactional queries against one local SQLite database; there is no application server or cloud synchronization layer.
Local administrator
Cedar Steel Trading · Ghana
React 19 desktop UI
Material UI · TanStack Query
Typed Tauri API boundary
Argument normalization · structured errors · session checks
Rust business services
Validation · accounting · inventory · reports · printing
Local SQLite database
Ledger · invoices · parties · payments · audit · backups
React operations interface
Desktop workflows for products, parties, purchases, returns, sales, expenses, payments, statements, reports, settings, printing, and backup.
Rust domain and service layer
Centralizes validation, transactions, stock calculations, accounting rules, lifecycle behavior, audit events, report queries, and printable HTML generation.
SQLite operational database
Stores the full local business history, with an append-oriented inventory ledger, cached stock levels, invoice snapshots, installment ledgers, migrations, and integrity triggers.
Backend & data
Data flow
- A page or dialog calls the typed frontend domain API, which invokes a registered Tauri command and converts structured backend errors into UI feedback.
- The Rust command verifies the local administrator session, then delegates to a service that validates input and opens a SQLite query or transaction.
- Purchases, sales, returns, adjustments, and cancellations write source records and inventory-ledger effects together; the current stock cache is recalculated inside the same transaction.
- TanStack Query invalidates related cached reads after a mutation so dashboards, statements, stock, and reports reflect the committed result.
Backend design
- Thin authenticated Tauri command adapters over reusable Rust services, keeping business rules testable without the desktop runtime.
- Transactional services for workflows that touch several ledgers, with immediate transactions for purchase returns to serialize competing stock changes.
- Structured `{ code, message }` errors across the Rust/TypeScript boundary, including automatic frontend session clearing on unauthorized responses.
- In-memory SQLite integration tests using real migrations and services for money, stock, lifecycle, payment integrity, returns, reset behavior, and report shape.
Database design
- An append-oriented `inventory_transactions` ledger is the stock history; `stock_levels` is a derived current-quantity cache maintained in the same transaction.
- Supplier-specific product rows keep stock and pricing independent, while a normalized `spec_key` groups equivalent steel specifications for supplier comparison.
- Sales and purchase lines snapshot historical cost and price; payments and installment tables preserve the detail behind cached paid and remaining totals.
- Append-only, ordered migrations evolve the database, while foreign keys, indexes, constraints, and SQLite triggers protect cross-table accounting rules.
Authentication & authorization
A single local administrator signs in with an Argon2-hashed credential. The authenticated session is held in memory for eight hours, and business commands enforce the session at the Tauri boundary. This protects application access but deliberately does not claim multi-user or operating-system-level database isolation.
Integrations
- Tauri dialog, process, and updater plugins for backup selection, restart, and signed desktop updates.
- GitHub Actions release workflow for universal Apple Silicon and Intel macOS builds and updater artifacts.
- Printable HTML plus CSV, Excel-compatible, and PDF exports for invoices, returns, operational tables, and reports.
Decisions & trade-offs
Decision
A Tauri desktop architecture with local SQLite instead of a browser application and hosted backend.
Why
The client’s core workflows need dependable local availability, direct desktop printing and file operations, and no dependency on a remote service for day-to-day trading.
Decision
Treat the inventory ledger as authoritative and the current stock table as a rebuildable cache.
Why
Preserves the history behind every quantity while keeping current-stock screens and reports fast, and provides a path to repair cache drift from ledger rows.
Decision
Represent the same steel specification as separate supplier variants linked by a normalized specification key.
Why
Stock, cost, and supplier attribution stay correct for each source while comparison reports can still group equivalent products and identify the cheapest supplier.
Decision
Use integer cents and transactional accounting effects across all business mutations.
Why
Avoids floating-point money errors and prevents a partial failure from leaving invoices, payments, returns, balances, and stock out of sync.
Trade-offs
- Local-first operation removes the application-server dependency and keeps the system usable offline, but the database is tied to one device and there is no shared multi-device synchronization.
- A single-administrator security model is appropriate for the current installation, but it does not provide per-employee roles, remote sign-in, or protection from someone who already has operating-system access to the database files.
- Maintaining both an append-oriented ledger and a current-stock cache adds coordination work to mutations, but gives the system both traceable history and fast operational reads.
Technical challenges
Keeping stock, supplier/customer balances, invoice payment state, price history, and reports consistent through cancellations and restoration.
Modeled operational lifecycle changes as reversible active/inactive effects, retained historical rows, and recalculated every affected summary inside the same SQLite transaction.
Supporting partial purchase returns without allowing duplicate requests, over-returning stock, or erasing the previous version of an edited return.
Validated returned quantities against both the original purchase and current stock, added idempotency keys, revisioned return items, and replaced accounting and inventory effects atomically when a return is edited.
Comparing equivalent steel bought from different suppliers without merging their inventory or cost history.
Kept one product row per supplier variant and generated a supplier-independent specification key for grouping in cheapest-supplier and settlement reports.
Deployment & operations
Deployment
- Packaged as a universal macOS desktop application for Apple Silicon and Intel Macs.
- GitHub Actions produces application bundles plus updater archives and signatures; the installed app checks the configured release endpoint for updates.
- Business data remains in the operating system’s local application-data directory, with daily automatic backups and operator-created manual recovery points.
Security
- Argon2 password hashing with random salts and an eight-hour in-memory administrator session.
- Authentication checks on business commands, additional administrator checks on purchase-return mutations and destructive reset, and audit rows for important operations.
- Foreign-key enforcement and SQLite triggers guard payment direction, party ownership, invoice linkage, and other data-integrity rules.
- Restore creates an emergency backup before replacing the active database; updater artifacts are verified with the embedded public key.
Performance
- SQLite WAL mode, foreign keys, a five-second busy timeout, and indexes for common operational filters and joins.
- A maintained `stock_levels` cache avoids replaying the full movement ledger for every current-stock read.
- TanStack Query caches frontend reads for 20 seconds and mutations explicitly invalidate only the related query groups.
Results
Delivered a versioned, release-ready desktop system—documented through version 1.0.11—that consolidates Cedar Steel Trading’s core inventory, purchasing, sales, accounting, reporting, printing, backup, and recovery workflows in one offline-first application.