Database Concept For Accounting
Database Concept For Accounting
to Accounting
Likely the best article you’ll find on accounting databases with a strong foundation in both SQL and Accounting
Practice.
This article is fairly lengthy, but it is well worth the read. It focuses on a financial and accounting database,
including the structure and the concepts. It’s almost a perfect marriage between SQL and database theory,
and basic accounting practices. I welcome feedback from both sides of the aisle for the purpose of making it
more understandable for both. But the real purpose of this article is to illustrate the level of service offered
by Compass Point Media in developing web-based applications, including accounting databases.
This is my first blog entry – ever. And it’s also right in the middle of a huge database project which is
essentially an accounting database for a conference planner. This client manages conferences which involves
registrations, cancellations, and refunds to attendees and exhibitors on behalf of their client. For example,
the International Firefighter’s Union (I’m making this up) wants to hold a conference in sunny Orlando, Florida.
They then call my client who will set up online registration forms for attendees and exhibitors (and organize
speakers and presenters). They will handle registration and cancellation, and get a commission for their
management of the conference. I design the interface which allows them to track that financial data. Trust
me, it’s a big project.
There are articles to get a good starting knowledge of SQL tables for holding orders, invoices, etc. on the web
However, that is NOT what you’re about to read here. What you’re about to read is highly advanced,
reasonably detailed and hopefully evokes your comments, feedback and I hope you learn something from it.
So let’s jump right into this; an accounting database should have two fundamental
tables: accounts and transactions. Now, if you’re coming from QuickBooks (like I am), the first table would
also be known as your “Chart of Accounts” which is basically a “master map” of a company’s bank accounts,
expense and income categories, assets and liabilities<a href=”#fn1″>[1]</a><a name=”xfn1″ id=”xfn1″></a>.
The second table may also be rightly called ajournal entry table. All transactions – checks, deposits, invoices,
cash sales, etc. – are essentially journal entries. Allow me to elaborate more.
We’ll start with the table to store the chart of accounts. Here are the bare-bones fields required for a chart of
accounts table:
finan_accounts
--------------
ID - primary key
Name - name of the account
Types_ID - type of account
Parent_ID - parent account
ID is the integer primary key of course. Name would be value such as “Subscriptions” or “Rental Income” or
“Loan from Mom”, or for bank accounts let’s say “Wells Fargo 7028″ – whatever you want to name the account.
The Types_ID field is necessary to specify which type of account it is, and I store these 14 values in a table
called finan_accounts_types (note the _types in the name extends the finan_accounts table):
finan_accounts_types values
ID Name Category
1 Bank Asset
2 Accounts Receivable Asset
3 Other Current Asset Asset
4 Fixed Asset Asset
5 Other Asset Asset
6 Accounts Payable Liability
7 Credit Card Liability
8 Other Current Liability Liability
9 Long Term Liability Liability
10 Equity Equity
11 Income Income
12 Other Income Income
13 Expense Expense
14 Cost of Goods Sold Expense
15 Other Expense Expense
I know you’re gripping the edge of your seat in excitement at this point; actually these are pretty standard.
The number to the left is just my index for them, and to the right you see which of the 5 basic account types
they are.
Finally, finan_accounts table has a field called Parent_ID. This is a heirarchical field which allows one record
to be the parent of another.Parent_ID is either NULL (meaning the account has no parent), or the value of its
parent.
Again, if you use QuickBooks, this should be very familiar to you. You see three different types of accounts:
Undeposited Funds, Accts Receivable, and Income accounts. Notice the indentation of some of the accounts.
You also see a heirarchy; “General Activities Income” is a child of Activities, which is a child of Conference
Registration (which is a root-level item). Or “Books and Publications”, for example, is a child or sub-account
of “Options”. Here is the actual data in the finan_accounts table:
finan_accounts records
ID Name Types_ID Parent_ID
30 Accounts Receivable 2 1
31 Undeposited Funds 3 1
1 Conference Registration 11 0
26 Activities 11 1
27 General Activities Income 11 26
18 Meals 11 1
6 Member/Day Registration 11 1
4 Member/Full Conference 11 1
2 Membership Dues 11 1
7 Non-member/Day Registration 11 1
5 Non-member/Full Conference 11 1
8 Options 11 1
15 Books and Publications 11 8
16 Electronics and Computer 11 8
11 Field Activity 11 8
14 Luncheon or Dinner 11 8
The cool thing about the last field (Parent_ID) is that it can be changed to “move the account around”; just
remember that the account and its parent must be the same type of account. You don’t want an income
account below a bank account. Be sure that you set the unique constraint thatName and Parent_ID (taken
together) must be unique.
Now we cover the other main table. As I said before, the transactions table (finan_transactions in my
database) is used to store journal entries. ALL transactions, including invoices and cash sales, are journal
entries. Journal entries are the basis of <a href=”http://en.wikipedia.org/wiki/Double_entry_bookkeeping”
target=”_blank”>double-entry bookkeeping</a>. The rules are simple:
1. You must transfer money between accounts existing on the chart of accounts
2. The net amount of the journal entry (summing up all debits and credits) must be zero.
Here are the minimum fields required for this table, which I callfinan_transactions:
finan_transactions
------------------
ID - primary key
Idx - relative line in the transaction
Name - description of the entry
Accounts_ID - foreign key, points back to finan_accounts.ID
Amount - dollary amount of the entry
To make my point, I’m going to show you a sample Invoice from Capt. Barton (a fictitious fire chief) and then
explain:
finan_transactions records
ID Idx Name Accounts_ID Amount
1 1 IFU Registration 4(Member/Full Conference) -185
2 2 Fireman’s Handbook 15(Books and Publications) -25
3 3 Roast Beef Dinner 18(Meals) -5.75
4 4 EMT Course 27(General Activities Income) -45
5 NULL Invoice #325 30(Accounts Receivable) 260.75
Notice the first four entries. We call these “Line Items”; each is an item showing on the invoice. Capt. Barton
has registered and is being invoiced for registration, dinner, a book and one EMT training course. The income
to which each item is attributed is shown in the third column. (Each of these are found in the chart of
accounts table, above. I have added the account name in parenthesis for simplicity). All records in the
transaction table must point back to an account in the Chart of Accounts table.
Next, we have a fifth entry with account equalling 30 (Accounts Receivable). Notice the far right columns. The
first four entries are negative, but the fifth is positive. You may be asking why the first three are negative at
this point, so I’ll explain.
With this new invoice, the company is now owed 260.75 by Capt. Barton. So, Accounts Receivable must
INCREASE by 260.75. Since the rule is that the sum of a journal entry equals zero, however, whatever flows
into one account must flow out of other account(s). So we “deduct” from these income accounts. In reality the
conference MADE 185.00 for registration when Capt. Barton registered, as well as 5.75 for selling a roast beef
dinner etc., so just think of “using” these accounts as taking from them; it should make sense that way.
Now, Capt. Barton comes in on the day of the conference and pays his 260.75; this is called (seriously) a
Payment. A payment would look like this:
finan_transactions records
ID Idx Name Accounts_ID Amount
6 NULL Payment check #99051 31(Undeposited Funds) 260.75
7 NULL Payment of Invoice #325 30(Accounts Receivable) -260.75
I will explain what happened. Before Capt. Barton wrote the check, we had an asset in the form of the money
Capt. Barton owed us. After Capt. Barton writes the check, we no longer have that asset, so 30(Accounts
Receivable) is reduced by 260.75. But, we have a check in the deposit box ready to go to the bank (which is
much nicer). We refer to that as “Undeposited Funds”. So, Accounts Receivable DECREASES, and Undeposited
Funds INCREASES. But again, the net effect is zero.
Now, I want to introduce you to some more complexity (life is complex). Suppose Capt. Barton wanted to pay
for his registration and anotherregistration with a single check. No problem; the payment would look like this:
<style>
.diagramGroup{
} </style>
finan_transactions records
ID Idx Name Accounts_ID Amount
6 NULL Payment of Invoice #325 30(Accounts Receivable) -260.75
7 NULL Payment of Invoice #326 30(Accounts Receivable) -120
8 NULL Payment check #99051 31(Undeposited Funds) 480.75
Here, Capt. Barton has paid for his registration (#325), and for another registration (#326) which totaled
120.00 (not shown here). He writes a check for 480.75, which is *split* between these two invoices. But again,
notice one thing: the total of the amounts on the right is still zero to be a journal entry. Accounts Receivable
went down, and Undeposited Funds went up by the same amount.
Adding More Fields And Tables
So far, the examples above are not meant to be fully functional but just to illustrate how we apply accounting
concepts in a relational database. You can be assured that what I had explained above is the heart of any
accounting system. Now, let’s start adding more fields and tables to make the system functional.
The above line items in finan_transactions are missing a few useful fields. Suppose Capt. Barton wants THREE
roast beef dinners? (Firefighters have an appetite; or he may have brought his wife). Let’s add
a Quantity andExtension field so that we can handle this efficiently. So an invoice like this:
finan_transactions records
ID Idx Name Accounts_ID Amount
1 1 IFU Registration 4(Member/Full Conference) -185
2 2 Fireman’s Handbook 15(Books and Publications) -25
3 3 Roast Beef Dinner 18(Meals) -5.75
4 4 Roast Beef Dinner 18(Meals) -5.75
5 5 Roast Beef Dinner 18(Meals) -5.75
6 6 EMT Course 27(General Activities Income) -45
7 NULL Invoice #325 30(Accounts Receivable) 272.25
finan_transactions records
ID Idx Name Accounts_ID Quantity Price Amount
1 1 IFU Registration 4(Member/Full Conference) 1 -185 -185
2 2 Fireman’s Handbook 15(Books and Publications) 1 -25 -25
3 3 Roast Beef Dinner 18(Meals) 3 -5.75 -17.25
4 4 EMT Course 27(General Activities Income) 1 -45 -45
5 NULL Invoice #325 30(Accounts Receivable) 1 272.25 272.25
Remember, though, the values in the FARTHEST RIGHT COLUMN must always sum to zero.
I never really explained the Idx field so far. One more convenience is the ability to indicate what order the
line items appear in. We could just assume that they appear in the order entered, but if we add special line
items like subtotals (which are calculations on other previous rows) or ever need to cut and paste line items in
our program, this will come in handy. The Idx field for the fifth row is NULL. This is because this is the
offsetting entry for the first four; it never appears on the invoice. You’ll hear me refer to it as the “root”
transaction sometimes.
finan_transactions records
ID Idx Name Accounts_ID Quantity Price Amount
1 1 IFU Registration 4(Member/Full Conference) 1 -185 -185
2 2 Fireman’s Handbook 15(Books and Publications) 1 -25 -25
3 3 Roast Beef Dinner 18(Meals) 3 -5.75 -17.25
27(General Activities
4 4 EMT Course 1 -45 -45
Income)
5 NULL Invoice #325 30(Accounts Receivable) 1 272.25 272.25
6 NULL Payment check #99051 31(Undeposited Funds) 1 272.25 272.25
Payment of Invoice -
7 NULL 30(Accounts Receivable) 1 -272.25
#325 272.25
This is good accounting because the sum of all activity is still zero. You and I both know that the first five lines
represent an invoice, and the next two are a payment. But the database does not know that; we need a way to
group the first four and last two transactions. We solve this by creating a new table, finan_headers, with the
following bare-bones fields:
finan_headers
-------------
ID - Primary key
HeaderNumber - a preferably unique number but duplicates are allowed
HeaderDate - date of the transaction
Headertypes_ID - see below for possible values
Now, for the values in the Headertypes_ID field, we’ll create ANOTHER table to list types of headers:
finan_headertypes
-----------------
ID
Type
ID Type
1 Invoice
2 Cash Sale
3 Payment
4 Credit Memo
5 Refund
6 Check
As you can see, these transaction types are pretty standard in accounting. With this, we are ready to group the
first transaction, that is, the registration:
finan_headers records
ID HeaderNumber HeaderDate Headertypes_ID
1 325 10/13/2007 1 (Invoice)
We also add a new (foreign key) column in finan_transactions pointing back to finan_headers, and so now our
registration looks like this:
finan_transactions records
ID Headers_ID Idx Name Accounts_ID Quantity Price Amount
4(Member/Full
1 1 1 IFU Registration 1 -185 -185
Conference)
Fireman’s 15(Books and
2 1 2 1 -25 -25
Handbook Publications)
Roast Beef
3 1 3 18(Meals) 3 -5.75 -17.25
Dinner
27(General Activities
4 1 4 EMT Course 1 -45 -45
Income)
30(Accounts
5 1 NULL … 1 272.25 272.25
Receivable)
If you’re paying close attention you’ll notice that I took out the “Name” value for the last row (it said Invoice
#325). The reason is that that information has a new home in the parent record
infinan_headers.HeaderNumber.
finan_headers records
ID HeaderNumber HeaderDate Headertypes_ID
2 99051 10/25/2007 3 (Payment)
finan_transactions records
ID Headers_ID Idx Name Accounts_ID Quantity Price Amount
Payment check 31(Undeposited
6 2 NULL 1 272.25 272.25
#99051 Funds)
Payment of 30(Accounts -
7 2 NULL 1 -272.25
Invoice #325 Receivable) 272.25
So there you go, we have now grouped our transactions together and we can do so for any type of transaction
for that matter.
Improving Performance
You’ll notice that there is no “total” column in finan_headers. I intend that that way because the total is not a
“real” number. The “real” total is the sum of the first four line items (times -1 in order to make sense to
humans). If we put a Total field in finan_headers, we’d have to update it anytime we edited the line items for
that transaction – leaving it susceptible to getting out of synch.
However, there is something we can do which I think is worthwhile. Let’s add a field Accounts_ID in the
header. Take a look at our registration (Invoice) and payment now:
finan_headers records
ID Accounts_ID HeaderNumber HeaderDate Headertypes_ID
1 30 325 10/13/2007 1 (Invoice)
2 31 99051 10/25/2007 3 (Payment)
I could also double-check my math on the entry by getting the total this way:
This query would get all the OTHER transaction records (the line items) except for the “root” transaction. The
two values should be the same.
We are not quite finished yet. We have meaningful data in the transactions table that tells us which account is
increasing or decreasing. We have created a headers table to group the transactions. However, there is no way
to indicate what payment applies to what invoice. To do this, we could add fields in finan_transactions
somewhere, but the more precise way is by creating two additional tables as follows:
finan_rlx
---------
finan_transactions_rlx
----------------------
finan_rlx records
ID
1
this is just the "wrapper" or envelope for the relationship
finan_rlx_transactions records
Rlx_ID Transactions_ID
1 1
1 2
With these two tables and their entries, I can now understand that the payment (finan_headers.ID = 2) was
applied to Capt. Barton’s Invoice (finan_headers.ID = 1). Also I can determine how much has been paid on an
invoice with the following query:
SELECT
SUM(e.Extension)*-1 AS Total
FROM
finan_headers a,
finan_transactions b,
finan_transactions_rlx c,
finan_transactions_rlx d,
finan_transactions e
a.Accounts_ID=b.Accounts_ID AND
b.ID=c.Transactions_ID AND
c.Rlx_ID=d.Rlx_ID AND
d.Transactions_ID!=b.Accounts_ID AND
d.Transactions_ID=e.ID AND
a.ID=6
AND e.ID!=b.ID
GROUP BY a.ID
This query could use some explaining (and I’m going to go into this more in my next article). First, we join the
header and root transaction for the invoice (a and b). By joining c we get all Rlx_ID’s (relations) to this
invoice. Joining d and e, we get the transactions which are in any other payments.
Some of the SQL queries required to join our information are simple, and some are complex. But this system is
sufficient to accommodate one payment to multiple invoices, or multiple payments to a single invoice. One
issue which has NOT been brought up is that payments under this system can actually be attributed to specific
line items within an invoice, depending on how much the payment is broken up, however this can be
accomplished.
What I have done by this tutorial, in effect, is propose an entire accounting database by the use of 7 basic
tables. The fields in any table can be added to, and extension tables could extend queries to include
inventory, registrant names and etc; however these 7 tables are the heart of the system. I really welcome your
feedback on this! My next article will deal more with these SQL queries and getting meaningful data from the
database.
<a href=”#xfn1″>[1]</a><a name=”fn1″ id=”fn1″></a> there are 5 basic categories of these accounts: Asset,
Liability, Income, Expense, and Equity. You can pretty much figure out which is which with a little research,
and you’ll learn this indirectly here.