-- 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.