Files
boc/aamos-ledger-rust/src/db.rs
T
Bernt bae705aa97 ARCHITECTURE: NFC roadmap, edge AI, audit logging
- Add NFC ePassport roadmap (ICAO 9303, eIDAS)
- Add TensorFlow.js edge face detection (BlazeFace)
- Add structured audit logger (GDPR-compliant)
- Risk scoring support

Part of KYC Apple Native UX v1.1.0
2026-06-29 16:24:48 +00:00

175 lines
5.0 KiB
Rust

use sqlx::{PgPool, Postgres, Transaction};
use uuid::Uuid;
use crate::models::*;
pub async fn create_account(pool: &PgPool, account: &CreateAccount) -> anyhow::Result<Account> {
let account = sqlx::query_as::<_, Account>(
r#"
INSERT INTO accounts (id, code, name, account_type, created_at)
VALUES ($1, $2, $3, $4, NOW())
RETURNING id, code, name, account_type, created_at
"#,
)
.bind(Uuid::new_v4())
.bind(&account.code)
.bind(&account.name)
.bind(&account.account_type)
.fetch_one(pool)
.await?;
Ok(account)
}
pub async fn get_accounts(pool: &PgPool) -> anyhow::Result<Vec<Account>> {
let accounts = sqlx::query_as::<_, Account>(
r#"SELECT id, code, name, account_type, created_at FROM accounts ORDER BY code"#,
)
.fetch_all(pool)
.await?;
Ok(accounts)
}
pub async fn get_account_by_id(pool: &PgPool, id: Uuid) -> anyhow::Result<Option<Account>> {
let account = sqlx::query_as::<_, Account>(
r#"SELECT id, code, name, account_type, created_at FROM accounts WHERE id = $1"#,
)
.bind(id)
.fetch_optional(pool)
.await?;
Ok(account)
}
pub async fn create_journal_entry(
pool: &PgPool,
entry: &CreateJournalEntry,
created_by: &str,
) -> anyhow::Result<JournalEntryWithLines> {
let mut tx: Transaction<'_, Postgres> = pool.begin().await?;
let entry_id = Uuid::new_v4();
let journal_entry = sqlx::query_as::<_, JournalEntry>(
r#"
INSERT INTO journal_entries (id, entry_number, description, entry_date, created_at, created_by)
VALUES ($1, $2, $3, $4, NOW(), $5)
RETURNING id, entry_number, description, entry_date, created_at, created_by
"#,
)
.bind(entry_id)
.bind(&entry.entry_number)
.bind(&entry.description)
.bind(entry.entry_date)
.bind(created_by)
.fetch_one(&mut *tx)
.await?;
let mut lines = Vec::new();
for line in &entry.lines {
let line_id = Uuid::new_v4();
let journal_line = sqlx::query_as::<_, JournalLineWithAccount>(
r#"
INSERT INTO journal_lines (id, journal_entry_id, account_id, description, debit, credit)
VALUES ($1, $2, $3, $4, $5, $6)
RETURNING
journal_lines.id,
journal_lines.journal_entry_id,
journal_lines.account_id,
(SELECT code FROM accounts WHERE id = $3) as account_code,
(SELECT name FROM accounts WHERE id = $3) as account_name,
journal_lines.description,
journal_lines.debit,
journal_lines.credit
"#,
)
.bind(line_id)
.bind(entry_id)
.bind(line.account_id)
.bind(&line.description)
.bind(&line.debit)
.bind(&line.credit)
.fetch_one(&mut *tx)
.await?;
lines.push(journal_line);
}
tx.commit().await?;
Ok(JournalEntryWithLines {
entry: journal_entry,
lines,
})
}
pub async fn get_journal_entries(pool: &PgPool) -> anyhow::Result<Vec<JournalEntry>> {
let entries = sqlx::query_as::<_, JournalEntry>(
r#"SELECT id, entry_number, description, entry_date, created_at, created_by FROM journal_entries ORDER BY entry_date DESC"#,
)
.fetch_all(pool)
.await?;
Ok(entries)
}
pub async fn get_journal_entry_by_id(
pool: &PgPool,
id: Uuid,
) -> anyhow::Result<Option<JournalEntryWithLines>> {
let entry = sqlx::query_as::<_, JournalEntry>(
r#"SELECT id, entry_number, description, entry_date, created_at, created_by FROM journal_entries WHERE id = $1"#,
)
.bind(id)
.fetch_optional(pool)
.await?;
let Some(entry) = entry else {
return Ok(None);
};
let lines = sqlx::query_as::<_, JournalLineWithAccount>(
r#"
SELECT
jl.id,
jl.journal_entry_id,
jl.account_id,
a.code as account_code,
a.name as account_name,
jl.description,
jl.debit,
jl.credit
FROM journal_lines jl
JOIN accounts a ON jl.account_id = a.id
WHERE jl.journal_entry_id = $1
"#,
)
.bind(id)
.fetch_all(pool)
.await?;
Ok(Some(JournalEntryWithLines { entry, lines }))
}
pub async fn get_trial_balance(pool: &PgPool) -> anyhow::Result<Vec<TrialBalanceRow>> {
let rows = sqlx::query_as::<_, TrialBalanceRow>(
r#"
SELECT
a.id as account_id,
a.code as account_code,
a.name as account_name,
COALESCE(SUM(jl.debit), 0)::FLOAT8 as total_debit,
COALESCE(SUM(jl.credit), 0)::FLOAT8 as total_credit,
(COALESCE(SUM(jl.debit), 0) - COALESCE(SUM(jl.credit), 0))::FLOAT8 as balance
FROM accounts a
LEFT JOIN journal_lines jl ON a.id = jl.account_id
GROUP BY a.id, a.code, a.name
ORDER BY a.code
"#,
)
.fetch_all(pool)
.await?;
Ok(rows)
}