Dialogbuchungen am selben Tag komplett bearbeiten können
[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   $::form->header;
224   print $::form->parse_html_template('gl/search', {
225     department_label => sub { ("$_[0]{description}--$_[0]{id}")x2 },
226     employee_label => sub { "$_[0]{id}--$_[0]{name}" },
227   });
228
229   $::lxdebug->leave_sub;
230 }
231
232 sub create_subtotal_row {
233   $main::lxdebug->enter_sub();
234
235   my ($totals, $columns, $column_alignment, $subtotal_columns, $class) = @_;
236
237   my $form     = $main::form;
238   my %myconfig = %main::myconfig;
239
240   my $row = { map { $_ => { 'data' => '', 'class' => $class, 'align' => $column_alignment->{$_}, } } @{ $columns } };
241
242   map { $row->{$_}->{data} = $form->format_amount(\%myconfig, $totals->{$_}, 2) } @{ $subtotal_columns };
243
244   map { $totals->{$_} = 0 } @{ $subtotal_columns };
245
246   $main::lxdebug->leave_sub();
247
248   return $row;
249 }
250
251 sub generate_report {
252   $main::lxdebug->enter_sub();
253
254   $main::auth->assert('general_ledger');
255
256   my $form     = $main::form;
257   my %myconfig = %main::myconfig;
258   my $locale   = $main::locale;
259
260   # 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
261
262   # <form method=post action=gl.pl>
263   # <input type=hidden name=sort value=datesort>    # form->{sort} setzen
264   # <input type=hidden name=nextsub value=generate_report>
265
266   # anhand von neuer Variable datesort wird jetzt $form->{sort} auf transdate oder gldate gesetzt
267   # damit ist die Hidden Variable "sort" wahrscheinlich sogar überflüssig
268
269   # ändert man die Sortierreihenfolge per Klick auf eine der Überschriften wird die Variable "sort" per GET übergeben, z.B. id,transdate, gldate, ...
270   # 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
271
272   if ( $form->{sort} eq 'datesort' ) {   # sollte bei einem Post (Aufruf aus Suchmaske) immer wahr sein
273       # je nachdem ob in Suchmaske "transdate" oder "gldate" ausgesucht wurde erstes Suchergebnis entsprechend sortieren
274       $form->{sort} = $form->{datesort};
275   };
276
277   # was passiert hier?
278   report_generator_set_default_sort("$form->{datesort}", 1);
279 #  report_generator_set_default_sort('transdate', 1);
280
281   GL->all_transactions(\%myconfig, \%$form);
282
283   my %acctype = ('A' => $locale->text('Asset'),
284                  'C' => $locale->text('Contra'),
285                  'L' => $locale->text('Liability'),
286                  'Q' => $locale->text('Equity'),
287                  'I' => $locale->text('Revenue'),
288                  'E' => $locale->text('Expense'),);
289
290   $form->{title} = $locale->text('Journal');
291   if ($form->{category} ne 'X') {
292     $form->{title} .= " : " . $locale->text($acctype{ $form->{category} });
293   }
294
295   $form->{landscape} = 1;
296
297   my $ml = ($form->{ml} =~ /(A|E|Q)/) ? -1 : 1;
298
299   my @columns = qw(
300     gldate         transdate        id             reference      description
301     notes          source           debit          debit_accno
302     credit         credit_accno     debit_tax      debit_tax_accno
303     credit_tax     credit_tax_accno projectnumbers balance employee
304   );
305
306   # add employee here, so that variable is still known and passed in url when choosing a different sort order in resulting table
307   my @hidden_variables = qw(accno source reference department description notes project_id datefrom dateto employee datesort category l_subtotal);
308   push @hidden_variables, map { "l_${_}" } @columns;
309   foreach ( @hidden_variables ) {
310       print URL "$_\n";
311   };
312
313   my (@options, @date_options);
314   push @options,      $locale->text('Account')     . " : $form->{accno} $form->{account_description}" if ($form->{accno});
315   push @options,      $locale->text('Source')      . " : $form->{source}"                             if ($form->{source});
316   push @options,      $locale->text('Reference')   . " : $form->{reference}"                          if ($form->{reference});
317   push @options,      $locale->text('Description') . " : $form->{description}"                        if ($form->{description});
318   push @options,      $locale->text('Notes')       . " : $form->{notes}"                              if ($form->{notes});
319   push @options,      $locale->text('Employee')       . " : $form->{employee_name}"                              if ($form->{employee_name});
320   my $datesorttext = $form->{datesort} eq 'transdate' ? $locale->text('Invoice Date') :  $locale->text('Booking Date');
321   push @date_options,      "$datesorttext"                              if ($form->{datesort} and ($form->{datefrom} or $form->{dateto}));
322   push @date_options, $locale->text('From'), $locale->date(\%myconfig, $form->{datefrom}, 1)          if ($form->{datefrom});
323   push @date_options, $locale->text('Bis'),  $locale->date(\%myconfig, $form->{dateto},   1)          if ($form->{dateto});
324   push @options,      join(' ', @date_options)                                                        if (scalar @date_options);
325
326   if ($form->{department}) {
327     my ($department) = split /--/, $form->{department};
328     push @options, $locale->text('Department') . " : $department";
329   }
330
331
332   my $callback = build_std_url('action=generate_report', grep { $form->{$_} } @hidden_variables);
333   print URL $callback;
334   close URL;
335
336   $form->{l_credit_accno}     = 'Y';
337   $form->{l_debit_accno}      = 'Y';
338   $form->{l_credit_tax}       = 'Y';
339   $form->{l_debit_tax}        = 'Y';
340 #  $form->{l_gldate}           = 'Y';  # Spalte mit gldate immer anzeigen
341   $form->{l_credit_tax_accno} = 'Y';
342   $form->{l_datesort} = 'Y';
343   $form->{l_debit_tax_accno}  = 'Y';
344   $form->{l_balance}          = $form->{accno} ? 'Y' : '';
345
346   my %column_defs = (
347     'id'               => { 'text' => $locale->text('ID'), },
348     'transdate'        => { 'text' => $locale->text('Invoice Date'), },
349     'gldate'           => { 'text' => $locale->text('Booking Date'), },
350     'reference'        => { 'text' => $locale->text('Reference'), },
351     'source'           => { 'text' => $locale->text('Source'), },
352     'description'      => { 'text' => $locale->text('Description'), },
353     'notes'            => { 'text' => $locale->text('Notes'), },
354     'debit'            => { 'text' => $locale->text('Debit'), },
355     'debit_accno'      => { 'text' => $locale->text('Debit Account'), },
356     'credit'           => { 'text' => $locale->text('Credit'), },
357     'credit_accno'     => { 'text' => $locale->text('Credit Account'), },
358     'debit_tax'        => { 'text' => $locale->text('Debit Tax'), },
359     'debit_tax_accno'  => { 'text' => $locale->text('Debit Tax Account'), },
360     'credit_tax'       => { 'text' => $locale->text('Credit Tax'), },
361     'credit_tax_accno' => { 'text' => $locale->text('Credit Tax Account'), },
362     'balance'          => { 'text' => $locale->text('Balance'), },
363     'projectnumbers'   => { 'text' => $locale->text('Project Numbers'), },
364     'employee'         => { 'text' => $locale->text('Employee'), },
365   );
366
367   foreach my $name (qw(id transdate gldate reference description debit_accno credit_accno debit_tax_accno credit_tax_accno)) {
368     my $sortname                = $name =~ m/accno/ ? 'accno' : $name;
369     my $sortdir                 = $sortname eq $form->{sort} ? 1 - $form->{sortdir} : $form->{sortdir};
370     $column_defs{$name}->{link} = $callback . "&sort=$sortname&sortdir=$sortdir";
371   }
372
373   map { $column_defs{$_}->{visible} = $form->{"l_${_}"} ? 1 : 0 } @columns;
374   map { $column_defs{$_}->{visible} = 0 } qw(debit_accno credit_accno debit_tax_accno credit_tax_accno) if $form->{accno};
375
376   my %column_alignment;
377   map { $column_alignment{$_}     = 'right'  } qw(balance id debit credit debit_tax credit_tax balance);
378   map { $column_alignment{$_}     = 'center' } qw(transdate gldate reference debit_accno credit_accno debit_tax_accno credit_tax_accno);
379   map { $column_alignment{$_}     = 'left' } qw(description source notes);
380   map { $column_defs{$_}->{align} = $column_alignment{$_} } keys %column_alignment;
381
382   my $report = SL::ReportGenerator->new(\%myconfig, $form);
383
384   $report->set_columns(%column_defs);
385   $report->set_column_order(@columns);
386
387   $report->set_export_options('generate_report', @hidden_variables, qw(sort sortdir));
388
389   $report->set_sort_indicator($form->{sort} eq 'accno' ? 'debit_accno' : $form->{sort}, $form->{sortdir});
390
391   $report->set_options('top_info_text'        => join("\n", @options),
392                        'output_format'        => 'HTML',
393                        'title'                => $form->{title},
394                        'attachment_basename'  => $locale->text('general_ledger_list') . strftime('_%Y%m%d', localtime time),
395     );
396   $report->set_options_from_form();
397   $locale->set_numberformat_wo_thousands_separator(\%myconfig) if lc($report->{options}->{output_format}) eq 'csv';
398
399   # add sort to callback
400   $form->{callback} = "$callback&sort=" . E($form->{sort}) . "&sortdir=" . E($form->{sortdir});
401
402
403   my @totals_columns = qw(debit credit debit_tax credit_tax);
404   my %subtotals      = map { $_ => 0 } @totals_columns;
405   my %totals         = map { $_ => 0 } @totals_columns;
406   my $idx            = 0;
407
408   foreach my $ref (@{ $form->{GL} }) {
409
410     my %rows;
411
412     foreach my $key (qw(debit credit debit_tax credit_tax)) {
413       $rows{$key} = [];
414       foreach my $idx (sort keys(%{ $ref->{$key} })) {
415         my $value         = $ref->{$key}->{$idx};
416         $subtotals{$key} += $value;
417         $totals{$key}    += $value;
418         if ($key =~ /debit.*/) {
419           $ml = -1;
420         } else {
421           $ml = 1;
422         }
423         $form->{balance}  = $form->{balance} + $value * $ml;
424         push @{ $rows{$key} }, $form->format_amount(\%myconfig, $value, 2);
425       }
426     }
427
428     foreach my $key (qw(debit_accno credit_accno debit_tax_accno credit_tax_accno ac_transdate source)) {
429       my $col = $key eq 'ac_transdate' ? 'transdate' : $key;
430       $rows{$col} = [ map { $ref->{$key}->{$_} } sort keys(%{ $ref->{$key} }) ];
431     }
432
433     my $row = { };
434     map { $row->{$_} = { 'data' => '', 'align' => $column_alignment{$_} } } @columns;
435
436     my $sh = "";
437     if ($form->{balance} < 0) {
438       $sh = " S";
439       $ml = -1;
440     } elsif ($form->{balance} > 0) {
441       $sh = " H";
442       $ml = 1;
443     }
444     my $data = $form->format_amount(\%myconfig, ($form->{balance} * $ml), 2);
445     $data .= $sh;
446
447     $row->{balance}->{data}        = $data;
448     $row->{projectnumbers}->{data} = join ", ", sort { lc($a) cmp lc($b) } keys %{ $ref->{projectnumbers} };
449
450     map { $row->{$_}->{data} = $ref->{$_} } qw(id reference description notes gldate employee);
451
452     map { $row->{$_}->{data} = \@{ $rows{$_} }; } qw(transdate debit credit debit_accno credit_accno debit_tax_accno credit_tax_accno source);
453
454     foreach my $col (qw(debit_accno credit_accno debit_tax_accno credit_tax_accno)) {
455       $row->{$col}->{link} = [ map { "${callback}&accno=" . E($_) } @{ $rows{$col} } ];
456     }
457
458     map { $row->{$_}->{data} = \@{ $rows{$_} } if ($ref->{"${_}_accno"} ne "") } qw(debit_tax credit_tax);
459
460     $row->{reference}->{link} = build_std_url("script=$ref->{module}.pl", 'action=edit', 'id=' . E($ref->{id}), 'callback');
461
462     my $row_set = [ $row ];
463
464     if ( ($form->{l_subtotal} eq 'Y' && !$form->{report_generator_csv_options_for_import} )
465         && (($idx == (scalar @{ $form->{GL} } - 1))
466             || ($ref->{ $form->{sort} } ne $form->{GL}->[$idx + 1]->{ $form->{sort} }))) {
467       push @{ $row_set }, create_subtotal_row(\%subtotals, \@columns, \%column_alignment, [ qw(debit credit) ], 'listsubtotal');
468     }
469
470     $report->add_data($row_set);
471
472     $idx++;
473   }
474
475   # = 0 for balanced ledger
476   my $balanced_ledger = $totals{debit} + $totals{debit_tax} - $totals{credit} - $totals{credit_tax};
477
478   my $row = create_subtotal_row(\%totals, \@columns, \%column_alignment, [ qw(debit credit debit_tax credit_tax) ], 'listtotal');
479
480   my $sh = "";
481   if ($form->{balance} < 0) {
482     $sh = " S";
483     $ml = -1;
484   } elsif ($form->{balance} > 0) {
485     $sh = " H";
486     $ml = 1;
487   }
488   my $data = $form->format_amount(\%myconfig, ($form->{balance} * $ml), 2);
489   $data .= $sh;
490
491   $row->{balance}->{data}        = $data;
492
493   if ( !$form->{report_generator_csv_options_for_import} ) {
494     $report->add_separator();
495     $report->add_data($row);
496   }
497
498   my $raw_bottom_info_text;
499
500   if (!$form->{accno} && (abs($balanced_ledger) >  0.001)) {
501     $raw_bottom_info_text .=
502         '<p><span class="unbalanced_ledger">'
503       . $locale->text('Unbalanced Ledger')
504       . ': '
505       . $form->format_amount(\%myconfig, $balanced_ledger, 3)
506       . '</span></p> ';
507   }
508
509   $raw_bottom_info_text .= $form->parse_html_template('gl/generate_report_bottom');
510
511   $report->set_options('raw_bottom_info_text' => $raw_bottom_info_text);
512
513   $report->generate_with_headers();
514
515   $main::lxdebug->leave_sub();
516 }
517
518 sub update {
519   $main::lxdebug->enter_sub();
520
521   $main::auth->assert('general_ledger');
522
523   my $form     = $main::form;
524   my %myconfig = %main::myconfig;
525
526   $form->{oldtransdate} = $form->{transdate};
527
528   my @a           = ();
529   my $count       = 0;
530   my $debittax    = 0;
531   my $credittax   = 0;
532   my $debitcount  = 0;
533   my $creditcount = 0;
534   my ($debitcredit, $amount);
535
536   my @flds =
537     qw(accno debit credit projectnumber fx_transaction source memo tax taxchart);
538
539   for my $i (1 .. $form->{rowcount}) {
540
541     unless (($form->{"debit_$i"} eq "") && ($form->{"credit_$i"} eq "")) {
542       for (qw(debit credit tax)) {
543         $form->{"${_}_$i"} =
544           $form->parse_amount(\%myconfig, $form->{"${_}_$i"});
545       }
546
547       push @a, {};
548       $debitcredit = ($form->{"debit_$i"} == 0) ? "0" : "1";
549       if ($debitcredit) {
550         $debitcount++;
551       } else {
552         $creditcount++;
553       }
554
555       if (($debitcount >= 2) && ($creditcount == 2)) {
556         $form->{"credit_$i"} = 0;
557         $form->{"tax_$i"}    = 0;
558         $creditcount--;
559         $form->{creditlock} = 1;
560       }
561       if (($creditcount >= 2) && ($debitcount == 2)) {
562         $form->{"debit_$i"} = 0;
563         $form->{"tax_$i"}   = 0;
564         $debitcount--;
565         $form->{debitlock} = 1;
566       }
567       if (($creditcount == 1) && ($debitcount == 2)) {
568         $form->{creditlock} = 1;
569       }
570       if (($creditcount == 2) && ($debitcount == 1)) {
571         $form->{debitlock} = 1;
572       }
573       if ($debitcredit && $credittax) {
574         $form->{"taxchart_$i"} = "0--0.00";
575       }
576       if (!$debitcredit && $debittax) {
577         $form->{"taxchart_$i"} = "0--0.00";
578       }
579       $amount =
580         ($form->{"debit_$i"} == 0)
581         ? $form->{"credit_$i"}
582         : $form->{"debit_$i"};
583       my $j = $#a;
584       if (($debitcredit && $credittax) || (!$debitcredit && $debittax)) {
585         $form->{"taxchart_$i"} = "0--0.00";
586         $form->{"tax_$i"}      = 0;
587       }
588       my ($taxkey, $rate) = split(/--/, $form->{"taxchart_$i"});
589       if ($taxkey > 1) {
590         if ($debitcredit) {
591           $debittax = 1;
592         } else {
593           $credittax = 1;
594         }
595         if ($form->{taxincluded}) {
596           $form->{"tax_$i"} = $amount / ($rate + 1) * $rate;
597         } else {
598           $form->{"tax_$i"} = $amount * $rate;
599         }
600       } else {
601         $form->{"tax_$i"} = 0;
602       }
603
604       for (@flds) { $a[$j]->{$_} = $form->{"${_}_$i"} }
605       $count++;
606     }
607   }
608
609   for my $i (1 .. $count) {
610     my $j = $i - 1;
611     for (@flds) { $form->{"${_}_$i"} = $a[$j]->{$_} }
612   }
613
614   for my $i ($count + 1 .. $form->{rowcount}) {
615     for (@flds) { delete $form->{"${_}_$i"} }
616   }
617
618   $form->{rowcount} = $count + 1;
619
620   &display_form;
621   $main::lxdebug->leave_sub();
622
623 }
624
625 sub display_form {
626   my ($init) = @_;
627   $main::lxdebug->enter_sub();
628
629   $main::auth->assert('general_ledger');
630
631   my $form     = $main::form;
632   my %myconfig = %main::myconfig;
633
634   &form_header($init);
635
636   #   for $i (1 .. $form->{rowcount}) {
637   #     $form->{totaldebit} += $form->parse_amount(\%myconfig, $form->{"debit_$i"});
638   #     $form->{totalcredit} += $form->parse_amount(\%myconfig, $form->{"credit_$i"});
639   #
640   #     &form_row($i);
641   #   }
642   &display_rows($init);
643   &form_footer;
644   $main::lxdebug->leave_sub();
645
646 }
647
648 sub display_rows {
649   my ($init) = @_;
650   $main::lxdebug->enter_sub();
651
652   $main::auth->assert('general_ledger');
653
654   my $form     = $main::form;
655   my %myconfig = %main::myconfig;
656   my $cgi      = $::request->{cgi};
657
658   $form->{debit_1}     = 0 if !$form->{"debit_1"};
659   $form->{totaldebit}  = 0;
660   $form->{totalcredit} = 0;
661
662   my %project_labels = ();
663   my @project_values = ("");
664   foreach my $item (@{ $form->{"ALL_PROJECTS"} }) {
665     push(@project_values, $item->{"id"});
666     $project_labels{$item->{"id"}} = $item->{"projectnumber"};
667   }
668
669   my %chart_labels = ();
670   my @chart_values = ();
671   my %charts = ();
672   my $taxchart_init;
673   foreach my $item (@{ $form->{ALL_CHARTS} }) {
674     if ($item->{charttype} eq 'H'){ #falls überschrift
675       next;                         #überspringen (Bug 1150)
676     }
677     my $key = $item->{accno} . "--" . $item->{tax_id};
678     $taxchart_init = $item->{tax_id} unless (@chart_values);
679     push(@chart_values, $key);
680     $chart_labels{$key} = $item->{accno} . "--" . $item->{description};
681     $charts{$item->{accno}} = $item;
682   }
683
684   my %taxchart_labels = ();
685   my @taxchart_values = ();
686   my %taxcharts = ();
687   foreach my $item (@{ $form->{ALL_TAXCHARTS} }) {
688     my $key = $item->{id} . "--" . $item->{rate};
689     $taxchart_init = $key if ($taxchart_init == $item->{id});
690     push(@taxchart_values, $key);
691     $taxchart_labels{$key} = $item->{taxdescription} . " " . $item->{rate} * 100 . ' %';
692     $taxcharts{$item->{id}} = $item;
693   }
694
695   my ($source, $memo, $source_hidden, $memo_hidden);
696   for my $i (1 .. $form->{rowcount}) {
697     if ($form->{show_details}) {
698       $source = qq|
699       <td><input name="source_$i" value="$form->{"source_$i"}" size="16"></td>|;
700       $memo = qq|
701       <td><input name="memo_$i" value="$form->{"memo_$i"}" size="16"></td>|;
702     } else {
703       $source_hidden = qq|
704       <input type="hidden" name="source_$i" value="$form->{"source_$i"}" size="16">|;
705       $memo_hidden = qq|
706       <input type="hidden" name="memo_$i" value="$form->{"memo_$i"}" size="16">|;
707     }
708
709     my $selected_accno_full;
710     my ($accno_row) = split(/--/, $form->{"accno_$i"});
711     my $item = $charts{$accno_row};
712     $selected_accno_full = "$item->{accno}--$item->{tax_id}";
713
714     my $selected_taxchart = $form->{"taxchart_$i"};
715     my ($selected_accno, $selected_tax_id) = split(/--/, $selected_accno_full);
716     my ($previous_accno, $previous_tax_id) = split(/--/, $form->{"previous_accno_$i"});
717
718     if ($previous_accno &&
719         ($previous_accno eq $selected_accno) &&
720         ($previous_tax_id ne $selected_tax_id)) {
721       my $item = $taxcharts{$selected_tax_id};
722       $selected_taxchart = "$item->{id}--$item->{rate}";
723     }
724
725     $selected_accno      = '' if ($init);
726     $selected_taxchart ||= $taxchart_init;
727
728     my $accno = qq|<td>| .
729       NTI($cgi->popup_menu('-name' => "accno_$i",
730                            '-id' => "accno_$i",
731                            '-onChange' => "setTaxkey($i)",
732                            '-style' => 'width:200px',
733                            '-values' => \@chart_values,
734                            '-labels' => \%chart_labels,
735                            '-default' => $selected_accno_full))
736       . $cgi->hidden('-name' => "previous_accno_$i",
737                      '-default' => $selected_accno_full)
738       . qq|</td>|;
739     my $tax_ddbox = qq|<td>| .
740       NTI($cgi->popup_menu('-name' => "taxchart_$i",
741                            '-id' => "taxchart_$i",
742                            '-style' => 'width:200px',
743                            '-values' => \@taxchart_values,
744                            '-labels' => \%taxchart_labels,
745                            '-default' => $selected_taxchart))
746       . qq|</td>|;
747
748     my ($fx_transaction, $checked);
749     if ($init) {
750       if ($form->{transfer}) {
751         $fx_transaction = qq|
752         <td><input name="fx_transaction_$i" class=checkbox type=checkbox value=1></td>
753     |;
754       }
755
756     } else {
757       if ($form->{"debit_$i"} != 0) {
758         $form->{totaldebit} += $form->{"debit_$i"};
759         if (!$form->{taxincluded}) {
760           $form->{totaldebit} += $form->{"tax_$i"};
761         }
762       } else {
763         $form->{totalcredit} += $form->{"credit_$i"};
764         if (!$form->{taxincluded}) {
765           $form->{totalcredit} += $form->{"tax_$i"};
766         }
767       }
768
769       for (qw(debit credit tax)) {
770         $form->{"${_}_$i"} =
771           ($form->{"${_}_$i"})
772           ? $form->format_amount(\%myconfig, $form->{"${_}_$i"}, 2)
773           : "";
774       }
775
776       if ($i < $form->{rowcount}) {
777         if ($form->{transfer}) {
778           $checked = ($form->{"fx_transaction_$i"}) ? "1" : "";
779           my $x = ($checked) ? "x" : "";
780           $fx_transaction = qq|
781       <td><input type=hidden name="fx_transaction_$i" value="$checked">$x</td>
782     |;
783         }
784         $form->hide_form("accno_$i");
785
786       } else {
787         if ($form->{transfer}) {
788           $fx_transaction = qq|
789       <td><input name="fx_transaction_$i" class=checkbox type=checkbox value=1></td>
790     |;
791         }
792       }
793     }
794     my $debitreadonly  = "";
795     my $creditreadonly = "";
796     if ($i == $form->{rowcount}) {
797       if ($form->{debitlock}) {
798         $debitreadonly = "readonly";
799       } elsif ($form->{creditlock}) {
800         $creditreadonly = "readonly";
801       }
802     }
803
804     my $projectnumber =
805       NTI($cgi->popup_menu('-name' => "project_id_$i",
806                            '-values' => \@project_values,
807                            '-labels' => \%project_labels,
808                            '-default' => $form->{"project_id_$i"} ));
809     my $projectnumber_hidden = qq|
810     <input type="hidden" name="project_id_$i" value="$form->{"project_id_$i"}">|;
811
812     my $copy2credit = $i == 1 ? 'onkeyup="copy_debit_to_credit()"' : '';
813
814     print qq|<tr valign=top>
815     $accno
816     <td id="chart_balance_$i" align="right">&nbsp;</td>
817     $fx_transaction
818     <td><input name="debit_$i" size="8" value="$form->{"debit_$i"}" accesskey=$i $copy2credit $debitreadonly></td>
819     <td><input name="credit_$i" size=8 value="$form->{"credit_$i"}" $creditreadonly></td>
820     <td><input type="hidden" name="tax_$i" value="$form->{"tax_$i"}">$form->{"tax_$i"}</td>
821     $tax_ddbox|;
822
823     if ($form->{show_details}) {
824       print qq|
825     $source
826     $memo
827     <td>$projectnumber</td>
828 |;
829     } else {
830     print qq|
831     $source_hidden
832     $memo_hidden
833     $projectnumber_hidden
834     |;
835     }
836     print qq|
837   </tr>
838 |;
839   }
840
841   $form->hide_form(qw(rowcount selectaccno));
842
843   $main::lxdebug->leave_sub();
844
845 }
846
847 sub _get_radieren {
848   return ($::instance_conf->get_gl_changeable == 2) ? ($::form->current_date(\%::myconfig) eq $::form->{gldate}) : ($::instance_conf->get_gl_changeable == 1);
849 }
850
851 sub form_header {
852   $::lxdebug->enter_sub;
853   $::auth->assert('general_ledger');
854
855   my ($init) = @_;
856
857   my @old_project_ids = grep { $_ } map{ $::form->{"project_id_$_"} } 1..$::form->{rowcount};
858
859   $::form->get_lists("projects"  => { "key"       => "ALL_PROJECTS",
860                                     "all"       => 0,
861                                     "old_id"    => \@old_project_ids },
862                    "charts"    => { "key"       => "ALL_CHARTS",
863                                     "transdate" => $::form->{transdate} },
864                    "taxcharts" => "ALL_TAXCHARTS");
865
866   GL->get_chart_balances('charts' => $::form->{ALL_CHARTS});
867
868   my $title      = $::form->{title};
869   $::form->{title} = $::locale->text("$title General Ledger Transaction");
870   # $locale->text('Add General Ledger Transaction')
871   # $locale->text('Edit General Ledger Transaction')
872
873   map { $::form->{$_} =~ s/\"/&quot;/g }
874     qw(chart taxchart);
875
876   $::form->{selectdepartment} =~ s/ selected//;
877   $::form->{selectdepartment} =~
878     s/option>\Q$::form->{department}\E/option selected>$::form->{department}/;
879
880   if ($init) {
881     $::request->{layout}->focus("#reference");
882     $::form->{taxincluded} = "1";
883   } else {
884     $::request->{layout}->focus("#accno_$::form->{rowcount}");
885   }
886
887   $::form->{previous_id}     ||= "--";
888   $::form->{previous_gldate} ||= "--";
889
890   $::form->header;
891   print $::form->parse_html_template('gl/form_header', {
892     hide_title => $title,
893     readonly   => $::form->{id} && ($::form->{locked} || !_get_radieren()),
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   print $::form->parse_html_template('gl/form_footer', {
912     radieren       => _get_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;