source: project/gazette/src/issues/ @ 25540

Last change on this file since 25540 was 25540, checked in by Mario Domenech Goulart, 9 years ago

gazette issue 21: more information about salmonella and the test infrastructure

File size: 43.0 KB
1((title . "Issue 21")
2 (authors "Andy Bennett")
3 (date . 1321465864))
5== 0. Introduction
7Welcome to the 21st issue of the Chicken Gazette. While the gazette
8was on hiatus the chicken community was definitely not.
10With the stabilisation of Chicken 4.7 well underway since the last
13Some people have also started to set up salmonella farms and C-Keen
14has completed his "Chicken Zoo".
19== 1. Chickens Out & About
21=== Nuremberg (December 2011)
22[[|Christian Kellermann]] wrote
23([[|2011/10/22 17:38Z]]) to
24confirm the planned Chicken Hacking Sprint scheduled for the beginning of December.
26Interested and available Chickenauts will meet in Nuremberg on the weekend of 9th, 10th and 11th
27of December. If you haven't done so already, you can let us know that you will be joining us by
28placing a note at the foot of the [[|wiki page]].
29If you can't attend in person then we'll be keeping an eye on IRC so feel free to book yourself
30a slot in your cave at home and join us throughout the weekend and into the evenings, Central European Time.
32For those of you keeping score, this will bring the total number of Chicken meets this year to
335 (FOSDEM, Cologne, FrOSCon, T-DOSE and Nuremberg).
36=== Cologne (March 2011)
38In March, Chickens met up at the Chaos Computer Club in Cologne, Germany for a Chicken Hacking Weekend. Here's
39[[|Moritz Heidkamp]]'s report on what they got up to.
44=== FrOSCon (August 2011)
45At FrOSCon in August we sported not only a project booth but a presentation room as well so we took the
46opportunity to lay on a couple of talks for the delegates.
48[[|Moritz Heidkamp]] presented
49"[[|An introduction to Lisp]]:
50Why to talk to computers in parentheses" whilst
51[[|Christian Kellermann]] offered
52"[[|A guided tour through the republic of CHICKEN]]:
53get up to speed with the practical scheme implementation".
54[[|Christian]] has been keeping his talk up-to-date and
55you can find the latest version [[|on the wiki]].
57Naturally, there are [[|pictures]].
60=== T-DOSE 2011 (November 2011)
61European Chickens met up at T-DOSE in Eindhoven, The Netherlands on the weekend of the 5th, 6th and 7th of
62November. [[|Peter Bex]] arranged for us to contribute a "Chicken
63Scheme Project Booth" and we subsequently lured a few Dutch hackers into #chicken on IRC.
65[[|Christian Kellermann]] knocked up some impressive demos
66of [[|PONG]] using Chicken's [[|Cairo Egg]].
68[[|Alaric Snell-Pym]] finished off the tests for his
69[[|Ugarit]] backup egg which allowed him to release version 1.0.
71...and [[|Moritz Heidkamp]] started on a replacement for the
72[[|Environments Egg]] as it has unfortunately has been overtaken
73by developments in the Chicken development branch.
75Pictures are up in the [[|usual place]] on the Chicken website.
79== 2. Chicken Talk
81=== R7RS to be dedicated to the memory of John McCarthy
83Matt Welland wrote in with the sad news that the "Father of Lisp", John McCarthy had sadly passed away ([[|2011/10/25 15:59Z]]). [[|John Cowan]], member of the R7RS working group, told us that R7RS,
84the upcoming version of the Scheme standard, would be dedicated to his memory ([[|16:22Z]])
87=== Parallel build patch for Chicken Makefile
89A lively discussion broke out on the [[|Chicken Users mailing list]] regarding
90[[|parallelisation of the Chicken build process]].
92Vitaly Magerya supplied a [[|patch]] (2011/10/06 12:37Z) to fix some problems
93he'd been having with builds failing when specifying the {{-j}} to {{gmake}}.
94[[|Mario Domenech Goulart]] noted that this functionality was already being
95addressed by [[|ticket 526]]
96([[|2011/10/06 12:39Z]]) and Vitaly was keen to see it committed
97soonly as he maintains the FreeBSD port and people had been asking him for parallel builds ([[|2011/10/06 14:24Z]]). Whilst still keen, [[|Mario]] [[|advised caution]] as he wanted to see it proven ([[|2011/10/06 14:40Z]]).
99Vitaly was keen to push things forward ([[|2011/10/06 15:45Z]]) and
100offered continued testing under FreeBSD 8.2-RELEASE on x86_64. He noted that, as far as he could tell, {{gmake check}} passed when using his
101patch, modified with [[|Moritz Wilhelmy's suggestions]]. Moreover, he
102offered to try the patch from [[|ticket 526]] as well.
104[[|Mario]] [[|suggested]]
105that using the [[|ticket 526]] patch was a good idea as it had, as far as he knew, received testing under Linux and MacOSX.
107Toby Thain spoke in favour of the patch, saying that a working parallel build gives confidence that the Makefile is, in fact, correct.
108He offered to test on three platforms, namely Solaris 10 SPARC SMP and both PowerPC and Intel version of MacOSX SMP.
110Vitaly updated the [[|ticket 526]] patch to the then current sources and confirmed that the build worked at {{-j8}}
111and the tests passed in his FreeBSD 8.2-RELEASE, x86_64 environment. He did, however, note that parallelisation does not work for the install phase
112([[|2011/10/08 12:29Z]]).
114Toby Thain reported successful builds on the 64-bit version MacOSX-10.6 as well as MacOSX-10.5 on a dual-processor G5. He noted "{{make -j8}}
115reduces the make step from 3:39 to 0:47 on my 8-core system". Unfortunately {{make check}} did not pass on either system, with or without the patch
116([[|2011/10/12 01:59Z]] &
117[[|2011/10/15 19:53Z]])
119[[|Christian Kellermann]] noted that it might necessary to install the compiled binaries
120([[|2011/10/15 20:07Z]]) however, Toby did not report back as to whether this
121had any effect on the results of the tests.
124=== 64-bit SPARC Build
126Nicolas Pelletier came to the [[|mailing list]]
127unable to build for Solaris 10 running on SPARC64 due to a missing {{apply-hack.S}}
128([[|2011/11/09 11:48Z]]).
130It turned out that there was no SPARC64 support for Chicken at all and [[|John Cowan]]
131came to the rescue ([[|15:36Z]])
132with instructions for how to add it to the supported architectures list and disable the
133apply hack. [[|Christian Kellermann]] chimed in with affirmations
134around the utility of using Chicken without an apply hack ([[|15:54Z]])
136Nicolas struggled with disabling the apply hack ([[|2011/11/10 11:10Z]])
137due to the Two Lies Of The README. Over the course of the next few minutes [[|Christian]]
138and [[|Mario Domenech Goulart]]]
139confirmed that the lies, which related to the way bootstrap compilers are distributed and invoked, had
140already been fixed in the development versions.
142Meanwhile, Toby Thain tried the build on his SPARC64 ([[|12:40Z]])
143and discovered that GNU make 3.82 is required as well as the addition of {{-lrt}} in {{Makefile.solaris}}. Toby's
144build eventually succeeded and appeared to be good, albeit having only used 1 of his 4 CPUs and with a couple of {{make check}} errors.
146Later on the Thursday, [[|Jim Ursetto]] noted ([[|16:47Z]])
147that the [[|parallel build patch]] in
148[[|ticket 526]] still works on Chicken Toby [[|confirmed]]
149that the patch did indeed work on Solaris and build time was reduced to 10 just of his Earth minutes from the original 40.
150Of course, user time across the four CPUs was still in the region of 40 minutes in total. :-)
152To round everything off, Toby offered [[|binary SYSV packages for Solaris 10 on SPARC64]].
153Contact him via the [[|mailing list]] if you're interested.
156=== Static Linking & Standalone Binaries.
158Serg Kozhemyakin [[|asked]] whether it is possible to build static
159binaries after having trouble using the {{-static}} flag to {{csc}}. He also wondered about building standalone binaries for Windows.
160[[|Christian Kellermann]] [[|advised]]
161against static linking as many of the eggs cannot cope with it. He did, however, suggest that the {{-deploy}} option may well solve both
162issues as it ensures that all the DLLs (including the Chicken runtime and other shared objects) are bundled together into a single directory.
165=== Tracing with {{,tr}} moved to an egg
167[[|Mario Domenech Goulart]] [[|helped]]
168Curtis Cooley in his [[|search for the {{,tr}} command]].
169The functionality had been moved from the core into an egg in Chicken 4.3.0.
172=== {{gmake check}} sometimes fails.
174Pekka Niiranen wrote to tell us about
175[[|problems with {{gmake check}} in Chicken 4.7.0-st]]
176on OpenBSD 5.0.
178[[|Jim Ursetto]] [[|said]]
179that one of the failures had been fixed in the stability branch after the Chicken 4.7.0-st release and he filed
180[[|ticket 724]] for the segmentation fault issue.
182[[|Peter Bex]] mentioned that the [[|ticket 724]] segmentation
183fault had also been observed under NetBSD and Arch Linux. He said that "It's known to be broken" but that it is not properly reproducible
184as it only happens some of the time ([[|2011/11/06 09:51|]]).
187== 3. Salmonella and other tests
188Salmonella is Chicken's egg testing framework.
190For some time we have had daily salmonella runs of the master development
191version of Chicken under Linux on x86. Throughout November, MacOSX-x86_64 has also
192been regularly experiencing the runs. [[|Mario Domenech Goulart]] who maintains the testing
193effort at [[]], as well as the [[|Salmonella codebase]]
194itself, is always on the lookout for more feeds. We are aware that testing on
195other platforms is being organised but if you've got a private Salmonella
196running regularly on an interesting operating system or version of Chicken then
197give us a shout and we'll see if we can get it included with the other reports
198on [[]]
200There is also [[|an entirely new version of Salmonella]] over at github so test that too. The new version is more modular -- some features that were previously built in salmonella are now independent eggs (e.g., [[|salmonella-html-report]] and [[|salmonella-feeds]].  There's also a new egg that provides a new feature: [[|salmonella-diff]] which generates HTML output to render differences between two salmonella logs.
202Here's short list of new salmonella features:
204* egg lint mode: salmonella can now easily check for common egg mistakes before the code hits the egg repository.  Just run {{salmonella --this-egg}} from the egg directory
205* a new execution mode that can significantly reduce salmonella execution times.  In this mode, salmonella won't set the temporary egg installation directory empty before installing new eggs.  While this mode won't spot egg dependencies problems, it can drastically effect the execution time
206* a new tool to take advantage of multi-core systems ({{salmonella-epidemy}})
207* a simple text mode log file viewer ({{salmonella-log-viewer}})
208* [[|salmonella-html-report]] generates reverse dependencies graphs.  That can be useful to ilustrate how many eggs depend on a given egg, for example
211[[|Alaric Snell-Pym]] is one of the people who has been setting up a Chicken testing
212environment. After [[|some discussion]], he chose Chicken 4.5.0 under
213NetBSD ([[|2011/10/06 21:18Z]]). He's been keeping track of all the
214Egg dependencies and plans to publish them as a list of NetBSD pkgsrc packages.
216Thanks to [[|bevuta IT GmbH]], the CHICKEN test
217infrastructure will soon have more machines available to test the
218CHICKEN compiler, tools and eggs.  A new salmonella environment for
219Linux/x86-64 is currently being set up.
221In addition to salmonella, work on the
222[[|Chicken Playground]] project has
223been resumed.  The Chicken Playground project provides environments
224that can be used by tools like {{chroot}} to test CHICKEN tools and
225eggs.  Those environments contain tools and libraries required to
226build and test most eggs.  Currently a Linux/x86 environment is
227available. A Linux/x86-64 environment is under development and should
228be available soon.
230The new test infrastructure, based on new versions of salmonella and
231Chicken Playground and running on new platforms will probably go on
232production during the Nuremberg hackaton.
235== 4. Core Development
239I need some blurb on this from people Who Know.
240I'd like something on the scrutinizer and stability work that's been going on as well as the proposal to bug fix until r7rs
241and then do Chicken 5.0.
244mario, felix, ckeen
245lots of bug fixes, sparc64 support, scrutinizer fixes
247 * On origin/master:
248   * (e1a5437): when conditional branches differ in the number of results, do not merge the results (felix, 2 days ago)
249   * (bae7d92): clustering optimization added (felix, 5 weeks ago)
250   * (b1907c5): use different default stack-sizes on 32/64 bit platforms (felix, 6 days ago)
251   * (986bd3f): disable apply-hack on sparc64 (felix, 6 days ago)
252   * (28a36bf): Remove scripts/make-egg-rss-feed.scm (imported into the chicken-infrastructure repo) (Mario Domenech Goulart, 5 days ago)
253   * (fbeffd1): scripts/tools.scm: needs regex for `string-match' (Mario Domenech Goulart, 5 days ago)
254   * (1fca9c6): use equal? in case MSYSTEM is not defined (makedist.scm) (felix, 6 days ago)
255   * (42a8263): don't remove build-info files on make confclean or it is not possible to build from a tarball without preinstalled chicken (felix, 9 days ago)
256   * (3bb9645): write debugging output for optimizations into generated source file (felix, 3 weeks ago)
257   * (e7fe2fb): allow procedure in operator-position of evaluated list (#705, suggested by John Cowan) (felix, 2 weeks ago)
258   * (e2fb31c): fix subtype check for fixed-size list/vector types in scrutinizer (felix, 10 days ago)
259   * (fd018df): strip std-envs of non-std identifiers, ignore ##sys#macro-environment when evaluating with an explicit environment (felix, 13 days ago)
260   * (fe8f63c): minor change in port-tests.scm to handle case when HOME is not defined (Windows) (felix, 2 weeks ago)
261   * (723a469): cut of union types above a certain length (felix, 2 weeks ago)
262   * (919a7c7): fixes in build + Windows test script, added note to README (thanks to mario) (felix, 2 weeks ago)
263   * (ddb96f9): Also added runtests.bat to manifest. (felix, 3 weeks ago)
264   * (0df9bb8): another trivial fix for making boot-chicken work on cmd.exe (felix, 3 weeks ago)
265   * (cea668f): trivial Windows-related corrections in rules.make, found by Mario - not yet working, though (felix, 3 weeks ago)
266   * (1bc311d): undocumented scrutinize declaration (obsolete), reported by ckeen (felix, 3 weeks ago)
267   * (10494f6): fixed stupid paren-matching error (felix, 3 weeks ago)
268   * (f9a6702): disable debug output in scrutinizer (felix, 5 weeks ago)
269   * (2483f8d): check not necessary for lambda and  warn if using keyword as variable in binding form (felix, 3 weeks ago)
270   * (aa6b2bb): cleaning up uninitialized variables in C_reclaim, add debug output to C_mutate on m-stack resize, grow m-stack at higher rate (felix, 3 weeks ago)
271   * (1e04156): use write-string port-class method (felix, 3 weeks ago)
272   * (59cdaaf): remove obsolete C_h_... allocation functions (pointed out by Joerg Wittenberger) (felix, 3 weeks ago)
273   * (159611d): handle macro-aliases when resolving declared identifier, making sure things are done right inside modules. (felix, 4 weeks ago)
274   * (0cc88a4): removed obsolete file (felix, 4 weeks ago)
275   * (1f56f3a): ##core#type makes subtype-check optional; quit compile when type-mismatches in strict mode (felix, 5 weeks ago)
278== 5. Stability is for Chickens
280[[|Jim Ursetto]] has set up a Chicken Stability Branch
281effort over at [[]]. Jim describes it as "a fork of the Chicken
282Scheme core which backports certain useful bugfixes and minor features from the future to 'stable'
283maintenance versions". Releases for stability/4.7.0 have version numbers like {{4.7.0.x-st}} (for
284example, {{}}). If you're interested in running a stable Chicken in production, we recommend
285that you check out Jim's work.
289== 6. Hatching Farm
291There has been a lot of egg activity over the course of the past few months. I've selected a few,
292focussing on ones that were announced or discussed on the [[|mailing list]].
294TODO: Write about the distributed egg repository
296By far the most notable thing to have happened is the announcment and implementation of the new
297Egg management system.
299* [[|Ivan Raikov]]'s [[|proposal]]
300  to recategorise eggs filed under "Uncategorized or invalid" and "Miscellaneous" was met with enthusiasm with a number of people pitching in to
301  help as well as [[|Peter Bex]] suggesting that the category list be revisited
302  ([[|2011/10/11 08:12Z]]). [[|Peter]]
303  specifically suggested the addition of a "Communication"s category.
305* [[|Thomas Chust]] announced some new eggs: [[|bindings to WebKitGTK+ and JavaScriptCore]], [[|webgate: a tiny (S)CGI application framework]] and a [[|small binding for the BerkelyDB library]]
307* [[|Christian Kellermann]]
308  [[|fixed some bugs]] in the
309  [[|Cairo Egg]] and tagged version 0.1.11.
311* [[|Alan Post]] [[|patched]] the
312  [[|Sandbox Egg]] to bring it up-to-date with API changes in the Chicken core. His patch was duly accepted.
316== 7. Donations
318It's now possible to donate to your favourite scheme (as long as it's Chicken)!
322We've already had two donations since the account opened a month ago so get donating before it fills up! :-)
324Thank you [[|Sven Hartrumpf]] and Ross Lonstein!
327== 8. Omelette Recipes
329This issue's omelette is the second installment of
330[[|Alaric Snell-Pym]]'s
331"financial book-keeping in scheme" series. If you missed the first part,
332check it out [[|here]].
334The dream of many Schemers is to write Scheme for a living. Due to the
335regrettable tendency of employers to frown upon good ideas, the best
336way to achieve this is to start your own company, so you can be your
337own boss, and frown upon the good ideas of others instead.
339However, running your own company comes at a price; and a big part of
340that price is the horror of "book keeping"; the requirement to track
341all flows of money and other, more abstract, forms of value in and out
342of your company.
344Some people will tell you book-keeping is simple. "Just keep all your
345receipts and bank statements and bills and invoices", they say. "Then
346send them to your accountant at the end of the year."
348"But what about value-added tax?", you ask (or "sales tax" in some
349countries). "And what about income tax paid at source for my employees
350(including myself)?". "And why do I need to pay an accountant so much
351to do a job that a computer can easily do in milliseconds?" And then
352the smug smile slowly drips from the face of the "Oh it's easy" crowd.
354Clearly, book-keeping is complicated. And yet also simple, in that it
355is determined by sets of rules.
357We know what to do, don't we? Let's get coding!
359The problem is that book-keeping involves several different kinds of
360inputs - bills (that people send you), invoices (that you send
361people), transfers of money (bills and invoices being paid), loans (to
362and from the company), employees being paid, interest payments from
363the bank, dividend payments, and so on; while it also involves several
364different outputs - tax reports for the various taxes involved (in the
365UK, I had to deal with VAT every three months, income tax and national
366insurance when paying myself and my wife as employees every month, an
367annual filing fee, and annual corporation tax and dividend payments),
368statutory filing of certain financial summaries (generally annually),
369and internal reporting: How much was I spending? How much did each
370client owe? How much should be in the bank by when? Plus, it's nice to
371be able to generate nice invoices to send to folks who owe you
372money. That's a form of specialised report, too, just reporting on a
373single invoice.
375Each of the output reports depend in complex ways on different
376information from the inputs. The VAT reports mainly have to add up how
377much VAT I've paid when being billed by others, and how much VAT I've
378charged when invoicing - meaning that VAT needs to be tracked on all
379bills and invoices so it can be extracted. They also want to know
380totals of actual money in and out of the company in the period (even
381stuff where VAT isn't an issue), presumably to check up on
382me. Meanwhile, end of year reports tend to need to know how much I've
383invoiced for various different kinds of work, and what I've spent on
384what kinds of things: buying equipment that will last me for several
385years is handled differently to expenses like travel, or buying stuff
386that I eventually resell to clients (so in our invoices, we need to
387keep track of money charged for services separately to money charged
388for things).
390Some reports care about virtual money moving hands. As soon as I
391invoice somebody, then the company now has a virtual asset - some
392money owed to it. That's worth as much as cash in the bank from some
393perspectives (generally, I have to pay tax on it as soon as it's
394invoiced, even if I've not been paid). And yet some care only about
395actual cash changing hands (working out my bank balance, for instance).
397Sometimes our clients invite us to incur expenses in doing work for
398them (such as extra travel) and then invoice them on for those
399expenses, so they pay us back - in which case, expenses need to be
400able to be tied to invoices, as well. Sometimes we decide to cancel an
401invoice, which can't be done by just pretending it never existed, for
402audit-trail reasons; we need to issue a "negative" invoice called a
403credit note.
405Just to complicate matters more, the actual movement of money isn't
406atomic. If I invoice somebody on date A, they might post me a cheque
407which arrives on date B, which I pay into the bank on date C, which
408actually clears into the account (and thereby appears on my bank
409statement, when I get it) on date D. So at date A the company now has
410a "we are owed" pretend-money asset, which goes through various stages
411until it finally turns into money in the bank on date D.
413I handled my book-keeping with some hacky scripts written in Chicken
414Scheme. What I'm going to document here is partly what I've done, and
415partly what I should have done - it was a very iterative process,
416refining the best way to handle stuff, and there's lots of
417improvements I've wanted to make but not had time to. So I'm going to
418describe the ideal case, not the hacky half-way house I actually have
419right now!
421The approach I took was to have a file called a "ledger" that I enter
422all my invoices and so on into. This is parsed to build up a bunch of
423data structures in memory, from which the various reports can easily
424be obtained. Firstly, for each kind of input object (invoices, bills,
425etc) there's a list of them, or more often a hashmap to make them easy
426to find by some identifier (I can give my invoices unique symbolic
427names, for instance). That contains the raw data as parsed from the
428ledger file. But then we also create summary structures, which are
429used by the more general reports to generate their output without
430having to special-case each and every different input object type, and
431to enable sharing of common functionality between reports.
433The main summary structure is the double-entry transaction list, which
434models the entire financial activity of the company as transfers
435between accounts.
437Imagine I invoice Widget Corp for setting up and installing a router:
439  INVOICE INV005: Issued 2011-04-25
440  Router setup and installation: GBP 800
441  1 router from my stock: GBP 350
442  1 train ticket for me to go to their site: GBP 35 (no VAT due)
443  Subtotal: GBP 1,185
444  VAT on the above: GBP 230
445  Total due: GBP 1,415
447As part of the work, I lose a router (worth GBP 350) from my stock,
448and have to spend GBP 35 on a train fare.
450This might expand into the following transactions:
452 * 2011-03-02: "Expense for Widget Corp (INV005)"
453   * +35 "Travel to site"
454   * cash -35
456 * 2011-04-25 "Invoice Widget Corp (INV005)"
457   * -800 (Router set up and installation)
458   * stock.balances -350 (1 of LX300 router, serial number 0343248)
459   * clients.widgetcorp.expenses -35 (Travel 2011-03-02)
460   * taxes.vat -230
461   * clients.widgetcorp.balance +1415
463And, eventually, they might pay me, which hits my bank account some
464time later:
466 * 2011-05-07 "Payment from Widget Corp (INV005)"
467   * clients.widgetcorp.balance -1415
468   * bank.balance +1415
470And then one day I'll pay my VAT bill, which will look something like:
472 * 2011-06-01 "VAT payment for period from 2011-03-01 to 2011-06-01"
473   * taxes.vat 230
474   * bank.balance -230
476Note a few tricky things. Each transactions "splits", as the lines
477within them are known, have to sum to zero for everything to balance
478correctly, which tells us that nothing has gone missing. So when we
479start being owed GBP 1,415 by Widget Corp, we need to account for
480where that asset has come from. Special accounts with names such as
481"" (for value generated by me working) and
482"clients.widgetcorp.expenses" (for previously-paid expenses that, as
483of this invoice, I can charge the client for) pop into
484existence. "taxes.vat" looks as if VAT is a form of income for me, as
485money comes "from" it in the transaction - which is sort of true; I'm
486charging Widget Corp for some VAT alongside for the actual work
487done. Figuring out what signs to put on all the items in the invoice
488is mind-bending and painful, but if you just concentrate on making it
489all add up to zero in the end and starting from things that are
490obvious (is money going into or out of the bank account, or the "owed
491to me by this customer" account?), you can figure it out.
493From the above, we can start to flesh out some data structures:
496(define-record txn
497               date customer code description splits)
499(define-record txn-split
500               account amount notes)
503(define *txns* (make-hash-table))
504(define (register-txn! txn)
505  (if (hash-table-exists? *txns* (txn-date txn))
506      (begin
507        (set! (hash-table-ref *txns* (txn-date txn))
508              (cons txn
509                    (hash-table-ref *txns* (txn-date txn)))))
510      (begin
511        (set! (hash-table-ref *txns* (txn-date txn))
512              (list txn)))))
515What is an "account"? There's a few kinds, and what kind of account it
516is matters in reporting. Accounts might be assets within the company -
517such as "clients.widgetcorp.balance" or "bank.balance" or
518"stock.balance". Or they may be places where money (be it real or
519virtual) is created from or destroyed by (from the perspective of the
520company), such as "" and "". The important
521difference is that balance-type accounts have a balance that is
522increased when money is sent to them and decreased when it's taken
523out, and that balance is part of the value of the company, while the
524income/expense type accounts don't. In my terminology, these are
525"balance" accounts and "delta" accounts. Each account also begins to a
526group, used to aggregate them in reports: there's income accounts,
527bank accounts, client accounts, and so on. And accounts may be tied to
528a third party - I've given an example of a client above, but also, the
529organisations that send me bills have balances (the money I owe
530them). In general, every third party (be they ones that bill me, or
531ones that I invoice, or both - I've interacted with other freelancers,
532sometimes working for them, sometimes vice versa) has a set of
533accounts attached to them for their balance, expenses I can claim from
534them, and so on. That implies another set of record types:
537(define-record third-party
538               name address balance-account expenses-account)
540(define-record account
541               name type group third-party)
544(define *third-parties* (make-hash-table))
545(define *accounts* (make-hash-table))
547(define (find-account acct-name)
548  (hash-table-ref *accounts* acct-name))
550(define (register-account! acct)
551  (set! (hash-table-ref *accounts* (account-name acct)) acct))
554An account's {{third-party}} slot may be {{#f}} if it's not part of a
555third party.
557Now, to make things easy, I parse the ledger by just defining a heap
558of procedures and macros, and then diving into the ledger with
559eval. It's just Scheme code that, as it is executed, builds up the
560data structures. We'll need some helpers to set up third parties
564(define (define-third-party name address group)
565        (let* ((balance-account
566                (make-account
567                 (string-append name ".balance")
568                 'balance
569                 group
570                 #f))
571               (expenses-account
572                (make-account
573                 (string-append name ".expenses")
574                 'delta
575                 'expenses-reclaimed
576                 #f))
577               (third-party
578                (make-third-party
579                 name address balance-account expenses-account)))
580          (account-third-party-set! balance-account third-party)
581          (account-third-party-set! expenses-account third-party)
582          (register-account! balance-account)
583          (register-account! expenses-account)
584          (set! (hash-table-ref *third-parties* name) third-party)))
587Now we can work on a way of representing bills and invoices. A nice
588input syntax would be:
591(define-third-party "clients.widgetcorp" "123 Any Street" 'clients)
592(register-account! (make-account "" 'delta 'income #f))
593(register-account! (make-account "" 'delta 'travel #f))
594(register-account! (make-account "stock.balance" 'balance 'stock #f))
595(register-account! (make-account "cash" 'balance 'cash #f))
596(register-account! (make-account "taxes.vat" 'balance 'vat #f))
598(invoice "INV005" "clients.widgetcorp" (ymd 2011 04 25)
599         (service "" 800 (vat20) "Router setup and installation")
600         (sale "stock.balance" 350 (vat20) "1 of LX300 router, serial number 0343248")
601         (expense (ymd 2011 03 02) "" "cash" 35 () "Travel to site"))
604The idea is that the sales taxes incurred on a line are specified as a
605list after the amount. If there's no taxes due, then we use an empty
606list. Otherwise we have a list of taxes, which are either plain tax
607names (to have the system compute the tax due itself) or two-element
608lists joining a tax name to a precomputed amount (often, when we pass
609on an expense, we know the tax we paid as it's on the receipt, so we
610should use that (even if they made a mistake working it out) rather
611than calculating our own).
613A nice way to implement that might be to make "invoice" a macro that
614absorbs its first three arguments as an invoice code, the name of the
615third party to invoice, and the date; then treats the rest as a body
616to be wrapped in a dynamic environment in which a parameter allows
617{{sale}}, {{expense}}, and {{service}} to add lines to the
618invoice. This is easily arranged:
621(define-record date year month day)
623(define-record invoice
624  name date third-party lines)
626(define (register-line! invoice line)
627  (invoice-lines-set! invoice
628                      (cons line (invoice-lines invoice))))
630;; Compute sales taxes
631(define (compute-tax tax amount)
632  (case tax
633    ((vat20) (* 0.20 amount)) ;; Current UK rate
634    ((vat15) (* 0.15 amount)) ;; Previous UK rate
635    ((vat175) (* 0.175 amount)))) ;; Previous UK rate
637;; Expand a list of taxes, some of which might be bare symbols
638;; naming taxes to work out, or (<tax> <amount>) lists for
639;; ready-computed taxes, into an alist of tax names to tax amounts
640(define (resolve-taxes amount taxes)
641  (map (lambda (tax-desc)
642         (if (list? tax-desc)
643             (cons (car tax-desc) (cadr tax-desc))
644             (cons tax-desc (compute-tax tax-desc amount))))
645       taxes))
647(define-record invoice-service-line
648  income-account amount taxes description)
650(define-syntax service
651  (syntax-rules ()
652    ((service income-account amount taxes description)
653     (register-service! (*current-invoice*) income-account amount 'taxes description))))
655(define (register-service! invoice income-account amount taxes description)
656  (let ((service
657         (make-invoice-service-line
658          (find-account income-account)
659          amount
660          (resolve-taxes amount taxes)
661          description)))
662    (register-line! invoice service)))
664(define-record invoice-sale-line
665  stock-account amount taxes description)
667(define (register-sale! invoice stock-account amount taxes description)
668  (let ((sale
669         (make-invoice-sale-line
670          (find-account stock-account)
671          amount
672          (resolve-taxes amount taxes)
673          description)))
674    (register-line! invoice sale)))
676(define-syntax sale
677  (syntax-rules ()
678    ((sale stock-account amount taxes description)
679     (register-sale! (*current-invoice*) stock-account amount 'taxes description))))
681(define-record invoice-expense-line
682  expense-account payment-account amount taxes description)
684(define (register-expense! invoice date expense-account payment-account amount taxes description)
685  (let ((expense
686         (make-invoice-expense-line
687          (find-account expense-account)
688          (find-account payment-account)
689          amount
690          (resolve-taxes amount taxes)
691          description)))
692    (register-line! invoice expense)))
694(define-syntax expense
695  (syntax-rules ()
696    ((expense (ymd year month day) expense-account payment-account amount taxes description)
697     (register-expense!
698      (*current-invoice*)
699      (make-date year month day)
700      expense-account
701      payment-account
702      amount
703      'taxes
704      description))))
706(define *current-invoice* (make-parameter #f))
707(define *invoices* (make-hash-table))
709(define-syntax invoice
710  (syntax-rules (service sale expense)
711    ((invoice name third-party (ymd year month day) body ...)
712     (let ((inv
713            (make-invoice name
714                          (make-date year month day)
715                          (hash-table-ref *third-parties* third-party)
716                          '())))
717       (parameterize
718        ((*current-invoice* inv))
719        (begin body ...))
720       (set! (hash-table-ref *invoices* name) inv)
721       (generate-invoice-transactions! inv)))))
724We end the expansion of the {{invoice}} macro with a call to
725{{generate-invoice-transactions!}}, which will do the task of creating
726the double-entry transactions for the invoice. Other types of summary
727structure can be added by calling additional generator procedures at
728this point. This is largely a matter of going through the invoice
729lines, handling them on a case-by-case basis to generate lists of
730transaction splits that we can append together to generate the invoice
731transaction. The case of expense lines is interesting, in that an
732extra transaction has to be generated for each expense, to record its
733initial spending, as well as a split to record the expense being
734claimed in the invoice.
736For now, let's just handle one case:
739(define (generate-invoice-transactions! inv)
740  (register-txn! (make-txn
741    (invoice-date inv)
742    (string-append "Invoice " (invoice-name inv) " for "
743                   (third-party-full-name (invoice-third-party inv)))
744    (let ((txn-balance-account
745           (third-party-balance-account
746            (invoice-third-party inv))))
747      (flatten
748       (map
749        (lambda (line)
750          (cond
751           ((invoice-expense-line? line)
752            (list)) ;; FIXME: Not implemented
753           ((invoice-sale-line? line)
754            (list)) ;; FIXME: Not implemented
755           ((invoice-service-line? line)
756            (list
757             (make-txn-split
758              (invoice-service-line-income-account line)
759              (- (invoice-service-line-amount line))
760              (invoice-service-line-description line))
761             (make-tax-splits
762              (invoice-service-line-taxes line)
763              txn-balance-account)
764             (make-txn-split
765              txn-balance-account
766              (invoice-service-line-amount line)
767              #f)))))
768        (invoice-lines inv)))))))
770(define (make-tax-splits taxes txn-balance-account)
771  (map (lambda (tax)
772         (let ((tax-type (car tax))
773               (tax-amount (cdr tax)))
774           (case tax-type
775             ((vat20 vat15 vat175)
776              (list
777               (make-txn-split
778                (find-account "taxes.vat")
779                (- tax-amount)
780                #f)
781               (make-txn-split
782                txn-balance-account
783                tax-amount
784                #f))))))
785       taxes))
788Feeding in the above example invoice, then checking out the resulting
789double-entry transaction list, shows that it worked:
792(hash-table-for-each *txns*
793                     (lambda (date txns)
794                       (for-each
795                        (lambda (txn)
796                          (printf "Date: ~A Desc: ~A\n"
797                                  (txn-date txn)
798                                  (txn-description txn))
799                          (for-each (lambda (split)
800                                      (printf "Acct: ~A Delta: ~A Notes: ~A\n"
801                                              (account-name (txn-split-account split))
802                                              (txn-split-amount split)
803                                              (txn-split-notes split))) (txn-splits txn)))
804                        txns)))
807   date: #<date> Desc: Invoice INV005 for Widget Corp
808   Acct: Delta: -800 Notes: Router setup and installation
809   Acct: taxes.vat Delta: -160.0 Notes: #f
810   Acct: clients.widgetcorp.balance Delta: 160.0 Notes: #f
811   Acct: clients.widgetcorp.balance Delta: 800 Notes: #f
813We've ended up with multiple splits for the same account, as we record
814that both VAT and the money due for the service are to come from the
815client's balance account - and other splits will add plenty more. To
816fix this, we need to write a procedure that canonicalises a list of
817splits, and call that on the splits before calling
818{{make-txn}}. Canonicalisation consists of finding all the splits that
819refer to the same account and have the same notes (be it {{#f}} or a
820string) and merging them into one with the total of the amounts. But
821I'll leave that (along with implementing bills, payments, and some
822actual reports) as an exercise to the reader... It's easy to imagine
823how to generate a VAT report from the list of transactions, by
824filtering them for membership of the required date range and looking
825for splits involving "taxes.vat", or to generate a nicely formatted
826invoice by extracting a single invoice record, or to work out the
827balance of an account at any point in time by adding up all the
828transaction splits that involve it up to that point in time. Also, the
829core engine needs to be wrapped up in a module that only exposes the
830required bindings, and hides internals.
832Having automated one's book-keeping and financial reporting, many
833operations (such as the VAT returns) can be done without involving an
834accountant; in my case, the accountant is only needed to help with the
835annual corporation tax computation and filing of official accounts,
836which requires deep understanding of the UK tax system to do
837everything properly. Having said that, if I studied the system
838properly (and tracked the changes each year), I'm sure I could
839automate that, too...
841== 9. About the Chicken Gazette
843The Gazette is produced occasionally by a volunteer from the Chicken
844community. The latest issue can be found at
845[[]] or you can follow it in your feed
846reader at [[]]. If you'd like to
847write an issue, [[|consult the wiki]]
848for the schedule and instructions!
Note: See TracBrowser for help on using the repository browser.