ea071d6fd8fe1b8aba4f6c0f5c0b20e13b51daa9
[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, String header)
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, header);
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, date period_begin, date period_end)
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    String     header("Saldibalans");
285
286    header += " van " + period_begin.format("%e %B %Y") + " tot " + period_end.format("%e %B %Y");
287
288    ps.NewPage(842, 595);
289    ps.FontSize(6);
290
291
292    for (i=0; i<nr_accs; i++)
293    {
294       balance saldo(accs[i]);
295
296       if (saldo.debit() != 0 || saldo.credit() != 0)
297       {
298          if (row == 64)
299          {
300             ps.FontSize(9);
301             PostScript_balans_layout(ps, header);
302             ps.NewPage(842, 595);
303             ps.FontSize(6);
304             y = 572;
305             row = 0;
306          }
307
308          if (row % 2 == 1)
309          {
310             ps.Rectangle(16, y-2, 739, 7);
311          }
312
313          total += accs[i];
314          saldi += ~balance(accs[i]);
315
316          ps.Text(20, y, String(accs[i].Number()));
317          ps.Text(50, y, accs[i].Name());
318
319          balance saldo(accs[i]);
320
321          ps.Text(200, y, saldo.debit().to_string());
322          ps.Text(270, y, saldo.credit().to_string());
323          saldo = ~saldo;
324          ps.Text(340, y, saldo.debit().to_string());
325          ps.Text(410, y, saldo.credit().to_string());
326
327          if (accs[i].BAL())
328          {
329             EndBalance += ~balance(accs[i]);
330
331             ps.Text(620, y, saldo.debit().to_string());
332             ps.Text(690, y, saldo.credit().to_string());
333          }
334          else
335          {
336             Result += ~balance(accs[i]);
337
338             ps.Text(480, y, saldo.debit().to_string());
339             ps.Text(550, y, saldo.credit().to_string());
340          }
341
342          y -= 7.0;
343          row ++;
344       }
345    }
346
347    ps.FontSize(9);
348    PostScript_balans_layout(ps, header);
349
350    balance profit;
351    profit = ~Result;
352    Result += -profit;
353    EndBalance += profit;
354
355    ps.LineAttributes(0.5, 1.0, 0.0);
356    ps.Line(16, 60, 755, 60);
357    y = 52;
358    ps.Text(50, y, "Resultaat");
359    ps.Text(480, y, (-profit).debit().to_string());
360    ps.Text(550, y, (-profit).credit().to_string());
361    ps.Text(620, y, profit.debit().to_string());
362    ps.Text(690, y, profit.credit().to_string());
363
364    ps.Line(16, 50, 755, 50);
365    y = 42;
366    ps.Text(50, y, "Totaal");
367    ps.Text(200, y, total.debit().to_string());
368    ps.Text(270, y, total.credit().to_string());
369    ps.Text(340, y, saldi.debit().to_string());
370    ps.Text(410, y, saldi.credit().to_string());
371    ps.Text(480, y, Result.debit().to_string());
372    ps.Text(550, y, Result.credit().to_string());
373    ps.Text(620, y, EndBalance.debit().to_string());
374    ps.Text(690, y, EndBalance.credit().to_string());
375 }
376
377 void Ledger::XML_saldi(std::ostream &s, date period_begin, date period_end)
378 {
379    int  i;
380
381    s << "<?xml version='1.0'?>\n";
382    s << "<accounting companyname='" << companyname << "'>\n";
383    s << "<balance-sheet begin='" << period_begin << "' end='" << period_end << "'>\n";
384    for (i=0; i<nr_accs; i++)
385    {
386       balance saldo = ~balance(accs[i]);
387
388       if (saldo.debit() != 0 || saldo.credit() != 0)
389       {
390          s << "  <account>\n";
391          s << "    <number>" << accs[i].Number() << "</number>\n";
392          s << "    <name>" << accs[i].Name() << "</name>\n";
393          if (accs[i].BAL())
394          {
395             s << "    <type>BALANCE</type>\n";
396          }
397          else
398          {
399             s << "    <type>COST</type>\n";
400          }
401          if (saldo.debit() == 0)
402          {
403             s << "    <balance>CREDIT</balance>\n";
404             s << "    <amount>" << saldo.credit() << "</amount>\n";
405          }
406          else
407          {
408             s << "    <balance>DEBIT</balance>\n";
409             s << "    <amount>" << saldo.debit() << "</amount>\n";
410          }
411          s << "  </account>\n";
412          s << "\n";
413       }
414    }
415    s << "</balance-sheet>\n";
416    s << "</accounting>\n";
417 }
418
419 Account & Ledger::operator[](acc_nr Anr)
420 {
421    int i;
422
423    for (i=0; accs[i] != Anr && i<nr_accs; i++);
424    if (i == nr_accs)
425       std::cerr << "Account " << Anr << " not found.\n";
426    return accs[i];
427 }
428
429 extern int n_m;
430 extern Mutation m[];
431 extern Booking b[];
432
433 void PostScript_account_layout(PostScript &ps)
434 {
435    ps.LineAttributes(1.0, 1.0, 0.0);
436    ps.Line(30, 770, 580, 770);
437    ps.Line(30, 740, 580, 740);
438    ps.Line(30,  20, 580,  20);
439
440    ps.Line(30, 770, 30, 20);
441    ps.Line(580, 770, 580, 20);
442
443    ps.LineAttributes(0.2, 1.0, 0.0);
444    ps.Line(400, 740, 400, 20);
445    ps.LineAttributes(0.2, 2.0, 2.0);
446    ps.Line(480, 740, 480, 20);
447 }
448
449 void Account::Postscript_sheet(PostScript &ps)
450 {
451    int      M, B;
452    balance  total;
453    balance  saldo;
454    int      row;
455    float    y;
456    bool     page_started = false;
457
458    row = 68;
459
460    /*  Go through all mutations, selecting for each account  */
461
462    for (M = 0; M < n_m; M++)
463    {
464       std::ostringstream   bookstr;
465
466       if (acc_nr(m[M]) == Anumber)
467       {
468          if (row == 68)
469          {
470             if (page_started)
471             {
472                PostScript_account_layout(ps);
473             }
474             page_started = true;
475             ps.NewPage(596, 842);
476             ps.FontSize(12);
477
478             ps.Text(40, 750, String(Anumber));
479             ps.Text(100, 750, name);
480
481             ps.FontSize(8);
482             y = 730;
483             row = 0;
484          }
485
486          saldo = m[M];
487          total += m[M];
488
489          if (row % 2 == 1)
490          {
491             ps.Rectangle(30, y-2, 550, 9);
492          }
493             /*  Search the booking date and description */
494
495          for (B = 0; b[B].number() != m[M].number(); B++);
496          bookstr << b[B];
497          ps.Text(40, y, bookstr.str().c_str());
498
499          ps.Text(400, y, saldo.debit().to_string());
500          ps.Text(480, y, saldo.credit().to_string());
501
502          y -= 10;
503          row++;
504          //s << b[B] << "|" << balance(m[M]) << " |\n";
505       }
506    }
507
508    /*  Print the footer with the totals  */
509
510 #if 0
511    s << "                 SALDO                                      ";
512    s << "               |" << -~total << " |\n";
513    s << "----------------------------------------------------------------";
514    s << "-----------+-------------+-----------+\n";
515    total += -~total;
516    s << "                 TOTAAL                                       ";
517    s << "             |" << total << " |\n";
518    s << "----------------------------------------------------------------";
519    s << "-----------+-------------+-----------+\n";
520 #endif
521
522    if (page_started)
523    {
524       ps.Text(100, 50, "SALDO");
525       saldo = -~total;
526          ps.Text(400, 50, saldo.debit().to_string());
527          ps.Text(480, 50, saldo.credit().to_string());
528       ps.Line(30,  40, 580,  40);
529       total += -~total;
530       ps.Text(100, 30, "TOTAAL");
531          ps.Text(400, 30, total.debit().to_string());
532          ps.Text(480, 30, total.credit().to_string());
533       PostScript_account_layout(ps);
534    }
535 }
536
537 void Account::sheet(std::ostream &s)
538 {
539    int      M, B;
540    balance  total;
541
542    s << "\n\n             " << Anumber << "  " << name << "\n";
543    s << "================================================================";
544    s << "===========+=============+===========+\n";
545
546    /*  Go through all mutations, selecting for each account  */
547
548    for (M = 0; M < n_m; M++)
549    {
550       if (acc_nr(m[M]) == Anumber)
551       {
552          total += m[M];
553
554             /*  Search the booking date and description */
555
556          for (B = 0; b[B].number() != m[M].number(); B++);
557          s << b[B] << "|" << balance(m[M]) << " |\n";
558       }
559    }
560
561    /*  Print the footer with the totals  */
562
563    s << "                 SALDO                                      ";
564    s << "               |" << -~total << " |\n";
565    s << "----------------------------------------------------------------";
566    s << "-----------+-------------+-----------+\n";
567    total += -~total;
568    s << "                 TOTAAL                                       ";
569    s << "             |" << total << " |\n";
570    s << "----------------------------------------------------------------";
571    s << "-----------+-------------+-----------+\n";
572 }
573
574 void Ledger::read(char *filename)
575 {
576    std::ifstream   in(filename);
577    Account   A;
578    char      line_begin;
579
580    if (!in)
581    {
582       std::cerr << "Cannot read ledger file" << filename << "\n";
583       return;
584    }
585
586    nr_accs = 0;
587    while (in)
588    {
589       in.get(line_begin);
590       if (isdigit(line_begin))
591       {
592          in.putback(line_begin);
593          in >> A;
594          accs[nr_accs++] = A;
595       }
596       else if (line_begin == '$')
597       {
598          // Optional attributes
599          String  attribute;
600          String  a_name, a_value;
601
602          in >> attribute;
603
604          int  separator = attribute.index('=');
605          do
606          {
607             separator--;
608          }
609          while (isspace(attribute[separator]));
610          a_name = attribute(0, separator+1);
611
612          separator = attribute.index('=');
613          do
614          {
615             separator++;
616          }
617          while (isspace(attribute[separator]));
618          a_value = attribute;
619          a_value(0, separator) = "";
620
621          if (a_name == "companyname")
622          {
623             companyname = a_value;
624          }
625          else
626          {
627             std::cerr << "Unrecognized attribute in Ledger: " << a_name << "\n";
628          }
629       }
630       else if (line_begin == '\n')
631       {
632          //  Ignore empty lines
633       }
634       else
635       {
636          //  Anything else is comment: eat it.
637          String comment;
638
639          in >> comment;
640       }
641    }
642    accs[nr_accs].Name() = String("Rekening bestaat niet");
643
644 }
645