cala_ledger/lib.rs
1//! # cala-ledger
2//!
3//! This crate provides a set of primitives for implementing an SQL-compatible
4//! double-entry accounting system. This system is engineered specifically is
5//! for dealing with money and building financial products.
6//!
7//! Visit the [website of the Cala project](https://cala.sh) for more info and
8//! tutorials.
9//!
10//! ## Quick Start
11//!
12//! Here is how to initialize a ledger create a primitive template and post a transaction.
13//! This is a toy example that brings all pieces together end-to-end.
14//! Not recommended for real use.
15//! ```rust
16//! use cala_ledger::{account::*, journal::*, tx_template::*, *};
17//! use rust_decimal::Decimal;
18//! use uuid::uuid;
19//!
20//! async fn init_cala(journal_id: JournalId) -> anyhow::Result<CalaLedger, anyhow::Error> {
21//! let cala_config = CalaLedgerConfig::builder()
22//! .pg_con("postgres://user:password@localhost:5432/pg")
23//! // .exec_migrations(true) # commented out for execution in CI
24//! .build()?;
25//! let cala = CalaLedger::init(cala_config).await?;
26//!
27//! // Initialize the journal - all entities are constructed via builders
28//! let new_journal = NewJournal::builder()
29//! .id(journal_id)
30//! .name("Ledger")
31//! .build()
32//! .expect("Couldn't build NewJournal");
33//! let _ = cala.journals().create(new_journal).await;
34//!
35//! // Initialize an income omnibus account
36//! let main_account_id = uuid!("00000000-0000-0000-0000-000000000001");
37//! let new_account = NewAccount::builder()
38//! .id(main_account_id)
39//! .name("Income")
40//! .code("Income")
41//! .build()?;
42//! cala.accounts().create(new_account).await?;
43//!
44//! // Create the trivial 'income' template
45//! let params = vec![
46//! NewParamDefinition::builder()
47//! .name("sender_account_id")
48//! .r#type(ParamDataType::Uuid)
49//! .build()?,
50//! NewParamDefinition::builder()
51//! .name("units")
52//! .r#type(ParamDataType::Decimal)
53//! .build()?,
54//! ];
55//!
56//! let entries = vec![
57//! NewTxTemplateEntry::builder()
58//! .entry_type("'INCOME_DR'")
59//! .account_id("params.sender_account_id")
60//! .layer("SETTLED")
61//! .direction("DEBIT")
62//! .units("params.units")
63//! .currency("'BTC'")
64//! .build()?,
65//! NewTxTemplateEntry::builder()
66//! .entry_type("'INCOME_CR'")
67//! .account_id(format!("uuid('{}')", main_account_id))
68//! .layer("SETTLED")
69//! .direction("CREDIT")
70//! .units("params.units")
71//! .currency("'BTC'")
72//! .build()?,
73//! ];
74//!
75//! let tx_code = "GENERAL_INCOME";
76//! let new_template = NewTxTemplate::builder()
77//! .id(uuid::Uuid::now_v7())
78//! .code(tx_code)
79//! .params(params)
80//! .transaction(
81//! NewTxTemplateTransaction::builder()
82//! .effective("date()")
83//! .journal_id(format!("uuid('{}')", journal_id))
84//! .build()?,
85//! )
86//! .entries(entries)
87//! .build()?;
88//!
89//! cala.tx_templates().create(new_template).await?;
90//! Ok(cala)
91//! }
92//!
93//! #[tokio::main]
94//! async fn main() -> anyhow::Result<()> {
95//! let journal_id = JournalId::from(uuid!("00000000-0000-0000-0000-000000000001"));
96//! let cala = init_cala(journal_id).await?;
97//! // The account that is sending to the general income account
98//! let sender_account_id = AccountId::new();
99//! let sender_account = NewAccount::builder()
100//! .id(sender_account_id)
101//! .name(format!("Sender-{}", sender_account_id))
102//! .code(format!("Sender-{}", sender_account_id))
103//! .build()?;
104//! cala.accounts().create(sender_account).await?;
105//! // Prepare the input parameters that the template requires
106//! let mut params = Params::new();
107//! params.insert("sender_account_id", sender_account_id);
108//! params.insert("units", Decimal::ONE);
109//! // Create the transaction via the template
110//! cala.post_transaction(TransactionId::new(), "GENERAL_INCOME", params)
111//! .await?;
112//!
113//! let account_balance = cala
114//! .balances()
115//! .find(journal_id, sender_account_id, "BTC".parse()?)
116//! .await?;
117//!
118//! let expected_balance = Decimal::new(-1, 0); // Define the expected balance
119//! assert_eq!(account_balance.settled(), expected_balance);
120//! Ok(())
121//! }
122//! ```
123
124#![cfg_attr(feature = "fail-on-warnings", deny(warnings))]
125#![cfg_attr(feature = "fail-on-warnings", deny(clippy::all))]
126
127mod cel_context;
128mod param;
129
130pub mod account;
131pub mod account_set;
132pub mod balance;
133pub mod entry;
134pub mod journal;
135pub mod migrate;
136pub mod transaction;
137pub mod tx_template;
138pub mod velocity;
139
140pub use es_entity;
141
142mod ledger;
143pub mod outbox;
144
145pub use ledger::*;
146
147pub mod primitives {
148 pub use cala_types::primitives::*;
149}
150pub use primitives::*;