You are right, the example schema in your gist makes it impossible to split journal entries.
What you need is a schema that has a row per individual journal entry. This is a more flexible design which will accommodate the complex scenarios that you are looking for.
Example
Schema
I would suggest a minimum of three tables to meet your requirement:
CREATE TABLE account(
account_id serial PRIMARY KEY,
account_name text NOT NULL
);
CREATE TABLE financial_transaction (
financial_transaction_id serial PRIMARY KEY,
datetimestamp timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
description text NOT NULL
);
CREATE TABLE journal(
journal_id serial PRIMARY KEY,
financial_transaction_id INTEGER REFERENCES financial_transaction(financial_transaction_id),
account_id INTEGER REFERENCES account(account_id),
amount NUMERIC(20, 2) NOT NULL,
is_credit boolean NOT NULL
);
Account Setup
We'll create the three accounts for your example. Although you'd likely not hard-code the id
, we do that here for the purposes of demonstration
INSERT INTO account (account_id, account_name) VALUES (1, 'Creditors control');
INSERT INTO account (account_id, account_name) VALUES (2, 'VAT');
INSERT INTO account (account_id, account_name) VALUES (3, 'Purchases');
Example Purchase Transaction
Each financial tramsaction involves exctly one row inserted into the financial_transaction
table and at least two rows inserted into the journal
table, and sum of the credits and debits of these rows must balance.
INSERT INTO financial_transaction (financial_transaction_id, description)
VALUES (1, 'Purchase of widget');
INSERT INTO journal (financial_transaction_id, account_id, amount, is_credit)
VALUES (1, 1, 1200, TRUE);
INSERT INTO journal (financial_transaction_id, account_id, amount, is_credit)
VALUES (1, 2, 200, FALSE);
INSERT INTO journal (financial_transaction_id, account_id, amount, is_credit)
VALUES (1, 3, 1000, FALSE);
Notes
You can see how split journals can be easily accommodated with this structure. The structure can be queried easily and transactions or accounts can be aggregated for different purposes.
If I were building this, I would probably create a trigger to enforce that by the end of any transaction, the sum of its credit and debit journal
entries balanced.
I might also want to define certain types of transactions and which types of accounts can be journalled to for those transaction types, depending on how complex or strict my system was.
Finally, I would create a rule, trigger or configure permissions to ensure that the row in these tables were not deleted from or updated. In a system such as this, all corrections should be made via additional transactions.