fb6166f18c82707bd85255147d75573617a4b6ef
[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 <body>
932
933 <form method=post action=gl.pl>
934 |;
935
936   map { $form->{$_} =~ s/\"/&quot;/g } qw(reference description);
937
938   delete $form->{header};
939
940   foreach my $key (keys %$form) {
941     next if (($key eq 'login') || ($key eq 'password') || ('' ne ref $form->{$key}));
942     print qq|<input type="hidden" name="$key" value="$form->{$key}">\n|;
943   }
944
945   print qq|
946 <h2 class=confirm>| . $locale->text('Confirm!') . qq|</h2>
947
948 <h4>|
949     . $locale->text('Are you sure you want to delete Transaction')
950     . qq| $form->{reference}</h4>
951
952 <input name=action class=submit type=submit value="|
953     . $locale->text('Yes') . qq|">
954 </form>
955 |;
956   $main::lxdebug->leave_sub();
957
958 }
959
960 sub yes {
961   $main::lxdebug->enter_sub();
962
963   my $form     = $main::form;
964   my %myconfig = %main::myconfig;
965   my $locale   = $main::locale;
966
967   if (GL->delete_transaction(\%myconfig, \%$form)){
968     # saving the history
969       if(!exists $form->{addition} && $form->{id} ne "") {
970         $form->{snumbers} = qq|ordnumber_| . $form->{ordnumber};
971         $form->{addition} = "DELETED";
972         $form->save_history;
973       }
974     # /saving the history
975     $form->redirect($locale->text('Transaction deleted!'))
976   }
977   $form->error($locale->text('Cannot delete transaction!'));
978   $main::lxdebug->leave_sub();
979
980 }
981
982 sub post_transaction {
983   $main::lxdebug->enter_sub();
984
985   my $form     = $main::form;
986   my %myconfig = %main::myconfig;
987   my $locale   = $main::locale;
988
989   # check if there is something in reference and date
990   $form->isblank("reference",   $locale->text('Reference missing!'));
991   $form->isblank("transdate",   $locale->text('Transaction Date missing!'));
992   $form->isblank("description", $locale->text('Description missing!'));
993
994   my $transdate = $form->datetonum($form->{transdate}, \%myconfig);
995   my $closedto  = $form->datetonum($form->{closedto},  \%myconfig);
996
997   my @a           = ();
998   my $count       = 0;
999   my $debittax    = 0;
1000   my $credittax   = 0;
1001   my $debitcount  = 0;
1002   my $creditcount = 0;
1003   my $debitcredit;
1004   my %split_safety = ();
1005
1006   my @flds = qw(accno debit credit projectnumber fx_transaction source memo tax taxchart);
1007
1008   for my $i (1 .. $form->{rowcount}) {
1009     next if $form->{"debit_$i"} eq "" && $form->{"credit_$i"} eq "";
1010
1011     for (qw(debit credit tax)) {
1012       $form->{"${_}_$i"} = $form->parse_amount(\%myconfig, $form->{"${_}_$i"});
1013     }
1014
1015     push @a, {};
1016     $debitcredit = ($form->{"debit_$i"} == 0) ? "0" : "1";
1017
1018     $split_safety{   $form->{"debit_$i"}  <=> 0 }++;
1019     $split_safety{ - $form->{"credit_$i"} <=> 0 }++;
1020
1021     if ($debitcredit) {
1022       $debitcount++;
1023     } else {
1024       $creditcount++;
1025     }
1026
1027     if (($debitcount >= 2) && ($creditcount == 2)) {
1028       $form->{"credit_$i"} = 0;
1029       $form->{"tax_$i"}    = 0;
1030       $creditcount--;
1031       $form->{creditlock} = 1;
1032     }
1033     if (($creditcount >= 2) && ($debitcount == 2)) {
1034       $form->{"debit_$i"} = 0;
1035       $form->{"tax_$i"}   = 0;
1036       $debitcount--;
1037       $form->{debitlock} = 1;
1038     }
1039     if (($creditcount == 1) && ($debitcount == 2)) {
1040       $form->{creditlock} = 1;
1041     }
1042     if (($creditcount == 2) && ($debitcount == 1)) {
1043       $form->{debitlock} = 1;
1044     }
1045     if ($debitcredit && $credittax) {
1046       $form->{"taxchart_$i"} = "0--0.00";
1047     }
1048     if (!$debitcredit && $debittax) {
1049       $form->{"taxchart_$i"} = "0--0.00";
1050     }
1051     my $amount = ($form->{"debit_$i"} == 0)
1052             ? $form->{"credit_$i"}
1053             : $form->{"debit_$i"};
1054     my $j = $#a;
1055     if (($debitcredit && $credittax) || (!$debitcredit && $debittax)) {
1056       $form->{"taxchart_$i"} = "0--0.00";
1057       $form->{"tax_$i"}      = 0;
1058     }
1059     my ($taxkey, $rate) = split(/--/, $form->{"taxchart_$i"});
1060     if ($taxkey > 1) {
1061       if ($debitcredit) {
1062         $debittax = 1;
1063       } else {
1064         $credittax = 1;
1065       }
1066       if ($form->{taxincluded}) {
1067         $form->{"tax_$i"} = $amount / ($rate + 1) * $rate;
1068         if ($debitcredit) {
1069           $form->{"debit_$i"} = $form->{"debit_$i"} - $form->{"tax_$i"};
1070         } else {
1071           $form->{"credit_$i"} = $form->{"credit_$i"} - $form->{"tax_$i"};
1072         }
1073       } else {
1074         $form->{"tax_$i"} = $amount * $rate;
1075       }
1076     } else {
1077       $form->{"tax_$i"} = 0;
1078     }
1079
1080     for (@flds) { $a[$j]->{$_} = $form->{"${_}_$i"} }
1081     $count++;
1082   }
1083
1084   if ($split_safety{-1} > 1 && $split_safety{1} > 1) {
1085     $::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."));
1086   }
1087
1088   for my $i (1 .. $count) {
1089     my $j = $i - 1;
1090     for (@flds) { $form->{"${_}_$i"} = $a[$j]->{$_} }
1091   }
1092
1093   for my $i ($count + 1 .. $form->{rowcount}) {
1094     for (@flds) { delete $form->{"${_}_$i"} }
1095   }
1096
1097   my ($debit, $credit, $taxtotal);
1098   for my $i (1 .. $form->{rowcount}) {
1099     my $dr  = $form->{"debit_$i"};
1100     my $cr  = $form->{"credit_$i"};
1101     my $tax = $form->{"tax_$i"};
1102     if ($dr && $cr) {
1103       $form->error($locale->text('Cannot post transaction with a debit and credit entry for the same account!'));
1104     }
1105     $debit    += $dr + $tax if $dr;
1106     $credit   += $cr + $tax if $cr;
1107     $taxtotal += $tax if $form->{taxincluded}
1108   }
1109
1110   $form->{taxincluded} = 0 if !$taxtotal;
1111
1112   # this is just for the wise guys
1113   $form->error($locale->text('Cannot post transaction for a closed period!'))
1114     if ($form->date_closed($form->{"transdate"}, \%myconfig));
1115   if ($form->round_amount($debit, 2) != $form->round_amount($credit, 2)) {
1116     $form->error($locale->text('Out of balance transaction!'));
1117   }
1118
1119   if ($form->round_amount($debit, 2) + $form->round_amount($credit, 2) == 0) {
1120     $form->error($locale->text('Empty transaction!'));
1121   }
1122
1123   if ((my $errno = GL->post_transaction(\%myconfig, \%$form)) <= -1) {
1124     $errno *= -1;
1125     my @err;
1126     $err[1] = $locale->text('Cannot have a value in both Debit and Credit!');
1127     $err[2] = $locale->text('Debit and credit out of balance!');
1128     $err[3] = $locale->text('Cannot post a transaction without a value!');
1129
1130     $form->error($err[$errno]);
1131   }
1132   undef($form->{callback});
1133   # saving the history
1134   if(!exists $form->{addition} && $form->{id} ne "") {
1135     $form->{snumbers} = qq|ordnumber_| . $form->{ordnumber};
1136     $form->{addition} = "SAVED";
1137     $form->{what_done} = $locale->text("Buchungsnummer") . " = " . $form->{id};
1138     $form->save_history;
1139   }
1140   # /saving the history
1141
1142   $main::lxdebug->leave_sub();
1143 }
1144
1145 sub post {
1146   $main::lxdebug->enter_sub();
1147
1148   $main::auth->assert('general_ledger');
1149
1150   my $form     = $main::form;
1151   my $locale   = $main::locale;
1152
1153   if ($::myconfig{mandatory_departments} && !$form->{department}) {
1154     $form->{saved_message} = $::locale->text('You have to specify a department.');
1155     update();
1156     exit;
1157   }
1158
1159   $form->{title}  = $locale->text("$form->{title} General Ledger Transaction");
1160   $form->{storno} = 0;
1161
1162   post_transaction();
1163
1164   remove_draft() if $form->{remove_draft};
1165
1166   $form->{callback} = build_std_url("action=add&DONT_LOAD_DRAFT=1", "show_details");
1167   $form->redirect($form->{callback});
1168
1169   $main::lxdebug->leave_sub();
1170 }
1171
1172 sub post_as_new {
1173   $main::lxdebug->enter_sub();
1174
1175   $main::auth->assert('general_ledger');
1176
1177   my $form     = $main::form;
1178
1179   $form->{id} = 0;
1180   &add;
1181   $main::lxdebug->leave_sub();
1182
1183 }
1184
1185 sub storno {
1186   $main::lxdebug->enter_sub();
1187
1188   $main::auth->assert('general_ledger');
1189
1190   my $form     = $main::form;
1191   my %myconfig = %main::myconfig;
1192   my $locale   = $main::locale;
1193
1194   # don't cancel cancelled transactions
1195   if (IS->has_storno(\%myconfig, $form, 'gl')) {
1196     $form->{title} = $locale->text("Cancel Accounts Receivables Transaction");
1197     $form->error($locale->text("Transaction has already been cancelled!"));
1198   }
1199
1200   GL->storno($form, \%myconfig, $form->{id});
1201
1202   # saving the history
1203   if(!exists $form->{addition} && $form->{id} ne "") {
1204     $form->{snumbers} = "ordnumber_$form->{ordnumber}";
1205     $form->{addition} = "STORNO";
1206     $form->save_history;
1207   }
1208   # /saving the history
1209
1210   $form->redirect(sprintf $locale->text("Transaction %d cancelled."), $form->{storno_id});
1211
1212   $main::lxdebug->leave_sub();
1213 }
1214
1215 sub continue {
1216   call_sub($main::form->{nextsub});
1217 }
1218
1219 1;