Änderbarkeit und Löschbarkeit von Belegen in Mandantenkonfiguration einstellbar.
[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 form_header {
848   $::lxdebug->enter_sub;
849   $::auth->assert('general_ledger');
850
851   my ($init) = @_;
852
853   my @old_project_ids = grep { $_ } map{ $::form->{"project_id_$_"} } 1..$::form->{rowcount};
854
855   $::form->get_lists("projects"  => { "key"       => "ALL_PROJECTS",
856                                     "all"       => 0,
857                                     "old_id"    => \@old_project_ids },
858                    "charts"    => { "key"       => "ALL_CHARTS",
859                                     "transdate" => $::form->{transdate} },
860                    "taxcharts" => "ALL_TAXCHARTS");
861
862   GL->get_chart_balances('charts' => $::form->{ALL_CHARTS});
863
864   my $title      = $::form->{title};
865   $::form->{title} = $::locale->text("$title General Ledger Transaction");
866   # $locale->text('Add General Ledger Transaction')
867   # $locale->text('Edit General Ledger Transaction')
868
869   map { $::form->{$_} =~ s/\"/&quot;/g }
870     qw(chart taxchart);
871
872   $::form->{selectdepartment} =~ s/ selected//;
873   $::form->{selectdepartment} =~
874     s/option>\Q$::form->{department}\E/option selected>$::form->{department}/;
875
876   if ($init) {
877     $::request->{layout}->focus("#reference");
878     $::form->{taxincluded} = "1";
879   } else {
880     $::request->{layout}->focus("#accno_$::form->{rowcount}");
881   }
882
883   $::form->{previous_id}     ||= "--";
884   $::form->{previous_gldate} ||= "--";
885
886   $::form->header;
887   print $::form->parse_html_template('gl/form_header', {
888     hide_title => $title,
889   });
890
891   $::lxdebug->leave_sub;
892
893 }
894
895 sub form_footer {
896   $::lxdebug->enter_sub;
897   $::auth->assert('general_ledger');
898
899   my ($follow_ups, $follow_ups_due);
900
901   if ($::form->{id}) {
902     $follow_ups     = FU->follow_ups('trans_id' => $::form->{id});
903     $follow_ups_due = sum map { $_->{due} * 1 } @{ $follow_ups || [] };
904   }
905
906   my $radieren = ($::instance_conf->get_gl_changeable == 2)
907                     ? ($::form->current_date(\%::myconfig) eq $::form->{gldate})
908                     : ($::instance_conf->get_gl_changeable == 1);
909
910   print $::form->parse_html_template('gl/form_footer', {
911     radieren       => $radieren,
912     follow_ups     => $follow_ups,
913     follow_ups_due => $follow_ups_due,
914   });
915
916   $::lxdebug->leave_sub;
917 }
918
919 sub delete {
920   $main::lxdebug->enter_sub();
921
922   my $form     = $main::form;
923   my $locale   = $main::locale;
924
925   $form->header;
926
927   print qq|
928 <form method=post action=gl.pl>
929 |;
930
931   map { $form->{$_} =~ s/\"/&quot;/g } qw(reference description);
932
933   delete $form->{header};
934
935   foreach my $key (keys %$form) {
936     next if (($key eq 'login') || ($key eq 'password') || ('' ne ref $form->{$key}));
937     print qq|<input type="hidden" name="$key" value="$form->{$key}">\n|;
938   }
939
940   print qq|
941 <h2 class=confirm>| . $locale->text('Confirm!') . qq|</h2>
942
943 <h4>|
944     . $locale->text('Are you sure you want to delete Transaction')
945     . qq| $form->{reference}</h4>
946
947 <input name=action class=submit type=submit value="|
948     . $locale->text('Yes') . qq|">
949 </form>
950 |;
951   $main::lxdebug->leave_sub();
952
953 }
954
955 sub yes {
956   $main::lxdebug->enter_sub();
957
958   my $form     = $main::form;
959   my %myconfig = %main::myconfig;
960   my $locale   = $main::locale;
961
962   if (GL->delete_transaction(\%myconfig, \%$form)){
963     # saving the history
964       if(!exists $form->{addition} && $form->{id} ne "") {
965         $form->{snumbers} = qq|ordnumber_| . $form->{ordnumber};
966         $form->{addition} = "DELETED";
967         $form->save_history;
968       }
969     # /saving the history
970     $form->redirect($locale->text('Transaction deleted!'))
971   }
972   $form->error($locale->text('Cannot delete transaction!'));
973   $main::lxdebug->leave_sub();
974
975 }
976
977 sub post_transaction {
978   $main::lxdebug->enter_sub();
979
980   my $form     = $main::form;
981   my %myconfig = %main::myconfig;
982   my $locale   = $main::locale;
983
984   # check if there is something in reference and date
985   $form->isblank("reference",   $locale->text('Reference missing!'));
986   $form->isblank("transdate",   $locale->text('Transaction Date missing!'));
987   $form->isblank("description", $locale->text('Description missing!'));
988
989   my $transdate = $form->datetonum($form->{transdate}, \%myconfig);
990   my $closedto  = $form->datetonum($form->{closedto},  \%myconfig);
991
992   my @a           = ();
993   my $count       = 0;
994   my $debittax    = 0;
995   my $credittax   = 0;
996   my $debitcount  = 0;
997   my $creditcount = 0;
998   my $debitcredit;
999   my %split_safety = ();
1000
1001   my @flds = qw(accno debit credit projectnumber fx_transaction source memo tax taxchart);
1002
1003   for my $i (1 .. $form->{rowcount}) {
1004     next if $form->{"debit_$i"} eq "" && $form->{"credit_$i"} eq "";
1005
1006     for (qw(debit credit tax)) {
1007       $form->{"${_}_$i"} = $form->parse_amount(\%myconfig, $form->{"${_}_$i"});
1008     }
1009
1010     push @a, {};
1011     $debitcredit = ($form->{"debit_$i"} == 0) ? "0" : "1";
1012
1013     $split_safety{   $form->{"debit_$i"}  <=> 0 }++;
1014     $split_safety{ - $form->{"credit_$i"} <=> 0 }++;
1015
1016     if ($debitcredit) {
1017       $debitcount++;
1018     } else {
1019       $creditcount++;
1020     }
1021
1022     if (($debitcount >= 2) && ($creditcount == 2)) {
1023       $form->{"credit_$i"} = 0;
1024       $form->{"tax_$i"}    = 0;
1025       $creditcount--;
1026       $form->{creditlock} = 1;
1027     }
1028     if (($creditcount >= 2) && ($debitcount == 2)) {
1029       $form->{"debit_$i"} = 0;
1030       $form->{"tax_$i"}   = 0;
1031       $debitcount--;
1032       $form->{debitlock} = 1;
1033     }
1034     if (($creditcount == 1) && ($debitcount == 2)) {
1035       $form->{creditlock} = 1;
1036     }
1037     if (($creditcount == 2) && ($debitcount == 1)) {
1038       $form->{debitlock} = 1;
1039     }
1040     if ($debitcredit && $credittax) {
1041       $form->{"taxchart_$i"} = "0--0.00";
1042     }
1043     if (!$debitcredit && $debittax) {
1044       $form->{"taxchart_$i"} = "0--0.00";
1045     }
1046     my $amount = ($form->{"debit_$i"} == 0)
1047             ? $form->{"credit_$i"}
1048             : $form->{"debit_$i"};
1049     my $j = $#a;
1050     if (($debitcredit && $credittax) || (!$debitcredit && $debittax)) {
1051       $form->{"taxchart_$i"} = "0--0.00";
1052       $form->{"tax_$i"}      = 0;
1053     }
1054     my ($taxkey, $rate) = split(/--/, $form->{"taxchart_$i"});
1055     if ($taxkey > 1) {
1056       if ($debitcredit) {
1057         $debittax = 1;
1058       } else {
1059         $credittax = 1;
1060       }
1061       if ($form->{taxincluded}) {
1062         $form->{"tax_$i"} = $amount / ($rate + 1) * $rate;
1063         if ($debitcredit) {
1064           $form->{"debit_$i"} = $form->{"debit_$i"} - $form->{"tax_$i"};
1065         } else {
1066           $form->{"credit_$i"} = $form->{"credit_$i"} - $form->{"tax_$i"};
1067         }
1068       } else {
1069         $form->{"tax_$i"} = $amount * $rate;
1070       }
1071     } else {
1072       $form->{"tax_$i"} = 0;
1073     }
1074
1075     for (@flds) { $a[$j]->{$_} = $form->{"${_}_$i"} }
1076     $count++;
1077   }
1078
1079   if ($split_safety{-1} > 1 && $split_safety{1} > 1) {
1080     $::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."));
1081   }
1082
1083   for my $i (1 .. $count) {
1084     my $j = $i - 1;
1085     for (@flds) { $form->{"${_}_$i"} = $a[$j]->{$_} }
1086   }
1087
1088   for my $i ($count + 1 .. $form->{rowcount}) {
1089     for (@flds) { delete $form->{"${_}_$i"} }
1090   }
1091
1092   my ($debit, $credit, $taxtotal);
1093   for my $i (1 .. $form->{rowcount}) {
1094     my $dr  = $form->{"debit_$i"};
1095     my $cr  = $form->{"credit_$i"};
1096     my $tax = $form->{"tax_$i"};
1097     if ($dr && $cr) {
1098       $form->error($locale->text('Cannot post transaction with a debit and credit entry for the same account!'));
1099     }
1100     $debit    += $dr + $tax if $dr;
1101     $credit   += $cr + $tax if $cr;
1102     $taxtotal += $tax if $form->{taxincluded}
1103   }
1104
1105   $form->{taxincluded} = 0 if !$taxtotal;
1106
1107   # this is just for the wise guys
1108   $form->error($locale->text('Cannot post transaction for a closed period!'))
1109     if ($form->date_closed($form->{"transdate"}, \%myconfig));
1110   if ($form->round_amount($debit, 2) != $form->round_amount($credit, 2)) {
1111     $form->error($locale->text('Out of balance transaction!'));
1112   }
1113
1114   if ($form->round_amount($debit, 2) + $form->round_amount($credit, 2) == 0) {
1115     $form->error($locale->text('Empty transaction!'));
1116   }
1117
1118   if ((my $errno = GL->post_transaction(\%myconfig, \%$form)) <= -1) {
1119     $errno *= -1;
1120     my @err;
1121     $err[1] = $locale->text('Cannot have a value in both Debit and Credit!');
1122     $err[2] = $locale->text('Debit and credit out of balance!');
1123     $err[3] = $locale->text('Cannot post a transaction without a value!');
1124
1125     $form->error($err[$errno]);
1126   }
1127   undef($form->{callback});
1128   # saving the history
1129   if(!exists $form->{addition} && $form->{id} ne "") {
1130     $form->{snumbers} = qq|ordnumber_| . $form->{ordnumber};
1131     $form->{addition} = "SAVED";
1132     $form->{what_done} = $locale->text("Buchungsnummer") . " = " . $form->{id};
1133     $form->save_history;
1134   }
1135   # /saving the history
1136
1137   $main::lxdebug->leave_sub();
1138 }
1139
1140 sub post {
1141   $main::lxdebug->enter_sub();
1142
1143   $main::auth->assert('general_ledger');
1144
1145   my $form     = $main::form;
1146   my $locale   = $main::locale;
1147
1148   if ($::myconfig{mandatory_departments} && !$form->{department}) {
1149     $form->{saved_message} = $::locale->text('You have to specify a department.');
1150     update();
1151     exit;
1152   }
1153
1154   $form->{title}  = $locale->text("$form->{title} General Ledger Transaction");
1155   $form->{storno} = 0;
1156
1157   post_transaction();
1158
1159   remove_draft() if $form->{remove_draft};
1160
1161   $form->{callback} = build_std_url("action=add&DONT_LOAD_DRAFT=1", "show_details");
1162   $form->redirect($form->{callback});
1163
1164   $main::lxdebug->leave_sub();
1165 }
1166
1167 sub post_as_new {
1168   $main::lxdebug->enter_sub();
1169
1170   $main::auth->assert('general_ledger');
1171
1172   my $form     = $main::form;
1173
1174   $form->{id} = 0;
1175   &add;
1176   $main::lxdebug->leave_sub();
1177
1178 }
1179
1180 sub storno {
1181   $main::lxdebug->enter_sub();
1182
1183   $main::auth->assert('general_ledger');
1184
1185   my $form     = $main::form;
1186   my %myconfig = %main::myconfig;
1187   my $locale   = $main::locale;
1188
1189   # don't cancel cancelled transactions
1190   if (IS->has_storno(\%myconfig, $form, 'gl')) {
1191     $form->{title} = $locale->text("Cancel Accounts Receivables Transaction");
1192     $form->error($locale->text("Transaction has already been cancelled!"));
1193   }
1194
1195   GL->storno($form, \%myconfig, $form->{id});
1196
1197   # saving the history
1198   if(!exists $form->{addition} && $form->{id} ne "") {
1199     $form->{snumbers} = "ordnumber_$form->{ordnumber}";
1200     $form->{addition} = "STORNO";
1201     $form->save_history;
1202   }
1203   # /saving the history
1204
1205   $form->redirect(sprintf $locale->text("Transaction %d cancelled."), $form->{storno_id});
1206
1207   $main::lxdebug->leave_sub();
1208 }
1209
1210 sub continue {
1211   call_sub($main::form->{nextsub});
1212 }
1213
1214 1;