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.
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 ↗The code
The full code this deep dive walks through — copy the patterns into your own project.
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
); #[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,
} -- 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]
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
Sign in to join the conversation.
Commenting as