setupPoints und setupDateFormat in ein partial Layout verschoben
[kivitendo-erp.git] / bin / mozilla / gl.pl
1 #=====================================================================
2 # LX-Office ERP
3 # Copyright (C) 2004
4 # Based on SQL-Ledger Version 2.1.9
5 # Web http://www.lx-office.org
6 #
7 #=====================================================================
8 # SQL-Ledger Accounting
9 # Copyright (c) 1998-2002
10 #
11 #  Author: Dieter Simader
12 #   Email: dsimader@sql-ledger.org
13 #     Web: http://www.sql-ledger.org
14 #
15 #
16 # This program is free software; you can redistribute it and/or modify
17 # it under the terms of the GNU General Public License as published by
18 # the Free Software Foundation; either version 2 of the License, or
19 # (at your option) any later version.
20 #
21 # This program is distributed in the hope that it will be useful,
22 # but WITHOUT ANY WARRANTY; without even the implied warranty of
23 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
24 # GNU General Public License for more details.
25 # You should have received a copy of the GNU General Public License
26 # along with this program; if not, write to the Free Software
27 # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
28 #======================================================================
29 #
30 # Genereal Ledger
31 #
32 #======================================================================
33
34 use utf8;
35 use strict;
36
37 use POSIX qw(strftime);
38 use List::Util qw(sum);
39
40 use SL::FU;
41 use SL::GL;
42 use SL::IS;
43 use SL::PE;
44 use SL::ReportGenerator;
45
46 require "bin/mozilla/common.pl";
47 require "bin/mozilla/drafts.pl";
48 require "bin/mozilla/reportgenerator.pl";
49
50 # this is for our long dates
51 # $locale->text('January')
52 # $locale->text('February')
53 # $locale->text('March')
54 # $locale->text('April')
55 # $locale->text('May ')
56 # $locale->text('June')
57 # $locale->text('July')
58 # $locale->text('August')
59 # $locale->text('September')
60 # $locale->text('October')
61 # $locale->text('November')
62 # $locale->text('December')
63
64 # this is for our short month
65 # $locale->text('Jan')
66 # $locale->text('Feb')
67 # $locale->text('Mar')
68 # $locale->text('Apr')
69 # $locale->text('May')
70 # $locale->text('Jun')
71 # $locale->text('Jul')
72 # $locale->text('Aug')
73 # $locale->text('Sep')
74 # $locale->text('Oct')
75 # $locale->text('Nov')
76 # $locale->text('Dec')
77
78 sub add {
79   $main::lxdebug->enter_sub();
80
81   $main::auth->assert('general_ledger');
82
83   my $form     = $main::form;
84   my %myconfig = %main::myconfig;
85
86   return $main::lxdebug->leave_sub() if (load_draft_maybe());
87
88   $form->{title} = "Add";
89
90   $form->{callback} = "gl.pl?action=add" unless $form->{callback};
91
92   # we use this only to set a default date
93   # yep. aber er holt hier auch schon ALL_CHARTS. Aufwand / Nutzen? jb
94   GL->transaction(\%myconfig, \%$form);
95
96   $form->{rowcount}  = 2;
97
98   $form->{debit}  = 0;
99   $form->{credit} = 0;
100   $form->{tax}    = 0;
101
102   # departments
103   $form->all_departments(\%myconfig);
104   if (@{ $form->{all_departments} || [] }) {
105     $form->{selectdepartment} = "<option>\n";
106
107     map {
108       $form->{selectdepartment} .=
109         "<option>$_->{description}--$_->{id}\n"
110     } (@{ $form->{all_departments} || [] });
111   }
112
113   $form->{show_details} = $myconfig{show_form_details} unless defined $form->{show_details};
114
115   &display_form(1);
116   $main::lxdebug->leave_sub();
117
118 }
119
120 sub prepare_transaction {
121   $main::lxdebug->enter_sub();
122
123   $main::auth->assert('general_ledger');
124
125   my $form     = $main::form;
126   my %myconfig = %main::myconfig;
127
128   GL->transaction(\%myconfig, \%$form);
129
130   $form->{amount} = $form->format_amount(\%myconfig, $form->{amount}, 2);
131
132   # departments
133   $form->all_departments(\%myconfig);
134   if (@{ $form->{all_departments} || [] }) {
135     $form->{selectdepartment} = "<option>\n";
136
137     map {
138       $form->{selectdepartment} .=
139         "<option>$_->{description}--$_->{id}\n"
140     } (@{ $form->{all_departments} || [] });
141   }
142
143   my $i        = 1;
144   my $tax      = 0;
145   my $taxaccno = "";
146   foreach my $ref (@{ $form->{GL} }) {
147     my $j = $i - 1;
148     if ($tax && ($ref->{accno} eq $taxaccno)) {
149       $form->{"tax_$j"}      = abs($ref->{amount});
150       $form->{"taxchart_$j"} = $ref->{id} . "--" . $ref->{taxrate};
151       if ($form->{taxincluded}) {
152         if ($ref->{amount} < 0) {
153           $form->{"debit_$j"} += $form->{"tax_$j"};
154         } else {
155           $form->{"credit_$j"} += $form->{"tax_$j"};
156         }
157       }
158       $form->{"project_id_$j"} = $ref->{project_id};
159
160     } else {
161       $form->{"accno_$i"} = "$ref->{accno}--$ref->{tax_id}";
162       for (qw(fx_transaction source memo)) { $form->{"${_}_$i"} = $ref->{$_} }
163       if ($ref->{amount} < 0) {
164         $form->{totaldebit} -= $ref->{amount};
165         $form->{"debit_$i"} = $ref->{amount} * -1;
166       } else {
167         $form->{totalcredit} += $ref->{amount};
168         $form->{"credit_$i"} = $ref->{amount};
169       }
170       $form->{"taxchart_$i"} = "0--0.00";
171       $form->{"project_id_$i"} = $ref->{project_id};
172       $i++;
173     }
174     if ($ref->{taxaccno} && !$tax) {
175       $taxaccno = $ref->{taxaccno};
176       $tax      = 1;
177     } else {
178       $taxaccno = "";
179       $tax      = 0;
180     }
181   }
182
183   $form->{rowcount} = $i;
184   $form->{locked}   =
185     ($form->datetonum($form->{transdate}, \%myconfig) <=
186      $form->datetonum($form->{closedto}, \%myconfig));
187
188   $main::lxdebug->leave_sub();
189 }
190
191 sub edit {
192   $main::lxdebug->enter_sub();
193
194   $main::auth->assert('general_ledger');
195
196   my $form     = $main::form;
197   my %myconfig = %main::myconfig;
198
199   prepare_transaction();
200
201   $form->{title} = "Edit";
202
203   $form->{show_details} = $myconfig{show_form_details} unless defined $form->{show_details};
204
205   form_header();
206   display_rows();
207   form_footer();
208
209   $main::lxdebug->leave_sub();
210 }
211
212
213 sub search {
214   $::lxdebug->enter_sub;
215   $::auth->assert('general_ledger');
216
217   $::form->all_departments(\%::myconfig);
218   $::form->get_lists(
219     projects  => { key => "ALL_PROJECTS", all => 1 },
220   );
221   $::form->{ALL_EMPLOYEES} = SL::DB::Manager::Employee->get_all(query => [ deleted => 0 ]);
222
223   my $onload = "focus()"
224
225   $::form->header;
226   print $::form->parse_html_template('gl/search', {
227     onload => $onload,
228     department_label => sub { ("$_[0]{description}--$_[0]{id}")x2 },
229     employee_label => sub { "$_[0]{id}--$_[0]{name}" },
230   });
231
232   $::lxdebug->leave_sub;
233 }
234
235 sub create_subtotal_row {
236   $main::lxdebug->enter_sub();
237
238   my ($totals, $columns, $column_alignment, $subtotal_columns, $class) = @_;
239
240   my $form     = $main::form;
241   my %myconfig = %main::myconfig;
242
243   my $row = { map { $_ => { 'data' => '', 'class' => $class, 'align' => $column_alignment->{$_}, } } @{ $columns } };
244
245   map { $row->{$_}->{data} = $form->format_amount(\%myconfig, $totals->{$_}, 2) } @{ $subtotal_columns };
246
247   map { $totals->{$_} = 0 } @{ $subtotal_columns };
248
249   $main::lxdebug->leave_sub();
250
251   return $row;
252 }
253
254 sub generate_report {
255   $main::lxdebug->enter_sub();
256
257   $main::auth->assert('general_ledger');
258
259   my $form     = $main::form;
260   my %myconfig = %main::myconfig;
261   my $locale   = $main::locale;
262
263   # generate_report wird beim ersten Aufruf per Weiter-Knopf und POST mit der hidden Variablen sort mit Wert "datesort" (früher "transdate" als Defaultsortiervariable) Ã¼bertragen
264
265   # <form method=post action=gl.pl>
266   # <input type=hidden name=sort value=datesort>    # form->{sort} setzen
267   # <input type=hidden name=nextsub value=generate_report>
268
269   # anhand von neuer Variable datesort wird jetzt $form->{sort} auf transdate oder gldate gesetzt
270   # damit ist die Hidden Variable "sort" wahrscheinlich sogar Ã¼berflüssig
271
272   # Ã¤ndert man die Sortierreihenfolge per Klick auf eine der Ãœberschriften wird die Variable "sort" per GET Ã¼bergeben, z.B. id,transdate, gldate, ...
273   # gl.pl?action=generate_report&employee=18383--Jan%20B%c3%bcren&datesort=transdate&category=X&l_transdate=Y&l_gldate=Y&l_id=Y&l_reference=Y&l_description=Y&l_source=Y&l_debit=Y&l_credit=Y&sort=gldate&sortdir=0
274
275   if ( $form->{sort} eq 'datesort' ) {   # sollte bei einem Post (Aufruf aus Suchmaske) immer wahr sein
276       # je nachdem ob in Suchmaske "transdate" oder "gldate" ausgesucht wurde erstes Suchergebnis entsprechend sortieren
277       $form->{sort} = $form->{datesort};
278   };
279
280   # was passiert hier?
281   report_generator_set_default_sort("$form->{datesort}", 1);
282 #  report_generator_set_default_sort('transdate', 1);
283
284   GL->all_transactions(\%myconfig, \%$form);
285
286   my %acctype = ('A' => $locale->text('Asset'),
287                  'C' => $locale->text('Contra'),
288                  'L' => $locale->text('Liability'),
289                  'Q' => $locale->text('Equity'),
290                  'I' => $locale->text('Revenue'),
291                  'E' => $locale->text('Expense'),);
292
293   $form->{title} = $locale->text('Journal');
294   if ($form->{category} ne 'X') {
295     $form->{title} .= " : " . $locale->text($acctype{ $form->{category} });
296   }
297
298   $form->{landscape} = 1;
299
300   my $ml = ($form->{ml} =~ /(A|E|Q)/) ? -1 : 1;
301
302   my @columns = qw(
303     gldate         transdate        id             reference      description
304     notes          source           debit          debit_accno
305     credit         credit_accno     debit_tax      debit_tax_accno
306     credit_tax     credit_tax_accno projectnumbers balance employee
307   );
308
309   # add employee here, so that variable is still known and passed in url when choosing a different sort order in resulting table
310   my @hidden_variables = qw(accno source reference department description notes project_id datefrom dateto employee datesort category l_subtotal);
311   push @hidden_variables, map { "l_${_}" } @columns;
312   foreach ( @hidden_variables ) {
313       print URL "$_\n";
314   };
315
316   my (@options, @date_options);
317   push @options,      $locale->text('Account')     . " : $form->{accno} $form->{account_description}" if ($form->{accno});
318   push @options,      $locale->text('Source')      . " : $form->{source}"                             if ($form->{source});
319   push @options,      $locale->text('Reference')   . " : $form->{reference}"                          if ($form->{reference});
320   push @options,      $locale->text('Description') . " : $form->{description}"                        if ($form->{description});
321   push @options,      $locale->text('Notes')       . " : $form->{notes}"                              if ($form->{notes});
322   push @options,      $locale->text('Employee')       . " : $form->{employee_name}"                              if ($form->{employee_name});
323   my $datesorttext = $form->{datesort} eq 'transdate' ? $locale->text('Invoice Date') :  $locale->text('Booking Date');
324   push @date_options,      "$datesorttext"                              if ($form->{datesort} and ($form->{datefrom} or $form->{dateto}));
325   push @date_options, $locale->text('From'), $locale->date(\%myconfig, $form->{datefrom}, 1)          if ($form->{datefrom});
326   push @date_options, $locale->text('Bis'),  $locale->date(\%myconfig, $form->{dateto},   1)          if ($form->{dateto});
327   push @options,      join(' ', @date_options)                                                        if (scalar @date_options);
328
329   if ($form->{department}) {
330     my ($department) = split /--/, $form->{department};
331     push @options, $locale->text('Department') . " : $department";
332   }
333
334
335   my $callback = build_std_url('action=generate_report', grep { $form->{$_} } @hidden_variables);
336   print URL $callback;
337   close URL;
338
339   $form->{l_credit_accno}     = 'Y';
340   $form->{l_debit_accno}      = 'Y';
341   $form->{l_credit_tax}       = 'Y';
342   $form->{l_debit_tax}        = 'Y';
343 #  $form->{l_gldate}           = 'Y';  # Spalte mit gldate immer anzeigen
344   $form->{l_credit_tax_accno} = 'Y';
345   $form->{l_datesort} = 'Y';
346   $form->{l_debit_tax_accno}  = 'Y';
347   $form->{l_balance}          = $form->{accno} ? 'Y' : '';
348
349   my %column_defs = (
350     'id'               => { 'text' => $locale->text('ID'), },
351     'transdate'        => { 'text' => $locale->text('Invoice Date'), },
352     'gldate'           => { 'text' => $locale->text('Booking Date'), },
353     'reference'        => { 'text' => $locale->text('Reference'), },
354     'source'           => { 'text' => $locale->text('Source'), },
355     'description'      => { 'text' => $locale->text('Description'), },
356     'notes'            => { 'text' => $locale->text('Notes'), },
357     'debit'            => { 'text' => $locale->text('Debit'), },
358     'debit_accno'      => { 'text' => $locale->text('Debit Account'), },
359     'credit'           => { 'text' => $locale->text('Credit'), },
360     'credit_accno'     => { 'text' => $locale->text('Credit Account'), },
361     'debit_tax'        => { 'text' => $locale->text('Debit Tax'), },
362     'debit_tax_accno'  => { 'text' => $locale->text('Debit Tax Account'), },
363     'credit_tax'       => { 'text' => $locale->text('Credit Tax'), },
364     'credit_tax_accno' => { 'text' => $locale->text('Credit Tax Account'), },
365     'balance'          => { 'text' => $locale->text('Balance'), },
366     'projectnumbers'   => { 'text' => $locale->text('Project Numbers'), },
367     'employee'         => { 'text' => $locale->text('Employee'), },
368   );
369
370   foreach my $name (qw(id transdate gldate reference description debit_accno credit_accno debit_tax_accno credit_tax_accno)) {
371     my $sortname                = $name =~ m/accno/ ? 'accno' : $name;
372     my $sortdir                 = $sortname eq $form->{sort} ? 1 - $form->{sortdir} : $form->{sortdir};
373     $column_defs{$name}->{link} = $callback . "&sort=$sortname&sortdir=$sortdir";
374   }
375
376   map { $column_defs{$_}->{visible} = $form->{"l_${_}"} ? 1 : 0 } @columns;
377   map { $column_defs{$_}->{visible} = 0 } qw(debit_accno credit_accno debit_tax_accno credit_tax_accno) if $form->{accno};
378
379   my %column_alignment;
380   map { $column_alignment{$_}     = 'right'  } qw(balance id debit credit debit_tax credit_tax balance);
381   map { $column_alignment{$_}     = 'center' } qw(transdate gldate reference debit_accno credit_accno debit_tax_accno credit_tax_accno);
382   map { $column_alignment{$_}     = 'left' } qw(description source notes);
383   map { $column_defs{$_}->{align} = $column_alignment{$_} } keys %column_alignment;
384
385   my $report = SL::ReportGenerator->new(\%myconfig, $form);
386
387   $report->set_columns(%column_defs);
388   $report->set_column_order(@columns);
389
390   $report->set_export_options('generate_report', @hidden_variables, qw(sort sortdir));
391
392   $report->set_sort_indicator($form->{sort} eq 'accno' ? 'debit_accno' : $form->{sort}, $form->{sortdir});
393
394   $report->set_options('top_info_text'        => join("\n", @options),
395                        'output_format'        => 'HTML',
396                        'title'                => $form->{title},
397                        'attachment_basename'  => $locale->text('general_ledger_list') . strftime('_%Y%m%d', localtime time),
398     );
399   $report->set_options_from_form();
400   $locale->set_numberformat_wo_thousands_separator(\%myconfig) if lc($report->{options}->{output_format}) eq 'csv';
401
402   # add sort to callback
403   $form->{callback} = "$callback&sort=" . E($form->{sort}) . "&sortdir=" . E($form->{sortdir});
404
405
406   my @totals_columns = qw(debit credit debit_tax credit_tax);
407   my %subtotals      = map { $_ => 0 } @totals_columns;
408   my %totals         = map { $_ => 0 } @totals_columns;
409   my $idx            = 0;
410
411   foreach my $ref (@{ $form->{GL} }) {
412
413     my %rows;
414
415     foreach my $key (qw(debit credit debit_tax credit_tax)) {
416       $rows{$key} = [];
417       foreach my $idx (sort keys(%{ $ref->{$key} })) {
418         my $value         = $ref->{$key}->{$idx};
419         $subtotals{$key} += $value;
420         $totals{$key}    += $value;
421         if ($key =~ /debit.*/) {
422           $ml = -1;
423         } else {
424           $ml = 1;
425         }
426         $form->{balance}  = $form->{balance} + $value * $ml;
427         push @{ $rows{$key} }, $form->format_amount(\%myconfig, $value, 2);
428       }
429     }
430
431     foreach my $key (qw(debit_accno credit_accno debit_tax_accno credit_tax_accno ac_transdate source)) {
432       my $col = $key eq 'ac_transdate' ? 'transdate' : $key;
433       $rows{$col} = [ map { $ref->{$key}->{$_} } sort keys(%{ $ref->{$key} }) ];
434     }
435
436     my $row = { };
437     map { $row->{$_} = { 'data' => '', 'align' => $column_alignment{$_} } } @columns;
438
439     my $sh = "";
440     if ($form->{balance} < 0) {
441       $sh = " S";
442       $ml = -1;
443     } elsif ($form->{balance} > 0) {
444       $sh = " H";
445       $ml = 1;
446     }
447     my $data = $form->format_amount(\%myconfig, ($form->{balance} * $ml), 2);
448     $data .= $sh;
449
450     $row->{balance}->{data}        = $data;
451     $row->{projectnumbers}->{data} = join ", ", sort { lc($a) cmp lc($b) } keys %{ $ref->{projectnumbers} };
452
453     map { $row->{$_}->{data} = $ref->{$_} } qw(id reference description notes gldate employee);
454
455     map { $row->{$_}->{data} = \@{ $rows{$_} }; } qw(transdate debit credit debit_accno credit_accno debit_tax_accno credit_tax_accno source);
456
457     foreach my $col (qw(debit_accno credit_accno debit_tax_accno credit_tax_accno)) {
458       $row->{$col}->{link} = [ map { "${callback}&accno=" . E($_) } @{ $rows{$col} } ];
459     }
460
461     map { $row->{$_}->{data} = \@{ $rows{$_} } if ($ref->{"${_}_accno"} ne "") } qw(debit_tax credit_tax);
462
463     $row->{reference}->{link} = build_std_url("script=$ref->{module}.pl", 'action=edit', 'id=' . E($ref->{id}), 'callback');
464
465     my $row_set = [ $row ];
466
467     if ( ($form->{l_subtotal} eq 'Y' && !$form->{report_generator_csv_options_for_import} )
468         && (($idx == (scalar @{ $form->{GL} } - 1))
469             || ($ref->{ $form->{sort} } ne $form->{GL}->[$idx + 1]->{ $form->{sort} }))) {
470       push @{ $row_set }, create_subtotal_row(\%subtotals, \@columns, \%column_alignment, [ qw(debit credit) ], 'listsubtotal');
471     }
472
473     $report->add_data($row_set);
474
475     $idx++;
476   }
477
478   # = 0 for balanced ledger
479   my $balanced_ledger = $totals{debit} + $totals{debit_tax} - $totals{credit} - $totals{credit_tax};
480
481   my $row = create_subtotal_row(\%totals, \@columns, \%column_alignment, [ qw(debit credit debit_tax credit_tax) ], 'listtotal');
482
483   my $sh = "";
484   if ($form->{balance} < 0) {
485     $sh = " S";
486     $ml = -1;
487   } elsif ($form->{balance} > 0) {
488     $sh = " H";
489     $ml = 1;
490   }
491   my $data = $form->format_amount(\%myconfig, ($form->{balance} * $ml), 2);
492   $data .= $sh;
493
494   $row->{balance}->{data}        = $data;
495
496   if ( !$form->{report_generator_csv_options_for_import} ) {
497     $report->add_separator();
498     $report->add_data($row);
499   }
500
501   my $raw_bottom_info_text;
502
503   if (!$form->{accno} && (abs($balanced_ledger) >  0.001)) {
504     $raw_bottom_info_text .=
505         '<p><span class="unbalanced_ledger">'
506       . $locale->text('Unbalanced Ledger')
507       . ': '
508       . $form->format_amount(\%myconfig, $balanced_ledger, 3)
509       . '</span></p> ';
510   }
511
512   $raw_bottom_info_text .= $form->parse_html_template('gl/generate_report_bottom');
513
514   $report->set_options('raw_bottom_info_text' => $raw_bottom_info_text);
515
516   $report->generate_with_headers();
517
518   $main::lxdebug->leave_sub();
519 }
520
521 sub update {
522   $main::lxdebug->enter_sub();
523
524   $main::auth->assert('general_ledger');
525
526   my $form     = $main::form;
527   my %myconfig = %main::myconfig;
528
529   $form->{oldtransdate} = $form->{transdate};
530
531   my @a           = ();
532   my $count       = 0;
533   my $debittax    = 0;
534   my $credittax   = 0;
535   my $debitcount  = 0;
536   my $creditcount = 0;
537   my ($debitcredit, $amount);
538
539   my @flds =
540     qw(accno debit credit projectnumber fx_transaction source memo tax taxchart);
541
542   for my $i (1 .. $form->{rowcount}) {
543
544     unless (($form->{"debit_$i"} eq "") && ($form->{"credit_$i"} eq "")) {
545       for (qw(debit credit tax)) {
546         $form->{"${_}_$i"} =
547           $form->parse_amount(\%myconfig, $form->{"${_}_$i"});
548       }
549
550       push @a, {};
551       $debitcredit = ($form->{"debit_$i"} == 0) ? "0" : "1";
552       if ($debitcredit) {
553         $debitcount++;
554       } else {
555         $creditcount++;
556       }
557
558       if (($debitcount >= 2) && ($creditcount == 2)) {
559         $form->{"credit_$i"} = 0;
560         $form->{"tax_$i"}    = 0;
561         $creditcount--;
562         $form->{creditlock} = 1;
563       }
564       if (($creditcount >= 2) && ($debitcount == 2)) {
565         $form->{"debit_$i"} = 0;
566         $form->{"tax_$i"}   = 0;
567         $debitcount--;
568         $form->{debitlock} = 1;
569       }
570       if (($creditcount == 1) && ($debitcount == 2)) {
571         $form->{creditlock} = 1;
572       }
573       if (($creditcount == 2) && ($debitcount == 1)) {
574         $form->{debitlock} = 1;
575       }
576       if ($debitcredit && $credittax) {
577         $form->{"taxchart_$i"} = "0--0.00";
578       }
579       if (!$debitcredit && $debittax) {
580         $form->{"taxchart_$i"} = "0--0.00";
581       }
582       $amount =
583         ($form->{"debit_$i"} == 0)
584         ? $form->{"credit_$i"}
585         : $form->{"debit_$i"};
586       my $j = $#a;
587       if (($debitcredit && $credittax) || (!$debitcredit && $debittax)) {
588         $form->{"taxchart_$i"} = "0--0.00";
589         $form->{"tax_$i"}      = 0;
590       }
591       my ($taxkey, $rate) = split(/--/, $form->{"taxchart_$i"});
592       if ($taxkey > 1) {
593         if ($debitcredit) {
594           $debittax = 1;
595         } else {
596           $credittax = 1;
597         }
598         if ($form->{taxincluded}) {
599           $form->{"tax_$i"} = $amount / ($rate + 1) * $rate;
600         } else {
601           $form->{"tax_$i"} = $amount * $rate;
602         }
603       } else {
604         $form->{"tax_$i"} = 0;
605       }
606
607       for (@flds) { $a[$j]->{$_} = $form->{"${_}_$i"} }
608       $count++;
609     }
610   }
611
612   for my $i (1 .. $count) {
613     my $j = $i - 1;
614     for (@flds) { $form->{"${_}_$i"} = $a[$j]->{$_} }
615   }
616
617   for my $i ($count + 1 .. $form->{rowcount}) {
618     for (@flds) { delete $form->{"${_}_$i"} }
619   }
620
621   $form->{rowcount} = $count + 1;
622
623   &display_form;
624   $main::lxdebug->leave_sub();
625
626 }
627
628 sub display_form {
629   my ($init) = @_;
630   $main::lxdebug->enter_sub();
631
632   $main::auth->assert('general_ledger');
633
634   my $form     = $main::form;
635   my %myconfig = %main::myconfig;
636
637   &form_header($init);
638
639   #   for $i (1 .. $form->{rowcount}) {
640   #     $form->{totaldebit} += $form->parse_amount(\%myconfig, $form->{"debit_$i"});
641   #     $form->{totalcredit} += $form->parse_amount(\%myconfig, $form->{"credit_$i"});
642   #
643   #     &form_row($i);
644   #   }
645   &display_rows($init);
646   &form_footer;
647   $main::lxdebug->leave_sub();
648
649 }
650
651 sub display_rows {
652   my ($init) = @_;
653   $main::lxdebug->enter_sub();
654
655   $main::auth->assert('general_ledger');
656
657   my $form     = $main::form;
658   my %myconfig = %main::myconfig;
659   my $cgi      = $::request->{cgi};
660
661   $form->{debit_1}     = 0 if !$form->{"debit_1"};
662   $form->{totaldebit}  = 0;
663   $form->{totalcredit} = 0;
664
665   my %project_labels = ();
666   my @project_values = ("");
667   foreach my $item (@{ $form->{"ALL_PROJECTS"} }) {
668     push(@project_values, $item->{"id"});
669     $project_labels{$item->{"id"}} = $item->{"projectnumber"};
670   }
671
672   my %chart_labels = ();
673   my @chart_values = ();
674   my %charts = ();
675   my $taxchart_init;
676   foreach my $item (@{ $form->{ALL_CHARTS} }) {
677     if ($item->{charttype} eq 'H'){ #falls Ã¼berschrift
678       next;                         #überspringen (Bug 1150)
679     }
680     my $key = $item->{accno} . "--" . $item->{tax_id};
681     $taxchart_init = $item->{tax_id} unless (@chart_values);
682     push(@chart_values, $key);
683     $chart_labels{$key} = $item->{accno} . "--" . $item->{description};
684     $charts{$item->{accno}} = $item;
685   }
686
687   my %taxchart_labels = ();
688   my @taxchart_values = ();
689   my %taxcharts = ();
690   foreach my $item (@{ $form->{ALL_TAXCHARTS} }) {
691     my $key = $item->{id} . "--" . $item->{rate};
692     $taxchart_init = $key if ($taxchart_init == $item->{id});
693     push(@taxchart_values, $key);
694     $taxchart_labels{$key} = $item->{taxdescription} . " " . $item->{rate} * 100 . ' %';
695     $taxcharts{$item->{id}} = $item;
696   }
697
698   my ($source, $memo, $source_hidden, $memo_hidden);
699   for my $i (1 .. $form->{rowcount}) {
700     if ($form->{show_details}) {
701       $source = qq|
702       <td><input name="source_$i" value="$form->{"source_$i"}" size="16"></td>|;
703       $memo = qq|
704       <td><input name="memo_$i" value="$form->{"memo_$i"}" size="16"></td>|;
705     } else {
706       $source_hidden = qq|
707       <input type="hidden" name="source_$i" value="$form->{"source_$i"}" size="16">|;
708       $memo_hidden = qq|
709       <input type="hidden" name="memo_$i" value="$form->{"memo_$i"}" size="16">|;
710     }
711
712     my $selected_accno_full;
713     my ($accno_row) = split(/--/, $form->{"accno_$i"});
714     my $item = $charts{$accno_row};
715     $selected_accno_full = "$item->{accno}--$item->{tax_id}";
716
717     my $selected_taxchart = $form->{"taxchart_$i"};
718     my ($selected_accno, $selected_tax_id) = split(/--/, $selected_accno_full);
719     my ($previous_accno, $previous_tax_id) = split(/--/, $form->{"previous_accno_$i"});
720
721     if ($previous_accno &&
722         ($previous_accno eq $selected_accno) &&
723         ($previous_tax_id ne $selected_tax_id)) {
724       my $item = $taxcharts{$selected_tax_id};
725       $selected_taxchart = "$item->{id}--$item->{rate}";
726     }
727
728     $selected_accno      = '' if ($init);
729     $selected_taxchart ||= $taxchart_init;
730
731     my $accno = qq|<td>| .
732       NTI($cgi->popup_menu('-name' => "accno_$i",
733                            '-id' => "accno_$i",
734                            '-onChange' => "setTaxkey($i)",
735                            '-style' => 'width:200px',
736                            '-values' => \@chart_values,
737                            '-labels' => \%chart_labels,
738                            '-default' => $selected_accno_full))
739       . $cgi->hidden('-name' => "previous_accno_$i",
740                      '-default' => $selected_accno_full)
741       . qq|</td>|;
742     my $tax_ddbox = qq|<td>| .
743       NTI($cgi->popup_menu('-name' => "taxchart_$i",
744                            '-id' => "taxchart_$i",
745                            '-style' => 'width:200px',
746                            '-values' => \@taxchart_values,
747                            '-labels' => \%taxchart_labels,
748                            '-default' => $selected_taxchart))
749       . qq|</td>|;
750
751     my ($fx_transaction, $checked);
752     if ($init) {
753       if ($form->{transfer}) {
754         $fx_transaction = qq|
755         <td><input name="fx_transaction_$i" class=checkbox type=checkbox value=1></td>
756     |;
757       }
758
759     } else {
760       if ($form->{"debit_$i"} != 0) {
761         $form->{totaldebit} += $form->{"debit_$i"};
762         if (!$form->{taxincluded}) {
763           $form->{totaldebit} += $form->{"tax_$i"};
764         }
765       } else {
766         $form->{totalcredit} += $form->{"credit_$i"};
767         if (!$form->{taxincluded}) {
768           $form->{totalcredit} += $form->{"tax_$i"};
769         }
770       }
771
772       for (qw(debit credit tax)) {
773         $form->{"${_}_$i"} =
774           ($form->{"${_}_$i"})
775           ? $form->format_amount(\%myconfig, $form->{"${_}_$i"}, 2)
776           : "";
777       }
778
779       if ($i < $form->{rowcount}) {
780         if ($form->{transfer}) {
781           $checked = ($form->{"fx_transaction_$i"}) ? "1" : "";
782           my $x = ($checked) ? "x" : "";
783           $fx_transaction = qq|
784       <td><input type=hidden name="fx_transaction_$i" value="$checked">$x</td>
785     |;
786         }
787         $form->hide_form("accno_$i");
788
789       } else {
790         if ($form->{transfer}) {
791           $fx_transaction = qq|
792       <td><input name="fx_transaction_$i" class=checkbox type=checkbox value=1></td>
793     |;
794         }
795       }
796     }
797     my $debitreadonly  = "";
798     my $creditreadonly = "";
799     if ($i == $form->{rowcount}) {
800       if ($form->{debitlock}) {
801         $debitreadonly = "readonly";
802       } elsif ($form->{creditlock}) {
803         $creditreadonly = "readonly";
804       }
805     }
806
807     my $projectnumber =
808       NTI($cgi->popup_menu('-name' => "project_id_$i",
809                            '-values' => \@project_values,
810                            '-labels' => \%project_labels,
811                            '-default' => $form->{"project_id_$i"} ));
812     my $projectnumber_hidden = qq|
813     <input type="hidden" name="project_id_$i" value="$form->{"project_id_$i"}">|;
814
815     my $copy2credit = $i == 1 ? 'onkeyup="copy_debit_to_credit()"' : '';
816
817     print qq|<tr valign=top>
818     $accno
819     <td id="chart_balance_$i" align="right">&nbsp;</td>
820     $fx_transaction
821     <td><input name="debit_$i" size="8" value="$form->{"debit_$i"}" accesskey=$i $copy2credit $debitreadonly></td>
822     <td><input name="credit_$i" size=8 value="$form->{"credit_$i"}" $creditreadonly></td>
823     <td><input type="hidden" name="tax_$i" value="$form->{"tax_$i"}">$form->{"tax_$i"}</td>
824     $tax_ddbox|;
825
826     if ($form->{show_details}) {
827       print qq|
828     $source
829     $memo
830     <td>$projectnumber</td>
831 |;
832     } else {
833     print qq|
834     $source_hidden
835     $memo_hidden
836     $projectnumber_hidden
837     |;
838     }
839     print qq|
840   </tr>
841 |;
842   }
843
844   $form->hide_form(qw(rowcount selectaccno));
845
846   $main::lxdebug->leave_sub();
847
848 }
849
850 sub form_header {
851   $::lxdebug->enter_sub;
852   $::auth->assert('general_ledger');
853
854   my ($init) = @_;
855
856   my @old_project_ids = grep { $_ } map{ $::form->{"project_id_$_"} } 1..$::form->{rowcount};
857
858   $::form->get_lists("projects"  => { "key"       => "ALL_PROJECTS",
859                                     "all"       => 0,
860                                     "old_id"    => \@old_project_ids },
861                    "charts"    => { "key"       => "ALL_CHARTS",
862                                     "transdate" => $::form->{transdate} },
863                    "taxcharts" => "ALL_TAXCHARTS");
864
865   GL->get_chart_balances('charts' => $::form->{ALL_CHARTS});
866
867   my $title      = $::form->{title};
868   $::form->{title} = $::locale->text("$title General Ledger Transaction");
869   # $locale->text('Add General Ledger Transaction')
870   # $locale->text('Edit General Ledger Transaction')
871
872   map { $::form->{$_} =~ s/\"/&quot;/g }
873     qw(chart taxchart);
874
875   $::form->{selectdepartment} =~ s/ selected//;
876   $::form->{selectdepartment} =~
877     s/option>\Q$::form->{department}\E/option selected>$::form->{department}/;
878
879   if ($init) {
880     $::form->{fokus} = "gl.reference";
881     $::form->{taxincluded} = "1";
882   } else {
883     $::form->{fokus} = qq|gl.accno_$::form->{rowcount}|;
884   }
885
886   $::form->{previous_id}     ||= "--";
887   $::form->{previous_gldate} ||= "--";
888
889   $::form->header;
890   print $::form->parse_html_template('gl/form_header', {
891     hide_title => $title,
892   });
893
894   $::lxdebug->leave_sub;
895
896 }
897
898 sub form_footer {
899   $::lxdebug->enter_sub;
900   $::auth->assert('general_ledger');
901
902   my ($follow_ups, $follow_ups_due);
903
904   if ($::form->{id}) {
905     $follow_ups     = FU->follow_ups('trans_id' => $::form->{id});
906     $follow_ups_due = sum map { $_->{due} * 1 } @{ $follow_ups || [] };
907   }
908
909   my $radieren = $::form->current_date(\%::myconfig) eq $::form->{gldate};
910
911   print $::form->parse_html_template('gl/form_footer', {
912     radieren       => $radieren,
913     follow_ups     => $follow_ups,
914     follow_ups_due => $follow_ups_due,
915   });
916
917   $::lxdebug->leave_sub;
918 }
919
920 sub delete {
921   $main::lxdebug->enter_sub();
922
923   my $form     = $main::form;
924   my $locale   = $main::locale;
925
926   $form->header;
927
928   print qq|
929 <form method=post action=gl.pl>
930 |;
931
932   map { $form->{$_} =~ s/\"/&quot;/g } qw(reference description);
933
934   delete $form->{header};
935
936   foreach my $key (keys %$form) {
937     next if (($key eq 'login') || ($key eq 'password') || ('' ne ref $form->{$key}));
938     print qq|<input type="hidden" name="$key" value="$form->{$key}">\n|;
939   }
940
941   print qq|
942 <h2 class=confirm>| . $locale->text('Confirm!') . qq|</h2>
943
944 <h4>|
945     . $locale->text('Are you sure you want to delete Transaction')
946     . qq| $form->{reference}</h4>
947
948 <input name=action class=submit type=submit value="|
949     . $locale->text('Yes') . qq|">
950 </form>
951 |;
952   $main::lxdebug->leave_sub();
953
954 }
955
956 sub yes {
957   $main::lxdebug->enter_sub();
958
959   my $form     = $main::form;
960   my %myconfig = %main::myconfig;
961   my $locale   = $main::locale;
962
963   if (GL->delete_transaction(\%myconfig, \%$form)){
964     # saving the history
965       if(!exists $form->{addition} && $form->{id} ne "") {
966         $form->{snumbers} = qq|ordnumber_| . $form->{ordnumber};
967         $form->{addition} = "DELETED";
968         $form->save_history;
969       }
970     # /saving the history
971     $form->redirect($locale->text('Transaction deleted!'))
972   }
973   $form->error($locale->text('Cannot delete transaction!'));
974   $main::lxdebug->leave_sub();
975
976 }
977
978 sub post_transaction {
979   $main::lxdebug->enter_sub();
980
981   my $form     = $main::form;
982   my %myconfig = %main::myconfig;
983   my $locale   = $main::locale;
984
985   # check if there is something in reference and date
986   $form->isblank("reference",   $locale->text('Reference missing!'));
987   $form->isblank("transdate",   $locale->text('Transaction Date missing!'));
988   $form->isblank("description", $locale->text('Description missing!'));
989
990   my $transdate = $form->datetonum($form->{transdate}, \%myconfig);
991   my $closedto  = $form->datetonum($form->{closedto},  \%myconfig);
992
993   my @a           = ();
994   my $count       = 0;
995   my $debittax    = 0;
996   my $credittax   = 0;
997   my $debitcount  = 0;
998   my $creditcount = 0;
999   my $debitcredit;
1000   my %split_safety = ();
1001
1002   my @flds = qw(accno debit credit projectnumber fx_transaction source memo tax taxchart);
1003
1004   for my $i (1 .. $form->{rowcount}) {
1005     next if $form->{"debit_$i"} eq "" && $form->{"credit_$i"} eq "";
1006
1007     for (qw(debit credit tax)) {
1008       $form->{"${_}_$i"} = $form->parse_amount(\%myconfig, $form->{"${_}_$i"});
1009     }
1010
1011     push @a, {};
1012     $debitcredit = ($form->{"debit_$i"} == 0) ? "0" : "1";
1013
1014     $split_safety{   $form->{"debit_$i"}  <=> 0 }++;
1015     $split_safety{ - $form->{"credit_$i"} <=> 0 }++;
1016
1017     if ($debitcredit) {
1018       $debitcount++;
1019     } else {
1020       $creditcount++;
1021     }
1022
1023     if (($debitcount >= 2) && ($creditcount == 2)) {
1024       $form->{"credit_$i"} = 0;
1025       $form->{"tax_$i"}    = 0;
1026       $creditcount--;
1027       $form->{creditlock} = 1;
1028     }
1029     if (($creditcount >= 2) && ($debitcount == 2)) {
1030       $form->{"debit_$i"} = 0;
1031       $form->{"tax_$i"}   = 0;
1032       $debitcount--;
1033       $form->{debitlock} = 1;
1034     }
1035     if (($creditcount == 1) && ($debitcount == 2)) {
1036       $form->{creditlock} = 1;
1037     }
1038     if (($creditcount == 2) && ($debitcount == 1)) {
1039       $form->{debitlock} = 1;
1040     }
1041     if ($debitcredit && $credittax) {
1042       $form->{"taxchart_$i"} = "0--0.00";
1043     }
1044     if (!$debitcredit && $debittax) {
1045       $form->{"taxchart_$i"} = "0--0.00";
1046     }
1047     my $amount = ($form->{"debit_$i"} == 0)
1048             ? $form->{"credit_$i"}
1049             : $form->{"debit_$i"};
1050     my $j = $#a;
1051     if (($debitcredit && $credittax) || (!$debitcredit && $debittax)) {
1052       $form->{"taxchart_$i"} = "0--0.00";
1053       $form->{"tax_$i"}      = 0;
1054     }
1055     my ($taxkey, $rate) = split(/--/, $form->{"taxchart_$i"});
1056     if ($taxkey > 1) {
1057       if ($debitcredit) {
1058         $debittax = 1;
1059       } else {
1060         $credittax = 1;
1061       }
1062       if ($form->{taxincluded}) {
1063         $form->{"tax_$i"} = $amount / ($rate + 1) * $rate;
1064         if ($debitcredit) {
1065           $form->{"debit_$i"} = $form->{"debit_$i"} - $form->{"tax_$i"};
1066         } else {
1067           $form->{"credit_$i"} = $form->{"credit_$i"} - $form->{"tax_$i"};
1068         }
1069       } else {
1070         $form->{"tax_$i"} = $amount * $rate;
1071       }
1072     } else {
1073       $form->{"tax_$i"} = 0;
1074     }
1075
1076     for (@flds) { $a[$j]->{$_} = $form->{"${_}_$i"} }
1077     $count++;
1078   }
1079
1080   if ($split_safety{-1} > 1 && $split_safety{1} > 1) {
1081     $::form->error($::locale->text("Split entry detected. The values you have entered will result in an entry with more than one position on both debit and credit. Due to known problems involving accounting software Lx-Office does not allow these."));
1082   }
1083
1084   for my $i (1 .. $count) {
1085     my $j = $i - 1;
1086     for (@flds) { $form->{"${_}_$i"} = $a[$j]->{$_} }
1087   }
1088
1089   for my $i ($count + 1 .. $form->{rowcount}) {
1090     for (@flds) { delete $form->{"${_}_$i"} }
1091   }
1092
1093   my ($debit, $credit, $taxtotal);
1094   for my $i (1 .. $form->{rowcount}) {
1095     my $dr  = $form->{"debit_$i"};
1096     my $cr  = $form->{"credit_$i"};
1097     my $tax = $form->{"tax_$i"};
1098     if ($dr && $cr) {
1099       $form->error($locale->text('Cannot post transaction with a debit and credit entry for the same account!'));
1100     }
1101     $debit    += $dr + $tax if $dr;
1102     $credit   += $cr + $tax if $cr;
1103     $taxtotal += $tax if $form->{taxincluded}
1104   }
1105
1106   $form->{taxincluded} = 0 if !$taxtotal;
1107
1108   # this is just for the wise guys
1109   $form->error($locale->text('Cannot post transaction for a closed period!'))
1110     if ($form->date_closed($form->{"transdate"}, \%myconfig));
1111   if ($form->round_amount($debit, 2) != $form->round_amount($credit, 2)) {
1112     $form->error($locale->text('Out of balance transaction!'));
1113   }
1114
1115   if ($form->round_amount($debit, 2) + $form->round_amount($credit, 2) == 0) {
1116     $form->error($locale->text('Empty transaction!'));
1117   }
1118
1119   if ((my $errno = GL->post_transaction(\%myconfig, \%$form)) <= -1) {
1120     $errno *= -1;
1121     my @err;
1122     $err[1] = $locale->text('Cannot have a value in both Debit and Credit!');
1123     $err[2] = $locale->text('Debit and credit out of balance!');
1124     $err[3] = $locale->text('Cannot post a transaction without a value!');
1125
1126     $form->error($err[$errno]);
1127   }
1128   undef($form->{callback});
1129   # saving the history
1130   if(!exists $form->{addition} && $form->{id} ne "") {
1131     $form->{snumbers} = qq|ordnumber_| . $form->{ordnumber};
1132     $form->{addition} = "SAVED";
1133     $form->{what_done} = $locale->text("Buchungsnummer") . " = " . $form->{id};
1134     $form->save_history;
1135   }
1136   # /saving the history
1137
1138   $main::lxdebug->leave_sub();
1139 }
1140
1141 sub post {
1142   $main::lxdebug->enter_sub();
1143
1144   $main::auth->assert('general_ledger');
1145
1146   my $form     = $main::form;
1147   my $locale   = $main::locale;
1148
1149   if ($::myconfig{mandatory_departments} && !$form->{department}) {
1150     $form->{saved_message} = $::locale->text('You have to specify a department.');
1151     update();
1152     exit;
1153   }
1154
1155   $form->{title}  = $locale->text("$form->{title} General Ledger Transaction");
1156   $form->{storno} = 0;
1157
1158   post_transaction();
1159
1160   remove_draft() if $form->{remove_draft};
1161
1162   $form->{callback} = build_std_url("action=add&DONT_LOAD_DRAFT=1", "show_details");
1163   $form->redirect($form->{callback});
1164
1165   $main::lxdebug->leave_sub();
1166 }
1167
1168 sub post_as_new {
1169   $main::lxdebug->enter_sub();
1170
1171   $main::auth->assert('general_ledger');
1172
1173   my $form     = $main::form;
1174
1175   $form->{id} = 0;
1176   &add;
1177   $main::lxdebug->leave_sub();
1178
1179 }
1180
1181 sub storno {
1182   $main::lxdebug->enter_sub();
1183
1184   $main::auth->assert('general_ledger');
1185
1186   my $form     = $main::form;
1187   my %myconfig = %main::myconfig;
1188   my $locale   = $main::locale;
1189
1190   # don't cancel cancelled transactions
1191   if (IS->has_storno(\%myconfig, $form, 'gl')) {
1192     $form->{title} = $locale->text("Cancel Accounts Receivables Transaction");
1193     $form->error($locale->text("Transaction has already been cancelled!"));
1194   }
1195
1196   GL->storno($form, \%myconfig, $form->{id});
1197
1198   # saving the history
1199   if(!exists $form->{addition} && $form->{id} ne "") {
1200     $form->{snumbers} = "ordnumber_$form->{ordnumber}";
1201     $form->{addition} = "STORNO";
1202     $form->save_history;
1203   }
1204   # /saving the history
1205
1206   $form->redirect(sprintf $locale->text("Transaction %d cancelled."), $form->{storno_id});
1207
1208   $main::lxdebug->leave_sub();
1209 }
1210
1211 sub continue {
1212   call_sub($main::form->{nextsub});
1213 }
1214
1215 1;