Initial revision of account
[account.git] / account.cpp
1
2 #include "account.h"
3
4 #include <sstream>
5 #include <fstream>
6
7 #include <iostream>
8 #include <iomanip>
9
10 acc_nr::acc_nr(String &s)
11 {
12    String s_nr;
13
14    std::cerr << "Account number from " << s;
15    group = s.dec();
16    s_nr    = s(4,2);
17    nr      = s_nr.dec();
18    std::cerr << " : " << *this << "\n";
19 }
20
21 acc_nr::operator String()
22 {
23    char _buf[8];
24    sprintf(_buf, "%03d.%02d", group, nr);
25
26    String s(_buf);
27    return s;
28 }
29
30 std::istream &operator>>(std::istream &s, acc_nr &a)
31 {
32    char c;
33
34    s >> a.group >> c >> a.nr;
35    return s;
36 }
37
38 std::ostream &operator<<(std::ostream &s, acc_nr a)
39 {
40    s << String(a);
41    return s;
42 }
43
44 std::istream &operator>>(std::istream &s, amount &a)
45 {
46    long   hundreds;
47    char   c;
48
49    s >> hundreds >> c >> a.value;
50    a.value += hundreds * 100;
51
52    return s;
53 }
54
55 std::ostream &operator<<(std::ostream &s, amount a)
56 {
57    if (a == 0)
58       s << "           ";
59    else
60    {
61       //s << form("%8d.%02d",a / 100, a % 100);
62       s << std::setw(8) << a / 100 << ".";
63       s << std::setw(2) << std::setfill('0') << a % 100;
64       s << std::setw(0) << std::setfill(' ');
65    }
66
67    return s;
68 }
69
70 String amount::to_string()
71 {
72    char _buf[20];
73    sprintf(_buf, "%8lu.%02lu", value / 100, value % 100);
74
75    if (value != 0)
76    {
77       String s(_buf);
78       return s;
79    }
80    else
81    {
82       return String("           ");
83    }
84 }
85 std::ostream &operator<<(std::ostream &s, balance b)
86 {
87    s << b.Debit << "  " << b.Credit;
88
89    return s;
90 }
91
92 std::istream &operator>>(std::istream &s, Booking &b)
93 {
94    char cmnt[128];
95
96    s >> b.Bnumber >> b.booked;
97    //s.get(cmnt, 128);
98    //b.comment = cmnt;
99    s >> b.comment;
100
101    return s;
102 }
103
104 std::ostream &operator<<(std::ostream &s, Booking &b)
105 {
106    //s << form("#%03d ",b.Bnumber) << b.booked << " " << b.comment;
107    //s << form("%.*s", 58 - ~b.comment, "                                                              ");
108    s << "#" << std::setw(4) << std::setfill('0') << b.Bnumber;
109    s << std::setw(0) << std::setfill(' ');
110    s << " " << b.booked << " ";
111    s << std::setfill(' ') << std::setw(58) << std::left << b.comment;
112    s << std::setw(0) << std::right ;
113
114    return s;
115 }
116
117 std::istream &operator>>(std::istream &s, Mutation &m)
118 {
119    int   nr_blanks = 0;
120    char  c;
121    char  buf[128];
122
123    while (s.get(c), c == ' ')
124       nr_blanks++;
125
126    s.putback(c);
127    s >> m.account;
128
129    amount  a;
130    //s.get(buf, 128);
131
132    //std::istringstream amnt(buf+strlen(buf)-10);
133    //amnt >> a;
134
135    String amnt;
136    int    p;
137
138    s >> amnt;
139    p = ~amnt - 1;
140    while (p > 0 && amnt[p] == ' ')
141    {
142       p--;
143    }
144    while (p > 0 && amnt[p] != ' ')
145    {
146       p--;
147    }
148
149    amnt = amnt(p, ~amnt - p);
150    int dot = amnt.index('.');
151    String cents = amnt(dot+1, 2);
152    a = long(amnt) * 100 + cents.dec();
153
154    if (nr_blanks < 7)
155       m.bal = balance(a, 0);
156    else
157       m.bal = balance(0, a);
158
159    return s;
160 }
161
162 std::ostream &operator<<(std::ostream &s, Mutation &m)
163 {
164    s << "#" << std::setw(4) << std::setfill('0') << m.booking_nr;
165    s << std::setw(0) << std::setfill(' ');
166    s << " " << m.account << m.bal;
167    return s;
168 }
169
170
171
172 std::istream &operator>>(std::istream &s, Account &acc)
173 {
174    char kind, name[128];
175
176    s >> acc.Anumber >> kind >> std::ws;
177    if (kind == 'B')
178       acc.kind = BALANCE;
179    else
180       acc.kind = COST;
181
182    //s.get(name, 128);
183    //acc.name = name;
184
185    s >> acc.name;
186
187    return s;
188 }
189
190 std::ostream &operator<<(std::ostream &s, Account &acc)
191 {
192    s << acc.Anumber << " " << std::setw(44) << std::left << acc.name;
193    s << std::setw(0) << std::right << " ";
194    //s << form("%.*s", 45 - ~acc.name, "                                                   ");
195    s << acc.Bal;
196
197    return s;
198 }
199
200 std::ostream& operator<<(std::ostream &s, Ledger &l)
201 {
202    int  i;
203    balance total, saldi;
204    balance EndBalance, Result;
205
206    s << l.nr_accs << " accounts in ledger:\n\n";
207    for (i=0; i<l.nr_accs; i++)
208    {
209       total += l.accs[i];
210       saldi += ~balance(l.accs[i]);
211       s << l.accs[i] << ~balance(l.accs[i]);
212       if (l.accs[i].BAL())
213       {
214          s << "                       " << ~balance(l.accs[i]);
215          EndBalance += ~balance(l.accs[i]);
216       }
217       else
218       {
219          s << ~balance(l.accs[i]);
220          Result += ~balance(l.accs[i]);
221       }
222       s << "\n";
223    }
224
225    balance profit;
226    profit = ~Result;
227
228    s << "\n       Result                                       ";
229    s << "                                                ";
230    s << -profit << profit << "\n";
231
232    Result += -profit;
233    EndBalance += profit;
234
235    s << "\n       Total                                        ";
236    s << total << saldi << Result << EndBalance << "\n";
237
238    return s;
239 }
240
241 void PostScript_balans_layout(PostScript &ps)
242 {
243    ps.Line(16, 595, 755, 595);
244    ps.Line(16, 580, 755, 580);
245    ps.Line(16, 40, 755, 40);
246    ps.Text(200, 582, "Saldibalans");
247
248    ps.Line( 16, 595,  16, 40);
249    ps.Line(755, 595, 755, 40);
250
251    ps.LineAttributes(0.2, 1.0, 0.0);
252    ps.Line(195, 580, 195, 40);
253    ps.Line(335, 580, 335, 40);
254    ps.Line(475, 580, 475, 40);
255    ps.Line(615, 580, 615, 40);
256
257    ps.LineAttributes(0.2, 2.0, 2.0);
258    ps.Line(265, 580, 265, 40);
259    ps.Line(405, 580, 405, 40);
260    ps.Line(545, 580, 545, 40);
261    ps.Line(685, 580, 685, 40);
262 }
263
264 void Ledger::accounts_report(const char * filename)
265 {
266    PostScript ps(filename);
267    int        i;
268
269    for (i=0; i<nr_accs; i++)
270    {
271       accs[i].Postscript_sheet(ps);
272    }
273 }
274
275 void Ledger::saldi_report(const char * filename)
276 {
277    PostScript ps(filename);
278    balance total, saldi;
279    balance EndBalance, Result;
280    int        i;
281    float      y = 572;
282    int        row = 0;
283
284    ps.NewPage(842, 595);
285    ps.FontSize(6);
286
287
288    for (i=0; i<nr_accs; i++)
289    {
290       balance saldo(accs[i]);
291
292       if (saldo.debit() != 0 || saldo.credit() != 0)
293       {
294          if (row == 64)
295          {
296             ps.FontSize(9);
297             PostScript_balans_layout(ps);
298             ps.NewPage(842, 595);
299             ps.FontSize(6);
300             y = 572;
301             row = 0;
302          }
303
304          if (row % 2 == 1)
305          {
306             ps.Rectangle(16, y-2, 739, 7);
307          }
308
309          total += accs[i];
310          saldi += ~balance(accs[i]);
311
312          ps.Text(20, y, String(accs[i].Number()));
313          ps.Text(50, y, accs[i].Name());
314
315          balance saldo(accs[i]);
316
317          ps.Text(200, y, saldo.debit().to_string());
318          ps.Text(270, y, saldo.credit().to_string());
319          saldo = ~saldo;
320          ps.Text(340, y, saldo.debit().to_string());
321          ps.Text(410, y, saldo.credit().to_string());
322
323          if (accs[i].BAL())
324          {
325             EndBalance += ~balance(accs[i]);
326
327             ps.Text(620, y, saldo.debit().to_string());
328             ps.Text(690, y, saldo.credit().to_string());
329          }
330          else
331          {
332             Result += ~balance(accs[i]);
333
334             ps.Text(480, y, saldo.debit().to_string());
335             ps.Text(550, y, saldo.credit().to_string());
336          }
337
338          y -= 7.0;
339          row ++;
340       }
341    }
342
343    ps.FontSize(9);
344    PostScript_balans_layout(ps);
345
346    balance profit;
347    profit = ~Result;
348    Result += -profit;
349    EndBalance += profit;
350
351    ps.LineAttributes(0.5, 1.0, 0.0);
352    ps.Line(16, 60, 755, 60);
353    y = 52;
354    ps.Text(50, y, "Resultaat");
355    ps.Text(480, y, (-profit).debit().to_string());
356    ps.Text(550, y, (-profit).credit().to_string());
357    ps.Text(620, y, profit.debit().to_string());
358    ps.Text(690, y, profit.credit().to_string());
359
360    ps.Line(16, 50, 755, 50);
361    y = 42;
362    ps.Text(50, y, "Totaal");
363    ps.Text(200, y, total.debit().to_string());
364    ps.Text(270, y, total.credit().to_string());
365    ps.Text(340, y, saldi.debit().to_string());
366    ps.Text(410, y, saldi.credit().to_string());
367    ps.Text(480, y, Result.debit().to_string());
368    ps.Text(550, y, Result.credit().to_string());
369    ps.Text(620, y, EndBalance.debit().to_string());
370    ps.Text(690, y, EndBalance.credit().to_string());
371 }
372
373 void Ledger::XML_saldi(std::ostream &s)
374 {
375    int  i;
376
377    s << "<?xml version='1.0'?>\n";
378    s << "<accounting>\n";
379    s << "<balance-sheet>\n";
380    for (i=0; i<nr_accs; i++)
381    {
382       balance saldo = ~balance(accs[i]);
383
384       if (saldo.debit() != 0 || saldo.credit() != 0)
385       {
386          s << "  <account>\n";
387          s << "    <number>" << accs[i].Number() << "</number>\n";
388          s << "    <name>" << accs[i].Name() << "</name>\n";
389          if (accs[i].BAL())
390          {
391             s << "    <type>BALANCE</type>\n";
392          }
393          else
394          {
395             s << "    <type>COST</type>\n";
396          }
397          if (saldo.debit() == 0)
398          {
399             s << "    <balance>CREDIT</balance>\n";
400             s << "    <amount>" << saldo.credit() << "</amount>\n";
401          }
402          else
403          {
404             s << "    <balance>DEBIT</balance>\n";
405             s << "    <amount>" << saldo.debit() << "</amount>\n";
406          }
407          s << "  </account>\n";
408          s << "\n";
409       }
410    }
411    s << "</balance-sheet>\n";
412    s << "</accounting>\n";
413 }
414
415 Account & Ledger::operator[](acc_nr Anr)
416 {
417    int i;
418
419    for (i=0; accs[i] != Anr && i<nr_accs; i++);
420    if (i == nr_accs)
421       std::cerr << "Account " << Anr << " not found.\n";
422    return accs[i];
423 }
424
425 extern int n_m;
426 extern Mutation m[];
427 extern Booking b[];
428
429 void PostScript_account_layout(PostScript &ps)
430 {
431    ps.LineAttributes(1.0, 1.0, 0.0);
432    ps.Line(30, 770, 580, 770);
433    ps.Line(30, 740, 580, 740);
434    ps.Line(30,  20, 580,  20);
435
436    ps.Line(30, 770, 30, 20);
437    ps.Line(580, 770, 580, 20);
438
439    ps.LineAttributes(0.2, 1.0, 0.0);
440    ps.Line(400, 740, 400, 20);
441    ps.LineAttributes(0.2, 2.0, 2.0);
442    ps.Line(480, 740, 480, 20);
443 }
444
445 void Account::Postscript_sheet(PostScript &ps)
446 {
447    int      M, B;
448    balance  total;
449    balance  saldo;
450    int      row;
451    float    y;
452    bool     page_started = false;
453
454    row = 68;
455
456    /*  Go through all mutations, selecting for each account  */
457
458    for (M = 0; M < n_m; M++)
459    {
460       std::ostringstream   bookstr;
461
462       if (acc_nr(m[M]) == Anumber)
463       {
464          if (row == 68)
465          {
466             if (page_started)
467             {
468                PostScript_account_layout(ps);
469             }
470             page_started = true;
471             ps.NewPage(596, 842);
472             ps.FontSize(12);
473
474             ps.Text(40, 750, String(Anumber));
475             ps.Text(100, 750, name);
476
477             ps.FontSize(8);
478             y = 730;
479             row = 0;
480          }
481
482          saldo = m[M];
483          total += m[M];
484
485          if (row % 2 == 1)
486          {
487             ps.Rectangle(30, y-2, 550, 9);
488          }
489             /*  Search the booking date and description */
490
491          for (B = 0; b[B].number() != m[M].number(); B++);
492          bookstr << b[B];
493          ps.Text(40, y, bookstr.str().c_str());
494
495          ps.Text(400, y, saldo.debit().to_string());
496          ps.Text(480, y, saldo.credit().to_string());
497
498          y -= 10;
499          row++;
500          //s << b[B] << "|" << balance(m[M]) << " |\n";
501       }
502    }
503
504    /*  Print the footer with the totals  */
505
506 #if 0
507    s << "                 SALDO                                      ";
508    s << "               |" << -~total << " |\n";
509    s << "----------------------------------------------------------------";
510    s << "-----------+-------------+-----------+\n";
511    total += -~total;
512    s << "                 TOTAAL                                       ";
513    s << "             |" << total << " |\n";
514    s << "----------------------------------------------------------------";
515    s << "-----------+-------------+-----------+\n";
516 #endif
517
518    if (page_started)
519    {
520       ps.Text(100, 50, "SALDO");
521       saldo = -~total;
522          ps.Text(400, 50, saldo.debit().to_string());
523          ps.Text(480, 50, saldo.credit().to_string());
524       ps.Line(30,  40, 580,  40);
525       total += -~total;
526       ps.Text(100, 30, "TOTAAL");
527          ps.Text(400, 30, total.debit().to_string());
528          ps.Text(480, 30, total.credit().to_string());
529       PostScript_account_layout(ps);
530    }
531 }
532
533 void Account::sheet(std::ostream &s)
534 {
535    int      M, B;
536    balance  total;
537
538    s << "\n\n             " << Anumber << "  " << name << "\n";
539    s << "================================================================";
540    s << "===========+=============+===========+\n";
541
542    /*  Go through all mutations, selecting for each account  */
543
544    for (M = 0; M < n_m; M++)
545    {
546       if (acc_nr(m[M]) == Anumber)
547       {
548          total += m[M];
549
550             /*  Search the booking date and description */
551
552          for (B = 0; b[B].number() != m[M].number(); B++);
553          s << b[B] << "|" << balance(m[M]) << " |\n";
554       }
555    }
556
557    /*  Print the footer with the totals  */
558
559    s << "                 SALDO                                      ";
560    s << "               |" << -~total << " |\n";
561    s << "----------------------------------------------------------------";
562    s << "-----------+-------------+-----------+\n";
563    total += -~total;
564    s << "                 TOTAAL                                       ";
565    s << "             |" << total << " |\n";
566    s << "----------------------------------------------------------------";
567    s << "-----------+-------------+-----------+\n";
568 }
569
570 void Ledger::read(char *filename)
571 {
572    std::ifstream   in(filename);
573    Account   A;
574
575    if (!in)
576    {
577       std::cerr << "Cannot read ledger file" << filename << "\n";
578       return;
579    }
580
581    nr_accs = 0;
582    while (in  >> A)
583       accs[nr_accs++] = A;
584
585    accs[nr_accs].Name() = String("Rekening bestaat niet");
586
587 }
588