What you have laid out as a schema might me a bare minimum for most
purposes.  I've spent most of the last 30 years configuring and implementing
financial systems, mostly General Ledger for a variety of clients and
developers.  Here are the top group of items I think you have missed:
- multiple entities.  This could be as simple as 'my money' and 'my
company's money'.  They are distinct and common enough to be included in any
accounting system.  And, since you may need to record movements between
entities, you will probably want this on the transaction table.
- departments, product lines, etc. Any groupings within an entity to
separate who is responsible for a transaction.  This could be in a retail
environment like grocery, frozen foods, produce and meat departments.  Think
of them as another way to slice and dice the collected information into
reports.
- account type.  I have never seen an accounting system without some method
of separating Assets, Liabilities, Equity, Revenue, and Expense accounts.
This can drive how you display a number in a report (liabilities, equity and
revenue all have normally credit [negative numbers, by convention] balances,
while assets and expenses all have normally debit balances.  In addition,
revenue and expense accounts do not carry balances forward past fiscal year
ends while the other three do.  Some systems expand on this basic idea to
create many more types such as bank, AR, Fixed Asset, ContraFixed Asset,
etc.  This is generally implemented on the Account table as a single
character field, validated by a separate table of valid values (very small)
- currency.  Most places and people will at some time need to record
transactions in a currency other than their home currency.  There is an
international list of 3 character codes you should be able to find on the
web andjust download.  This may be as simple as taking a vacation to another
country.  This affects the transaction table and also ends up needing some
kind of exchange rate table that relates the two currencies with a rate for
that day (also available on the web).  Also recognize that accounting
standards (GAAP, IFRS, and various government agencies) require the use of
different exchange rates for different kinds of conversions.  Another reason
for account type.
- calendar.  Any accounting is period-driven - what you define as a period
is largely up to you.  But, the concept of fiscal year is deeply ingrained
in our laws.  Some countries go so far as to mandate a fiscal year for all
companies within their borders.  In the US, the basic period for external
financial reporting (for listed stock companies) is the quarter.  Most
companies also divide the year into 12-14 periods, which sum to the
quarters.  Monthly periods are most common, with 13 4-week periods as a
common alternative, often in retailing.  And both versions may have an
adjustment period at year-end to distinguish special entries.  Banks are a
special case, requiring daily accounting periods and the calculation of
something called 'average daily balance' [and another table to it] (but you
aren't likely to be developing a banking system!).
- ledger.  One of the most important features of any accounting system is
the recognition that the effort to sum all relevant transactions each time
you want to know an account balance is time and processing resource
consuming.  So, every accounting system I've seen provides a table, often
just called Ledger, with basically the following columns:
k  entity
k  accounting year
k  accounting period (within year)
k  account
k (any other groupings like departments or product lines)
k currency
   balance (Debits are positive, Credits are negative) [in some countries,
especially in Europe, the law requres separate balance fields are required
for Debits and Credits.  These countries also have negative debits and
positive credits, further complicating the transaction table.]
('k' indicates part of a key; some people simply add a unique Id as the key
and then use these fields for indexing.)

This Ledger table provides summary information that can quickly be used to
answer questions from management and owners  This table can be many times
smaller than the transaction table; 1 million transactions/month summarized
to 10,000 Ledger rows is not unusual (although this would be extremely huge
for SQLite).

This also implies a process to sum all new transactions for an
entity/account/othergrouping combination affecting a period and adding it to
any pre-existing balance (if any) or inserting the row if there was none
previously.  Generally, this is called 'Posting' the transactions.

- unique id.  Some countries, including Mexico (in my experience), require a
unique id for any transaction that extends beyond the entity, like a reciept
given out to a customer.  This must be tracked in the transaction table, if
you are in an affected country.

Now, I don't know what you are trying to use this schema to accomplish, but
I hope I've been able to give you some ideas that may (or not) help you.
The intent is not to frustrate you, but to give you information to tailor
your system to meet your needs.

BTW, my accounting systems experience has so far included McCormack & Dodge,
MSA, Software International (aka CA Universe), Lawson, SmartStream, Oracle,
PeopleSoft, Coda, Clarus, NetSuite, QuickBooks, Platinum, Solomon, GNUCash,
some homegrown systems, and a sampling of others on various OS's and
databases.  And, I have worked (at various times) with systems for the USA,
Canada, Mexico, UK, France, Netherlands, and some exposure to Australian
rules (which include recording depreciation to 5 decimal places).

Alan B. Cohen, CPA
AIM/YM/GMT/MSMess/Skype:   alanbcohen
Email:  alanbco...@gmail.com  ICQ:   49972123
Phone:+1 443/451-5369    Efax: +1 413/653-6474

Hi all,

Below the basic SQL schema I have so far for double entry bookkeeping.

Does this look like a viable schema? I think I'm interpreting the
general structure of double entry bookkeeping and earlier discussion
here correctly. I welcome any comments specifically on the schema.

Basically each Transaction can have multiple Entries and each Entry
refers to one Account. An Entry may have an optional Narrative, stored
in the "Entries Narrative" table. Each Entry may have none, one or
more References, such as invoice numbers, order numbers, cheque
numbers, stored in the "Entries References" table.

create table Accounts
(
         ID integer
               primary key
       , Code text
               collate nocase
               unique
               not null
               references "Entries" ("Account Code")
       , Name text                                     -- brief name
               collate nocase
)
;
create table "Transactions"
(
         ID integer
               primary key
               references "Entries" ("Transaction ID")
       , Date date                                     -- julianday of the
date that the transaction occurred
)
;
create table "Entries"
(
         ID integer
               primary key
               references "Entries Narrative" (ID)
       , "Transaction ID" integer
               references Transactions (ID)
                       on delete cascade
                       on update cascade
       , "Account Code" text
               collate nocase
               references Accounts (Code)
                       on delete restrict
                       on update cascade
       , Amount integer                                -- amount in cents,
positive or negative
)
;
create table "Entries References"               -- Optional reference(s) for
each
Entry
(
         ID integer
               primary key
       , "Entry ID" integer
               references Entries (ID)
                       on delete cascade
                       on update cascade
       , "Reference" text                              -- internal or
external reference such as
invoice or cheque number
               collate nocase
               not null
)
;
create table "Entries Narrative"                -- Optional description for
each Entry
(
         ID integer
               primary key
               references "Entries" (ID)
                       on delete cascade
                       on update cascade
       , Narrative text
               not null
)
;

-- To prevent deletion of Transactions and Entries:

create trigger "Entries delete"
before delete
on "Entries"
begin
       select raise(rollback, 'You cannot delete Entries. You must instead
reverse out the Entries.');
end
;

-- And here is a view showing all the Entries grouped by Transaction:

create view "Entries Report"
as
select
         Entries.ID as ID
       , Transactions.ID as "Transaction ID"
       , date(Transactions.Date, '0.1 seconds', 'localtime') as Date
       , case when Amount < 0 then round(-Amount/100.0,2) end as Debit
       , case when Amount >= 0 then round(Amount/100.0,2) end as Credit
       , Accounts.Code
       , Accounts.Name
       , Narrative
       , group_concat("Reference", ', ') as "References"
from Entries
       left join "Entries Narrative" on Entries.ID = "Entries Narrative".ID
       left join "Entries References" on Entries.ID = "Entries
References"."Entry ID"
       left join Transactions on Entries."Transaction ID" = Transactions.ID
       left join Accounts on Entries."Account Code" = Accounts.Code
group by "Transaction ID", ID
;
_______________________________________________
sqlite-users mailing list
sqlite-users@sqlite.org
http://sqlite.org:8080/cgi-bin/mailman/listinfo/sqlite-users

Reply via email to