Manuelle ergänzungen zum vorherigen commit
[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              . qq|;setupDateFormat('|. $::myconfig{dateformat} . qq|', '| . $::locale->text("Falsches Datumsformat!") . qq|')|
225              . qq|;setupPoints('|. $::myconfig{numberformat} .   qq|', '| . $::locale->text("wrongformat") . qq|')|;
226
227   $::form->header;
228   print $::form->parse_html_template('gl/search', {
229     onload => $onload,
230     department_label => sub { ("$_[0]{description}--$_[0]{id}")x2 },
231     employee_label => sub { "$_[0]{id}--$_[0]{name}" },
232   });
233
234   $::lxdebug->leave_sub;
235 }
236
237 sub create_subtotal_row {
238   $main::lxdebug->enter_sub();
239
240   my ($totals, $columns, $column_alignment, $subtotal_columns, $class) = @_;
241
242   my $form     = $main::form;
243   my %myconfig = %main::myconfig;
244
245   my $row = { map { $_ => { 'data' => '', 'class' => $class, 'align' => $column_alignment->{$_}, } } @{ $columns } };
246
247   map { $row->{$_}->{data} = $form->format_amount(\%myconfig, $totals->{$_}, 2) } @{ $subtotal_columns };
248
249   map { $totals->{$_} = 0 } @{ $subtotal_columns };
250
251   $main::lxdebug->leave_sub();
252
253   return $row;
254 }
255
256 sub generate_report {
257   $main::lxdebug->enter_sub();
258
259   $main::auth->assert('general_ledger');
260
261   my $form     = $main::form;
262   my %myconfig = %main::myconfig;
263   my $locale   = $main::locale;
264
265   # 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
266
267   # <form method=post action=gl.pl>
268   # <input type=hidden name=sort value=datesort>    # form->{sort} setzen
269   # <input type=hidden name=nextsub value=generate_report>
270
271   # anhand von neuer Variable datesort wird jetzt $form->{sort} auf transdate oder gldate gesetzt
272   # damit ist die Hidden Variable "sort" wahrscheinlich sogar ĂĽberflĂĽssig
273
274   # Ă¤ndert man die Sortierreihenfolge per Klick auf eine der Ăśberschriften wird die Variable "sort" per GET ĂĽbergeben, z.B. id,transdate, gldate, ...
275   # 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
276
277   if ( $form->{sort} eq 'datesort' ) {   # sollte bei einem Post (Aufruf aus Suchmaske) immer wahr sein
278       # je nachdem ob in Suchmaske "transdate" oder "gldate" ausgesucht wurde erstes Suchergebnis entsprechend sortieren
279       $form->{sort} = $form->{datesort};
280   };
281
282   # was passiert hier?
283   report_generator_set_default_sort("$form->{datesort}", 1);
284 #  report_generator_set_default_sort('transdate', 1);
285
286   GL->all_transactions(\%myconfig, \%$form);
287
288   my %acctype = ('A' => $locale->text('Asset'),
289                  'C' => $locale->text('Contra'),
290                  'L' => $locale->text('Liability'),
291                  'Q' => $locale->text('Equity'),
292                  'I' => $locale->text('Revenue'),
293                  'E' => $locale->text('Expense'),);
294
295   $form->{title} = $locale->text('Journal');
296   if ($form->{category} ne 'X') {
297     $form->{title} .= " : " . $locale->text($acctype{ $form->{category} });
298   }
299
300   $form->{landscape} = 1;
301
302   my $ml = ($form->{ml} =~ /(A|E|Q)/) ? -1 : 1;
303
304   my @columns = qw(
305     gldate         transdate        id             reference      description
306     notes          source           debit          debit_accno
307     credit         credit_accno     debit_tax      debit_tax_accno
308     credit_tax     credit_tax_accno projectnumbers balance employee
309   );
310
311   # add employee here, so that variable is still known and passed in url when choosing a different sort order in resulting table
312   my @hidden_variables = qw(accno source reference department description notes project_id datefrom dateto employee datesort category l_subtotal);
313   push @hidden_variables, map { "l_${_}" } @columns;
314   foreach ( @hidden_variables ) {
315       print URL "$_\n";
316   };
317
318   my (@options, @date_options);
319   push @options,      $locale->text('Account')     . " : $form->{accno} $form->{account_description}" if ($form->{accno});
320   push @options,      $locale->text('Source')      . " : $form->{source}"                             if ($form->{source});
321   push @options,      $locale->text('Reference')   . " : $form->{reference}"                          if ($form->{reference});
322   push @options,      $locale->text('Description') . " : $form->{description}"                        if ($form->{description});
323   push @options,      $locale->text('Notes')       . " : $form->{notes}"                              if ($form->{notes});
324   push @options,      $locale->text('Employee')       . " : $form->{employee_name}"                              if ($form->{employee_name});
325   my $datesorttext = $form->{datesort} eq 'transdate' ? $locale->text('Invoice Date') :  $locale->text('Booking Date');
326   push @date_options,      "$datesorttext"                              if ($form->{datesort} and ($form->{datefrom} or $form->{dateto}));
327   push @date_options, $locale->text('From'), $locale->date(\%myconfig, $form->{datefrom}, 1)          if ($form->{datefrom});
328   push @date_options, $locale->text('Bis'),  $locale->date(\%myconfig, $form->{dateto},   1)          if ($form->{dateto});
329   push @options,      join(' ', @date_options)                                                        if (scalar @date_options);
330
331   if ($form->{department}) {
332     my ($department) = split /--/, $form->{department};
333     push @options, $locale->text('Department') . " : $department";
334   }
335
336
337   my $callback = build_std_url('action=generate_report', grep { $form->{$_} } @hidden_variables);
338   print URL $callback;
339   close URL;
340
341   $form->{l_credit_accno}     = 'Y';
342   $form->{l_debit_accno}      = 'Y';
343   $form->{l_credit_tax}       = 'Y';
344   $form->{l_debit_tax}        = 'Y';
345 #  $form->{l_gldate}           = 'Y';  # Spalte mit gldate immer anzeigen
346   $form->{l_credit_tax_accno} = 'Y';
347   $form->{l_datesort} = 'Y';
348   $form->{l_debit_tax_accno}  = 'Y';
349   $form->{l_balance}          = $form->{accno} ? 'Y' : '';
350
351   my %column_defs = (
352     'id'               => { 'text' => $locale->text('ID'), },
353     'transdate'        => { 'text' => $locale->text('Invoice Date'), },
354     'gldate'           => { 'text' => $locale->text('Booking Date'), },
355     'reference'        => { 'text' => $locale->text('Reference'), },
356     'source'           => { 'text' => $locale->text('Source'), },
357     'description'      => { 'text' => $locale->text('Description'), },
358     'notes'            => { 'text' => $locale->text('Notes'), },
359     'debit'            => { 'text' => $locale->text('Debit'), },
360     'debit_accno'      => { 'text' => $locale->text('Debit Account'), },
361     'credit'           => { 'text' => $locale->text('Credit'), },
362     'credit_accno'     => { 'text' => $locale->text('Credit Account'), },
363     'debit_tax'        => { 'text' => $locale->text('Debit Tax'), },
364     'debit_tax_accno'  => { 'text' => $locale->text('Debit Tax Account'), },
365     'credit_tax'       => { 'text' => $locale->text('Credit Tax'), },
366     'credit_tax_accno' => { 'text' => $locale->text('Credit Tax Account'), },
367     'balance'          => { 'text' => $locale->text('Balance'), },
368     'projectnumbers'   => { 'text' => $locale->text('Project Numbers'), },
369     'employee'         => { 'text' => $locale->text('Employee'), },
370   );
371
372   foreach my $name (qw(id transdate gldate reference description debit_accno credit_accno debit_tax_accno credit_tax_accno)) {
373     my $sortname                = $name =~ m/accno/ ? 'accno' : $name;
374     my $sortdir                 = $sortname eq $form->{sort} ? 1 - $form->{sortdir} : $form->{sortdir};
375     $column_defs{$name}->{link} = $callback . "&sort=$sortname&sortdir=$sortdir";
376   }
377
378   map { $column_defs{$_}->{visible} = $form->{"l_${_}"} ? 1 : 0 } @columns;
379   map { $column_defs{$_}->{visible} = 0 } qw(debit_accno credit_accno debit_tax_accno credit_tax_accno) if $form->{accno};
380
381   my %column_alignment;
382   map { $column_alignment{$_}     = 'right'  } qw(balance id debit credit debit_tax credit_tax balance);
383   map { $column_alignment{$_}     = 'center' } qw(transdate gldate reference debit_accno credit_accno debit_tax_accno credit_tax_accno);
384   map { $column_alignment{$_}     = 'left' } qw(description source notes);
385   map { $column_defs{$_}->{align} = $column_alignment{$_} } keys %column_alignment;
386
387   my $report = SL::ReportGenerator->new(\%myconfig, $form);
388
389   $report->set_columns(%column_defs);
390   $report->set_column_order(@columns);
391
392   $report->set_export_options('generate_report', @hidden_variables, qw(sort sortdir));
393
394   $report->set_sort_indicator($form->{sort} eq 'accno' ? 'debit_accno' : $form->{sort}, $form->{sortdir});
395
396   $report->set_options('top_info_text'        => join("\n", @options),
397                        'output_format'        => 'HTML',
398                        'title'                => $form->{title},
399                        'attachment_basename'  => $locale->text('general_ledger_list') . strftime('_%Y%m%d', localtime time),
400     );
401   $report->set_options_from_form();
402   $locale->set_numberformat_wo_thousands_separator(\%myconfig) if lc($report->{options}->{output_format}) eq 'csv';
403
404   # add sort to callback
405   $form->{callback} = "$callback&sort=" . E($form->{sort}) . "&sortdir=" . E($form->{sortdir});
406
407
408   my @totals_columns = qw(debit credit debit_tax credit_tax);
409   my %subtotals      = map { $_ => 0 } @totals_columns;
410   my %totals         = map { $_ => 0 } @totals_columns;
411   my $idx            = 0;
412
413   foreach my $ref (@{ $form->{GL} }) {
414
415     my %rows;
416
417     foreach my $key (qw(debit credit debit_tax credit_tax)) {
418       $rows{$key} = [];
419       foreach my $idx (sort keys(%{ $ref->{$key} })) {
420         my $value         = $ref->{$key}->{$idx};
421         $subtotals{$key} += $value;
422         $totals{$key}    += $value;
423         if ($key =~ /debit.*/) {
424           $ml = -1;
425         } else {
426           $ml = 1;
427         }
428         $form->{balance}  = $form->{balance} + $value * $ml;
429         push @{ $rows{$key} }, $form->format_amount(\%myconfig, $value, 2);
430       }
431     }
432
433     foreach my $key (qw(debit_accno credit_accno debit_tax_accno credit_tax_accno ac_transdate source)) {
434       my $col = $key eq 'ac_transdate' ? 'transdate' : $key;
435       $rows{$col} = [ map { $ref->{$key}->{$_} } sort keys(%{ $ref->{$key} }) ];
436     }
437
438     my $row = { };
439     map { $row->{$_} = { 'data' => '', 'align' => $column_alignment{$_} } } @columns;
440
441     my $sh = "";
442     if ($form->{balance} < 0) {
443       $sh = " S";
444       $ml = -1;
445     } elsif ($form->{balance} > 0) {
446       $sh = " H";
447       $ml = 1;
448     }
449     my $data = $form->format_amount(\%myconfig, ($form->{balance} * $ml), 2);
450     $data .= $sh;
451
452     $row->{balance}->{data}        = $data;
453     $row->{projectnumbers}->{data} = join ", ", sort { lc($a) cmp lc($b) } keys %{ $ref->{projectnumbers} };
454
455     map { $row->{$_}->{data} = $ref->{$_} } qw(id reference description notes gldate employee);
456
457     map { $row->{$_}->{data} = \@{ $rows{$_} }; } qw(transdate debit credit debit_accno credit_accno debit_tax_accno credit_tax_accno source);
458
459     foreach my $col (qw(debit_accno credit_accno debit_tax_accno credit_tax_accno)) {
460       $row->{$col}->{link} = [ map { "${callback}&accno=" . E($_) } @{ $rows{$col} } ];
461     }
462
463     map { $row->{$_}->{data} = \@{ $rows{$_} } if ($ref->{"${_}_accno"} ne "") } qw(debit_tax credit_tax);
464
465     $row->{reference}->{link} = build_std_url("script=$ref->{module}.pl", 'action=edit', 'id=' . E($ref->{id}), 'callback');
466
467     my $row_set = [ $row ];
468
469     if ( ($form->{l_subtotal} eq 'Y' && !$form->{report_generator_csv_options_for_import} )
470         && (($idx == (scalar @{ $form->{GL} } - 1))
471             || ($ref->{ $form->{sort} } ne $form->{GL}->[$idx + 1]->{ $form->{sort} }))) {
472       push @{ $row_set }, create_subtotal_row(\%subtotals, \@columns, \%column_alignment, [ qw(debit credit) ], 'listsubtotal');
473     }
474
475     $report->add_data($row_set);
476
477     $idx++;
478   }
479
480   # = 0 for balanced ledger
481   my $balanced_ledger = $totals{debit} + $totals{debit_tax} - $totals{credit} - $totals{credit_tax};
482
483   my $row = create_subtotal_row(\%totals, \@columns, \%column_alignment, [ qw(debit credit debit_tax credit_tax) ], 'listtotal');
484
485   my $sh = "";
486   if ($form->{balance} < 0) {
487     $sh = " S";
488     $ml = -1;
489   } elsif ($form->{balance} > 0) {
490     $sh = " H";
491     $ml = 1;
492   }
493   my $data = $form->format_amount(\%myconfig, ($form->{balance} * $ml), 2);
494   $data .= $sh;
495
496   $row->{balance}->{data}        = $data;
497
498   if ( !$form->{report_generator_csv_options_for_import} ) {
499     $report->add_separator();
500     $report->add_data($row);
501   }
502
503   my $raw_bottom_info_text;
504
505   if (!$form->{accno} && (abs($balanced_ledger) >  0.001)) {
506     $raw_bottom_info_text .=
507         '<p><span class="unbalanced_ledger">'
508       . $locale->text('Unbalanced Ledger')
509       . ': '
510       . $form->format_amount(\%myconfig, $balanced_ledger, 3)
511       . '</span></p> ';
512   }
513
514   $raw_bottom_info_text .= $form->parse_html_template('gl/generate_report_bottom');
515
516   $report->set_options('raw_bottom_info_text' => $raw_bottom_info_text);
517
518   $report->generate_with_headers();
519
520   $main::lxdebug->leave_sub();
521 }
522
523 sub update {
524   $main::lxdebug->enter_sub();
525
526   $main::auth->assert('general_ledger');
527
528   my $form     = $main::form;
529   my %myconfig = %main::myconfig;
530
531   $form->{oldtransdate} = $form->{transdate};
532
533   my @a           = ();
534   my $count       = 0;
535   my $debittax    = 0;
536   my $credittax   = 0;
537   my $debitcount  = 0;
538   my $creditcount = 0;
539   my ($debitcredit, $amount);
540
541   my @flds =
542     qw(accno debit credit projectnumber fx_transaction source memo tax taxchart);
543
544   for my $i (1 .. $form->{rowcount}) {
545
546     unless (($form->{"debit_$i"} eq "") && ($form->{"credit_$i"} eq "")) {
547       for (qw(debit credit tax)) {
548         $form->{"${_}_$i"} =
549           $form->parse_amount(\%myconfig, $form->{"${_}_$i"});
550       }
551
552       push @a, {};
553       $debitcredit = ($form->{"debit_$i"} == 0) ? "0" : "1";
554       if ($debitcredit) {
555         $debitcount++;
556       } else {
557         $creditcount++;
558       }
559
560       if (($debitcount >= 2) && ($creditcount == 2)) {
561         $form->{"credit_$i"} = 0;
562         $form->{"tax_$i"}    = 0;
563         $creditcount--;
564         $form->{creditlock} = 1;
565       }
566       if (($creditcount >= 2) && ($debitcount == 2)) {
567         $form->{"debit_$i"} = 0;
568         $form->{"tax_$i"}   = 0;
569         $debitcount--;
570         $form->{debitlock} = 1;
571       }
572       if (($creditcount == 1) && ($debitcount == 2)) {
573         $form->{creditlock} = 1;
574       }
575       if (($creditcount == 2) && ($debitcount == 1)) {
576         $form->{debitlock} = 1;
577       }
578       if ($debitcredit && $credittax) {
579         $form->{"taxchart_$i"} = "0--0.00";
580       }
581       if (!$debitcredit && $debittax) {
582         $form->{"taxchart_$i"} = "0--0.00";
583       }
584       $amount =
585         ($form->{"debit_$i"} == 0)
586         ? $form->{"credit_$i"}
587         : $form->{"debit_$i"};
588       my $j = $#a;
589       if (($debitcredit && $credittax) || (!$debitcredit && $debittax)) {
590         $form->{"taxchart_$i"} = "0--0.00";
591         $form->{"tax_$i"}      = 0;
592       }
593       my ($taxkey, $rate) = split(/--/, $form->{"taxchart_$i"});
594       if ($taxkey > 1) {
595         if ($debitcredit) {
596           $debittax = 1;
597         } else {
598           $credittax = 1;
599         }
600         if ($form->{taxincluded}) {
601           $form->{"tax_$i"} = $amount / ($rate + 1) * $rate;
602         } else {
603           $form->{"tax_$i"} = $amount * $rate;
604         }
605       } else {
606         $form->{"tax_$i"} = 0;
607       }
608
609       for (@flds) { $a[$j]->{$_} = $form->{"${_}_$i"} }
610       $count++;
611     }
612   }
613
614   for my $i (1 .. $count) {
615     my $j = $i - 1;
616     for (@flds) { $form->{"${_}_$i"} = $a[$j]->{$_} }
617   }
618
619   for my $i ($count + 1 .. $form->{rowcount}) {
620     for (@flds) { delete $form->{"${_}_$i"} }
621   }
622
623   $form->{rowcount} = $count + 1;
624
625   &display_form;
626   $main::lxdebug->leave_sub();
627
628 }
629
630 sub display_form {
631   my ($init) = @_;
632   $main::lxdebug->enter_sub();
633
634   $main::auth->assert('general_ledger');
635
636   my $form     = $main::form;
637   my %myconfig = %main::myconfig;
638
639   &form_header($init);
640
641   #   for $i (1 .. $form->{rowcount}) {
642   #     $form->{totaldebit} += $form->parse_amount(\%myconfig, $form->{"debit_$i"});
643   #     $form->{totalcredit} += $form->parse_amount(\%myconfig, $form->{"credit_$i"});
644   #
645   #     &form_row($i);
646   #   }
647   &display_rows($init);
648   &form_footer;
649   $main::lxdebug->leave_sub();
650
651 }
652
653 sub display_rows {
654   my ($init) = @_;
655   $main::lxdebug->enter_sub();
656
657   $main::auth->assert('general_ledger');
658
659   my $form     = $main::form;
660   my %myconfig = %main::myconfig;
661   my $cgi      = $::request->{cgi};
662
663   $form->{debit_1}     = 0 if !$form->{"debit_1"};
664   $form->{totaldebit}  = 0;
665   $form->{totalcredit} = 0;
666
667   my %project_labels = ();
668   my @project_values = ("");
669   foreach my $item (@{ $form->{"ALL_PROJECTS"} }) {
670     push(@project_values, $item->{"id"});
671     $project_labels{$item->{"id"}} = $item->{"projectnumber"};
672   }
673
674   my %chart_labels = ();
675   my @chart_values = ();
676   my %charts = ();
677   my $taxchart_init;
678   foreach my $item (@{ $form->{ALL_CHARTS} }) {
679     if ($item->{charttype} eq 'H'){ #falls ĂĽberschrift
680       next;                         #ĂĽberspringen (Bug 1150)
681     }
682     my $key = $item->{accno} . "--" . $item->{tax_id};
683     $taxchart_init = $item->{tax_id} unless (@chart_values);
684     push(@chart_values, $key);
685     $chart_labels{$key} = $item->{accno} . "--" . $item->{description};
686     $charts{$item->{accno}} = $item;
687   }
688
689   my %taxchart_labels = ();
690   my @taxchart_values = ();
691   my %taxcharts = ();
692   foreach my $item (@{ $form->{ALL_TAXCHARTS} }) {
693     my $key = $item->{id} . "--" . $item->{rate};
694     $taxchart_init = $key if ($taxchart_init == $item->{id});
695     push(@taxchart_values, $key);
696     $taxchart_labels{$key} = $item->{taxdescription} . " " . $item->{rate} * 100 . ' %';
697     $taxcharts{$item->{id}} = $item;
698   }
699
700   my ($source, $memo, $source_hidden, $memo_hidden);
701   for my $i (1 .. $form->{rowcount}) {
702     if ($form->{show_details}) {
703       $source = qq|
704       <td><input name="source_$i" value="$form->{"source_$i"}" size="16"></td>|;
705       $memo = qq|
706       <td><input name="memo_$i" value="$form->{"memo_$i"}" size="16"></td>|;
707     } else {
708       $source_hidden = qq|
709       <input type="hidden" name="source_$i" value="$form->{"source_$i"}" size="16">|;
710       $memo_hidden = qq|
711       <input type="hidden" name="memo_$i" value="$form->{"memo_$i"}" size="16">|;
712     }
713
714     my $selected_accno_full;
715     my ($accno_row) = split(/--/, $form->{"accno_$i"});
716     my $item = $charts{$accno_row};
717     $selected_accno_full = "$item->{accno}--$item->{tax_id}";
718
719     my $selected_taxchart = $form->{"taxchart_$i"};
720     my ($selected_accno, $selected_tax_id) = split(/--/, $selected_accno_full);
721     my ($previous_accno, $previous_tax_id) = split(/--/, $form->{"previous_accno_$i"});
722
723     if ($previous_accno &&
724         ($previous_accno eq $selected_accno) &&
725         ($previous_tax_id ne $selected_tax_id)) {
726       my $item = $taxcharts{$selected_tax_id};
727       $selected_taxchart = "$item->{id}--$item->{rate}";
728     }
729
730     $selected_accno      = '' if ($init);
731     $selected_taxchart ||= $taxchart_init;
732
733     my $accno = qq|<td>| .
734       NTI($cgi->popup_menu('-name' => "accno_$i",
735                            '-id' => "accno_$i",
736                            '-onChange' => "setTaxkey($i)",
737                            '-style' => 'width:200px',
738                            '-values' => \@chart_values,
739                            '-labels' => \%chart_labels,
740                            '-default' => $selected_accno_full))
741       . $cgi->hidden('-name' => "previous_accno_$i",
742                      '-default' => $selected_accno_full)
743       . qq|</td>|;
744     my $tax_ddbox = qq|<td>| .
745       NTI($cgi->popup_menu('-name' => "taxchart_$i",
746                            '-id' => "taxchart_$i",
747                            '-style' => 'width:200px',
748                            '-values' => \@taxchart_values,
749                            '-labels' => \%taxchart_labels,
750                            '-default' => $selected_taxchart))
751       . qq|</td>|;
752
753     my ($fx_transaction, $checked);
754     if ($init) {
755       if ($form->{transfer}) {
756         $fx_transaction = qq|
757         <td><input name="fx_transaction_$i" class=checkbox type=checkbox value=1></td>
758     |;
759       }
760
761     } else {
762       if ($form->{"debit_$i"} != 0) {
763         $form->{totaldebit} += $form->{"debit_$i"};
764         if (!$form->{taxincluded}) {
765           $form->{totaldebit} += $form->{"tax_$i"};
766         }
767       } else {
768         $form->{totalcredit} += $form->{"credit_$i"};
769         if (!$form->{taxincluded}) {
770           $form->{totalcredit} += $form->{"tax_$i"};
771         }
772       }
773
774       for (qw(debit credit tax)) {
775         $form->{"${_}_$i"} =
776           ($form->{"${_}_$i"})
777           ? $form->format_amount(\%myconfig, $form->{"${_}_$i"}, 2)
778           : "";
779       }
780
781       if ($i < $form->{rowcount}) {
782         if ($form->{transfer}) {
783           $checked = ($form->{"fx_transaction_$i"}) ? "1" : "";
784           my $x = ($checked) ? "x" : "";
785           $fx_transaction = qq|
786       <td><input type=hidden name="fx_transaction_$i" value="$checked">$x</td>
787     |;
788         }
789         $form->hide_form("accno_$i");
790
791       } else {
792         if ($form->{transfer}) {
793           $fx_transaction = qq|
794       <td><input name="fx_transaction_$i" class=checkbox type=checkbox value=1></td>
795     |;
796         }
797       }
798     }
799     my $debitreadonly  = "";
800     my $creditreadonly = "";
801     if ($i == $form->{rowcount}) {
802       if ($form->{debitlock}) {
803         $debitreadonly = "readonly";
804       } elsif ($form->{creditlock}) {
805         $creditreadonly = "readonly";
806       }
807     }
808
809     my $projectnumber =
810       NTI($cgi->popup_menu('-name' => "project_id_$i",
811                            '-values' => \@project_values,
812                            '-labels' => \%project_labels,
813                            '-default' => $form->{"project_id_$i"} ));
814     my $projectnumber_hidden = qq|
815     <input type="hidden" name="project_id_$i" value="$form->{"project_id_$i"}">|;
816
817     my $copy2credit = $i == 1 ? 'onkeyup="copy_debit_to_credit()"' : '';
818
819     print qq|<tr valign=top>
820     $accno
821     <td id="chart_balance_$i" align="right">&nbsp;</td>
822     $fx_transaction
823     <td><input name="debit_$i" size="8" value="$form->{"debit_$i"}" accesskey=$i $copy2credit $debitreadonly></td>
824     <td><input name="credit_$i" size=8 value="$form->{"credit_$i"}" $creditreadonly></td>
825     <td><input type="hidden" name="tax_$i" value="$form->{"tax_$i"}">$form->{"tax_$i"}</td>
826     $tax_ddbox|;
827
828     if ($form->{show_details}) {
829       print qq|
830     $source
831     $memo
832     <td>$projectnumber</td>
833 |;
834     } else {
835     print qq|
836     $source_hidden
837     $memo_hidden
838     $projectnumber_hidden
839     |;
840     }
841     print qq|
842   </tr>
843 |;
844   }
845
846   $form->hide_form(qw(rowcount selectaccno));
847
848   $main::lxdebug->leave_sub();
849
850 }
851
852 sub form_header {
853   $::lxdebug->enter_sub;
854   $::auth->assert('general_ledger');
855
856   my ($init) = @_;
857
858   my @old_project_ids = grep { $_ } map{ $::form->{"project_id_$_"} } 1..$::form->{rowcount};
859
860   $::form->get_lists("projects"  => { "key"       => "ALL_PROJECTS",
861                                     "all"       => 0,
862                                     "old_id"    => \@old_project_ids },
863                    "charts"    => { "key"       => "ALL_CHARTS",
864                                     "transdate" => $::form->{transdate} },
865                    "taxcharts" => "ALL_TAXCHARTS");
866
867   GL->get_chart_balances('charts' => $::form->{ALL_CHARTS});
868
869   my $title      = $::form->{title};
870   $::form->{title} = $::locale->text("$title General Ledger Transaction");
871   # $locale->text('Add General Ledger Transaction')
872   # $locale->text('Edit General Ledger Transaction')
873
874   map { $::form->{$_} =~ s/\"/&quot;/g }
875     qw(chart taxchart);
876
877   $::form->{selectdepartment} =~ s/ selected//;
878   $::form->{selectdepartment} =~
879     s/option>\Q$::form->{department}\E/option selected>$::form->{department}/;
880
881   if ($init) {
882     $::form->{fokus} = "gl.reference";
883     $::form->{taxincluded} = "1";
884   } else {
885     $::form->{fokus} = qq|gl.accno_$::form->{rowcount}|;
886   }
887
888   $::form->{previous_id}     ||= "--";
889   $::form->{previous_gldate} ||= "--";
890
891   $::form->header;
892   print $::form->parse_html_template('gl/form_header', {
893     hide_title => $title,
894   });
895
896   $::lxdebug->leave_sub;
897
898 }
899
900 sub form_footer {
901   $::lxdebug->enter_sub;
902   $::auth->assert('general_ledger');
903
904   my ($follow_ups, $follow_ups_due);
905
906   if ($::form->{id}) {
907     $follow_ups     = FU->follow_ups('trans_id' => $::form->{id});
908     $follow_ups_due = sum map { $_->{due} * 1 } @{ $follow_ups || [] };
909   }
910
911   my $radieren = $::form->current_date(\%::myconfig) eq $::form->{gldate};
912
913   print $::form->parse_html_template('gl/form_footer', {
914     radieren       => $radieren,
915     follow_ups     => $follow_ups,
916     follow_ups_due => $follow_ups_due,
917   });
918
919   $::lxdebug->leave_sub;
920 }
921
922 sub delete {
923   $main::lxdebug->enter_sub();
924
925   my $form     = $main::form;
926   my $locale   = $main::locale;
927
928   $form->header;
929
930   print qq|
931 <form method=post action=gl.pl>
932 |;
933
934   map { $form->{$_} =~ s/\"/&quot;/g } qw(reference description);
935
936   delete $form->{header};
937
938   foreach my $key (keys %$form) {
939     next if (($key eq 'login') || ($key eq 'password') || ('' ne ref $form->{$key}));
940     print qq|<input type="hidden" name="$key" value="$form->{$key}">\n|;
941   }
942
943   print qq|
944 <h2 class=confirm>| . $locale->text('Confirm!') . qq|</h2>
945
946 <h4>|
947     . $locale->text('Are you sure you want to delete Transaction')
948     . qq| $form->{reference}</h4>
949
950 <input name=action class=submit type=submit value="|
951     . $locale->text('Yes') . qq|">
952 </form>
953 |;
954   $main::lxdebug->leave_sub();
955
956 }
957
958 sub yes {
959   $main::lxdebug->enter_sub();
960
961   my $form     = $main::form;
962   my %myconfig = %main::myconfig;
963   my $locale   = $main::locale;
964
965   if (GL->delete_transaction(\%myconfig, \%$form)){
966     # saving the history
967       if(!exists $form->{addition} && $form->{id} ne "") {
968         $form->{snumbers} = qq|ordnumber_| . $form->{ordnumber};
969         $form->{addition} = "DELETED";
970         $form->save_history;
971       }
972     # /saving the history
973     $form->redirect($locale->text('Transaction deleted!'))
974   }
975   $form->error($locale->text('Cannot delete transaction!'));
976   $main::lxdebug->leave_sub();
977
978 }
979
980 sub post_transaction {
981   $main::lxdebug->enter_sub();
982
983   my $form     = $main::form;
984   my %myconfig = %main::myconfig;
985   my $locale   = $main::locale;
986
987   # check if there is something in reference and date
988   $form->isblank("reference",   $locale->text('Reference missing!'));
989   $form->isblank("transdate",   $locale->text('Transaction Date missing!'));
990   $form->isblank("description", $locale->text('Description missing!'));
991
992   my $transdate = $form->datetonum($form->{transdate}, \%myconfig);
993   my $closedto  = $form->datetonum($form->{closedto},  \%myconfig);
994
995   my @a           = ();
996   my $count       = 0;
997   my $debittax    = 0;
998   my $credittax   = 0;
999   my $debitcount  = 0;
1000   my $creditcount = 0;
1001   my $debitcredit;
1002   my %split_safety = ();
1003
1004   my @flds = qw(accno debit credit projectnumber fx_transaction source memo tax taxchart);
1005
1006   for my $i (1 .. $form->{rowcount}) {
1007     next if $form->{"debit_$i"} eq "" && $form->{"credit_$i"} eq "";
1008
1009     for (qw(debit credit tax)) {
1010       $form->{"${_}_$i"} = $form->parse_amount(\%myconfig, $form->{"${_}_$i"});
1011     }
1012
1013     push @a, {};
1014     $debitcredit = ($form->{"debit_$i"} == 0) ? "0" : "1";
1015
1016     $split_safety{   $form->{"debit_$i"}  <=> 0 }++;
1017     $split_safety{ - $form->{"credit_$i"} <=> 0 }++;
1018
1019     if ($debitcredit) {
1020       $debitcount++;
1021     } else {
1022       $creditcount++;
1023     }
1024
1025     if (($debitcount >= 2) && ($creditcount == 2)) {
1026       $form->{"credit_$i"} = 0;
1027       $form->{"tax_$i"}    = 0;
1028       $creditcount--;
1029       $form->{creditlock} = 1;
1030     }
1031     if (($creditcount >= 2) && ($debitcount == 2)) {
1032       $form->{"debit_$i"} = 0;
1033       $form->{"tax_$i"}   = 0;
1034       $debitcount--;
1035       $form->{debitlock} = 1;
1036     }
1037     if (($creditcount == 1) && ($debitcount == 2)) {
1038       $form->{creditlock} = 1;
1039     }
1040     if (($creditcount == 2) && ($debitcount == 1)) {
1041       $form->{debitlock} = 1;
1042     }
1043     if ($debitcredit && $credittax) {
1044       $form->{"taxchart_$i"} = "0--0.00";
1045     }
1046     if (!$debitcredit && $debittax) {
1047       $form->{"taxchart_$i"} = "0--0.00";
1048     }
1049     my $amount = ($form->{"debit_$i"} == 0)
1050             ? $form->{"credit_$i"}
1051             : $form->{"debit_$i"};
1052     my $j = $#a;
1053     if (($debitcredit && $credittax) || (!$debitcredit && $debittax)) {
1054       $form->{"taxchart_$i"} = "0--0.00";
1055       $form->{"tax_$i"}      = 0;
1056     }
1057     my ($taxkey, $rate) = split(/--/, $form->{"taxchart_$i"});
1058     if ($taxkey > 1) {
1059       if ($debitcredit) {
1060         $debittax = 1;
1061       } else {
1062         $credittax = 1;
1063       }
1064       if ($form->{taxincluded}) {
1065         $form->{"tax_$i"} = $amount / ($rate + 1) * $rate;
1066         if ($debitcredit) {
1067           $form->{"debit_$i"} = $form->{"debit_$i"} - $form->{"tax_$i"};
1068         } else {
1069           $form->{"credit_$i"} = $form->{"credit_$i"} - $form->{"tax_$i"};
1070         }
1071       } else {
1072         $form->{"tax_$i"} = $amount * $rate;
1073       }
1074     } else {
1075       $form->{"tax_$i"} = 0;
1076     }
1077
1078     for (@flds) { $a[$j]->{$_} = $form->{"${_}_$i"} }
1079     $count++;
1080   }
1081
1082   if ($split_safety{-1} > 1 && $split_safety{1} > 1) {
1083     $::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."));
1084   }
1085
1086   for my $i (1 .. $count) {
1087     my $j = $i - 1;
1088     for (@flds) { $form->{"${_}_$i"} = $a[$j]->{$_} }
1089   }
1090
1091   for my $i ($count + 1 .. $form->{rowcount}) {
1092     for (@flds) { delete $form->{"${_}_$i"} }
1093   }
1094
1095   my ($debit, $credit, $taxtotal);
1096   for my $i (1 .. $form->{rowcount}) {
1097     my $dr  = $form->{"debit_$i"};
1098     my $cr  = $form->{"credit_$i"};
1099     my $tax = $form->{"tax_$i"};
1100     if ($dr && $cr) {
1101       $form->error($locale->text('Cannot post transaction with a debit and credit entry for the same account!'));
1102     }
1103     $debit    += $dr + $tax if $dr;
1104     $credit   += $cr + $tax if $cr;
1105     $taxtotal += $tax if $form->{taxincluded}
1106   }
1107
1108   $form->{taxincluded} = 0 if !$taxtotal;
1109
1110   # this is just for the wise guys
1111   $form->error($locale->text('Cannot post transaction for a closed period!'))
1112     if ($form->date_closed($form->{"transdate"}, \%myconfig));
1113   if ($form->round_amount($debit, 2) != $form->round_amount($credit, 2)) {
1114     $form->error($locale->text('Out of balance transaction!'));
1115   }
1116
1117   if ($form->round_amount($debit, 2) + $form->round_amount($credit, 2) == 0) {
1118     $form->error($locale->text('Empty transaction!'));
1119   }
1120
1121   if ((my $errno = GL->post_transaction(\%myconfig, \%$form)) <= -1) {
1122     $errno *= -1;
1123     my @err;
1124     $err[1] = $locale->text('Cannot have a value in both Debit and Credit!');
1125     $err[2] = $locale->text('Debit and credit out of balance!');
1126     $err[3] = $locale->text('Cannot post a transaction without a value!');
1127
1128     $form->error($err[$errno]);
1129   }
1130   undef($form->{callback});
1131   # saving the history
1132   if(!exists $form->{addition} && $form->{id} ne "") {
1133     $form->{snumbers} = qq|ordnumber_| . $form->{ordnumber};
1134     $form->{addition} = "SAVED";
1135     $form->{what_done} = $locale->text("Buchungsnummer") . " = " . $form->{id};
1136     $form->save_history;
1137   }
1138   # /saving the history
1139
1140   $main::lxdebug->leave_sub();
1141 }
1142
1143 sub post {
1144   $main::lxdebug->enter_sub();
1145
1146   $main::auth->assert('general_ledger');
1147
1148   my $form     = $main::form;
1149   my $locale   = $main::locale;
1150
1151   if ($::myconfig{mandatory_departments} && !$form->{department}) {
1152     $form->{saved_message} = $::locale->text('You have to specify a department.');
1153     update();
1154     exit;
1155   }
1156
1157   $form->{title}  = $locale->text("$form->{title} General Ledger Transaction");
1158   $form->{storno} = 0;
1159
1160   post_transaction();
1161
1162   remove_draft() if $form->{remove_draft};
1163
1164   $form->{callback} = build_std_url("action=add&DONT_LOAD_DRAFT=1", "show_details");
1165   $form->redirect($form->{callback});
1166
1167   $main::lxdebug->leave_sub();
1168 }
1169
1170 sub post_as_new {
1171   $main::lxdebug->enter_sub();
1172
1173   $main::auth->assert('general_ledger');
1174
1175   my $form     = $main::form;
1176
1177   $form->{id} = 0;
1178   &add;
1179   $main::lxdebug->leave_sub();
1180
1181 }
1182
1183 sub storno {
1184   $main::lxdebug->enter_sub();
1185
1186   $main::auth->assert('general_ledger');
1187
1188   my $form     = $main::form;
1189   my %myconfig = %main::myconfig;
1190   my $locale   = $main::locale;
1191
1192   # don't cancel cancelled transactions
1193   if (IS->has_storno(\%myconfig, $form, 'gl')) {
1194     $form->{title} = $locale->text("Cancel Accounts Receivables Transaction");
1195     $form->error($locale->text("Transaction has already been cancelled!"));
1196   }
1197
1198   GL->storno($form, \%myconfig, $form->{id});
1199
1200   # saving the history
1201   if(!exists $form->{addition} && $form->{id} ne "") {
1202     $form->{snumbers} = "ordnumber_$form->{ordnumber}";
1203     $form->{addition} = "STORNO";
1204     $form->save_history;
1205   }
1206   # /saving the history
1207
1208   $form->redirect(sprintf $locale->text("Transaction %d cancelled."), $form->{storno_id});
1209
1210   $main::lxdebug->leave_sub();
1211 }
1212
1213 sub continue {
1214   call_sub($main::form->{nextsub});
1215 }
1216
1217 1;