-- LedgerOutput.mesa Edited by Sweet March 27, 1981 12:07 PM
DIRECTORY
InlineDefs: FROM "inlinedefs",
IODefs: FROM "iodefs",
LedgerDefs: FROM "ledgerdefs",
OutputDefs: FROM "outputdefs",
PressDefs,
StringDefs: FROM "stringdefs",
SystemDefs: FROM "systemdefs";
LedgerOutput: PROGRAM
IMPORTS InlineDefs, IODefs, LedgerDefs, OutputDefs, PressDefs, StringDefs, SystemDefs
EXPORTS LedgerDefs =
BEGIN OPEN PressDefs, LedgerDefs;
pageGroup: PUBLIC PageGroup ← both;
leftShift: Mica ← 380;
rightShift: Mica ← 610;
bravoLedger: PUBLIC BOOLEAN ← FALSE;
ARJOut: PUBLIC PROCEDURE [s: STRING, l: CARDINAL] =
BEGIN OPEN OutputDefs;
THROUGH [s.length..l) DO
PutChar[IODefs.SP];
ENDLOOP;
PutString[s];
END;
ALJOut: PUBLIC PROCEDURE [s: STRING, l: CARDINAL] =
BEGIN OPEN OutputDefs;
PutString[s];
THROUGH [s.length..l) DO
PutChar[IODefs.SP];
ENDLOOP;
END;
ADateOut: PUBLIC PROCEDURE [date: SmallDate, checkNum: CARDINAL] =
BEGIN OPEN OutputDefs;
PutDecimal[date.month];
PutChar['/];
PutDecimal[date.day];
PutChar['/];
PutDecimal[date.year];
PutChar[IODefs.TAB];
IF checkNum = 0 THEN PutChar['-] ELSE PutDecimal[checkNum];
END;
LedgerOut: PUBLIC PROCEDURE [p: POINTER, len: CARDINAL] =
BEGIN
ENABLE BadData =>
BEGIN OPEN IODefs;
lip: POINTER TO SortItem = p;
WriteString[" Aborting item "];
WriteDecimal[lip.date.month]; WriteChar['/];
WriteDecimal[lip.date.day]; WriteChar['/];
WriteDecimal[lip.date.year]; WriteChar[SP];
WriteDecimal[lip.check]; WriteChar[CR];
GO TO gunIt;
END;
sip: POINTER TO SortItem = p;
adesc: StringDefs.SubStringDescriptor ← [base: @sip.string, offset: 0, length: 1];
ss: StringDefs.SubString = @adesc;
NoteTotal: PROCEDURE [payee: STRING, val: Money] =
BEGIN
NewItem[sip.date, sip.check, payee, val];
END;
IF ~bravoLedger THEN
SetCurrentFont[Hv8, medium, regular]; -- to compute nw in Place
BreakApart[ss, NoteTotal, Place];
EXITS gunIt => NULL;
END;
BreakApart: PUBLIC PROCEDURE [
ss: StringDefs.SubString,
total: PROCEDURE [STRING, Money],
detail: PROCEDURE [Column, Money, STRING, BOOLEAN]] =
BEGIN
payee, note: STRING;
col: Column;
val: Money;
ded: BOOLEAN;
princ: BOOLEAN ← TRUE;
pStuff: RECORD [val: Money, ded: BOOLEAN, col: Column, note: STRING];
payee ← GetUpTo[ss, IODefs.TAB];
[val, ded] ← GetMoney[ss];
total[payee, val];
DO
[col, note] ← GetCategory[ss];
IF totalInfo[col].isTotal THEN
WarnSS["Distribution to a total column", ss];
IF princ THEN
BEGIN
pStuff ← [val, ded, col, note];
princ ← FALSE;
END
ELSE
BEGIN
detail[col, val, note, ded];
pStuff.val ← pStuff.val - val;
END;
SkipBlanks[ss];
IF Exhausted[ss] THEN EXIT;
IF CurrentChar[ss] # '/ THEN WarnSS["Expected /",ss];
Bump[ss];
[val, ded] ← GetMoney[ss];
ENDLOOP;
detail[pStuff.col, pStuff.val, pStuff.note, pStuff.ded];
END;
row: ARRAY [1..LAST[Row]] OF ARRAY Column OF ColumnEntry ←
ALL[ALL[[free[]]]];
head: ARRAY [1..LAST[Row]] OF RowHead;
monthTotal: ARRAY Column OF Money ← ALL[0];
ytdTotal: PUBLIC ARRAY Column OF Money ← ALL[0];
negMonthTotal, posMonthTotal: Money ← 0;
negYtdTotal, posYtdTotal: Money ← 0;
firstCurrentRow: Row ← 0;
maxCurrentRow: Row ← 0;
Place: PUBLIC PROCEDURE [col: Column, amt: Money, note: STRING,
ded: BOOLEAN] =
BEGIN
nw: Mica ← IF note = NIL THEN 0
ELSE IF bravoLedger THEN 1 ELSE GetWidthOfString[note];
nc: CARDINAL = HowMany[of: CategoryColWidth, in: nw];
r: Row;
noteCol, c: Column;
flush: Side;
roomLeft: BOOLEAN = col IN [nc..BreakColumn) OR col >= BreakColumn+nc;
roomRight: BOOLEAN = (col+nc) < BreakColumn OR
(col >= BreakColumn AND col+nc <= LAST[Column]);
-- find room for amt and note
FOR r ← firstCurrentRow, r+1 DO
IF r > LastCheckRow THEN
BEGIN
rr: Row;
FinishPage[FALSE]; -- sets row[1..firstCurrentRow) to ALL free
FOR rr IN [firstCurrentRow..LAST[Row]] DO
row[1+rr-firstCurrentRow] ← row[rr];
row[rr] ← ALL[[free[]]];
ENDLOOP;
r ← r - firstCurrentRow + 1;
head[1] ← head[firstCurrentRow];
maxCurrentRow ← maxCurrentRow + 1 - firstCurrentRow;
firstCurrentRow ← 1;
END;
IF row[r][col].tag # free THEN LOOP;
IF nw = 0 THEN EXIT;
IF roomRight THEN FOR c IN (col..col+nc] DO
IF row[r][c].tag # free THEN EXIT;
REPEAT
FINISHED => BEGIN noteCol ← col+1; flush ← left; GO TO found; END;
ENDLOOP;
IF roomLeft THEN FOR c IN [col-nc..col) DO
IF row[r][c].tag # free THEN EXIT;
REPEAT
FINISHED => BEGIN noteCol ← col-nc; flush ← right; GO TO found; END;
ENDLOOP;
REPEAT
found => NULL;
ENDLOOP;
-- amount goes at r,col
-- note goes at r,noteCol
maxCurrentRow ← MAX[maxCurrentRow, r];
row[r][col] ← [money[ded, amt]];
IF nw # 0 THEN
BEGIN
row[r][noteCol] ← [note[note, flush, nc]];
FOR c IN (noteCol..noteCol+nc) DO row[r][c] ← [taken[]]; ENDLOOP;
END;
END;
lastItemDate: SmallDate ← NullDate;
NewItem: PUBLIC PROCEDURE [date: SmallDate, check: CARDINAL, payee: STRING, amt: Money] =
BEGIN
r: Row;
IF lastItemDate = NullDate THEN IODefs.WriteString["Writing..."];
FOR r IN (firstCurrentRow..maxCurrentRow] DO
head[r] ← [taken[]];
ENDLOOP;
firstCurrentRow ← maxCurrentRow+1;
IF date = NullDate THEN
BEGIN
FinishPage[TRUE];
firstCurrentRow ← maxCurrentRow ← 0;
lastItemDate ← date;
RETURN
END;
IF lastItemDate # NullDate AND lastItemDate.month # date.month THEN
BEGIN FinishPage[TRUE]; firstCurrentRow ← 1; END;
IF firstCurrentRow > LAST[Row] THEN
BEGIN FinishPage[FALSE]; firstCurrentRow ← 1 END;
maxCurrentRow ← firstCurrentRow;
head[firstCurrentRow] ← [first[
checkNum: check,
date: date,
payee: payee,
amount: amt]];
lastItemDate ← date;
END;
MoneyToString: PUBLIC PROCEDURE [s: STRING, v: Money] =
BEGIN OPEN StringDefs;
j: CARDINAL;
neg: BOOLEAN ← v<0;
s.length ← 0;
IF v = 0 THEN
BEGIN
AppendChar[s,IF bravoLedger THEN '- ELSE IODefs.ControlS];
RETURN
END;
IF neg THEN BEGIN v ← -v; AppendChar[s, '<]; END;
AppendLongDecimal[s, v/100];
AppendChar[s, '.];
j ← InlineDefs.LowHalf[v MOD 100];
AppendChar[s, '0 + j/10];
AppendChar[s, '0 + j MOD 10];
IF neg THEN AppendChar[s, '>];
END;
FinishPage: PUBLIC PROCEDURE [totals: BOOLEAN] =
BEGIN OPEN StringDefs;
str: STRING ← [50];
r: Row;
rY: Mica;
c: Column;
DateAndNumber: PROCEDURE [date: SmallDate, check: CARDINAL] =
BEGIN
SetCurrentFont[Hv10, medium, regular];
str.length ← 0;
AppendDecimal[str, date.day];
RJString[x: DateColPos,
y: rY,
s: str,
width: DateColWidth];
str.length ← 0;
IF check = 0 THEN AppendChar[str, IODefs.ControlS]
ELSE AppendDecimal[str, check];
RJString[x: CheckColPos,
y: rY,
s: str,
width: CheckColWidth];
END;
OutCat: PROCEDURE [cl: Column] RETURNS [width: Mica] =
BEGIN
WITH row[r][cl] SELECT FROM
note =>
BEGIN
SetCurrentFont[Hv8, medium, regular];
IF flush = right THEN
BEGIN
RJString[x: CategoryX[cl],
y: rY,
s: s,
width: (columns+1)*CategoryColWidth - 3*Dot -
OutCat[cl+columns]];
END
ELSE
LJString[x: CategoryX[cl],
y: rY,
s: s];
width ← 0;
SystemDefs.FreeHeapString[s];
END;
money =>
BEGIN
av: Money = IF categoryNeg[cl] THEN -v ELSE v;
SetCurrentFont[Hv8, medium,
(IF deductable THEN italic ELSE regular)];
MoneyToString[str, av];
width ← GetWidthOfString[str];
RJString[x: CategoryX[cl],
y: rY,
s: str,
width: CategoryColWidth];
monthTotal[cl] ← monthTotal[cl] + v;
END;
ENDCASE;
row[r][cl] ← [free[]];
END;
FreeCat: PROCEDURE [cl: Column] =
BEGIN
WITH row[r][cl] SELECT FROM
note => SystemDefs.FreeHeapString[s];
money => monthTotal[cl] ← monthTotal[cl] + v;
ENDCASE;
row[r][cl] ← [free[]];
END;
IF bravoLedger THEN
BEGIN
BravoFinishPage[totals];
RETURN;
END;
IF totals THEN
BEGIN
IF firstCurrentRow +(IF budgetGiven THEN TotalRows ELSE 2) > LAST[Row]+1 THEN
BEGIN
FinishPage[FALSE];
firstCurrentRow ← maxCurrentRow ← 1;
END;
LastCheckRow ← LAST[Row]-(IF budgetGiven THEN TotalRows ELSE 2)
END;
BEGIN -- to set up noLeft label
IF pageGroup = right THEN
BEGIN
FOR r IN [1..firstCurrentRow) DO
FOR c IN [0..BreakColumn) DO FreeCat[c] ENDLOOP;
ENDLOOP;
IF totals THEN FOR c IN [0..BreakColumn) DO
FakeTotal[c];
ENDLOOP;
GO TO noLeft;
END;
bindingMargin ← -leftShift;
CreateForm[1, lastItemDate, totals];
FOR r IN [1..firstCurrentRow) DO
rY ← RowY[r];
WITH head[r] SELECT FROM
first =>
BEGIN
DateAndNumber[date, checkNum];
LJString[x: PayeeColPos,
y: rY,
s: payee];
MoneyToString[str, ABS[amount]];
IF amount < 0 THEN
BEGIN
negMonthTotal ← negMonthTotal - amount;
RJString[x: IncomeColPos,
y: rY,
s: str,
width: AmountColWidth];
END
ELSE
BEGIN
posMonthTotal ← posMonthTotal + amount;
RJString[x: ExpenseColPos,
y: rY,
s: str,
width: AmountColWidth];
END;
SystemDefs.FreeHeapString[payee];
END;
ENDCASE;
FOR c IN [0..BreakColumn) DO [] ← OutCat[c] ENDLOOP;
ENDLOOP;
IF totals THEN
BEGIN
SetCurrentFont[Hv10, medium, regular];
TotalOut[x: IncomeColPos, width: AmountColWidth, i: 1,
val: negMonthTotal];
TotalOut[x: ExpenseColPos, width: AmountColWidth, i: 1,
val: posMonthTotal];
TotalOut[x: NetColPos, width: NetAmountColWidth, i: 1,
val: negMonthTotal - posMonthTotal];
TotalOut[x: IncomeColPos, width: AmountColWidth,
i: IF budgetGiven THEN 4 ELSE 2,
val: negYtdTotal ← negYtdTotal + negMonthTotal];
TotalOut[x: ExpenseColPos, width: AmountColWidth,
i: IF budgetGiven THEN 4 ELSE 2,
val: posYtdTotal ← posYtdTotal + posMonthTotal];
TotalOut[x: NetColPos, width: NetAmountColWidth,
i: IF budgetGiven THEN 4 ELSE 2,
val: negYtdTotal - posYtdTotal];
negMonthTotal ← posMonthTotal ← 0;
SetCurrentFont[Hv8, medium, regular];
FOR c IN [0..BreakColumn) DO
DoTotal[c];
ENDLOOP;
END;
PressDefs.WritePage[pfd];
EXITS
noLeft => NULL;
END;
BEGIN -- to set up noRight label
IF pageGroup = left THEN
BEGIN
FOR r IN [1..firstCurrentRow) DO
FOR c IN [BreakColumn..LAST[Column]] DO FreeCat[c] ENDLOOP;
ENDLOOP;
IF totals THEN FOR c IN [BreakColumn..LAST[Column]] DO
FakeTotal[c];
ENDLOOP;
GO TO noRight;
END;
bindingMargin ← rightShift;
CreateForm[2, lastItemDate, totals];
FOR r IN [1..firstCurrentRow) DO
rY ← RowY[r];
FOR c IN [BreakColumn..LAST[Column]] DO [] ← OutCat[c] ENDLOOP;
ENDLOOP;
IF totals THEN
BEGIN
SetCurrentFont[Hv8, medium, regular];
FOR c IN [BreakColumn..LAST[Column]] DO
DoTotal[c];
ENDLOOP;
END;
PressDefs.WritePage[pfd];
EXITS
noRight => NULL;
END;
IF totals THEN monthTotal ← ALL[0];
LastCheckRow ← LAST[Row];
END;
-- l12256d2998(0,4608)(1,5664)(2,10272)(3,12256)
--
ATotCols: CARDINAL = 12;
ADetailCols: CARDINAL = 10;
BravoFinishPage: PROCEDURE [totals: BOOLEAN] =
BEGIN OPEN OutputDefs;
str: STRING ← [50];
r: Row;
c: Column;
itemAmount: Money;
firstCat: BOOLEAN ← TRUE;
activeItem: BOOLEAN ← FALSE;
OutCat: PROCEDURE [cl: Column] =
BEGIN
WITH row[r][cl] SELECT FROM
note =>
BEGIN
IF flush = right THEN OutCat[cl+columns];
PutString[" ("];
PutString[s];
PutChar[')];
SystemDefs.FreeHeapString[s];
END;
money =>
BEGIN
av: Money = IF categoryNeg[cl] THEN -v ELSE v;
IF v # itemAmount THEN
BEGIN
IF firstCat THEN
BEGIN PutChar[IODefs.TAB]; firstCat ← FALSE; END
ELSE PutChar[IODefs.CR];
MoneyToString[str, av];
ARJOut[str, ADetailCols];
END;
PutChar[IODefs.SP]; PutString[categoryKey[cl]];
monthTotal[cl] ← monthTotal[cl] + v;
END;
ENDCASE;
row[r][cl] ← [free[]];
END;
BravoFinishLine: PROCEDURE =
BEGIN
IF ~activeItem THEN RETURN;
PutChar[IODefs.ControlZ];
PutString["l12256d2998"L];
IF firstOut THEN
BEGIN
PutString["(0,4608)(1,5664)(2,10272)(3,12256)"L];
firstOut ← FALSE;
END;
activeItem ← FALSE;
PutCR[];
END;
FOR r IN [1..firstCurrentRow) DO
WITH head[r] SELECT FROM
first =>
BEGIN
BravoFinishLine[];
ADateOut[date, checkNum];
PutChar[IODefs.TAB];
PutString[payee];
PutChar[IODefs.TAB];
itemAmount ← amount;
firstCat ← TRUE;
MoneyToString[str, amount];
ARJOut[str, ADetailCols];
IF amount < 0 THEN
negMonthTotal ← negMonthTotal - amount
ELSE posMonthTotal ← posMonthTotal + amount;
SystemDefs.FreeHeapString[payee];
activeItem ← TRUE;
END;
ENDCASE;
FOR c IN Column DO OutCat[c]; ENDLOOP;
ENDLOOP;
BravoFinishLine[];
IF totals THEN
BEGIN
PutCR[]; PutCR[]; PutString["TOTALS"];
PutCR[];
ALJOut[MonthName[lastItemDate.month], 10];
PutString[" deposits: "];
MoneyToString[str, negMonthTotal];
ARJOut[str, ATotCols];
PutCR[];
ALJOut[MonthName[lastItemDate.month], 10];
PutString[" withdrawals: "];
MoneyToString[str, posMonthTotal];
ARJOut[str, ATotCols];
PutCR[];
ALJOut[MonthName[lastItemDate.month], 10];
PutString[" net: "];
MoneyToString[str, negMonthTotal - posMonthTotal];
ARJOut[str, ATotCols];
PutCR[];
ALJOut["YTD", 10];
PutString[" deposits: "];
MoneyToString[str, negYtdTotal ← negYtdTotal + negMonthTotal];
ARJOut[str, ATotCols];
PutCR[];
ALJOut["YTD", 10];
PutString[" withdrawals: "];
MoneyToString[str, posYtdTotal ← posYtdTotal + posMonthTotal];
ARJOut[str, ATotCols];
PutCR[];
ALJOut["YTD", 10];
PutString[" net: "];
MoneyToString[str, negYtdTotal - posYtdTotal];
ARJOut[str, ATotCols];
PutCR[];
PutChar[IODefs.ControlZ]; PutString["l2998"]; PutCR[];
negMonthTotal ← posMonthTotal ← 0;
PutString["Category"];
ARJOut[MonthName[lastItemDate.month], ATotCols];
IF budgetGiven THEN
BEGIN
ARJOut["budget", ATotCols];
ARJOut["net", ATotCols];
END;
ARJOut["YTD", ATotCols];
IF budgetGiven THEN
BEGIN
ARJOut["budget", ATotCols];
ARJOut["net", ATotCols];
END;
PutCR[];
FOR c IN Column DO
IF categoryKey[c] # NIL THEN
BEGIN
ALJOut[categoryKey[c], 8];
DoTotal[c]; PutCR[];
END;
ENDLOOP;
monthTotal ← ALL[0];
PutChar[IODefs.FF];
PutChar[IODefs.ControlZ]; PutString["l2998"]; PutCR[];
END;
END;
DoTotal: PROCEDURE [c: Column] =
BEGIN
ti: TotalRec = totalInfo[c];
TotalOut[x: CategoryX[c], width: CategoryColWidth, i: 1,
val: monthTotal[c], invertSign: categoryNeg[c]];
IF ti.hasTotal THEN
monthTotal[ti.myTotal] ←
monthTotal[ti.myTotal] + monthTotal[c];
IF budgetGiven THEN
BEGIN
mb: Money;
mb ← budget[c][lastItemDate.month];
TotalOut[x: CategoryX[c], width: CategoryColWidth, i: 2,
val: mb, invertSign: categoryNeg[c]];
TotalOut[x: CategoryX[c], width: CategoryColWidth, i: 3,
val: mb - monthTotal[c]];
END;
TotalOut[x: CategoryX[c], width: CategoryColWidth,
i: IF budgetGiven THEN 4 ELSE 2,
val: ytdTotal[c] ← ytdTotal[c] + monthTotal[c],
invertSign: categoryNeg[c]];
IF budgetGiven THEN
BEGIN
ytdb: Money;
m: CARDINAL;
ytdb ← 0;
FOR m IN [1..lastItemDate.month] DO
ytdb ← ytdb + budget[c][m];
ENDLOOP;
TotalOut[x: CategoryX[c], width: CategoryColWidth, i: 5,
val: ytdb, invertSign: categoryNeg[c]];
TotalOut[x: CategoryX[c], width: CategoryColWidth, i: 6,
val: ytdb - ytdTotal[c]];
END;
END;
FakeTotal: PROCEDURE [c: Column] =
BEGIN
ti: TotalRec = totalInfo[c];
IF ti.hasTotal THEN
monthTotal[ti.myTotal] ←
monthTotal[ti.myTotal] + monthTotal[c];
ytdTotal[c] ← ytdTotal[c] + monthTotal[c];
END;
TotalOut: PROCEDURE [i: [1..TotalRows], x, width: Mica, val: Money, invertSign: BOOLEAN ← FALSE] =
BEGIN
str: STRING ← [20];
MoneyToString[str, IF invertSign THEN -val ELSE val];
IF bravoLedger THEN ARJOut[str, ATotCols]
ELSE RJString[x: x,
y: RowY[LastCheckRow+i],
s: str,
width: width];
END;
END.