#!/bin/sh
: ; exec klone $0 "$@"
; The above line finds the klone executable in the $PATH
;;Maintains a coffe log. add data at the end of this script and run it
;;to generate a mail, a web page, and archive this script via RCS
;;Can be customized for all kinds of servings (cans, bars...)

;;=============================================================================
;;                    Customize here:
;;=============================================================================

(setq generated-dir "/u/www/0/www/htdocs/files/koala/coffee") ;where this script is
;;rest of files will be found in generated-dir
(setq generated-html "coffee.html")	; genetared page
(setq generated-url "http://www.inria.fr/koala/coffee")	;script URL
(setq generated-text "coffee.txt")	; generated mail (also outputted)
(setq script-name "coffee")		; script name
(setq header-html "coffee-header.html")	; prepended to html page at generation
(setq title "Ampere Coffee Log")	; title of the page
(setq maintainer-id 'colas)		; maintainer id (ignored now)
(setq supplies-type 
  '(c "coffee" w "water" s "sugar" m "milk" u "cups" h "hardware" ? "???"))
(setq drank-verb "drank")		; consuming verb (past), "drank", "eat"
(setq cup-noun "cups")			; serving name, "cups", "cans", "bars"
(setq cup-value 2.50)			; value of a cup/serving
(setq currency-symbol "%0 F")		; "%0 F", "$%0"...
					; %0 is expansed into the amount
(setq currency-name "francs")		; francs, dollars...

;;=============================================================================
;;                    Definitions
;;=============================================================================
;; This script is run to manage a cooperative consuming of good.
;; People put money ("Fund") into a common pool, or buy supplies which amount
;; is credited to their account, and each time an item (coffee cup, chocolate 
;; bar, soda cans...) is consumed they put a mark on a log sheet.
;; By entering Funds, Supplies, and Cups consumed into this script you can 
;; manage easily such a cooperative system.
;; The operation principle is to maintain 2 databases: one of abbrevs (a base
;; of people names and emails indexed by short IDs), and a base of events
;; "entries", list of events in chronological order
;; Events are of type Log, and are inputted at the end of this script as
;; arguments to the declare-log function (abbrevs are decalred with
;; the decalre-abbrevs function)
;; Events have types specified by a capital letter (funds, cups drank, date,
;; supplies bought...)
;; Running the script computes amounts and some stats, and output detailled
;; info on a web page

;; Author: Colas Nahaboo, http://www.inria.fr/koala/colas, Dec 1997.

(setq args (getopts "USAGE: %0 [options] "
    ("-v" () verbose "verbose operation")
    ("-t" id trace-id "trace changes to this id")
    ("-d" dir other-generated-dir "where to generate files, see script source")
    ("-email" () only-list-emails "Only lists non-() emails")
    ("-debug" () enter-debugger-on-error "enter klone debugger on error")
    ("-stackdump" () stackdump-on-error "verbose stack dump on error")
))

;; main logic
(defun main (&aux (list-of-id-and-amounts (list)))
  ;; data is inputted via declare-* functions
  (setq *current-directory* generated-dir)
  ;; sort amount by debt
  (dohash (id amount accounts)
    (lappend list-of-id-and-amounts (list id amount)))
  (sort list-of-id-and-amounts (lambda (x y) (compare (1 x) (1 y))))
  ;; then generate data from in memory bases
  (generate-html list-of-id-and-amounts) ;complete html report
  (generate-text list-of-id-and-amounts) ;summary report in ascii
)

(if enter-debugger-on-error (kdb t))
(if stackdump-on-error (stack-dump-on-error t))

(defstruct Abbrev id name email)
(defstruct Log 
  type					;atom: F S C D + -
  id					;user or ()
  (amount 0)				;amount to balance
  value					;type-specific details
  (date curdate)			;date of transaction
)
(defstruct Stats id name cups cupsval funds supplies) ;subtotals per id
(defvar stats (list))			;list of Stats
(defun Log-name (l) (Abbrev-name (get abbrevs (Log-id l))))

(defvar abbrevs (list))			;p-list of id Abbrev
(defvar entries (list))			;log of all entries
(defvar abbrev-maxlen 0)		;max length of abbrevs
(defvar abbrev-maxnamelen 0)		;max length of full names
(defvar accounts (Hashtable ()))	; id balance
(if other-generated-dir (setq generated-dir other-generated-dir))
(defvar curdate "01/01/70")

(if trace-id
  (defun account+ (id amount) 
    (if (= (String trace-id) (String id))
      (PF "  == %0: %1 + %2 ==> %3\n" id (get accounts id 0) amount
	(+ amount (get accounts id 0))
    ))
    (put accounts id (+ amount (get accounts id 0)))
  )
  (defun account+ (id amount) (put accounts id (+ amount (get accounts id 0))))
)

;;=============================================================================
;;                    Input processing functions
;;=============================================================================
;; Quoting functions to process and build databases in memory from data
;; entered into this script (at the end)

(defunq declare-abbrevs (&rest l &aux 
    id
    (i 0)
    non-first
  )
  (while (setq id (getn l i))
    (lappend abbrevs id)
    (setq abbrev-maxlen (max abbrev-maxlen (length id)))
    (lappend abbrevs (make-Abbrev :id (get l i) 
	:name (setq name (get l (incf i)))
	:email (get l (incf i))
    ))
    (setq abbrev-maxnamelen (max abbrev-maxnamelen 
	(length (Abbrev-name (get abbrevs -1)))))
    (incf i)
  )
  (if only-list-emails (progn
      (dohash (id abbrev abbrevs)
	(if (Abbrev-email abbrev) (progn
	    (if non-first
	      (PF ",\n        ")
	      (setq non-first t)
	    )
	    (PF "%0"  (Abbrev-email abbrev))
      )))
      (PF "\n")
      (exit 0)
  ))
)

(defun log-get-id (l i)			;sets id as a side-effect
  (if (not (getn abbrevs (setq id (getn l i))))
    (fatal-error 1 "LOG: unknown ID %0\n" id)
))

(defunq declare-log (&rest l &aux 
    tag
    id
    n
    (i 0)
  )
  (setq cup-value-orig cup-value)
  (while (setq tag (getn l i)) (incf i) 
    (lappend entries
      (if 
	(= tag 'F) (progn		; Funds: F id n
	  (log-get-id l i)
	  (account+ id (setq n (Number (get l (incf i)))))
	  (add-stats id Stats-funds n)
	  (make-Log :type tag :id id :amount n)
	)
	(= tag 'S) (progn		; Supplies: S id n t
	  (log-get-id l i)
	  (account+ id (setq n (Number (get l (incf i)))))
	  (add-stats id Stats-supplies n)
	  (make-Log :type tag :id id :amount n :value (get l (incf i)))
	)
	(= tag 'C) (progn		; Cups: C id n
	  (log-get-id l i)
	  (account+ id (setq n (- (* cup-value (Number (get l (incf i)))))))
	  (add-stats id Stats-cups (get l i))
	  (add-stats id Stats-cupsval (* cup-value (Number (get l i))))
	  (make-Log :type tag :id id :amount n :value (get l i))
	)
	(= tag 'D) (progn		; Date: D date
	  (setq curdate (get l i))
	  (make-Log :type tag)
	)
	(= tag '+) (progn		; Misc Funds: + id n desc
	  (log-get-id l i)
	  (account+ id (setq n (Number (get l (incf i)))))
	  (make-Log :type tag :id id :amount n :value (get l (incf i)))
	)
	(= tag '-) (progn		; Misc Debts: - id n desc
	  (log-get-id l i)
	  (account+ id (- (setq n (Number (get l (incf i))))))
	  (make-Log :type tag :id id :amount n :value (get l (incf i)))
	)
	(= tag 'V) (progn		; sets cup Value: V amount
	  (make-Log :type tag :value (setq cup-value (check-int (get l i))))
	)
	t (fatal-error 1 "Unknown log tag: %0, aborting\n" tag)
    ))
    (incf i)
))

(defun add-stats (id slot value &aux 
    (s (catch 'Found (dolist (s stats)
	  (if (= (Stats-id s) id) (throw 'Found s)))
	()
    ))
  )
  (if (not s) (lappend stats (setq s 
	(make-Stats :id id :name (Abbrev-name (get abbrevs id))
	  :cups 0 :cupsval 0 :funds 0 :supplies 0
  ))))
  (slot s (+ value (slot s)))
)

;;=============================================================================
;;                    misc utils
;;=============================================================================

;; expand-real is included here for old klone versions

(setq expand-real:re (regcomp "^([0-9+-]+)[.]([0-9]*)$"))
(setq expand-real:re-zeros (regcomp "^0*$"))
(defun expand-real (x before after 
    &optional omit-zeros (decimal-point ".")
    &aux s
    (*real-precision* 1000))		;avoid scientific notation
  (if (regexec expand-real:re (setq s (PF String "%0" (Real x)))) 
    (with (ip (regsub expand-real:re 1) fp (regsub expand-real:re 2))
      (+
	(make-string (- before (length ip)))
	ip
	(if (and omit-zeros (regexec expand-real:re-zeros fp))
	""
	(+ decimal-point (if (> (length fp) after) (subseq fp 0 after) fp)
	  (make-string (- after (length fp)) #\0)
    ))))
    s
))

(setq comma:isfirst t)
(defun comma (&optional first)
  (if first (setq comma:isfirst t)
    (if (not comma:isfirst) ", " (progn (setq comma:isfirst ()) ""))
))

(defun incf-plist (l id n &aux prev)
  (setq prev (get l id 0))
  (put l id (+ prev n))
)
(defun html-mailto (email)
  (if (and email (/= "" email))
    (PF String "<a href=\"mailto:%0\">%0</a>" email) "")
)
(defun currency-representation (amount)
  (PF String currency-symbol amount)
)
(defun check-int (n) 
  (if (or (not (typep n Number)) (< n 0))
    (fatal-error 1 "Invalid cup value: %0, aborting\n" n) 
    n
))
;;=============================================================================
;;                    report generating
;;=============================================================================
;;======================================================================== HTML

(defun generate-html (l &aux fd (tc 0) (tcv 0) (bal 0) (ts 0) (tf 0) (th 0)
    (sd (list))
    (rev-entries (list)))
  ;; Header (copied from file)
  (sh cp ,header-html ,generated-html)
  (setq fd (open generated-html :direction :output))
  
  ;; Debitors
  (PF fd "<h1>Date:  %0</h1>\n<h1>People owing money  (need to pay!)</h1>\n<table>" curdate)
  (dolist (e l) (if (< (1 e) 0) 
      (PF fd "<tr><td><b>%0</b></td><td><b>%1</b></td><td> %2</td></tr>\n"
	(Abbrev-name (get abbrevs (0 e)))
	(expand-real (1 e) 4 2 t)
	(html-mailto (Abbrev-email (get abbrevs (0 e))))
  )))
  
  ;; Creditors
  (PF fd "</table>\n<hr><h1>People with positive accounts</h1>\n<table>")
  (dolist (e l) (if (> (1 e) 0) 
      (PF fd "<tr><td>%0</td><td>%1</td><td> %2</td></tr>\n"
	(Abbrev-name (get abbrevs (0 e)))
	(expand-real (1 e) 4 2 t)
	(html-mailto (Abbrev-email (get abbrevs (0 e))))
  )))
  
  ;; Acounts at 0
  (PF fd "</table>\n<hr><h1>People with empty accounts</h1>\n<p>")
  (comma t)
  (dolist (e l) (if (= (1 e) 0) 
      (PF fd "%1%0 %2" (Abbrev-name (get abbrevs (0 e))) (comma)
	(html-mailto (Abbrev-email (get abbrevs (0 e))))
  )))
  ;; Stats
  (PF fd "\n<hr>\n<h1>Stats</h1>\n")
  (setq cup-value cup-value-orig)
  (dolist (e entries) 
    (if (= 'C (Log-type e)) (progn 
	(incf tc (Log-value e))
	(incf tcv (* cup-value (Log-value e)))
    ))
    (if (= 'V (Log-type e)) (setq cup-value (Log-value e)))
    (if (and (= 'S (Log-type e)) (= 'h (Log-value e)))
      (incf th (Log-amount e))
    )
  )
  (PF fd "Total %1 %2: %0\n<br>" tc cup-noun drank-verb)
  (dohash (id amount accounts) (incf bal amount))
  (PF fd "Total money in pool: <b>%0</b>  (sum of user accounts current balance)\n<br>" bal)
  (dolist (e entries) (if (= 'S (Log-type e)) (progn
	(incf ts (Log-amount e))
	(incf-plist sd (Log-value e) (Log-amount e))
  )))
  (PF fd "Total cost of supplies (excluding hardware, which amounts to %1): %0, composed of:\n<ul>"
    (- ts th) th)
  (dohash (type amount sd)
    (if (/= 'h type)
      (PF fd "<li>%0: %1  (%2%3)\n" (get supplies-type type) amount 
	(Int (* 100 (/ amount (- ts th)))) "%")
  ))
  (PF fd "</ul>")
  (PF fd 
    "Mean cost of a cup: <b>%0</b>. With hardware included: <b>%1</b>\n<br>" 
    (expand-real (/ (Real (- ts th)) tc) 4 2)
    (expand-real (/ (Real ts) tc) 4 2))
  (dolist (e entries) (if (= 'F (Log-type e)) (incf tf (Log-amount e))))
  (PF fd "Total money brought by people in cash: %0\n<br>" tf)
  (PF fd "Profit: %0  (cup_values - supplies)\n<br>" (- tcv ts))
  
  ;; Per-user stats
  (PF fd "\n<hr>\n<h1>Per-user Stats</h1>\n<table borders=\"border\">")
  (PF fd "<tr><th>name</th><th>%0</th><th>funds</th><th>supplies</th></tr>\n"
    cup-noun)
  ;;sort stats per cups
  (sort stats (lambda (s1 s2) (compare (Stats-cups s2) (Stats-cups s1))))
  (dolist (s stats)
    (PF fd "<tr><td>%0</td><td align=\"right\">%1  <small> (%2)</small></td><td align=\"right\">%3</td><td align=\"right\">%4</td></tr>\n"
      (Stats-name s) (Stats-cups s) 
      (currency-representation (Stats-cupsval s))
      (Stats-funds s) (Stats-supplies s)
    )
  )
  (PF fd "</table>")
  
  ;; Detailed log
  (PF fd "\n<hr>\n<h1>Detailed Log</h1>\n")
  (dolist (e entries) (insert rev-entries 0 e))
  (setq last-printed-date "")
  (dolist (e rev-entries)
    (if (/= last-printed-date (Log-date e)) (progn
	(PF fd "\n<h3>%0</h3>\n" (Log-date e))
	(setq last-printed-date (Log-date e))
	(comma t)
    ))
    (if 
      (= 'F (Log-type e))
      (PF fd "%2%0 payed %1" (Log-name e) 
	(currency-representation (Log-amount e)) (comma) 
      )
      (= 'C (Log-type e))
      (PF fd "%2%0 %3 %1 %4" (Log-name e) (Log-value e) (comma)
	drank-verb cup-noun)
      (= 'S (Log-type e))
      (PF fd "%2%0 bought for %1 of %3" (Log-name e)
	(currency-representation (Log-amount e))
	(comma) (get supplies-type (Log-value e)) 
      )
      (= '+ (Log-type e))
      (PF fd "%2%0 payed %1 for %3" (Log-name e) 
	(currency-representation (Log-amount e)) (comma) 
	(Log-value e)
      )
      (= '- (Log-type e))
      (PF fd "%2%0 owes the community %1 for %3" (Log-name e) 
	(currency-representation (Log-amount e)) (comma) 
	(Log-value e)
      )
      (= 'V (Log-type e))
      (PF fd "%2Cup value set to %0" (currency-representation (Log-value e))
	() (comma)
      )
  ))
  
  ;; End
  (PF fd "</BODY></HTML>\n")
)

;;================================================================ ASCII report

(defun generate-text (l &aux
    (fd (open generated-text :direction :io :if-exists :supersede))
    (to-pay (list))
    last-id
  )
  (dolist (e l) (if (< (1 e) 0) (lappend to-pay (0 e)))) ;compute debitors
  (setq last-id (get to-pay -1))
  (PF fd "To:")
  (dolist (id to-pay) (PF fd "\t%0%1" (Abbrev-email (get abbrevs id))
      (if (not (eq last-id id)) ",\n" "\n")
  ))
  (PF fd "\n%0  -  %1\n\n" title curdate)
  (PF fd "PEOPLE NEEDING TO PAY / DOIVENT DE L'ARGENT:\n")
  (PF fd "============================================\n")
  (PF fd "  %0%1  %2\n\n" "NAME / NOM" 
    (make-string (- abbrev-maxnamelen (length "NAME / NOM")))
    (toupper currency-name)
  )
  (dolist (e l) (if (< (1 e) 0) 
      (PF fd "  %0%2 %1\n" (Abbrev-name (get abbrevs (0 e)))
	(expand-real (- (1 e)) 4 2 t)
	(make-string (- abbrev-maxnamelen (length 
	      (Abbrev-name (get abbrevs (0 e))))))
  )))
  (PF fd "\nDetails on %0\n\n" generated-url)
  
  ;; echo on stdout
  (close fd)
  (sh cat ,generated-text)
  ;; archive via RCS this script
  (sh  ci -l -q ,script-name < /dev/null)
)

;;############################################################################
;;########################   ENTER DATA HERE   ###############################
;;############################################################################

(declare-abbrevs
  ;; ID   FullName    Email_or_()
  koala "Koala Fund" ()			; caisse noire koala
  howcome "Hakon Lie" "Hakon.Lie@sophia.inria.fr"
  colas "Colas Nahaboo" "colas@sophia.inria.fr"
  ml "Marie-Line Ramfos" "Marie-Line.Ramfos@sophia.inria.fr"
  chris "Chris Lilley" "Chris.Lilley@sophia.inria.fr"
  bert "Bert Bos" "Bert.Bos@sophia.inria.fr"
  yves "Yves Lafon" "Yves.Lafon@sophia.inria.fr"
  phk "Philippe Kaplan" "Philippe.Kaplan@sophia.inria.fr"
  jml "Jean-Michel Léon" ()		; Jean-Michel.Leon@sophia.inria.fr
  abaird "Anselm Baird-Smith" ()	; abaird	
  w3c "Guests w3c" "Daniel.Dardailler@sophia.inria.fr"
  koala "guests koala" "colas@sophia.inria.fr"
  jmi "Janet Bertot" "Janet.Bertot@sophia.inria.fr"
  boy "Stéphane Boyera" "Stephane.Boyera@sophia.inria.fr"
  osofia "Olivier Sofia" ()		; osofia
  arnaud "Arnaud Le Hors" "Arnaud.Le_hors@sophia.inria.fr"
  benoit "Benoit Mahe" "Benoit.Mahe@sophia.inria.fr"
  roy "Roy Platton" ()
  jpo "Jean-Philippe Orsini" ()		; jporsini@sophia.inria.fr
  eka "Thierry Kormann" "Thierry.Kormann@sophia.inria.fr"
  plh "Philippe Le Hegaret" "plehegar@sophia.inria.fr"
  ian "Ian Jacobs" ()			; ijacobs		
  ext "Externals" ()
  janne "Janne Saarela" jsaarela
  isa "Isabelle Robert" irobert
  peto "Pierre Clerissi" pcleriss
  helene "Hélène Le Guen" hleguen
  josef "Josef Dietl" "Josef.Dietl@sophia.inria.fr"
)

(declare-log				; chronological order
  D "1 Jan 97"
  V 2.50 
  S koala 3000 h			; achat de la machine sur fonds koala
  D "1 Feb 97"
  C howcome 33 C colas 50 C chris 70 C bert 68 C yves 16 C phk 26 C jml 30
  C abaird 37 C w3c 8 C jmi 7 C boy 2 C ml 5 C osofia 10 C benoit 10 
  C arnaud 14 C roy 5
  S colas 475 c S chris 20 c S chris 12 m S chris 8 m
  D "1 Apr 97"
  F howcome 100 F bert 200 F yves 40 F phk 100 F jml 75
  F abaird 92.5 F jmi 50 F boy 10 F ml 20 F osofia 25 F benoit 25 F arnaud 35
  F roy 15
  D "1 May 97"
  C howcome 19 C colas 20 C chris 31 C bert 12 C yves 7 C phk 7 C abaird 8
  C jmi 8 C ml 12 C osofia 5 C benoit 3 C arnaud 14 C roy 27 C ian 17
  S colas 94 c S colas 32 w S chris 3 m S chris 5 s
  D "15 May 97"
  F howcome 100 F chris 300 F yves 20 F ml 30 F osofia 25 F roy 80 F ian 56
  D "1 Jun 97"
  C howcome 26 C colas 30 C chris 39 C bert 51 C yves 3 C phk 20 C abaird 34
  C jmi 1 C boy 2 C ml 9 C osofia 31 C benoit 15 C arnaud 44 C roy 6 C ian 11
  C jpo 21 C eka 29 C plh 47
  S colas 49 w S chris 13 s S bert 132 c S phk 18 w S arnaud 70 c S eka 6 w
  S plh 36 c
  D "1 Jul 97"
  F yves 5 F abaird 120 F w3c 20 F ml 20 F osofia 70 F benoit 63
  F arnaud 100 F ian 30 F jpo 52 F eka 100 F plh 100
  C osofia 2				;cloture de compte
  D "1 Aug 97"
  C howcome 7 C colas 13 C chris 9 C bert 13 C phk 4 C abaird 6 C ml 6 
  C benoit 1 C arnaud 9 C jpo 13 C eka 16 C plh 23
  S colas 107 c S colas 12 w S ml 40 c S arnaud 40 c
  D "11 Sep 97"
  F chris 11.5 F phk 50 F jpo 35 F plh 40 F ext 20
  D "1 Oct 97"
  C ext 8
  D "1 Dec 97"				;feuille "Nov 97"
  C colas 50 C bert 30 C arnaud 28 C benoit 20 C ml 28 C phk 12 C janne 28
  C chris 31 C isa 14 C yves 14 C jpo 9 C jmi 4 C boy 1 C plh 9 C eka 1
  S colas 130 w S colas 190 c S janne 11.4 w S chris 3 m
  ;; start of daily use
  D "12 Dec 97" F jpo 50
  D "15 Dec 97" F bert 200
  D "17 Dec 97" F ml 40 F boy 5
  D "19 Dec 97" F janne 60
  D "5 Jan 98" F plh 50
  D "12 Jan 98" F yves 36
  D "15 Jan 98"
  C bert 22 C colas 33 C janne 12 C plh 33 C arnaud 26 C ml 21 C phk 11
  C chris 17 C yves 4 C benoit 14 C jpo 32 C eka 5 C boy 3 C howcome 2 C isa 2
  C ian 5
  S plh 57.3 c S plh 18.9 w S phk 18.9 w S ian 38 c
  F arnaud 120 F yves 10 F benoit 70 F phk 20 F jpo 51 F eka 50 
  D "16 Jan 98" F isa 50
  D "9 Feb 98" F howcome 20
  D "10 Feb 98"
  C plh 24 C jpo 31 C benoit 13 C arnaud 11 C ml 15 C colas 14 C yves 5 
  C bert 16 C phk 5 C jmi 1 C chris 9 C isa 5 C eka 10 C janne 4 C boy 1 
  C howcome 2
  F benoit 100 F ml 150 F plh 40 F yves 12
  D "13 Feb 98" F boy 20
  D "11 Mar 98" S yves 114.6 c S yves 37.8 w
  D "17 Mar 98"
  C bert 30 C colas 35 C plh 31 C jpo 19 C chris 10 C benoit 23 C phk 12
  C eka 14 C howcome 2 C ml 30 C janne 9 C isa 8 C arnaud 20 C boy 1 C yves 16
  S phk 18.9 w
  F jpo 130 F phk 50 F ml 100
  D "30 Mar 98"
  F arnaud 100 F eka 50 F bert 100
  D "3 Apr 98" F jmi 10 F janne 61.10
  D "15 Apr 98" - chris 86.09 "custom taxes for parcel arrived at INRIA"
  D "25 Apr 98" V 1.5
  D "29 Apr 98" 
  C ml 39 C colas 30 C eka 20 C plh 49 C arnaud 15 C benoit 27 C isa 17
  C bert 31 C phk 11 C yves 6 C peto 30 C janne 9 C helene 15 C jmi 1 C jpo 20
  F jpo 24.5 ;; offerts par colas
  S colas 75.6 w S colas 114.6 c S phk 76.4 c S peto 18.9 c
  D "30 Apr 98"
  F eka 50
  F koala -3000
  D "6 May 98" F plh 200
  D "7 May 98" F isa 100
  D "15 May 98" S colas 94.5 w S colas 191 c F colas -700
  D "20 May 98" F howcome 100
  D "28 May 98"
  C plh 28 C helene 25 C colas 18 C ml 17 C benoit 11 C yves 5 C bert 22 
  C peto 18 C isa 4 C phk 5 C eka 4 C janne 10 C chris 4 C arnaud 1 C howcome 7
  S plh 18 w
  D "23 Jun 98" F peto 100
  D "25 Jun 98" F helene 150
  D "30 Jun 98"
  S bert 18.90 w S colas 94.5 w S colas 191 c
  C eka 9 C phk 11 C ml 23 C helene 30 C peto 20 C benoit 17 C plh 51
  C josef 3 C isa 8 C janne 10 C yves 6 C chris 5 C bert 5 C arnaud 5 
  C colas 13 C howcome 2
  D "1 Jul 98" 
  F josef 5 F bert 50 F janne 50
)

;; F id n   = funds deposited
;; S id n t = supplies bought (t: coffee, water, sugar, milk, cUps, ?)
;; C id n   = number of drank cups
;; D date   = freeform string

(main)

;;; EMACS MODES
;;; Local Variables: ***
;;; mode:lisp ***
;;; End: ***
