Efision CLI
High-performance Rust CLI for managing an automotive parts ERP system — wheels, tyres, accessories across multiple regions.
Efision connects to SQL Server 2025 databases and provides 29 commands across 6 business domains: wheels, tyres, accessories, orders, warehouse stock, and customers.
Key Features
- Multi-region support — AU, UK, US, DEMO databases with a single binary
- Comprehensive wheel management — count, export, import, merge, batch-update, part number validation
- Fast — count queries <100ms, full export of 3,878 products <1s
- Small footprint — 1.9 MB binary, ~10 MB memory, <100ms startup
- 5-layer clean architecture — business logic is database-agnostic
Quick Start
# Build
cargo build --release
# Count wheels in AU region
./efision -r au wheels count
# Export all wheels to CSV
./efision -r au wheels export -o wheels.csv
Command Overview
| Domain | Commands |
|---|---|
| Wheels | count, export, summary, activate, deactivate, delete, merge, check-pn, batch-update, import |
| Tyres | count, export, summary, search |
| Accessories | count, export, summary, search |
| Orders | list, search, summary, export |
| Stock | list, search |
| Customers | search, orders |
All commands require the -r <region> flag. See Global Options for details.
Getting Started
Prerequisites
- Rust 1.75+ — install via rustup
- SQL Server access — database credentials for at least one region
- Network access — direct TCP connectivity to the database server (port 1433)
No ODBC driver is required — Efision uses Tiberius, a pure Rust TDS driver.
Build
# Debug build (faster compile, slower runtime)
cargo build
# Release build (slower compile, optimized binary — recommended)
cargo build --release
Or use the build script:
./scripts/build-release.sh
Run
# From project root (debug)
./target/debug/efision -r <region> <command>
# From project root (release)
./target/release/efision -r <region> <command>
# Copy binary to project root for convenience
cp target/release/efision ./efision
./efision -r <region> <command>
First Commands
# Count active wheels
./efision -r au wheels count
# Export wheels to CSV
./efision -r au wheels export -o wheels.csv
# View wheel summary
./efision -r au wheels summary
# Search customers
./efision -r au customers search -q "Smith"
Next Steps
- Installation — detailed build and install instructions
- Configuration — set up database credentials
- Global Options — flags that apply to all commands
Installation
Prerequisites
| Requirement | Version | Notes |
|---|---|---|
| Rust | 1.75+ | Install via rustup |
| Cargo | (bundled with Rust) | Build tool and package manager |
Building from Source
# Clone the repository
git clone https://github.com/Inovit-Inc/efision.git
cd efision
# Copy and configure environment
cp .env.example .env
# Edit .env with your database credentials (see Configuration)
# Build release binary
cargo build --release
The optimized binary is at target/release/efision (~1.9 MB).
Install Location
Option 1 — copy to project root:
cp target/release/efision ./efision
Option 2 — install to system PATH:
cp target/release/efision /usr/local/bin/efision
Verify Installation
efision --help
High-performance product management CLI for INOVIT ERP systems
Usage: efision [OPTIONS] <COMMAND>
Commands:
wheels Wheel product management
tyres Tyre product management
accessories Accessory product management
orders Order management
stock Warehouse stock management
customers Customer management
Options:
-r, --region <REGION> Target database region (au, uk, us, demo)
-h, --help Print help
Configuration
Efision reads database credentials from a .env file in the project root.
Environment Variables
Each region requires 5 variables following the pattern DB_<REGION>_*:
DB_AU_HOST=your_host
DB_AU_PORT=1433
DB_AU_NAME=your_database
DB_AU_USER=your_user
DB_AU_PASSWORD=your_password
Repeat for each region you need access to:
| Region | Prefix | Description |
|---|---|---|
au | DB_AU_* | Australia |
uk | DB_UK_* | United Kingdom |
us | DB_US_* | United States |
demo | DB_DEMO_* | Demo/test environment |
Setup
-
Copy the example file:
cp .env.example .env -
Edit
.envwith your database credentials. -
Test connectivity:
efision -r au wheels count
Notes
- The
.envfile is git-ignored — never commit database credentials. - You only need to configure regions you will access. Unused regions can be left blank.
- The default port for SQL Server is
1433. - Efision uses Tiberius (pure Rust TDS driver) — no ODBC driver needed.
Global Options
--region / -r (Required)
Specifies the target database region. This flag is required for all commands.
| Value | Description |
|---|---|
au | Australia |
uk | United Kingdom |
us | United States |
demo | Demo/test environment |
The flag can be placed before or after the subcommand:
efision -r au wheels count # before subcommand
efision wheels count -r au # after subcommand (also works)
--help / -h
Print help information. Works at any level:
efision --help # top-level help
efision wheels --help # wheels subcommand help
efision wheels batch-update --help # batch-update help
Quick Reference
efision -r <region> wheels count [filter flags...]
efision -r <region> wheels export [-o file] [filter flags...]
efision -r <region> wheels summary [filter flags...]
efision -r <region> wheels activate -p <part_number>
efision -r <region> wheels deactivate -p <part_number>
efision -r <region> wheels delete -p <part_number>
efision -r <region> wheels merge -p <pn> [-p <pn>...] [--skip-vouchers]
efision -r <region> wheels merge --all [--dry-run] [--skip-vouchers]
efision -r <region> wheels check-pn [--fix] [filter flags...]
efision -r <region> wheels batch-update csv <file>
efision -r <region> wheels batch-update view [filter flags...]
efision -r <region> wheels batch-update inline [filter flags...] [set flags...]
Filter: --brand --model --diameter --width --holes --pcd1 --pcd2 --et --cb --base-finish --procedures --status
Set: --set-et --set-cb --set-cap --set-bolt --set-holes --set-pcd1 --set-pcd2 --set-status --set-base-finish --set-procedures
efision -r <region> tyres count
efision -r <region> tyres export [-o file]
efision -r <region> tyres summary
efision -r <region> tyres search -q <query>
efision -r <region> accessories count
efision -r <region> accessories export [-o file]
efision -r <region> accessories summary
efision -r <region> accessories search -q <query>
efision -r <region> orders list [-l limit] [-s status]
efision -r <region> orders search -o <order_no>
efision -r <region> orders summary
efision -r <region> orders export [-o file] [-s status]
efision -r <region> stock list [-l] [-t threshold]
efision -r <region> stock search -s <sku>
efision -r <region> customers search -q <query>
efision -r <region> customers orders -c <customer_id>
Wheels
Wheel product management commands. All commands require the global -r <region> flag.
Wheel Filter Flags
All wheel read commands (count, export, summary, check-pn, batch-update view/inline) accept these filter flags:
| Flag | Short | Type | Description |
|---|---|---|---|
--brand | -b | string | Brand name |
--model | -m | string | Model name or design number (matches pdName or pdNO) |
--diameter | int | Diameter in inches | |
--width | string | Width in inches | |
--holes | int | Number of bolt holes | |
--pcd1 | string | PCD1 value (e.g. "114.3") | |
--pcd2 | string | PCD2 value for dual PCD | |
--et | int | ET (offset) value | |
--cb | string | Center bore (CB) value | |
--base-finish | string | Base finish name (e.g. "Black") | |
--procedures | string | Procedures (comma-separated, exact match, order-insensitive) | |
--status | string | Status (true/false, active/inactive, 1/0) |
Commands
| Command | Description | Page |
|---|---|---|
count / export / summary | Query and export wheels | Query Commands |
activate / deactivate / delete | Change product status | Lifecycle Commands |
merge | Merge duplicate products | Merge |
check-pn | Validate part numbers | Check Part Number |
batch-update | Bulk update wheel specs | Batch Update |
import | Create wheels from CSV | Import |
Wheels — Query Commands
wheels count
Count wheel products, optionally filtered.
Usage: efision -r <region> wheels count [filter flags...]
Example (all active):
efision -r demo wheels count
Efision Wheels Count
============================================================
Counting DEMO wheel products... done.
DEMO Region: 7405 active wheel products
Example (filtered):
efision -r au wheels count --brand INOVIT --diameter 19
Efision Wheels Count
============================================================
Counting AU wheel products... done.
Filters: Brand: INOVIT, Diameter: 19
AU Region: 142 wheel products
wheels export
Export wheel products to a CSV file.
Usage: efision -r <region> wheels export [-o <file>] [filter flags...]
| Flag | Short | Default | Description |
|---|---|---|---|
--output | -o | export.csv | Output file path |
CSV columns:
Part Numbers, Brand, Model, Diameter, Width, Profile, Direction, Holes, P.C.D, ET, C/B, Bolts, Base Finish, Procedures, Cap Code, Active, Stock
Example (all active):
efision -r au wheels export -o wheels.csv
Efision Wheels Export
============================================================
Region: AU
Output: wheels.csv
Fetching and exporting... done.
Successfully exported 8044 wheel products!
Output: wheels.csv
Example (filter by brand):
efision -r au wheels export --brand INOVIT -o inovit-wheels.csv
Efision Wheels Export
============================================================
Region: AU
Output: inovit-wheels.csv
Brand: INOVIT
Fetching and exporting... done.
Successfully exported 1951 wheel products!
Output: inovit-wheels.csv
Example (filter by brand, model, and diameter):
efision -r au wheels export --brand INOVIT --model Speed --diameter 19
wheels summary
Show summary statistics for wheel products, optionally filtered.
Usage: efision -r <region> wheels summary [filter flags...]
Example (all):
efision -r demo wheels summary
Wheel Products Summary - DEMO Region
============================================================
Total Products: 7415
Total Models: 574
Brands: 13
By Brand
----------------------------------------
2408
Asuka Racing 623
HEMI 103
Hartes Metal 196
INOVIT 1951
ION 8
...
Example (filtered):
efision -r au wheels summary --brand INOVIT
Wheels — Lifecycle Commands
wheels activate
Activate a wheel product by part number. Sets pStatus = 1.
Usage: efision -r <region> wheels activate -p <part_number>
| Flag | Short | Required | Description |
|---|---|---|---|
--part-number | -p | Yes | Part number (pNO) to activate |
Workflow:
- Searches for matching wheel products by part number
- Displays matching candidates (if multiple, prompts to select one)
- Checks if product is already Active (warns and exits if so)
- Shows a confirmation prompt with product details
- Executes status update
Example (successful activation):
efision -r demo wheels activate -p "03424M0K35KCPAMF1ANA"
Efision Wheels Activate
============================================================
Region: DEMO
Part Number: 03424M0K35KCPAMF1ANA
Searching for wheel product... done.
Found 1 matching product(s):
------------------------------------------------------------------------------------------
# ID Part Number Brand Name Status
------------------------------------------------------------------------------------------
1 5599 03424M0K35KCPAMF1ANA Gusto Inactive
Activate About to activate product:
Product ID: 5599
Part Number: 03424M0K35KCPAMF1ANA
...
Status: Inactive -> Active
Are you sure you want to activate this product? [y/N] yes
Updating product status... done.
OK Successfully activated wheel product 03424M0K35KCPAMF1ANA (ID: 5599)
Example (already active):
efision -r demo wheels activate -p "03424M0K20KCPAMF1ANA"
! Product 03424M0K20KCPAMF1ANA is already Active.
wheels deactivate
Deactivate a wheel product by part number. Sets pStatus = 0.
Usage: efision -r <region> wheels deactivate -p <part_number>
| Flag | Short | Required | Description |
|---|---|---|---|
--part-number | -p | Yes | Part number (pNO) to deactivate |
Workflow: Same as activate, but in reverse.
Example:
efision -r demo wheels deactivate -p "03424M0K20KCPAMF1ANA"
wheels delete
Delete a wheel product by part number. Includes safety checks for business dependencies and requires interactive confirmation.
Usage: efision -r <region> wheels delete -p <part_number>
| Flag | Short | Required | Description |
|---|---|---|---|
--part-number | -p | Yes | Part number (pNO) to delete |
Workflow:
- Searches for matching wheel products by part number
- Displays matching candidates (if multiple, prompts to select one)
- Checks 16 business dependency tables for blocking references
- Shows a warning with product details
- Requires interactive confirmation (
y/N) - Executes transactional delete (CategoryProduct -> ProductWheels -> Product)
Example (successful delete):
efision -r demo wheels delete -p "03424M0K35KCPAMF1ANA"
Efision Wheels Delete
============================================================
Region: DEMO
Part Number: 03424M0K35KCPAMF1ANA
Searching for wheel product... done.
Checking business dependencies... done.
WARNING About to permanently delete:
Product ID: 5599
Part Number: 03424M0K35KCPAMF1ANA
...
This action cannot be undone!
Are you sure you want to delete this product? [y/N] yes
Deleting product... done.
OK Successfully deleted wheel product 03424M0K35KCPAMF1ANA (ID: 5599)
Example (blocked by dependencies):
efision -r demo wheels delete -p "03424M0K20KCPAMF1ANA"
ERROR Cannot delete product 03424M0K20KCPAMF1ANA - it has active business references:
x 1 record(s) in BackOrderDetail
x 1 record(s) in WarehouseStock
x 1 record(s) in StockOutOrderDetail
x 2 record(s) in StockInOrderDetail
x 1 record(s) in PurchaseOrderItem
x 2 record(s) in InvoiceInDetail
x 2 record(s) in ProductModelCompanyPrice
Remove these references first before deleting the product.
Wheels — Merge
Merge wheel products into a primary product. Redirects all business references from secondary product(s) to the primary using Proc_MergeProduct, then deletes the secondaries.
Modes
- Duplicate mode (single
-p): Finds all products with the same part number. First match becomes primary, rest are merged into it. - Cross-part-number mode (multiple
-p): First-pis the primary (kept), remaining-pvalues are secondaries (merged and deleted). - All mode (
--all): Finds and merges all duplicate part numbers in the database.
Usage: efision -r <region> wheels merge [-p <pn>... | --all [--dry-run]] [--skip-vouchers]
| Flag | Short | Default | Description |
|---|---|---|---|
--part-number | -p | (none) | Part number(s). Single: merge duplicates. Multiple: first is primary, rest are secondaries. |
--all | false | Merge ALL duplicate part numbers in the database | |
--dry-run | false | Show duplicates without merging (only with --all) | |
--skip-vouchers | false | Skip updating VoucherItem accounting records |
What the Merge Does
- Updates all references to the secondary product across 18 business tables
- Optionally updates VoucherItem accounting memos (enabled by default)
- Deletes the secondary product records (CategoryProduct, ProductWheels, Product)
- All operations within a single database transaction
Examples
Merge duplicates (same part number):
efision -r demo wheels merge -p "W-18x8-BK"
Merge different part numbers:
efision -r demo wheels merge -p "W-18x8-BK-A" -p "W-18x8-BK-B" -p "W-18x8-BK-C"
Merge all duplicates (dry run):
efision -r demo wheels merge --all --dry-run
Skip voucher handling:
efision -r demo wheels merge -p "W-18x8-BK" --skip-vouchers
Wheels — Check Part Number
Check wheel part numbers against the generation formula and optionally fix mismatches. Default mode is check-only (dry run).
Part Number Formula (Version 1)
DesignNO + [P+Profile] + [Directional 1st letter] + Dia + WidthCode + Holes
+ PCD1Code + [PCD2Code] + ET + FinishShortName + AppendantShortNames
+ BoltShortName + CBCode + CapNO
Usage: efision -r <region> wheels check-pn [--fix] [filter flags...]
| Flag | Default | Description |
|---|---|---|
--fix | false | Actually update mismatched part numbers (requires confirmation) |
Plus all wheel filter flags.
Examples
Check only (dry run):
efision -r demo wheels check-pn
Efision Wheels Part Number Check
============================================================
Region: DEMO
Mode: Check only (dry run)
Checking part numbers... done.
Total checked: 7405
Correct: 7390
Mismatched: 12
Lookup errors: 3
Mismatched Part Numbers:
------------------------------------------------------------------------------------------------------------------------
# ID Brand Name Current pNO Expected pNO Status
------------------------------------------------------------------------------------------------------------------------
1 1001 INOVIT Speed 06719Y5V40S4AK 067P3L19Y5V40S4AK Active
2 1002 INOVIT Force 03418M4V-18SMF4AK 034P2R18M4V-18SMF4AK Active
...
INFO Run with --fix to update these part numbers.
Fix mismatches:
efision -r demo wheels check-pn --fix
Filter by brand:
efision -r demo wheels check-pn --brand INOVIT
Filter by model (name or design number):
efision -r demo wheels check-pn --model Torque
efision -r demo wheels check-pn --model 067
Notes
- Only
Product.pNOis modified (no other tables changed) - Updates are wrapped in a database transaction (all-or-nothing)
- Lookup errors indicate missing entries in reference tables. Fix lookup tables first, then re-run.
--modelmatches both design name (pdName) and design number (pdNO)
Wheels — Batch Update
Batch update wheel product parameters. Split into three subcommands:
| Subcommand | Description |
|---|---|
csv <file> | Update from CSV file (per-row filter + values) |
view [filter flags...] | Preview matching wheels without updating (read-only) |
inline [filter flags...] [set flags...] | Apply uniform changes to all matching wheels |
Set-Value Flags (inline mode)
| Flag | Type | Description |
|---|---|---|
--set-et | int | New ET value |
--set-cb | string | New CB value |
--set-cap | string | New cap part number (e.g. "CP865PS80") |
--set-bolt | string | New bolt name (e.g. "M14x2.0") |
--set-holes | int | New number of holes |
--set-pcd1 | string | New PCD1 value |
--set-pcd2 | string | New PCD2 value |
--set-status | string | New status (true/false, active/inactive, 1/0) |
--set-base-finish | string | New base finish name |
--set-procedures | string | New procedures (comma-separated, replaces entire list) |
batch-update csv
Usage: efision -r <region> wheels batch-update csv <file>
CSV Format:
Columns without prefix are filter criteria; columns with new_ prefix are values to set.
model,diameter,width,holes,pcd1,pcd2,et,new_et
Blitz,19,8.5,5,112,114.3,32,35
Blitz,19,8.5,5,112,114.3,40,38
Available CSV columns:
- Filter:
model,brand,diameter,width,holes,pcd1,pcd2,et,cb,base_finish,procedures - Set-value:
new_et,new_cb,new_cap,new_bolt,new_holes,new_pcd1,new_pcd2,new_status,new_base_finish,new_procedures
Example:
efision -r demo wheels batch-update csv updates.csv
batch-update view
Preview matching wheels without updating.
Usage: efision -r <region> wheels batch-update view [filter flags...]
Example:
efision -r demo wheels batch-update view --brand INOVIT --diameter 19
Wheels Batch Filter View
Configuration
. Region DEMO
. Mode View
Filters
. Brand INOVIT . Diameter 19
Finding matching wheels... done.
Matching wheel(s) 35:
# Part Number Brand Model Size HxPCD ET CB Cap Bolt Status Base Finish Procedures
1 02319K5T38S1ANA INOVIT Speed 19x8.5 5x112 38 73.10 CP506AL68 32x15x60 Active Silver Machined Face
2 02319K5B40S1BNA INOVIT Speed 19x8.5 5x120 40 72.60 CP506AL68 32x15x60 Active Black CNC/Paint
...
batch-update inline
Apply uniform changes to all matching wheels. At least one filter flag and one set-value flag are required.
Usage: efision -r <region> wheels batch-update inline [filter flags...] [set flags...]
Update CB:
efision -r demo wheels batch-update inline \
--brand "INOVIT" --holes 5 --pcd1 "115" \
--set-cb 71.5
Update multiple fields:
efision -r demo wheels batch-update inline \
--model "Blitz" --holes 5 --pcd1 "112" --pcd2 "114.3" \
--set-cb 71.5 --set-cap "CP865PS80" --set-bolt "M14x2.0"
Deactivate by finish:
efision -r demo wheels batch-update inline \
--brand "INOVIT" --base-finish "Black" \
--set-status false
Change procedures:
efision -r demo wheels batch-update inline \
--brand "INOVIT" --model "Speed" --diameter 19 \
--set-base-finish "Silver" --set-procedures "Machined Face,Diamond Cut"
Notes
- Each CSV row must have at least one filter column and one
new_column - Bolt, cap, base finish, and procedure values are resolved from human-readable names to database IDs
--modelfilter matches against both design name (pdName) and design code (pdNO)- PCD filter uses separate
--pcd1and--pcd2flags. Use only--pcd1for single-PCD wheels - Procedures filter performs exact match (order-insensitive)
--set-statusupdatesProduct.pStatus. Acceptstrue/false,active/inactive, or1/0--set-proceduresreplaces the entire procedure list (does not append)- Spec updates target
ProductWheelsrows (bypwID); status updates targetProductrows (bypID) - All updates execute within a single database transaction (all-or-nothing)
- Part numbers are automatically regenerated after spec changes
- Filter validation: When no wheels match (inline mode), the CLI checks whether the brand/model exists and lists available values
- Mold range validation: Proposed ET, CB, PCD values are checked against mold constraints. Violations are shown as a warning table before confirmation
- Mold range auto-expansion: After update, any exceeded mold ranges are automatically expanded
Wheels — Import
Import wheel products from a CSV file. Automatically resolves human-readable names to database IDs, generates part numbers and descriptions, creates molds if needed, and inserts products in a single transaction.
Usage: efision -r <region> wheels import <file> [--dry-run]
| Flag | Default | Description |
|---|---|---|
--dry-run | false | Validate and preview without inserting |
CSV Format
All 16 columns are required in the header. See the table below for which values can be empty.
brand,model,diameter,width,profile,directional,holes,pcd1,pcd2,et,cb,bolt,cap,base_finish,procedures,status
INOVIT,Speed,19,9.5,0,,5,112,,40,73.1,32x15x60,CP506AL68,Black,"Machined Face,Dark Tint,Satin Lacquer",inactive
Hartes Metal,Strike,22,12.0,0,,8,165.1,,-44,125.2,34x16x60,CP708PS130,Black,Milled Dimple,inactive
| Column | Type | Can be empty | Empty default |
|---|---|---|---|
brand | string | No | — |
model | string | No | — |
diameter | int | No | — |
width | float | No | — |
profile | int | Yes | 0 |
directional | string | Yes | “” (non-directional) |
holes | int | No | — |
pcd1 | string | No | — |
pcd2 | string | Yes | “” (single PCD) |
et | int | No | — |
cb | float | No | — |
bolt | string | No | — |
cap | string | No | — |
base_finish | string | No | — |
procedures | string | Yes | “” (no procedures) |
status | string | No | active/inactive/1/0 |
Name Resolution
Brand, model, bolt, cap, finish, and procedure names are resolved to database IDs. The model column matches against both pdName and pdNO in ProductDesign.
Workflow
- Parses CSV and validates required fields
- Resolves all names to database IDs (with in-memory caching)
- Auto-creates
ProductMoldif no mold exists for the design + diameter + width combination - Generates part numbers using the V1 formula (same as
check-pn) - Generates descriptions (same formula as batch-update)
- Checks for duplicate specs already in the database (skips duplicates)
- Shows a preview table with generated part numbers
- If
--dry-run: stops here - Prompts for confirmation (
y/N) - Inserts
Product+ProductWheels+CategoryProductin a single transaction - Links design-level appendant categories (
ProductAndMoldAppendant)
Examples
Dry run (validate only):
efision -r us wheels import new-wheels.csv --dry-run
Efision Wheels Import
============================================================
Region: US
Mode: Dry run (no changes)
File: new-wheels.csv
Parsed 41 row(s) from CSV
41 wheel(s) to import:
# Brand Model Size HxPCD ET CB Finish Procedures Status Part Number
1 INOVIT Speed 19x9.5 5x112 40 73.10 Black Machined Face/Dark Tint/... Inactive 02319M5T40KMFTDTM1ANA
2 INOVIT Speed 20x10 5x120 40 72.60 Black Machined Face/Dark Tint/... Inactive 02320N5B40KMFTDTM1BNA
...
Dry run complete. Run without --dry-run to import.
Import with duplicate detection:
efision -r us wheels import new-wheels.csv
3 row(s) have duplicate specs already in database:
Row Existing pID Existing PN
5 14580 02319M5TF38KMFTDTM1ANA
...
38 wheel(s) to import:
...
Import 38 wheel product(s) into US region? [y/N] yes
Successfully imported 38 wheel product(s)!
Errors shown for unresolvable values:
efision -r demo wheels import bad-data.csv --dry-run
2 row(s) have errors:
Row Field Error
5 bolt bolt '29x15x60' not found
8 model model 'NewDesign' not found for brand 'INOVIT'
Notes
- All inserts execute within a single database transaction (all-or-nothing)
- Part numbers are generated using the same V1 formula as
check-pn - Molds are auto-created when a design + size combination doesn’t exist yet (initial ET/PCD/CB ranges set to the row’s values)
- Duplicate specs (matching design + all wheel specs) are detected and skipped, not rejected
proceduresuses comma-separated names (e.g."Machined Face,Dark Tint,Satin Lacquer"), same asbatch-updatebrandmust matchProductBrand.pbNameexactly;modelmatchesProductDesign.pdNameorpdNO- Lookup errors (missing bolt, cap, finish, procedure, PCD code, CB code, width code) are reported per-row with the specific field and value that failed
Tyres
Tyre product management commands. All commands require the global -r <region> flag.
tyres count
Count tyre products.
Usage: efision -r <region> tyres count
efision -r au tyres count
tyres export
Export tyre products to CSV.
Usage: efision -r <region> tyres export [-o <file>]
| Flag | Short | Default | Description |
|---|---|---|---|
--output | -o | products-export.csv | Output file path |
efision -r au tyres export -o tyres.csv
tyres summary
Show summary statistics for tyre products.
Usage: efision -r <region> tyres summary
efision -r au tyres summary
tyres search
Search tyre products by keyword.
Usage: efision -r <region> tyres search -q <query>
| Flag | Short | Required | Description |
|---|---|---|---|
--query | -q | Yes | Search keyword |
efision -r au tyres search -q "Michelin"
Accessories
Accessory product management commands. Same flags and usage as Tyres, but for accessory products. All commands require the global -r <region> flag.
accessories count
Usage: efision -r <region> accessories count
efision -r au accessories count
accessories export
Usage: efision -r <region> accessories export [-o <file>]
| Flag | Short | Default | Description |
|---|---|---|---|
--output | -o | products-export.csv | Output file path |
efision -r au accessories export -o accessories.csv
accessories summary
Usage: efision -r <region> accessories summary
efision -r au accessories summary
accessories search
Usage: efision -r <region> accessories search -q <query>
| Flag | Short | Required | Description |
|---|---|---|---|
--query | -q | Yes | Search keyword |
efision -r au accessories search -q "cap"
Orders
Order management commands. All commands require the global -r <region> flag.
orders list
List recent orders.
Usage: efision -r <region> orders list [OPTIONS]
| Flag | Short | Default | Description |
|---|---|---|---|
--limit | -l | 50 | Maximum number of orders to return |
--status | -s | (none) | Filter by order status |
Example:
efision -r au orders list --limit 10
Orders List
============================================================
Region: AU
Limit: 10
Fetching orders... done.
Found 10 orders:
------------------------------------------------------------
ID Order No Customer Amount Status Date
------------------------------------------------------------
12345 SO-20240101 Example Customer Ltd $1,250.00 Completed 2024-01-01
...
Example (filter by status):
efision -r au orders list --status pending --limit 20
orders search
Search for a specific order by order number.
Usage: efision -r <region> orders search --order-no <ORDER_NO>
| Flag | Short | Required | Description |
|---|---|---|---|
--order-no | -o | Yes | Order number to search |
Example:
efision -r au orders search --order-no "SO-20240101"
Order Search
============================================================
Region: AU
Order No: SO-20240101
Searching... done.
Order Details
------------------------------------------------------------
ID: 12345
Order No: SO-20240101
Customer: Example Customer Ltd
Customer ID: 678
Total Amount: $1,250.00
Status: Completed
Items Count: 5
Order Date: 2024-01-01
ETF: 2024-01-15
orders summary
Show order summary statistics.
Usage: efision -r <region> orders summary
efision -r au orders summary
orders export
Export orders to CSV.
Usage: efision -r <region> orders export [OPTIONS]
| Flag | Short | Default | Description |
|---|---|---|---|
--output | -o | orders-export.csv | Output file path |
--status | -s | (none) | Filter by order status |
Example:
efision -r au orders export -o orders.csv --status completed
Stock (Warehouse)
Warehouse stock management commands. All commands require the global -r <region> flag.
stock list
List warehouse stock levels.
Usage: efision -r <region> stock list [OPTIONS]
| Flag | Short | Default | Description |
|---|---|---|---|
--low-stock | -l | false | Show only low stock items |
--threshold | -t | 10 | Low stock threshold quantity |
Example (all stock):
efision -r au stock list
Example (low stock alert):
efision -r au stock list --low-stock --threshold 5
Low Stock (< 5)
============================================================
Region: AU
Threshold: 5
Fetching stock... done.
Found 23 items:
--------------------------------------------------------------------------------------------------------------
Product Code Type Qty Warehouse Available
--------------------------------------------------------------------------------------------------------------
INOVIT-SP-001 Wheels 3 Main Warehouse 3
...
stock search
Search warehouse stock by SKU or product code.
Usage: efision -r <region> stock search --sku <SKU>
| Flag | Short | Required | Description |
|---|---|---|---|
--sku | -s | Yes | SKU or product code to search |
Example:
efision -r au stock search --sku "INOVIT-SP-001"
Warehouse Search
============================================================
Region: AU
SKU: INOVIT-SP-001
Searching... done.
Found 2 locations:
------------------------------------------------------------
Product: INOVIT-SP-001
Type: Wheels
Warehouse: Main Warehouse
Quantity: 50
Available: 45
------------------------------------------------------------
Product: INOVIT-SP-001
Type: Wheels
Warehouse: Overflow
Quantity: 20
Available: 20
Customers
Customer management commands. All commands require the global -r <region> flag.
customers search
Search customers by name, email, or company.
Usage: efision -r <region> customers search -q <query>
| Flag | Short | Required | Description |
|---|---|---|---|
--query | -q | Yes | Search keyword (name, email, or company) |
Example:
efision -r au customers search -q "Smith"
Customer Search
============================================================
Region: AU
Query: Smith
Searching... done.
Found 3 customers:
----------------------------------------------------------------------------------------------------
ID Name Email Company
----------------------------------------------------------------------------------------------------
101 John Smith john@example.com Smith Auto Parts
102 Jane Smith jane@smithco.com Smith & Co
customers orders
Get orders for a specific customer by ID.
Usage: efision -r <region> customers orders -c <customer_id>
| Flag | Short | Required | Description |
|---|---|---|---|
--customer-id | -c | Yes | Customer ID (numeric) |
Example:
efision -r au customers orders -c 101
Customer Orders
============================================================
Region: AU
Customer ID: 101
Fetching orders... done.
Found 5 orders:
----------------------------------------------------------------------------------------------------
Order No Amount Status Date
----------------------------------------------------------------------------------------------------
SO-20240101 $1,250.00 Completed 2024-01-01
SO-20240215 $800.00 Shipped 2024-02-15
...
Architecture Overview
Efision follows a 5-layer Clean Architecture pattern where business logic is completely separated from infrastructure concerns.
Layer Diagram
CLI (commands/) → Services → Domain (Entities + Repositories) ← Infrastructure (sqlserver/) → Database
| Layer | Location | Responsibility |
|---|---|---|
| CLI | crates/cli/ | Command parsing, user interaction, output formatting |
| Services | crates/core/src/services/ | Business logic, orchestration (database-agnostic) |
| Domain | crates/core/src/domain/ | Entities, DTOs, Repository trait definitions |
| Infrastructure | crates/core/src/infrastructure/ | SQL Server implementations of repository traits |
| Config | crates/core/src/infrastructure/config.rs | Database connection, region resolution |
Key Principle
Services are generic over repository traits:
#![allow(unused)]
fn main() {
struct WheelService<R: WheelRepository> {
repo: R,
}
}
This means the business logic never depends on SQL Server directly. To switch databases, only the infrastructure layer changes.
Cargo Workspace
The project is split into two crates:
| Crate | Package | Role |
|---|---|---|
crates/core/ | efision-core | Shared library: domain, services, infrastructure |
crates/cli/ | efision-cli | Binary: CLI commands, user interaction |
This separation allows future crates (e.g., crates/api/ for a REST API) to share the same business logic.
Business Domains
| Domain | Entities | Repository | Service |
|---|---|---|---|
| Products | domain/entities/products/ | infrastructure/sqlserver/products/ | services/products/ |
| Sales | domain/entities/sales/ | infrastructure/sqlserver/sales/ | services/sales/ |
| Warehouse | domain/entities/warehouse/ | infrastructure/sqlserver/warehouse/ | services/warehouse/ |
| Customers | domain/entities/customers/ | infrastructure/sqlserver/customers/ | services/customers/ |
Database Stats
| Stat | Value |
|---|---|
| Tables in schema | 305 |
| Entity files | 64 (across 4 business domains) |
| Repository traits | 5 |
5-Layer Clean Architecture
Dependency Rule
Dependencies point inward — outer layers depend on inner layers, never the reverse.
Presentation (CLI/API)
↓ depends on
Application (Services)
↓ depends on
Domain (Entities, Traits)
↑ implemented by
Infrastructure (SQL Server)
The Domain layer has zero external dependencies. Repository traits are defined in the domain and implemented in infrastructure.
Layer Details
1. Domain Layer
The innermost layer. Contains business entities, DTOs, and repository trait definitions.
Entities (crates/core/src/domain/entities/):
- Data model structs with
serdederives - Mirror the SQL Server table structure
- DB column abbreviations preserved (e.g.,
pNO,pStatus,pwDia,boID)
Repository Traits (crates/core/src/domain/repositories/):
- Async traits using
#[async_trait] - Define the operations each domain supports
- No knowledge of SQL Server or any specific database
2. Service Layer
Business logic that orchestrates domain operations.
#![allow(unused)]
fn main() {
pub struct WheelService<R: WheelRepository> {
repo: R,
}
impl<R: WheelRepository> WheelService<R> {
pub async fn count(&mut self, filters: &WheelFilters) -> Result<i32> {
self.repo.count(filters).await
}
}
}
Key patterns:
- Generic over
R: Repository— database-agnostic - Takes
&mut selffor mutable repository access - Uses
anyhow::Resultwith.context()for error chaining
3. Infrastructure Layer
SQL Server implementations of the repository traits.
Location: crates/core/src/infrastructure/sqlserver/
- Uses Tiberius (pure Rust TDS driver) for SQL Server connectivity
- Hand-written SQL queries (no ORM)
- Parameterized queries for SQL injection prevention
- All operations are async
4. Application Layer
Currently thin — the CLI commands serve as the application layer, orchestrating service calls.
As the project evolves (Phase 2: REST API), this layer will include:
- Use cases (application services)
- Input validation
- Authorization checks
- DTO transformations
5. Presentation Layer
CLI (crates/cli/src/commands/):
- Clap derive macros for argument parsing
dialoguerfor interactive promptsowo-colorsfor colored terminal outputcomfy-tablefor table formattingindicatiffor progress bars
Error Handling Strategy
| Layer | Approach |
|---|---|
| Domain | thiserror custom error types |
| Service | anyhow::Context for error chaining |
| Infrastructure | Map Tiberius/SQL errors to domain errors |
| CLI | Display user-friendly messages, exit with appropriate codes |
Database
SQL Server Connection
| Property | Value |
|---|---|
| Engine | SQL Server 2025 |
| Driver | Tiberius (pure Rust TDS protocol implementation) |
| Protocol | TDS 7.3 |
| ORM | None — hand-written SQL with parameterized queries |
| Async | Fully async via Tokio |
Multi-Region Architecture
Efision supports multiple database regions, each representing a separate business entity:
| Region | Flag | Environment Variables |
|---|---|---|
| Australia | -r au | DB_AU_HOST, DB_AU_PORT, DB_AU_NAME, DB_AU_USER, DB_AU_PASSWORD |
| United Kingdom | -r uk | DB_UK_* |
| United States | -r us | DB_US_* |
| Demo | -r demo | DB_DEMO_* |
Region resolution happens in crates/core/src/infrastructure/config.rs. The CLI’s --region flag selects which set of environment variables to use for the database connection.
Why No ORM?
Efision uses hand-written SQL instead of an ORM for several reasons:
- Legacy schema — 305 tables with non-standard naming conventions that don’t map cleanly to ORM patterns
- Performance — direct TDS protocol access avoids ORM overhead
- Complex queries — stored procedures, cross-table updates, and business-specific queries are easier to express in raw SQL
- Gradual migration — only the tables needed for current features are mapped as entities
Schema
The full database schema (305 tables) is available at docs/schema.sql in the repository.
Why Tiberius?
| Feature | Tiberius | pyodbc (Python) |
|---|---|---|
| Dependencies | Pure Rust (no C deps) | Requires ODBC driver |
| Async | Native async (Tokio) | No native async |
| Performance | ~5-7x faster | Baseline |
| Memory | ~10 MB | ~50 MB |
| Deployment | Single binary | Python runtime + driver |
| Platform | macOS, Linux, Windows | Same |
See SQL Server Compatibility for detailed driver research.
Future ERP Vision
Project Vision
Goal: Build a modern, full-featured ERP system to replace the legacy .NET ERP.
Phases:
- Phase 1 (Complete): Read-only data access + data models, CLI tool
- Phase 2: Backend REST API (read-write support)
- Phase 3: CLI enhancements (full CRUD)
- Phase 4: Web frontend
Complete Architecture Design
Target Architecture: DDD + Clean Architecture
┌─────────────────────────────────────────────────────────────┐
│ Presentation Layer │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ Web UI │ │ REST API │ │ CLI Tool │ │
│ │ (React) │ │ (Axum) │ │ (Clap) │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ │
└─────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ Application Layer │
│ • Use Cases (Application Services) │
│ • DTOs (Data Transfer Objects) │
│ • Validation & Authorization │
└─────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ Domain Layer │
│ • Entities (Aggregates) │
│ • Domain Services │
│ • Repository Interfaces (Traits) │
│ • Domain Events │
└─────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ Infrastructure Layer │
│ • Database (SQL Server / PostgreSQL) │
│ • Caching (Redis) │
│ • Message Queue (optional) │
│ • External Services (email, payments) │
└─────────────────────────────────────────────────────────────┘
Tech Stack (Planned)
Backend (Rust)
| Component | Technology | Purpose |
|---|---|---|
| Web Framework | Axum 0.7+ | HTTP routing, middleware |
| Async Runtime | Tokio 1.0+ | Standard Rust async runtime |
| Database | SQLx 0.7+ or SeaORM | Compile-time SQL checking / Full ORM |
| Auth | jsonwebtoken + argon2 | JWT + password hashing |
| API Docs | utoipa 4.0+ | Auto-generated OpenAPI docs |
| Logging | tracing | Structured logging |
| Validation | validator | Input validation |
Frontend (Planned)
| Component | Technology |
|---|---|
| Framework | React 19 + TypeScript |
| Build Tool | Vite 7.0+ |
| UI Library | Ant Design 6.x |
| Server State | TanStack Query 5 |
| Client State | Zustand |
| Router | React Router 7 |
| CSS | Tailwind CSS 4 |
Database Migration Path
- Short-term: Continue with SQL Server (add read-write support)
- Mid-term: Prepare PostgreSQL migration
- Long-term: Fully migrate to PostgreSQL (open source, better Rust ecosystem support)
Phase 2: Backend API
Estimated duration: 2-3 months
Milestone 1: Foundation (2 weeks)
- Application Layer setup (use cases, DTOs, validation)
- Migrate from Tiberius to SQLx/SeaORM
- Read-write transaction support
- Connection pool configuration
Milestone 2: API Server (4 weeks)
- Axum server with route structure
- Middleware (Auth, Logging, CORS, Rate Limiting)
- JWT authentication + RBAC
- Core Product APIs (CRUD)
Milestone 3: Business Modules (6 weeks)
- Orders API (CRUD + status workflow + fulfillment)
- Warehouse API (stock management + transfers)
- Customers API (CRM features)
- Finance API (invoicing + payments)
Milestone 4: Testing & Documentation (2 weeks)
- Unit tests (>80% coverage)
- Integration tests
- OpenAPI documentation
- Deployment setup
Phase 4: Web Frontend
Estimated duration: 3-4 months
- Infrastructure setup (React 19 + Vite + Ant Design)
- Core features: Login, Dashboard, Product/Order/Warehouse management
- Advanced features: Real-time updates, file uploads, PDF generation, Excel import/export
Performance Targets
| Metric | Target |
|---|---|
| API response time (P95) | <100ms |
| API throughput | >1000 req/s |
| Frontend initial load | <2s |
| Lighthouse score | >90 |
| Test coverage | >80% |
Security Design
- JWT with refresh tokens
- Role-Based Access Control (RBAC)
- HTTPS only (TLS 1.3)
- Parameterized SQL queries
- CSRF protection
- Rate limiting
- Audit logging
Roadmap Summary
Q1 2026: Phase 2 - Backend API
Q2 2026: Phase 3 - CLI Enhancements
Q3-Q4 2026: Phase 4 - Web Frontend
Created: 2026-02-05 | Status: Draft for Review
Development Setup
Prerequisites
| Requirement | Version | Install |
|---|---|---|
| Rust | 1.75+ | rustup.rs |
| SQL Server access | 2025 | Database credentials for at least one region |
No ODBC driver is needed — Efision uses Tiberius (pure Rust TDS driver).
Clone and Configure
git clone https://github.com/Inovit-Inc/efision.git
cd efision
# Set up environment
cp .env.example .env
# Edit .env with your database credentials
Build and Run
# Debug build
cargo build
# Release build (optimized)
cargo build --release
# Run in development
cargo run -p efision-cli -- -r demo wheels count
Test
# Run all tests
cargo test
# Run tests with output
cargo test -- --nocapture
Lint
# Run clippy (required before committing)
cargo clippy
# Format code
cargo fmt
IDE Setup
For VS Code, install the rust-analyzer extension. The Cargo workspace is automatically detected.
Optional Tools
- ODBC Driver — only needed for
sqlcmd/bcpdiagnostic tools - See SQL Server Compatibility for Tiberius driver details
ODBC Driver Installation
Note: The Rust CLI uses Tiberius, which connects to SQL Server directly via the TDS protocol — no ODBC driver is required to run the CLI. This guide is only needed if you want diagnostic tools like
sqlcmdandbcp.
Important: Use ODBC Driver 17 (not 18). Driver 18 dropped support for SQL Server 2008 R2.
macOS (Homebrew)
brew install unixodbc
brew tap microsoft/mssql-release https://github.com/Microsoft/homebrew-mssql-release
brew update
HOMEBREW_ACCEPT_EULA=Y brew install msodbcsql17
# (Optional) Install command-line tools
HOMEBREW_ACCEPT_EULA=Y brew install mssql-tools
Windows
Download and install from https://learn.microsoft.com/en-us/sql/connect/odbc/download-odbc-driver-for-sql-server
Or use winget:
winget install --id=Microsoft.msodbcsql.17 -e
# (Optional) Install command-line tools
winget install --id=Microsoft.Sqlcmd -e
Ubuntu/Debian
Option 1: Automated Install
sudo ./scripts/install-odbc-ubuntu.sh
Option 2: Manual Install
# 1. Install unixODBC
sudo apt-get update
sudo apt-get install -y unixodbc unixodbc-dev curl gnupg
# 2. Add Microsoft repository
curl -fsSL https://packages.microsoft.com/keys/microsoft.asc | sudo gpg --dearmor -o /usr/share/keyrings/microsoft-prod.gpg
echo "deb [signed-by=/usr/share/keyrings/microsoft-prod.gpg] https://packages.microsoft.com/config/ubuntu/$(lsb_release -rs)/prod.list main" | sudo tee /etc/apt/sources.list.d/mssql-release.list
# 3. Install ODBC Driver 17
sudo apt-get update
sudo ACCEPT_EULA=Y apt-get install -y msodbcsql17
# 4. (Optional) Install command-line tools
sudo ACCEPT_EULA=Y apt-get install -y mssql-tools
echo 'export PATH="$PATH:/opt/mssql-tools/bin"' >> ~/.bashrc
Other Linux Distributions
See official Microsoft documentation: https://learn.microsoft.com/en-us/sql/connect/odbc/linux-mac/installing-the-microsoft-odbc-driver-for-sql-server
Verify Installation
# Check ODBC configuration
odbcinst -j
# List installed ODBC drivers
odbcinst -q -d
You should see:
[ODBC Driver 17 for SQL Server]
Troubleshooting
Issue: Repository not found (Ubuntu)
Solution: Adjust the Ubuntu version in the repository URL (e.g., 20.04, 22.04, 24.04)
Issue: GPG key error (Ubuntu)
Solution: Re-import the Microsoft GPG key:
curl -fsSL https://packages.microsoft.com/keys/microsoft.asc | sudo gpg --dearmor -o /usr/share/keyrings/microsoft-prod.gpg
Issue: Connection timeout
Solution: Check firewall settings and network connectivity:
# Test SQL Server port
nc -zv your-server.database.windows.net 1433
SQL Server Compatibility
Tiberius — Recommended Rust Driver
| Property | Value |
|---|---|
| Repository | prisma/tiberius |
| Version | 0.12.x |
| Type | Pure Rust TDS protocol implementation |
| Maintained by | Prisma team |
TDS Protocol Support
- TDS 7.2 (SQL Server 2008)
- TDS 7.3 (SQL Server 2008 R2)
- TDS 7.4 (SQL Server 2012+)
Features
- Pure Rust (no C dependencies)
- Async-only (Tokio)
- Cross-platform (macOS, Linux, Windows)
- Active maintenance
TLS/SSL Compatibility
SQL Server 2008 R2 uses TLS 1.0/1.1 which is disabled by default in modern systems. Rust’s rustls does not support these deprecated protocols.
Solution: Disable Encryption
For internal network access, disable TLS encryption:
#![allow(unused)]
fn main() {
let mut config = Config::new();
config.host("your_host");
config.port(1433);
config.authentication(AuthMethod::sql_server("user", "password"));
config.database("your_database");
config.trust_cert();
config.encryption(EncryptionLevel::NotSupported);
}
This is the same approach as Python’s Encrypt=no connection string option.
Alternative: Use native-tls Backend
[dependencies]
tiberius = { version = "0.12", default-features = false, features = ["tds73", "native-tls"] }
Then configure OpenSSL to allow legacy TLS:
export OPENSSL_CONF=/path/to/openssl-legacy.cnf
Comparison with Python (pyodbc)
| Feature | Rust (tiberius) | Python (pyodbc) |
|---|---|---|
| SQL Server Support | TDS 7.2-7.4 | Via ODBC driver |
| TLS 1.0 Workaround | EncryptionLevel::NotSupported | Encrypt=no |
| Performance | ~5-7x faster | Baseline |
| Memory | ~10 MB | ~50 MB |
| Dependencies | Pure Rust | Python + ODBC driver |
| Async | Native (Tokio) | No native async |
| Type Safety | Compile-time | Runtime (Pydantic) |
| Deployment | Single binary | Complex |
Current Configuration
[dependencies]
tiberius = { version = "0.12", features = ["tds73", "chrono", "rust_decimal"] }
tokio = { version = "1", features = ["full"] }
tokio-util = { version = "0.7", features = ["compat"] }
Migration Path
When migrating to modern SQL Server (2016+) or PostgreSQL, full TLS 1.2+ encryption can be enabled.
Adding a Feature
Follow this 7-step pattern when adding a new feature to Efision. Each step corresponds to a layer in the clean architecture.
Step-by-Step
1. Entity
Create data model structs in crates/core/src/domain/entities/{domain}/.
#![allow(unused)]
fn main() {
// domain/entities/products/my_product.rs
use serde::{Deserialize, Serialize};
#[derive(Debug, Serialize, Deserialize)]
pub struct MyProduct {
#[serde(rename = "pID")]
pub id: i32,
#[serde(rename = "pNO")]
pub part_number: String,
// ...
}
}
- Use
serdederives with#[serde(rename = "...")]for DB column mapping - Preserve DB column abbreviations in field names where appropriate
2. DTO (if needed)
Create DTOs in crates/core/src/domain/dtos/ for API boundaries or CLI output formatting.
3. Repository Trait
Define an async trait in crates/core/src/domain/repositories/{domain}/.
#![allow(unused)]
fn main() {
// domain/repositories/products/my_product_repo.rs
use async_trait::async_trait;
use anyhow::Result;
#[async_trait]
pub trait MyProductRepository {
async fn count(&mut self) -> Result<i32>;
async fn find_all(&mut self) -> Result<Vec<MyProduct>>;
// ...
}
}
4. SQL Server Implementation
Implement the trait in crates/core/src/infrastructure/sqlserver/{domain}/.
#![allow(unused)]
fn main() {
// infrastructure/sqlserver/products/my_product_repo.rs
use async_trait::async_trait;
pub struct SqlServerMyProductRepo {
client: Client<Compat<TcpStream>>,
}
#[async_trait]
impl MyProductRepository for SqlServerMyProductRepo {
async fn count(&mut self) -> Result<i32> {
let row = self.client
.query("SELECT COUNT(*) AS total FROM MyTable WHERE status = 1", &[])
.await?
.into_row()
.await?
.context("No row returned")?;
Ok(row.get("total").context("Missing total column")?)
}
}
}
- Always use parameterized queries
- Map Tiberius errors to meaningful domain errors
5. Service
Create business logic in crates/core/src/services/{domain}/.
#![allow(unused)]
fn main() {
// services/products/my_product_service.rs
pub struct MyProductService<R: MyProductRepository> {
repo: R,
}
impl<R: MyProductRepository> MyProductService<R> {
pub fn new(repo: R) -> Self {
Self { repo }
}
pub async fn count(&mut self) -> Result<i32> {
self.repo.count().await
}
}
}
6. CLI Command
Wire up the command in crates/cli/src/commands/ using clap derive macros.
#![allow(unused)]
fn main() {
#[derive(Subcommand)]
pub enum MyProductCommands {
/// Count products
Count,
/// Export products to CSV
Export {
#[arg(short, long, default_value = "export.csv")]
output: String,
},
}
}
7. Route in main.rs
Add the match arm in crates/cli/src/main.rs to connect the command to the service.
Checklist
- Entity struct with serde derives
- Repository trait with async methods
- SQL Server implementation with parameterized queries
- Service with generic
<R: Repository>pattern - CLI command with clap derives
- Route in main.rs
-
cargo clippypasses -
cargo testpasses
Coding Conventions
Error Handling
| Layer | Approach |
|---|---|
| Domain | thiserror for custom error types |
| Services | anyhow::Result with .context() chaining |
| Infrastructure | Map driver errors to domain errors |
| CLI | Display user-friendly messages |
Rules:
- No
unwrap()in production code expect()requires a clear explanation message- Errors should be caught at the boundary closest to where they can be meaningfully handled
Async
- All I/O is async
#[tokio::main]entry point- Services take
&mut selffor mutable repository access
Naming
| Element | Convention | Example |
|---|---|---|
| Variables / functions | snake_case | wheel_count |
| Types / structs | PascalCase | WheelProduct |
| DB columns in entities | Preserve abbreviations | pNO, pStatus, pwDia |
| Service structs | {Domain}Service<R: {Domain}Repository> | WheelService<R: WheelRepository> |
| Repo implementations | SqlServer{Domain}Repo | SqlServerWheelRepo |
Serialization
serdederive on all entities#[serde(rename = "...")]for DB column / CSV mapping- Custom serializers for booleans, decimals, and dates
CLI Output
owo-colorsfor terminal coloringdialoguerfor interactive promptscomfy-tablefor table formattingindicatiffor progress bars
Module Organization
- One concept per file
mod.rsexports the public interface- Keep functions under 50 lines; extract sub-functions when exceeding
Project Structure
Workspace Layout
efision/
├── Cargo.toml # Workspace root
├── crates/
│ ├── core/ # efision-core: shared business logic
│ │ ├── Cargo.toml
│ │ └── src/
│ │ ├── lib.rs
│ │ ├── domain/
│ │ │ ├── entities/ # Data model structs
│ │ │ │ ├── customers/
│ │ │ │ ├── products/
│ │ │ │ ├── sales/
│ │ │ │ └── warehouse/
│ │ │ ├── dtos/ # Data transfer objects
│ │ │ └── repositories/ # Repository traits
│ │ ├── infrastructure/
│ │ │ ├── config.rs # DB connection, region resolution
│ │ │ └── sqlserver/ # SQL Server implementations
│ │ │ ├── customers/
│ │ │ ├── products/
│ │ │ ├── sales/
│ │ │ └── warehouse/
│ │ └── services/ # Business logic
│ │ ├── customers/
│ │ ├── products/
│ │ ├── sales/
│ │ └── warehouse/
│ └── cli/ # efision-cli: command-line binary
│ ├── Cargo.toml
│ └── src/
│ ├── main.rs # Entry point, command routing
│ └── commands/
│ └── commands.rs # Clap command definitions
├── docs/ # Documentation source (schema, architecture)
├── docs-site/ # mdBook documentation site
├── scripts/ # Build and setup scripts
├── sheets-sync/ # Google Sheets sync tools
├── pn-references/ # Part number reference data
├── .env # Database credentials (git-ignored)
└── .env.example # Template for .env
Crate Responsibilities
efision-core
The shared library containing all business logic. Has no knowledge of CLI or any specific presentation layer.
- domain/entities/ — Rust structs mirroring SQL Server tables, with
serdederives - domain/repositories/ — Async trait definitions (the “ports”)
- infrastructure/sqlserver/ — Tiberius-based implementations (the “adapters”)
- services/ — Business logic generic over repository traits
efision-cli
The CLI binary. Depends on efision-core for all business operations.
- commands/ — Clap derive macro definitions for all CLI commands
- main.rs — Entry point, region/config resolution, command routing
Key Dependencies
| Crate | Purpose |
|---|---|
tiberius | SQL Server TDS driver |
tokio | Async runtime |
clap (derive) | CLI argument parsing |
serde + csv | Serialization and CSV export |
anyhow + thiserror | Error handling |
chrono | Date/time types |
rust_decimal | Precise decimal arithmetic |
dialoguer | Interactive CLI prompts |
owo-colors | Terminal color output |
comfy-table | Table formatting |
indicatif | Progress bars |
Part Number Formula
Efision uses a deterministic formula (Version 1) to generate wheel part numbers from product specifications.
Formula Structure
DesignNO + [P+Profile] + [Directional 1st letter] + Dia + WidthCode + Holes
+ PCD1Code + [PCD2Code] + ET + FinishShortName + AppendantShortNames
+ BoltShortName + CBCode + CapNO
Segments in brackets [] are optional — included only when the value is non-default.
Segment Breakdown
| Segment | Source | Example |
|---|---|---|
DesignNO | ProductDesign.pdNO | 067 |
P+Profile | Only if profile > 0 | P3L |
Directional | First letter if directional | R or L |
Dia | Diameter (integer) | 19 |
WidthCode | Lookup from width | Y (for 8.5“) |
Holes | Number of bolt holes | 5 |
PCD1Code | Lookup from PCD value | V (for 114.3) |
PCD2Code | Only if dual PCD | T (for 112) |
ET | ET offset value | 40 |
FinishShortName | Lookup from finish | S (Silver), K (Black) |
AppendantShortNames | Concatenated procedure codes | MF (Machined Face) |
BoltShortName | Lookup from bolt spec | 4A |
CBCode | Lookup from CB value | NA |
CapNO | Cap part number code | CP506AL68 |
Example
For an INOVIT Speed 19x8.5 5x112 ET38 Silver Machined Face wheel:
067 + 19 + Y + 5 + V + 40 + S + MF + 4A + NA
↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓
Design Dia Width Holes PCD1 ET Finish Proc Bolt CB
Result: 06719Y5V40SMF4ANA
Related Commands
wheels check-pn— validate existing part numbers against this formulawheels import— auto-generates part numbers for new productswheels batch-update— regenerates part numbers after spec changes
Lookup Tables
The formula references several lookup tables in the database:
- Width codes — maps width values (e.g.,
8.5) to single-character codes - PCD codes — maps PCD values (e.g.,
114.3) to short codes - CB codes — maps center bore values to short codes
- Finish short names — maps finish types to abbreviations
- Bolt short names — maps bolt specs to codes
- Appendant short names — maps procedures to abbreviations
Lookup errors during check-pn or import indicate missing entries in these reference tables.
Database Tables Analysis & ORM Mapping Plan
Overview
This document analyzes all database tables used in the Legacy ERP system and provides a roadmap for creating ORM mappings in the Efision project.
Total tables identified: 230+ tables
Current status: 15 Product-related tables mapped ✅
Table Categories
1. 📦 Product Management (Priority: HIGH)
Current Status: 15/21 tables mapped (71%)
✅ Already Mapped
-
Product- Main product table -
ProductWheels- Wheel-specific attributes -
ProductDesign- Design patterns -
ProductBrand- Brand information -
ProductPcd- PCD (bolt pattern) -
ProductFinishType- Finish types -
ProductBaseAppendantType- Base appendant types -
Bolt- Bolt specifications -
Cap- Cap specifications
✅ Phase 1 Complete (2026-02-05)
-
ProductType- Product type definitions -
ProductSeries- Product series groupings -
ProductCategory- Product classification -
ProductGallery- Product images -
ProductBasePrice- Base pricing matrix -
ProductBOM- Bill of Materials
⏳ To Be Mapped (Phase 2+)
-
ProductRelated- Related products -
ProductRelation- Product relationships -
ProductView- Product views/stats -
Model- Product models -
ProductModelCompanyPrice- Company-specific pricing -
ProductCoupon- Product coupons
📊 Sub-categories
Tires:
-
ProductTire- Tire specifications -
ProductDiameter- Tire diameters -
ProductWidth- Tire widths
Clothing/Fabric:
-
ProductClothing- Clothing products -
ProductFabric- Fabric products -
ProductDesignClothing- Clothing designs
Molds:
-
ProductMold- Mold information -
ProductMoldExtension- Mold extensions -
ProductMoldExtensionCheck- Mold extension checks -
ProductMoldExtensionCheckDetail- Check details
Semi-finished:
-
ProductSemi- Semi-finished products
Packing:
-
ProductPacking- Packing specifications -
PackingSpec- Packing spec details -
PackingSpecItem- Packing spec items
Other attributes:
-
ProductEt- ET (offset) values -
ProductCb- CB values -
ProductButtom- Button specifications -
ProductNORelation- Product number relations
2. 🛒 Sales & Orders (Priority: HIGH)
Current Status: 0/20 tables mapped (0%)
Core Orders
-
Ordert- Main orders table -
OrderDetail- Order line items -
OrderLog- Order history logs -
OrdersNotepad- Order notes -
OrdersSalesCommissiom- Sales commissions -
BackOrder- Back orders -
BackOrderDetail- Back order details -
BackOrderLog- Back order logs
Shopping Cart
-
ShoppingCart- Shopping carts -
ShoppingCartItem- Cart items
Pricing & Discounts
-
PriceLevel- Price levels -
Coupon- Coupons -
CouponDetail- Coupon details -
UserCoupon- User-specific coupons -
ProductCustomAppendantPrice- Custom appendant pricing -
ProductCustomBasePrice- Custom base pricing -
ProductAppendantCategoryPrice- Appendant category pricing -
ProductBaseAppendantPrice- Base appendant pricing
Tax
-
TaxRate- Tax rates -
TaxReturn- Tax returns
3. 📦 Warehouse & Inventory (Priority: HIGH)
Current Status: 0/21 tables mapped (0%)
Stock Management
-
WarehouseStock- Current stock levels -
WarehouseStockLog- Stock movement logs -
StockLog- General stock logs -
ClosingStock- Closing stock -
ClosingStockDetail- Closing stock details
Stock In/Out
-
StockInOrder- Stock in orders -
StockInOrderDetail- Stock in details -
StockOutOrder- Stock out orders -
StockOutOrderDetail- Stock out details
Stock Take
-
StockTake- Stock takes -
WarehouseTakeStock- Warehouse stock takes -
WarehouseTakeStockDetail- Stock take details
Warehouses
-
Warehouse- Warehouse information -
WarehouseUsers- Warehouse users
Pallets & Containers
-
Pallet- Pallet tracking -
Containers- Container tracking
Requisitions
-
Requisition- Material requisitions -
RequisitionDetail- Requisition details -
MarginRequistion- Margin requisitions
Purchase
-
PurchaseRequisition- Purchase requisitions -
PurchaseRequisitionDetail- Purchase requisition details
4. 💰 Finance & Accounting (Priority: MEDIUM)
Current Status: 0/35+ tables mapped (0%)
Accounts
-
Account- Chart of accounts -
Accounts- Account details -
AccountingSubject- Accounting subjects -
AccountLog- Account logs -
AccountAllocation- Account allocations -
AccountTransfer- Account transfers -
AccountVerification- Account verifications -
ReconcileAccount- Account reconciliations -
ReconcileAccountItem- Reconciliation items
Vouchers & Ledgers
-
Acct_Voucher- Accounting vouchers -
Acct_VoucherItem- Voucher items -
Acct_Ledger- General ledger -
Acct_LedgerAmount- Ledger amounts -
Acct_LedgerCategory- Ledger categories -
Acct_LedgerRegion- Ledger regions -
Acct_Business- Business transactions -
Acct_BusinessLedgerRelation- Business-ledger relations -
Voucher- General vouchers -
VoucherItem- Voucher line items
Payments
-
ReceivePayment- Payment receipts -
ReceivePaymentDetail- Receipt details -
ContraPayment- Contra payments -
ContraPaymentDetail- Contra payment details -
PaymentMethod- Payment methods -
PaymentMethodNode- Payment method nodes -
PaymentNode- Payment nodes -
PaymentType- Payment types
Fixed Assets
-
FixedAsset- Fixed assets -
FixedAssetDetail- Asset details -
FixedAssetDepreciation- Depreciation -
FixedAssetDepreciationDetail- Depreciation details
Currency & Exchange
-
Currency- Currency definitions -
InitCurrency- Initial currency -
ExchangeLosing- Exchange losses -
ExchangeLosingDetail- Loss details -
ExchangeLosingCurrency- Currency losses -
ExchangeLoseDetail- Loss detail records
Financial Reports
-
IncomeStatement- Income statements -
IncomeStatementDetail- Statement details -
CashFlowSetting- Cash flow settings -
CashFlowSettingDetail- Cash flow details
Banking
-
Bank- Bank accounts -
LetterCredit- Letters of credit -
Loan- Loans
Credits
-
Credits- Credit system -
CreditsConfigure- Credit configuration
5. 🏭 Manufacturing (Priority: MEDIUM)
Current Status: 0/30+ tables mapped (0%)
Manufacturing Orders
-
MO- Manufacturing orders -
MOItem- MO line items -
ManufactureOrder- Manufacture orders -
ManufactureOrderDetail- Order details -
ManufacturePlan- Manufacturing plans -
ManufacturePlanDetail- Plan details -
ProduceSchedule- Production schedules -
ProduceScheduleItem- Schedule items
BOM & MRP
-
BOM- Bill of Materials -
BOMDetail- BOM details -
MRP- Material Requirements Planning -
MRPDetail- MRP details
Manufacturing Processes
-
Process- Process definitions -
ProcessExchange- Process exchanges -
ProcessExchangeDetail- Exchange details
CNC Operations
-
CNC- CNC machines/operations -
ManufactureCNC- CNC manufacturing -
ManufactureCNCDetail- CNC details
Packing Operations
-
ManufacturePacking- Packing operations -
ManufacturePackingDetail- Packing details
Painting Operations
-
ManufacturePainting- Painting operations -
ManufacturePaintingDetail- Painting details
Foundry
-
Foundry- Foundry operations -
FoundryDetail- Foundry details -
FoundryDetailLog- Foundry logs
Quality Control
-
AluminiumCheck- Aluminum quality checks -
AluminiumCheckDetail- Check details
Melting
-
Melter- Melting operations
6. 🤝 CRM & Customers (Priority: MEDIUM)
Current Status: 0/15+ tables mapped (0%)
Customers
-
Customers- Customer information -
CompanySales- Company sales data -
CompanyUsers- Company users -
OtherContacts- Other contacts
Custom Definitions
-
CustomerDefined- Customer definitions -
CustomerDefinedDetail- Definition details
Complaints
-
Complain- Complaints -
ComplainItem- Complaint items
Appointments
-
Appointment- Appointments
User Preferences
-
UserLastView- User last views -
UserProductSeries- User product series -
UserPriority- User priorities -
CategoryProduct- Category products
7. 👥 User & System Management (Priority: MEDIUM)
Current Status: 0/25+ tables mapped (0%)
Users
-
Users- User accounts -
UserGroups- User groups -
CheckUser- User checks
Permissions
-
Rights- User rights -
RightsFlow- Rights flow -
Action- Actions
System Logs
-
SystemLog- System logs -
SystemType- System types -
CsvDownloadLog- CSV download logs
Workflows
-
Flow- Workflow definitions -
FlowObject- Flow objects
Messages
-
Messages- Messages -
MessageSet- Message settings -
MessageCategory- Message categories -
MessageRelation- Message relations -
UserMessage- User messages -
UserOperateMessage- User operation messages
-
SystemEmail- System emails -
SystemEmailUser- Email users -
EmailTemplate- Email templates -
UserGroupsEmailTemplate- Group email templates
HR & Payroll
-
Attendance- Attendance records -
LeaveForm- Leave forms -
Payroll- Payroll -
PayrollDetail- Payroll details -
PayrollSetting- Payroll settings -
PayrollSettingDetail- Payroll setting details -
Reimbursement- Reimbursements -
ReimbursementDetail- Reimbursement details
8. 📋 Invoicing & Documentation (Priority: MEDIUM)
Current Status: 0/5 tables mapped (0%)
-
InvoiceIn- Incoming invoices -
InvoiceInDetail- Invoice details -
PurchaseOrder- Purchase orders -
PurchaseOrderItem- PO line items
9. 🌐 Content & Media (Priority: LOW)
Current Status: 0/15+ tables mapped (0%)
News & Help
-
News- News articles -
NewsCategory- News categories -
Help- Help content -
HelpDetail- Help details
Resources
-
Resource- Resources -
ResourceItem- Resource items -
ResourceTag- Tags -
ResourceTagRelationship- Tag relationships
Galleries
-
YongLe_Gallery- YongLe gallery
Syndication
-
Syndication- Syndication -
SyndicationItem- Syndication items -
SyndicationOld- Old syndication -
SyndicationItemOld- Old syndication items
Advertisements
-
Advertisement- Advertisements
10. 🌍 Reference Data (Priority: LOW)
Current Status: 0/10+ tables mapped (0%)
-
Country- Countries -
Port- Ports -
ShippingAgent- Shipping agents -
location- Locations -
Entity- Generic entities -
Sequence- Sequence generators
11. 📊 Dashboard & Analytics (Priority: LOW)
Current Status: 0/2 tables mapped (0%)
-
dashboardModel- Dashboard models -
dashboardRight- Dashboard rights
Implementation Roadmap
Phase 1: Core Product Tables ✅ COMPLETE (2026-02-05)
Goal: Complete Product module core tables
Completed tables:
ProductType- Product type definitions (Python + Rust)ProductSeries- Series groupings (Python + Rust)ProductCategory- Hierarchical classification (Python + Rust)ProductGallery- Product images (Python + Rust)ProductBasePrice- Base pricing matrix (Python + Rust)ProductBOM- Bill of Materials (Python + Rust)
Completed tasks:
- Create SQLModel mappings (Python) - 6 models
- Create Domain models (Rust) - 6 structs
- Create Repository traits (Rust) - 6 traits
- Create SQL Server implementations (Rust) - 6 repos
- Write integration tests - 24 Rust tests (3 regions × 6 tables + 6 repo tests)
- Write Python tests - 7 import tests + 22 DB tests
- Update documentation
Phase 2: Sales & Orders (Week 2)
Goal: Enable order management
Priority tables:
Ordert- Main ordersOrderDetail- Order itemsCustomers- Customer dataPriceLevel- PricingShoppingCart- E-commerce
Phase 3: Warehouse & Inventory (Week 3)
Goal: Enable inventory tracking
Priority tables:
WarehouseStock- Current stockStockInOrder- Stock inStockOutOrder- Stock outWarehouse- Warehouse info
Phase 4: Finance & Accounting (Week 4)
Goal: Enable financial reporting
Priority tables:
Account- Chart of accountsAcct_Voucher- VouchersReceivePayment- PaymentsCurrency- Currency
Phase 5: Manufacturing (Week 5+)
Goal: Enable production planning
Priority tables:
MO- Manufacturing ordersBOM- Bill of materialsManufacturePlan- PlanningProcess- Processes
Technical Implementation Guidelines
Domain Entity Template
#![allow(unused)]
fn main() {
// crates/core/src/domain/entities/{domain}/table_name.rs
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TableName {
pub id: i32,
pub field_name: Option<String>,
pub created_at: Option<String>,
pub foreign_id: Option<i32>,
}
impl TableName {
pub fn new(id: i32) -> Self {
Self {
id,
field_name: None,
created_at: None,
foreign_id: None,
}
}
// Business logic methods here
}
}
Repository Trait Template
#![allow(unused)]
fn main() {
// crates/core/src/domain/repositories/{domain}/table_name_repository.rs
use crate::domain::entities::TableName;
use anyhow::Result;
#[async_trait::async_trait]
pub trait TableNameRepository: Send + Sync {
async fn find_by_id(&self, id: i32) -> Result<Option<TableName>>;
async fn find_all(&self) -> Result<Vec<TableName>>;
async fn count(&self) -> Result<i32>;
}
}
Testing Template
#![allow(unused)]
fn main() {
// crates/core/tests/test_table_name.rs
#[cfg(test)]
mod tests {
use efision_core::infrastructure::config::DatabaseConfig;
use efision_core::infrastructure::sqlserver::TableNameRepo;
use efision_core::domain::repositories::TableNameRepository;
#[tokio::test]
async fn test_count() {
let config = DatabaseConfig::from_region("AU");
let repo = TableNameRepo::new(config).await.unwrap();
let count = repo.count().await.unwrap();
assert!(count > 0);
println!("Total records: {}", count);
}
}
}
Naming Conventions
Rust (Snake Case for modules, PascalCase for types)
- Module:
table_name.rs - Struct:
TableName - Repository trait:
TableNameRepository - Implementation:
SqlServerTableNameRepo - Service:
TableNameService
Column Name Mapping Strategy
SQL Server uses PascalCase for column names (e.g., ProductID, CreateTime).
Python: Use sa_column_kwargs={"name": "ColumnName"} to map snake_case to PascalCase.
Rust: Use SQL aliases in queries: SELECT ProductID as product_id.
Database Connection Settings
All tables use the same connection settings as current implementation:
#![allow(unused)]
fn main() {
config.encryption(tiberius::EncryptionLevel::NotSupported);
config.application_name("efision");
config.readonly(true);
}
Documentation Requirements
For each new table mapping, create/update:
- Entity: Add to
crates/core/src/domain/entities/{domain}/ - Repository: Add trait to
crates/core/src/domain/repositories/{domain}/ - SQL Implementation: Add to
crates/core/src/infrastructure/sqlserver/{domain}/ - Tests: Document test results
- Relationships: Document foreign key relationships (even if not enforced)
- Business logic: Document any business rules in domain models
Progress Tracking
Overall completion: 15/230+ tables (6.5%)
By category:
- ✅ Product Management: 15/21 (71%)
- ⏸️ Sales & Orders: 0/20 (0%)
- ⏸️ Warehouse: 0/21 (0%)
- ⏸️ Finance: 0/35+ (0%)
- ⏸️ Manufacturing: 0/30+ (0%)
- ⏸️ CRM: 0/15+ (0%)
- ⏸️ User Management: 0/25+ (0%)
- ⏸️ Invoicing: 0/5 (0%)
- ⏸️ Content: 0/15+ (0%)
- ⏸️ Reference Data: 0/10+ (0%)
- ⏸️ Dashboard: 0/2 (0%)
Next Actions
- Review this document with Henry ✅
- Confirm Phase 1 priority tables ✅
- Phase 1 implementation ✅ (2026-02-05)
- Start Phase 2: Sales & Orders ⏳
Last updated: 2026-02-05 Maintained by: Lobster 🦞
Performance Benchmarks
CLI Performance
| Metric | Value |
|---|---|
| Count query | <100ms |
| Export 3,878 products | <1s |
| Binary size | 1.9 MB |
| Startup time | <100ms |
| Memory usage | ~10 MB |
Comparison with Python
Export 1,000 Products
| Language | Time | Memory |
|---|---|---|
| Python (pyodbc) | ~2.0s | ~50 MB |
| Rust (tiberius) | ~0.3s | ~10 MB |
Rust advantage: 5-7x faster, 5x less memory.
Why It’s Fast
- Tiberius — native TDS protocol, no ODBC layer overhead
- Tokio — async I/O, non-blocking database access
- Zero-copy where possible — serde deserialization directly from TDS rows
- Release build optimizations — LTO, single codegen unit, stripped symbols
Build Optimization Settings
[profile.release]
opt-level = 3
lto = true
codegen-units = 1
strip = true
Roadmap
Phase Overview
| Phase | Status | Description |
|---|---|---|
| 1 — CLI Tool | Done | 24 commands, 64 entities, 6 domains |
| 1.5 — Workspace | Done | Cargo workspace, shared efision-core |
| 1.6 — CLI Restructure | Done | Global --region, unified filters, batch-update subcommands |
| 2 — Web API | Planned | Axum REST API, JWT auth, connection pooling |
| 3 — Web Frontend | Planned | React 19, Ant Design, TanStack Query |
| 4 — DB Migration | Planned | PostgreSQL, zero-downtime cutover |
Phase 2: Web API
- Axum 0.7+ REST API server
- JWT authentication with RBAC
- Connection pooling (SQLx or SeaORM)
- CRUD operations for all business domains
- OpenAPI documentation (utoipa)
- Rate limiting, CORS, structured logging
Phase 3: Web Frontend
- React 19 + TypeScript + Vite
- Ant Design component library
- TanStack Query for server state
- Dashboard, product management, order tracking
- Real-time updates via WebSocket
Phase 4: Database Migration
- Migrate from SQL Server to PostgreSQL
- Zero-downtime cutover strategy
- Open source, no license cost
- Better Rust ecosystem support (SQLx, SeaORM)
See Future ERP Vision for detailed implementation plans and timelines.