Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

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

DomainCommands
Wheelscount, export, summary, activate, deactivate, delete, merge, check-pn, batch-update, import
Tyrescount, export, summary, search
Accessoriescount, export, summary, search
Orderslist, search, summary, export
Stocklist, search
Customerssearch, 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

Prerequisites

RequirementVersionNotes
Rust1.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:

RegionPrefixDescription
auDB_AU_*Australia
ukDB_UK_*United Kingdom
usDB_US_*United States
demoDB_DEMO_*Demo/test environment

Setup

  1. Copy the example file:

    cp .env.example .env
    
  2. Edit .env with your database credentials.

  3. Test connectivity:

    efision -r au wheels count
    

Notes

  • The .env file 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.

ValueDescription
auAustralia
ukUnited Kingdom
usUnited States
demoDemo/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:

FlagShortTypeDescription
--brand-bstringBrand name
--model-mstringModel name or design number (matches pdName or pdNO)
--diameterintDiameter in inches
--widthstringWidth in inches
--holesintNumber of bolt holes
--pcd1stringPCD1 value (e.g. "114.3")
--pcd2stringPCD2 value for dual PCD
--etintET (offset) value
--cbstringCenter bore (CB) value
--base-finishstringBase finish name (e.g. "Black")
--proceduresstringProcedures (comma-separated, exact match, order-insensitive)
--statusstringStatus (true/false, active/inactive, 1/0)

Commands

CommandDescriptionPage
count / export / summaryQuery and export wheelsQuery Commands
activate / deactivate / deleteChange product statusLifecycle Commands
mergeMerge duplicate productsMerge
check-pnValidate part numbersCheck Part Number
batch-updateBulk update wheel specsBatch Update
importCreate wheels from CSVImport

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...]
FlagShortDefaultDescription
--output-oexport.csvOutput 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>
FlagShortRequiredDescription
--part-number-pYesPart number (pNO) to activate

Workflow:

  1. Searches for matching wheel products by part number
  2. Displays matching candidates (if multiple, prompts to select one)
  3. Checks if product is already Active (warns and exits if so)
  4. Shows a confirmation prompt with product details
  5. 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>
FlagShortRequiredDescription
--part-number-pYesPart 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>
FlagShortRequiredDescription
--part-number-pYesPart number (pNO) to delete

Workflow:

  1. Searches for matching wheel products by part number
  2. Displays matching candidates (if multiple, prompts to select one)
  3. Checks 16 business dependency tables for blocking references
  4. Shows a warning with product details
  5. Requires interactive confirmation (y/N)
  6. 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 -p is the primary (kept), remaining -p values 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]
FlagShortDefaultDescription
--part-number-p(none)Part number(s). Single: merge duplicates. Multiple: first is primary, rest are secondaries.
--allfalseMerge ALL duplicate part numbers in the database
--dry-runfalseShow duplicates without merging (only with --all)
--skip-vouchersfalseSkip 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...]
FlagDefaultDescription
--fixfalseActually 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.pNO is 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.
  • --model matches both design name (pdName) and design number (pdNO)

Wheels — Batch Update

Batch update wheel product parameters. Split into three subcommands:

SubcommandDescription
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)

FlagTypeDescription
--set-etintNew ET value
--set-cbstringNew CB value
--set-capstringNew cap part number (e.g. "CP865PS80")
--set-boltstringNew bolt name (e.g. "M14x2.0")
--set-holesintNew number of holes
--set-pcd1stringNew PCD1 value
--set-pcd2stringNew PCD2 value
--set-statusstringNew status (true/false, active/inactive, 1/0)
--set-base-finishstringNew base finish name
--set-proceduresstringNew 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
  • --model filter matches against both design name (pdName) and design code (pdNO)
  • PCD filter uses separate --pcd1 and --pcd2 flags. Use only --pcd1 for single-PCD wheels
  • Procedures filter performs exact match (order-insensitive)
  • --set-status updates Product.pStatus. Accepts true/false, active/inactive, or 1/0
  • --set-procedures replaces the entire procedure list (does not append)
  • Spec updates target ProductWheels rows (by pwID); status updates target Product rows (by pID)
  • 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]
FlagDefaultDescription
--dry-runfalseValidate 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
ColumnTypeCan be emptyEmpty default
brandstringNo
modelstringNo
diameterintNo
widthfloatNo
profileintYes0
directionalstringYes“” (non-directional)
holesintNo
pcd1stringNo
pcd2stringYes“” (single PCD)
etintNo
cbfloatNo
boltstringNo
capstringNo
base_finishstringNo
proceduresstringYes“” (no procedures)
statusstringNoactive/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

  1. Parses CSV and validates required fields
  2. Resolves all names to database IDs (with in-memory caching)
  3. Auto-creates ProductMold if no mold exists for the design + diameter + width combination
  4. Generates part numbers using the V1 formula (same as check-pn)
  5. Generates descriptions (same formula as batch-update)
  6. Checks for duplicate specs already in the database (skips duplicates)
  7. Shows a preview table with generated part numbers
  8. If --dry-run: stops here
  9. Prompts for confirmation (y/N)
  10. Inserts Product + ProductWheels + CategoryProduct in a single transaction
  11. 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
  • procedures uses comma-separated names (e.g. "Machined Face,Dark Tint,Satin Lacquer"), same as batch-update
  • brand must match ProductBrand.pbName exactly; model matches ProductDesign.pdName or pdNO
  • 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>]
FlagShortDefaultDescription
--output-oproducts-export.csvOutput 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

Search tyre products by keyword.

Usage: efision -r <region> tyres search -q <query>
FlagShortRequiredDescription
--query-qYesSearch 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>]
FlagShortDefaultDescription
--output-oproducts-export.csvOutput file path
efision -r au accessories export -o accessories.csv

accessories summary

Usage: efision -r <region> accessories summary
efision -r au accessories summary
Usage: efision -r <region> accessories search -q <query>
FlagShortRequiredDescription
--query-qYesSearch 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]
FlagShortDefaultDescription
--limit-l50Maximum 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

Search for a specific order by order number.

Usage: efision -r <region> orders search --order-no <ORDER_NO>
FlagShortRequiredDescription
--order-no-oYesOrder 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]
FlagShortDefaultDescription
--output-oorders-export.csvOutput 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]
FlagShortDefaultDescription
--low-stock-lfalseShow only low stock items
--threshold-t10Low 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
...

Search warehouse stock by SKU or product code.

Usage: efision -r <region> stock search --sku <SKU>
FlagShortRequiredDescription
--sku-sYesSKU 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.


Search customers by name, email, or company.

Usage: efision -r <region> customers search -q <query>
FlagShortRequiredDescription
--query-qYesSearch 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>
FlagShortRequiredDescription
--customer-id-cYesCustomer 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
LayerLocationResponsibility
CLIcrates/cli/Command parsing, user interaction, output formatting
Servicescrates/core/src/services/Business logic, orchestration (database-agnostic)
Domaincrates/core/src/domain/Entities, DTOs, Repository trait definitions
Infrastructurecrates/core/src/infrastructure/SQL Server implementations of repository traits
Configcrates/core/src/infrastructure/config.rsDatabase 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:

CratePackageRole
crates/core/efision-coreShared library: domain, services, infrastructure
crates/cli/efision-cliBinary: 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

DomainEntitiesRepositoryService
Productsdomain/entities/products/infrastructure/sqlserver/products/services/products/
Salesdomain/entities/sales/infrastructure/sqlserver/sales/services/sales/
Warehousedomain/entities/warehouse/infrastructure/sqlserver/warehouse/services/warehouse/
Customersdomain/entities/customers/infrastructure/sqlserver/customers/services/customers/

Database Stats

StatValue
Tables in schema305
Entity files64 (across 4 business domains)
Repository traits5

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 serde derives
  • 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 self for mutable repository access
  • Uses anyhow::Result with .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
  • dialoguer for interactive prompts
  • owo-colors for colored terminal output
  • comfy-table for table formatting
  • indicatif for progress bars

Error Handling Strategy

LayerApproach
Domainthiserror custom error types
Serviceanyhow::Context for error chaining
InfrastructureMap Tiberius/SQL errors to domain errors
CLIDisplay user-friendly messages, exit with appropriate codes

Database

SQL Server Connection

PropertyValue
EngineSQL Server 2025
DriverTiberius (pure Rust TDS protocol implementation)
ProtocolTDS 7.3
ORMNone — hand-written SQL with parameterized queries
AsyncFully async via Tokio

Multi-Region Architecture

Efision supports multiple database regions, each representing a separate business entity:

RegionFlagEnvironment Variables
Australia-r auDB_AU_HOST, DB_AU_PORT, DB_AU_NAME, DB_AU_USER, DB_AU_PASSWORD
United Kingdom-r ukDB_UK_*
United States-r usDB_US_*
Demo-r demoDB_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:

  1. Legacy schema — 305 tables with non-standard naming conventions that don’t map cleanly to ORM patterns
  2. Performance — direct TDS protocol access avoids ORM overhead
  3. Complex queries — stored procedures, cross-table updates, and business-specific queries are easier to express in raw SQL
  4. 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?

FeatureTiberiuspyodbc (Python)
DependenciesPure Rust (no C deps)Requires ODBC driver
AsyncNative async (Tokio)No native async
Performance~5-7x fasterBaseline
Memory~10 MB~50 MB
DeploymentSingle binaryPython runtime + driver
PlatformmacOS, Linux, WindowsSame

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:

  1. Phase 1 (Complete): Read-only data access + data models, CLI tool
  2. Phase 2: Backend REST API (read-write support)
  3. Phase 3: CLI enhancements (full CRUD)
  4. 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)

ComponentTechnologyPurpose
Web FrameworkAxum 0.7+HTTP routing, middleware
Async RuntimeTokio 1.0+Standard Rust async runtime
DatabaseSQLx 0.7+ or SeaORMCompile-time SQL checking / Full ORM
Authjsonwebtoken + argon2JWT + password hashing
API Docsutoipa 4.0+Auto-generated OpenAPI docs
LoggingtracingStructured logging
ValidationvalidatorInput validation

Frontend (Planned)

ComponentTechnology
FrameworkReact 19 + TypeScript
Build ToolVite 7.0+
UI LibraryAnt Design 6.x
Server StateTanStack Query 5
Client StateZustand
RouterReact Router 7
CSSTailwind 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

MetricTarget
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

RequirementVersionInstall
Rust1.75+rustup.rs
SQL Server access2025Database 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 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 sqlcmd and bcp.

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

PropertyValue
Repositoryprisma/tiberius
Version0.12.x
TypePure Rust TDS protocol implementation
Maintained byPrisma 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)

FeatureRust (tiberius)Python (pyodbc)
SQL Server SupportTDS 7.2-7.4Via ODBC driver
TLS 1.0 WorkaroundEncryptionLevel::NotSupportedEncrypt=no
Performance~5-7x fasterBaseline
Memory~10 MB~50 MB
DependenciesPure RustPython + ODBC driver
AsyncNative (Tokio)No native async
Type SafetyCompile-timeRuntime (Pydantic)
DeploymentSingle binaryComplex

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 serde derives 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 clippy passes
  • cargo test passes

Coding Conventions

Error Handling

LayerApproach
Domainthiserror for custom error types
Servicesanyhow::Result with .context() chaining
InfrastructureMap driver errors to domain errors
CLIDisplay 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 self for mutable repository access

Naming

ElementConventionExample
Variables / functionssnake_casewheel_count
Types / structsPascalCaseWheelProduct
DB columns in entitiesPreserve abbreviationspNO, pStatus, pwDia
Service structs{Domain}Service<R: {Domain}Repository>WheelService<R: WheelRepository>
Repo implementationsSqlServer{Domain}RepoSqlServerWheelRepo

Serialization

  • serde derive on all entities
  • #[serde(rename = "...")] for DB column / CSV mapping
  • Custom serializers for booleans, decimals, and dates

CLI Output

  • owo-colors for terminal coloring
  • dialoguer for interactive prompts
  • comfy-table for table formatting
  • indicatif for progress bars

Module Organization

  • One concept per file
  • mod.rs exports 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 serde derives
  • 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

CratePurpose
tiberiusSQL Server TDS driver
tokioAsync runtime
clap (derive)CLI argument parsing
serde + csvSerialization and CSV export
anyhow + thiserrorError handling
chronoDate/time types
rust_decimalPrecise decimal arithmetic
dialoguerInteractive CLI prompts
owo-colorsTerminal color output
comfy-tableTable formatting
indicatifProgress 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

SegmentSourceExample
DesignNOProductDesign.pdNO067
P+ProfileOnly if profile > 0P3L
DirectionalFirst letter if directionalR or L
DiaDiameter (integer)19
WidthCodeLookup from widthY (for 8.5“)
HolesNumber of bolt holes5
PCD1CodeLookup from PCD valueV (for 114.3)
PCD2CodeOnly if dual PCDT (for 112)
ETET offset value40
FinishShortNameLookup from finishS (Silver), K (Black)
AppendantShortNamesConcatenated procedure codesMF (Machined Face)
BoltShortNameLookup from bolt spec4A
CBCodeLookup from CB valueNA
CapNOCap part number codeCP506AL68

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

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

Email

  • 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:

  1. ProductType - Product type definitions (Python + Rust)
  2. ProductSeries - Series groupings (Python + Rust)
  3. ProductCategory - Hierarchical classification (Python + Rust)
  4. ProductGallery - Product images (Python + Rust)
  5. ProductBasePrice - Base pricing matrix (Python + Rust)
  6. 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:

  1. Ordert - Main orders
  2. OrderDetail - Order items
  3. Customers - Customer data
  4. PriceLevel - Pricing
  5. ShoppingCart - E-commerce

Phase 3: Warehouse & Inventory (Week 3)

Goal: Enable inventory tracking

Priority tables:

  1. WarehouseStock - Current stock
  2. StockInOrder - Stock in
  3. StockOutOrder - Stock out
  4. Warehouse - Warehouse info

Phase 4: Finance & Accounting (Week 4)

Goal: Enable financial reporting

Priority tables:

  1. Account - Chart of accounts
  2. Acct_Voucher - Vouchers
  3. ReceivePayment - Payments
  4. Currency - Currency

Phase 5: Manufacturing (Week 5+)

Goal: Enable production planning

Priority tables:

  1. MO - Manufacturing orders
  2. BOM - Bill of materials
  3. ManufacturePlan - Planning
  4. Process - 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:

  1. Entity: Add to crates/core/src/domain/entities/{domain}/
  2. Repository: Add trait to crates/core/src/domain/repositories/{domain}/
  3. SQL Implementation: Add to crates/core/src/infrastructure/sqlserver/{domain}/
  4. Tests: Document test results
  5. Relationships: Document foreign key relationships (even if not enforced)
  6. 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

  1. Review this document with Henry
  2. Confirm Phase 1 priority tables
  3. Phase 1 implementation ✅ (2026-02-05)
  4. Start Phase 2: Sales & Orders

Last updated: 2026-02-05 Maintained by: Lobster 🦞

Performance Benchmarks

CLI Performance

MetricValue
Count query<100ms
Export 3,878 products<1s
Binary size1.9 MB
Startup time<100ms
Memory usage~10 MB

Comparison with Python

Export 1,000 Products

LanguageTimeMemory
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

PhaseStatusDescription
1 — CLI ToolDone24 commands, 64 entities, 6 domains
1.5 — WorkspaceDoneCargo workspace, shared efision-core
1.6 — CLI RestructureDoneGlobal --region, unified filters, batch-update subcommands
2 — Web APIPlannedAxum REST API, JWT auth, connection pooling
3 — Web FrontendPlannedReact 19, Ant Design, TanStack Query
4 — DB MigrationPlannedPostgreSQL, 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.