Page Numbers: Yes First Page: 1
Heading:
[IVY]<KRL>document>ex-accounting
Status: first pass
An accounting program in KRL-1
This program is intended as a general purpose accounting program for a joint household. It has three basic parts -- data entry, balancing, and information retrieval. It attempts to deal with the kinds of complexities and vagueness which are typical (we have some actual data from Mitch we might try using).
The data entry is organized around the notion of "form filling" in which three things are primary:
The units form guides for displaying a form with a series of fields up on the screen and giving the user a chance to fill in values. A new entry is created by giving a description (in KRL) with any desired amount of detail, and the system then puts up a form to fill in the rest.
There are defaults which are put up on the initial form and used if not changed
There can be multiple layers of definition of specialized expenditures (e.g. rent) defined in terms of each other, and on a given input, the user sees a form at the right level of detail.
The accounting is organized around standard double entry bookkeeping, viewing each transaction as being paid for by some party to the benefit of some party. Parties can be either individuals or proportional groups, in which cost (or benefit) is shared according to arbitrary proportions between a set of individuals or groups. Ledgers are organized by months, and a month’s ledger can be balanced to determine who owes or is owed how much. It is assumed that some of the transactions reported to the system will be the payments based on this calculation. Balances get carried forward from month to month.
The information retrieval component is based on the use of multiple category hierarchies to provide orthogonal partitions (e.g. type of expense, place spent, etc.) which can be used either to provide tabular output or answer specific questions. The matcher is used to decide whether an expense fits a given description, and arbitrary descriptions can be built up in terms of other ones, and given as arguments for retrieval.
The units
# Expenditure↑11: WhenAllFilled(LISP ’(EnterInLedger (This instance)))
self:
↑22: WhenIdentified(LISP ’(FillForm (This instance) ’Expenditure))
WhenIdentified(LISP ’(Classify (This instance) ’ExpenseType))
amount: A DollarAmount

payer:
A Person
Default(LispValue ’(GetPersonFromLoginName))

paymentDate:
A Day
Default(LispValue ’(GetTodaysDate))

paymentMethod:
A FinancialInstrument
Default(A CashPayment)

expenseDate:
A TimePeriod
Default(My paymentDate)

benefitted:
An AccountingEntity
Default(My payer)

provider:
An AccountingEntity
Default(My payer)

remarks:
A String
# # # # # # # # # # # # # # #
# DollarAmount
self:
A Number
dollars: An Integer
cents: An Integer; AtMost(99)
# Day
self:
A TimePeriod
number: An Integer
month: A Month
# Month
self: A TimePeriod
name: A MonthName
year: An Integer; Default(1977)
# TimePeriod
# # # # # # # # # # # # # # #
# AccountingEntity
self: Or(A Person, A ProportionalGroup)
# AccountingPeriod
self: A TimePeriod
# Column
ledgerPage: A LedgerPage
account: A Person
balance:↑1 A Number; InitialValue(0.0)1:A ChangingValue
# Ledger
participants:
SetOf(A Person)
pages: SequenceOf(A LedgerPage)
startingMonth: A Month
# LedgerPage↑11:WhenIdentified(CreateSet(’columns))
CreateAndStoreGiven(’month)
Comment ("This should be compacted, with entries and columns being
compacted into sequences of co-reference handles")
ledger:
A Ledger
month: A Month
entries: SequenceOf(An Expenditure with paymentDate = A Day with
month = My month)
columns: SetOf(A Column)
MapSet(The participants from My ledger,
<’person, ’column>,
[#& person: A Person
column: A Column with
ledgerPage = My self
account = My person]
# Proportion↑11:HasFunctional (self, Share, shareHolder, proportion)
group:
A ProportionalGroup
shareHolder: An AccountingGroup
proportion: A Number; GreaterThan(0.0); AtMost(1.0)
# ProportionalGroup
self:
An AccountingEntity
shares: SetOf(A Proportion with group = My self)
# # # # # # # # # # # # # # #
# RentPayment↑11: WhenIdentified(LISP ’(FillForm (This instance) ’RentPayment)))
self:
An Expenditure with
expense = A HousingExpense
benefitted = Household
amount = A DollarAmount with dollars = 250
cents = 0
paymentMethod= Default(A Check with account = JointAccount)
expenseDate = My month
provider = JointAccountHolders
month: A Month with year = Default(1977)
Default(LispValue ’(GetCurrentMonth))
# Housing↑11: Category(ExpenseType)
self:
An Expense; A Necessity
# Food↑11: Category(ExpenseType)
self:
An Expense
# ExpenseType
self:
A CategoryDimension with tree =
||Expense =
Housing
Food =
Groceries = Produce, PreparedFoods, Meat, Dairy,
Juices, Alcohol, OtherFood
Restuarant
Entertainment = Movies, Concerts, MiscEntertainment
Household = CleaningGoods, Insurance, MiscHousehold
Transportation =
Car = CarRepairs, CarInsurance, Gas
PublicTransit = Plane, Bus, Train
BooksAndRecords = SchoolBooks, FunBooks, Magazines, Records
MiscExpense||
# NeedCategory
self:
A CategoryDimension with tree = <Expense = Luxury, Amenity, Necessity>
# # # # # # # # # # # # # # #
# Participants1
self: {Adam, Eve, Lilith, Harvey}
# Household
self: A ProportionalGroup with
shares = {Share(Adam, .35), Share(Eve, .35), Share(Lilith, .30)}
# JointAccountHolders
self: A ProportionalGroup with
shares = {Share(Adam, .5), Share(Eve, .5)}
# HouseLedger
self:
A Ledger with
participants = Participants1
startingMonth = A Month with name = ’July
year = 1977
# # # # # # # # # # # # # # #

DEFINEQ((Balance(LedgerPage)
(PROG((columns ($ \GetAnchorList(The columns from
a LedgerPage thatIs !R LedgerPage)))
(lastPage ($ \SeekAnchor(Predecessor(!R LedgerPage,
The ledger from
a LedgerPage thatIs !R LedgerPage))))
(entries ($ \GetAnchorList(The entries from
a LedgerPage thatIs !R LedgerPage))))

(for column in columns bind oldBalance
do (oldBalance ←
(if lastPage
then ($ \GetAnchor(The balance from
A Column with
account = The account from a Column
thatIs !R column
thatIs MemberOf(The columns from a LedgerPage
thatIs !R lastPage))/)
else 0.0))
($ \Describe(!H column, \A Column with balance = !L oldBalance)/)

(for entry in entries
do (CalculateExpenditure entry LedgerPage))]
# # # # # # # # # # # # # # #
DEFINEQ((CalculateExpenditure(entry ledgerPage)
(PROG(oldBalance
(benefitted ($ \GetAnchor(The benefitted from an Expenditure
thatIs !R entry)/))
(provider ($ \GetAnchor(The provider from an Expenditure
thatIs !R entry)/))
(amount ($ \GetAnchor(The amount from an Expenditure
thatIs !R entry)/))

(for share in (GetPersonalShares benefitted 1.0)
do (Debit ($ \GetAnchor(The shareHolder from
a Proportion thatIs !R share)/)
(TIMES amount ($ \GetAnchor
(The proportion from
a Proportion thatIs !R share)/))))

(for share in (GetPersonalShares provider 1.0)
do (Credit ($ \GetAnchor(The shareHolder from
a Proportion thatIs !R share)/)
(TIMES amount ($ \GetAnchor
(The proportion from
a Proportion thatIs !R share)/)]
# # # # # # # # # # # # # # #
DEFINEQ((DivvyUp(LedgerPage)
(PROG((columns ($ \GetAnchorSequence(The columns from
a LedgerPage thatIs !R LedgerPage)/)))
(for column in columns do (printBalance column))
(* something fancy here to decide who should pay whom how much -- to be
worked out later)]
# # # # # # # # # # # # # # #
DEFINEQ((EnterInLedger(expenditure)
(PROG((ledgerPage
($ \GetIdentity(A LedgerPage with
month = The month from a Date thatIs
the PaymentDate from
An Expenditure thatIs !R expenditure))/))
(* Kdeclare "
expenditure: A SelfAnchor; Which Describes An Expenditure
ledgerPage: A SelfAnchor; Which Describes A LedgerPage")

($ \AddToEndOfSequence(The entries from a LedgerPage thatIs !R ledgerPage,
!R expenditure)/]
# # # # # # # # # # # # # # #
DEFINEQ((FillForm(instance unitName)
(PROG((protoUnit (UnitFor unitName)) changes names identities)
(* Kdeclare "
instance: A SelfAnchor; HasDescriptor(A Unit-Perspective with Prototype = My protoUnit)
unitName: An Atom; The unitName from A Unit
protoUnit: The metaAnchor from A Unit with name = My unitName")

(InitializeDisplayForm unitName)
(* I assume this will be done by someone else)

(for slotName in (SlotList protoUnit) bind identity
do
identity ← ($ \SeekIdentityUsingDefaults(The !N slotName from
a !N unitName thatIs !R instance)/))

(DisplayEntry slotName (ProduceStringFor identity) <it was a default>)
(* DisplayEntry takes a name, string, and flag which it uses to indicate
to user that entry is a default (e.g. different backround on displays)))


(changes ← (LetUserChangeEntries))
(* Kdeclare "
changes: ListOf(A List; Which IsSequence <MemberOf(The SlotNames from a Unit
thatIs My protoUnit),
A String>)")
(* I am assuming Bill or Mitch will do LetUserChangeEntries.)

(names ← (for pair in changes collect pair:1))
(identities ← (for pair in changes
collect (GetIdentityFromString pair:2
(GetProtoHandle unitName pair:1))))
($ \Describe(!H instance, A !N unitName with !!!Rnames = identities)/]
# # # # # # # # # # # # # # #
DEFINEQ((GenerateExpenditures(description ledgerPage)
(* this is a generator of anchors for the set of expenditures on the ledgerPage fitting the description
it includes breaking down complex expenditures and finding the appropriate subparts)
(* Kdeclare "
description: An Anchor; Which Describes An Expenditure
ledgerPage: A SelfAnchor; Which Describes A LedgerPage

(for expenditure in ($ \GetAnchorList(The entries from a LedgerPage
thatIs !R ledgerPage)/))
bind matchResult
do (if (matchResult ← ($ \QuickKnownMatch(!H expenditure, !H description)/))
then (if matchResult = ’SUCCEED then (Produce expenditure))
else matchResult ← ($ \A Match with
datum = !H expenditure
pattern = !H description
valueReturned = ???residue???
productionSet = ???/)
(* the idea here is that there are two cases: If the match fails for lack of specification (e.g. the pattern says
"groceries" and the entry says"food", then put it on a maybe list. If the match fails because a
proportional group is being matched against an individual or other group, then find the right
proportions.)
]
# # # # # # # # # # # # # # #
DEFINEQ((GetCurrentMonth()
(HELP "Not written yet")
(* this needs to return a handle for a unit for the month)]
# # # # # # # # # # # # # # #
DEFINEQ((GetObjectFromString(string prototype)
(PROG((type (GetTypeNameFromPrototypeHandle prototype)))
(if type
then (SELECTQ type
(’Date (MakeDateFromString string))
(’DollarAmount (MakeDollarsFromString string))
(’Month (MakeMonthFromString string))
(’Person (GetPerson string))
..... (should this be factored better??]
# # # # # # # # # # # # # # #
DEFINEQ((GetPersonalShares(accountingGroup factor)
(if ($ \Match(!H accountingGroup, \A Person)/)
then <($ \MakeUnit(A Proportion with
shareHolder = !R accountingGroup
proportion = !L factor)/)>
else (for share in ($ \GetAnchors(The shares from a ProportionalGroup
thatIs !R accountingGroup)/)
collect (GetPersonalShares
($ \GetAnchor(The shareHolder from a Proportion
thatIs !R share)/)
(TIMES factor ($ \GetPointer(The proportion from a Proportion
thatIs !R share)/)]
# # # # # # # # # # # # # # #
DEFINEQ((GetPersonFromLoginName()
(HELP "Not written yet")
(* this needs to return a handle for a unit for the person)]
# # # # # # # # # # # # # # #
DEFINEQ((GetTodaysDate()
(HELP "Not written yet")
(* this needs to return a handle for a unit for the date)]
# # # # # # # # # # # # # # #
DEFINEQ((Summarize(LedgerPage)
(PROG((entries ($ \GetAnchorSequence(The entries from
a LedgerPage thatIs !R LedgerPage)/)))

(* this needs to print out a standarized summary giving categories, etc.
should it have a style sheet or something like that?)]
# # # # # # # # # # # # # # #
Notes: Haven’t yet dealt with splitting things apart and keeping the initial entry form -- e.g. if you enter something as food with x% produce and y% alcohol.
Work history
Start: July 6, 1977 10:02 AMStop: July 6, 1977 11:14 AM
Start: July 6, 1977 3:05 PMStop: July 6, 1977 4:10 PM
Start: July 6, 1977 6:30 PMStop: July 6, 1977 8:06 PM
Start: July 15, 1977 2:45 PMStop: July 15, 1977 3:48 PM
Start: July 17, 1977 10:23 AMStop: July 17, 1977 11:03 AM
Start: July 17, 1977 11:31 AMStop: July 17, 1977 1:34 PM