Structa Cloud Structa Cloud

Engineering

The Formints data model

The SQLite schema, Rust models and Tauri commands behind Formints — moved here from the product page so each code section lives with its story.

Engineering 9 min read ← All posts

Formints keeps its point-of-sale core deliberately small: one SQLite database, a handful of Rust models, and Tauri commands that read and write them. This post is the code that used to sit on the product page — moved here so it can be read, discussed and copied with the story attached.

The schema

Everything starts from two tables: a sale and its line items. Money is stored in cents (never floats), the terminal id scopes the row, and the timestamp is generated by SQLite itself.

The model

The Rust side is a Diesel Queryable mirror of the schema — one field per column, typed, serializable for the frontend. The invoice command shows the pattern end to end: borrow a connection from the pool, load the sale and its items, render the PDF.

The full code is below. Copy the patterns — not the database — into your own project.

variants & screenshots

See it in each edition

A screenshot per variant — open any one to jump straight to the live preview.

Community terminal

The free, offline-first single-terminal edition.

Preview Community ↗

Cloud dashboard

The hosted multi-terminal master, managed for you.

Preview Cloud ↗
code sections

The code

The full code this deep dive walks through — copy the patterns into your own project.

SQLite schema (Diesel up.sql)
sql
CREATE TABLE sales (
  id INTEGER PRIMARY KEY AUTOINCREMENT,
  terminal_id TEXT NOT NULL,
  total_cents INTEGER NOT NULL,
  payment_method TEXT NOT NULL,
  created_at TEXT NOT NULL DEFAULT (datetime('now'))
);

CREATE TABLE sale_items (
  id INTEGER PRIMARY KEY AUTOINCREMENT,
  sale_id INTEGER NOT NULL REFERENCES sales(id),
  product_id TEXT NOT NULL,
  quantity INTEGER NOT NULL,
  unit_cents INTEGER NOT NULL
);
Rust model (src-tauri/src/db/models.rs)
rust
#[derive(Queryable, Insertable, Serialize)]
#[diesel(table_name = crate::db::schema::sales)]
pub struct Sale {
    pub id: i32,
    pub terminal_id: String,
    pub total_cents: i32,
    pub payment_method: String,
    pub created_at: String,
}
Diesel migration (up.sql)
sql
-- Loyalty + refunds land on top of the core schema.
ALTER TABLE sales ADD COLUMN loyalty_points INTEGER NOT NULL DEFAULT 0;

CREATE TABLE refunds (
  id INTEGER PRIMARY KEY AUTOINCREMENT,
  sale_id INTEGER NOT NULL REFERENCES sales(id),
  amount_cents INTEGER NOT NULL,
  reason TEXT NOT NULL,
  created_at TEXT NOT NULL DEFAULT (datetime('now'))
);
Tauri command (invoice PDF)
rust
#[tauri::command]
pub fn generate_invoice(sale_id: i32, state: State<AppState>) -> Result<String, String> {
    let conn = &mut state.pool.get().map_err(|e| e.to_string())?;
    let sale: Sale = sales::table
        .find(sale_id)
        .first(conn)
        .map_err(|e| e.to_string())?;
    let items: Vec<SaleItem> = sale_items::table
        .filter(sale_items::sale_id.eq(sale_id))
        .load(conn)
        .map_err(|e| e.to_string())?;
    render_invoice_pdf(&sale, &items)
}

Comments

A useful first release beats a noisy roadmap

We start with the customer journey, launch a focused slice, and leave your team with the content and tools to keep improving it.

ready when you are · text/html