global $::cgi in bin/mozilla/login.pl entfernt
[kivitendo-erp.git] / bin / mozilla / gl.pl
index f4f87cc..8623153 100644 (file)
@@ -24,7 +24,8 @@
 # GNU General Public License for more details.
 # You should have received a copy of the GNU General Public License
 # along with this program; if not, write to the Free Software
-# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+# MA 02110-1335, USA.
 #======================================================================
 #
 # Genereal Ledger
@@ -35,16 +36,21 @@ use utf8;
 use strict;
 
 use POSIX qw(strftime);
-use List::Util qw(sum);
+use List::Util qw(first sum);
 
+use SL::DB::RecordTemplate;
+use SL::DB::Tax;
 use SL::FU;
 use SL::GL;
+use SL::Helper::Flash qw(flash);
 use SL::IS;
-use SL::PE;
 use SL::ReportGenerator;
+use SL::DBUtils qw(selectrow_query selectall_hashref_query);
+use SL::Webdav;
+use SL::Locale::String qw(t8);
+use SL::Helper::GlAttachments qw(count_gl_attachments);
 
 require "bin/mozilla/common.pl";
-require "bin/mozilla/drafts.pl";
 require "bin/mozilla/reportgenerator.pl";
 
 # this is for our long dates
@@ -75,16 +81,134 @@ require "bin/mozilla/reportgenerator.pl";
 # $locale->text('Nov')
 # $locale->text('Dec')
 
+sub load_record_template {
+  $::auth->assert('gl_transactions');
+
+  # Load existing template and verify that its one for this module.
+  my $template = SL::DB::RecordTemplate
+    ->new(id => $::form->{id})
+    ->load(
+      with_object => [ qw(customer payment currency record_items record_items.chart) ],
+    );
+
+  die "invalid template type" unless $template->template_type eq 'gl_transaction';
+
+  $template->substitute_variables;
+
+  # Clean the current $::form before rebuilding it from the template.
+  my $form_defaults = delete $::form->{form_defaults};
+  delete @{ $::form }{ grep { !m{^(?:script|login)$}i } keys %{ $::form } };
+
+  my $dummy_form = {};
+  GL->transaction(\%::myconfig, $dummy_form);
+
+  # Fill $::form from the template.
+  my $today                   = DateTime->today_local;
+  $::form->{title}            = "Add";
+  $::form->{transdate}        = $today->to_kivitendo;
+  $::form->{duedate}          = $today->to_kivitendo;
+  $::form->{rowcount}         = @{ $template->items };
+  $::form->{paidaccounts}     = 1;
+  $::form->{$_}               = $template->$_     for qw(department_id taxincluded ob_transaction cb_transaction reference description);
+  $::form->{$_}               = $dummy_form->{$_} for qw(closedto revtrans previous_id previous_gldate);
+
+  my $row = 0;
+  foreach my $item (@{ $template->items }) {
+    $row++;
+
+    my $active_taxkey = $item->chart->get_active_taxkey;
+    my $taxes         = SL::DB::Manager::Tax->get_all(
+      where   => [ chart_categories => { like => '%' . $item->chart->category . '%' }],
+      sort_by => 'taxkey, rate',
+    );
+
+    my $tax   = first { $item->tax_id          == $_->id } @{ $taxes };
+    $tax    //= first { $active_taxkey->tax_id == $_->id } @{ $taxes };
+    $tax    //= $taxes->[0];
+
+    if (!$tax) {
+      $row--;
+      next;
+    }
+
+    $::form->{"accno_id_${row}"}          = $item->chart_id;
+    $::form->{"previous_accno_id_${row}"} = $item->chart_id;
+    $::form->{"debit_${row}"}             = $::form->format_amount(\%::myconfig, $item->amount1, 2) if $item->amount1 * 1;
+    $::form->{"credit_${row}"}            = $::form->format_amount(\%::myconfig, $item->amount2, 2) if $item->amount2 * 1;
+    $::form->{"taxchart_${row}"}          = $item->tax_id . '--' . $tax->rate;
+    $::form->{"${_}_${row}"}              = $item->$_ for qw(source memo project_id);
+  }
+
+  $::form->{$_} = $form_defaults->{$_} for keys %{ $form_defaults // {} };
+
+  flash('info', $::locale->text("The record template '#1' has been loaded.", $template->template_name));
+
+  update(
+    keep_rows_without_amount => 1,
+    dont_add_new_row         => 1,
+  );
+}
+
+sub save_record_template {
+  $::auth->assert('gl_transactions');
+
+  my $template = $::form->{record_template_id} ? SL::DB::RecordTemplate->new(id => $::form->{record_template_id})->load : SL::DB::RecordTemplate->new;
+  my $js       = SL::ClientJS->new(controller => SL::Controller::Base->new);
+  my $new_name = $template->template_name_to_use($::form->{record_template_new_template_name});
+
+  $js->dialog->close('#record_template_dialog');
+
+  my @items = grep {
+    $_->{chart_id} && (($_->{tax_id} // '') ne '')
+  } map {
+    +{ chart_id   => $::form->{"accno_id_${_}"},
+       amount1    => $::form->parse_amount(\%::myconfig, $::form->{"debit_${_}"}),
+       amount2    => $::form->parse_amount(\%::myconfig, $::form->{"credit_${_}"}),
+       tax_id     => (split m{--}, $::form->{"taxchart_${_}"})[0],
+       project_id => $::form->{"project_id_${_}"} || undef,
+       source     => $::form->{"source_${_}"},
+       memo       => $::form->{"memo_${_}"},
+     }
+  } (1..($::form->{rowcount} || 1));
+
+  $template->assign_attributes(
+    template_type  => 'gl_transaction',
+    template_name  => $new_name,
+
+    currency_id    => $::instance_conf->get_currency_id,
+    department_id  => $::form->{department_id}    || undef,
+    project_id     => $::form->{globalproject_id} || undef,
+    taxincluded    => $::form->{taxincluded}     ? 1 : 0,
+    ob_transaction => $::form->{ob_transaction}  ? 1 : 0,
+    cb_transaction => $::form->{cb_transaction}  ? 1 : 0,
+    reference      => $::form->{reference},
+    description    => $::form->{description},
+
+    items          => \@items,
+  );
+
+  eval {
+    $template->save;
+    1;
+  } or do {
+    return $js
+      ->flash('error', $::locale->text("Saving the record template '#1' failed.", $new_name))
+      ->render;
+  };
+
+  return $js
+    ->flash('info', $::locale->text("The record template '#1' has been saved.", $new_name))
+    ->render;
+}
+
 sub add {
   $main::lxdebug->enter_sub();
 
-  $main::auth->assert('general_ledger');
+  $main::auth->assert('gl_transactions');
 
   my $form     = $main::form;
   my %myconfig = %main::myconfig;
 
-  return $main::lxdebug->leave_sub() if (load_draft_maybe());
-
   $form->{title} = "Add";
 
   $form->{callback} = "gl.pl?action=add" unless $form->{callback};
@@ -99,16 +223,7 @@ sub add {
   $form->{credit} = 0;
   $form->{tax}    = 0;
 
-  # departments
-  $form->all_departments(\%myconfig);
-  if (@{ $form->{all_departments} || [] }) {
-    $form->{selectdepartment} = "<option>\n";
-
-    map {
-      $form->{selectdepartment} .=
-        "<option>$_->{description}--$_->{id}\n"
-    } (@{ $form->{all_departments} || [] });
-  }
+  $::form->{ALL_DEPARTMENTS} = SL::DB::Manager::Department->get_all;
 
   $form->{show_details} = $myconfig{show_form_details} unless defined $form->{show_details};
 
@@ -120,7 +235,7 @@ sub add {
 sub prepare_transaction {
   $main::lxdebug->enter_sub();
 
-  $main::auth->assert('general_ledger');
+  $main::auth->assert('gl_transactions');
 
   my $form     = $main::form;
   my %myconfig = %main::myconfig;
@@ -129,16 +244,7 @@ sub prepare_transaction {
 
   $form->{amount} = $form->format_amount(\%myconfig, $form->{amount}, 2);
 
-  # departments
-  $form->all_departments(\%myconfig);
-  if (@{ $form->{all_departments} || [] }) {
-    $form->{selectdepartment} = "<option>\n";
-
-    map {
-      $form->{selectdepartment} .=
-        "<option>$_->{description}--$_->{id}\n"
-    } (@{ $form->{all_departments} || [] });
-  }
+  $::form->{ALL_DEPARTMENTS} = SL::DB::Manager::Department->get_all;
 
   my $i        = 1;
   my $tax      = 0;
@@ -158,7 +264,7 @@ sub prepare_transaction {
       $form->{"project_id_$j"} = $ref->{project_id};
 
     } else {
-      $form->{"accno_$i"} = "$ref->{accno}--$ref->{tax_id}";
+      $form->{"accno_id_$i"} = $ref->{chart_id};
       for (qw(fx_transaction source memo)) { $form->{"${_}_$i"} = $ref->{$_} }
       if ($ref->{amount} < 0) {
         $form->{totaldebit} -= $ref->{amount};
@@ -167,7 +273,7 @@ sub prepare_transaction {
         $form->{totalcredit} += $ref->{amount};
         $form->{"credit_$i"} = $ref->{amount};
       }
-      $form->{"taxchart_$i"} = "0--0.00";
+      $form->{"taxchart_$i"} = $ref->{id}."--0.00000";
       $form->{"project_id_$i"} = $ref->{project_id};
       $i++;
     }
@@ -191,7 +297,7 @@ sub prepare_transaction {
 sub edit {
   $main::lxdebug->enter_sub();
 
-  $main::auth->assert('general_ledger');
+  $main::auth->assert('gl_transactions');
 
   my $form     = $main::form;
   my %myconfig = %main::myconfig;
@@ -202,6 +308,17 @@ sub edit {
 
   $form->{show_details} = $myconfig{show_form_details} unless defined $form->{show_details};
 
+  if ($form->{id} && $::instance_conf->get_webdav) {
+    my $webdav = SL::Webdav->new(
+      type     => 'general_ledger',
+      number   => $form->{id},
+    );
+    my @all_objects = $webdav->get_all_objects;
+    @{ $form->{WEBDAV} } = map { { name => $_->filename,
+                                   type => t8('File'),
+                                   link => File::Spec->catfile($_->full_filedescriptor),
+                               } } @all_objects;
+  }
   form_header();
   display_rows();
   form_footer();
@@ -212,22 +329,18 @@ sub edit {
 
 sub search {
   $::lxdebug->enter_sub;
-  $::auth->assert('general_ledger');
+  $::auth->assert('general_ledger | gl_transactions');
 
-  $::form->all_departments(\%::myconfig);
   $::form->get_lists(
     projects  => { key => "ALL_PROJECTS", all => 1 },
-    employees => "ALL_EMPLOYEES",
   );
+  $::form->{ALL_EMPLOYEES} = SL::DB::Manager::Employee->get_all_sorted(query => [ deleted => 0 ]);
+  $::form->{ALL_DEPARTMENTS} = SL::DB::Manager::Department->get_all;
 
-  my $onload = "focus()"
-             . qq|;setupDateFormat('|. $::myconfig{dateformat} . qq|', '| . $::locale->text("Falsches Datumsformat!") . qq|')|
-             . qq|;setupPoints('|. $::myconfig{numberformat} .   qq|', '| . $::locale->text("wrongformat") . qq|')|;
+  setup_gl_search_action_bar();
 
   $::form->header;
   print $::form->parse_html_template('gl/search', {
-    onload => $onload,
-    department_label => sub { ("$_[0]{description}--$_[0]{id}")x2 },
     employee_label => sub { "$_[0]{id}--$_[0]{name}" },
   });
 
@@ -256,22 +369,22 @@ sub create_subtotal_row {
 sub generate_report {
   $main::lxdebug->enter_sub();
 
-  $main::auth->assert('general_ledger');
+  $main::auth->assert('general_ledger | gl_transactions');
 
   my $form     = $main::form;
   my %myconfig = %main::myconfig;
   my $locale   = $main::locale;
 
-  # 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
+  # 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
 
   # <form method=post action=gl.pl>
   # <input type=hidden name=sort value=datesort>    # form->{sort} setzen
   # <input type=hidden name=nextsub value=generate_report>
 
   # anhand von neuer Variable datesort wird jetzt $form->{sort} auf transdate oder gldate gesetzt
-  # damit ist die Hidden Variable "sort" wahrscheinlich sogar überflüssig
+  # damit ist die Hidden Variable "sort" wahrscheinlich sogar überflüssig
 
-  # ändert man die Sortierreihenfolge per Klick auf eine der Überschriften wird die Variable "sort" per GET übergeben, z.B. id,transdate, gldate, ...
+  # ändert man die Sortierreihenfolge per Klick auf eine der Überschriften wird die Variable "sort" per GET übergeben, z.B. id,transdate, gldate, ...
   # 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
 
   if ( $form->{sort} eq 'datesort' ) {   # sollte bei einem Post (Aufruf aus Suchmaske) immer wahr sein
@@ -302,18 +415,17 @@ sub generate_report {
   my $ml = ($form->{ml} =~ /(A|E|Q)/) ? -1 : 1;
 
   my @columns = qw(
-    gldate         transdate        id             reference      description
-    notes          source           debit          debit_accno
+    transdate      gldate   id      reference      description
+    notes          source   doccnt  debit          debit_accno
     credit         credit_accno     debit_tax      debit_tax_accno
     credit_tax     credit_tax_accno projectnumbers balance employee
   );
 
   # add employee here, so that variable is still known and passed in url when choosing a different sort order in resulting table
-  my @hidden_variables = qw(accno source reference department description notes project_id datefrom dateto employee datesort category l_subtotal);
+  my @hidden_variables = qw(accno source reference description notes project_id datefrom dateto employee_id datesort category l_subtotal);
   push @hidden_variables, map { "l_${_}" } @columns;
-  foreach ( @hidden_variables ) {
-      print URL "$_\n";
-  };
+
+  my $employee = $form->{employee_id} ? SL::DB::Employee->new(id => $form->{employee_id})->load->name : '';
 
   my (@options, @date_options);
   push @options,      $locale->text('Account')     . " : $form->{accno} $form->{account_description}" if ($form->{accno});
@@ -321,22 +433,19 @@ sub generate_report {
   push @options,      $locale->text('Reference')   . " : $form->{reference}"                          if ($form->{reference});
   push @options,      $locale->text('Description') . " : $form->{description}"                        if ($form->{description});
   push @options,      $locale->text('Notes')       . " : $form->{notes}"                              if ($form->{notes});
-  push @options,      $locale->text('Employee')       . " : $form->{employee_name}"                              if ($form->{employee_name});
-  my $datesorttext = $form->{datesort} eq 'transdate' ? $locale->text('Invoice Date') :  $locale->text('Booking Date');
+  push @options,      $locale->text('Employee')    . " : $employee"                                   if $employee;
+  my $datesorttext = $form->{datesort} eq 'transdate' ? $locale->text('Transdate') :  $locale->text('Gldate');
   push @date_options,      "$datesorttext"                              if ($form->{datesort} and ($form->{datefrom} or $form->{dateto}));
   push @date_options, $locale->text('From'), $locale->date(\%myconfig, $form->{datefrom}, 1)          if ($form->{datefrom});
   push @date_options, $locale->text('Bis'),  $locale->date(\%myconfig, $form->{dateto},   1)          if ($form->{dateto});
   push @options,      join(' ', @date_options)                                                        if (scalar @date_options);
 
-  if ($form->{department}) {
-    my ($department) = split /--/, $form->{department};
-    push @options, $locale->text('Department') . " : $department";
+  if ($form->{department_id}) {
+    my $department = SL::DB::Manager::Department->find_by( id => $form->{department_id} );
+    push @options, $locale->text('Department') . " : " . $department->description;
   }
 
-
   my $callback = build_std_url('action=generate_report', grep { $form->{$_} } @hidden_variables);
-  print URL $callback;
-  close URL;
 
   $form->{l_credit_accno}     = 'Y';
   $form->{l_debit_accno}      = 'Y';
@@ -347,13 +456,15 @@ sub generate_report {
   $form->{l_datesort} = 'Y';
   $form->{l_debit_tax_accno}  = 'Y';
   $form->{l_balance}          = $form->{accno} ? 'Y' : '';
+  $form->{l_doccnt}           = $form->{l_source} ? 'Y' : '';
 
   my %column_defs = (
     'id'               => { 'text' => $locale->text('ID'), },
-    'transdate'        => { 'text' => $locale->text('Invoice Date'), },
-    'gldate'           => { 'text' => $locale->text('Booking Date'), },
+    'transdate'        => { 'text' => $locale->text('Transdate'), },
+    'gldate'           => { 'text' => $locale->text('Gldate'), },
     'reference'        => { 'text' => $locale->text('Reference'), },
     'source'           => { 'text' => $locale->text('Source'), },
+    'doccnt'           => { 'text' => $locale->text('Document Count'), },
     'description'      => { 'text' => $locale->text('Description'), },
     'notes'            => { 'text' => $locale->text('Notes'), },
     'debit'            => { 'text' => $locale->text('Debit'), },
@@ -389,7 +500,8 @@ sub generate_report {
   $report->set_columns(%column_defs);
   $report->set_column_order(@columns);
 
-  $report->set_export_options('generate_report', @hidden_variables, qw(sort sortdir));
+  $form->{l_attachments} = 'Y';
+  $report->set_export_options('generate_report', @hidden_variables, qw(sort sortdir l_attachments));
 
   $report->set_sort_indicator($form->{sort} eq 'accno' ? 'debit_accno' : $form->{sort}, $form->{sortdir});
 
@@ -438,6 +550,10 @@ sub generate_report {
     my $row = { };
     map { $row->{$_} = { 'data' => '', 'align' => $column_alignment{$_} } } @columns;
 
+    if ( $form->{l_doccnt} ) {
+      $row->{doccnt}->{data} = SL::Helper::GlAttachments->count_gl_pdf_attachments($ref->{id},$ref->{type});
+    }
+
     my $sh = "";
     if ($form->{balance} < 0) {
       $sh = " S";
@@ -466,7 +582,7 @@ sub generate_report {
 
     my $row_set = [ $row ];
 
-    if (($form->{l_subtotal} eq 'Y')
+    if ( ($form->{l_subtotal} eq 'Y' && !$form->{report_generator_csv_options_for_import} )
         && (($idx == (scalar @{ $form->{GL} } - 1))
             || ($ref->{ $form->{sort} } ne $form->{GL}->[$idx + 1]->{ $form->{sort} }))) {
       push @{ $row_set }, create_subtotal_row(\%subtotals, \@columns, \%column_alignment, [ qw(debit credit) ], 'listsubtotal');
@@ -477,8 +593,6 @@ sub generate_report {
     $idx++;
   }
 
-  $report->add_separator();
-
   # = 0 for balanced ledger
   my $balanced_ledger = $totals{debit} + $totals{debit_tax} - $totals{credit} - $totals{credit_tax};
 
@@ -497,7 +611,10 @@ sub generate_report {
 
   $row->{balance}->{data}        = $data;
 
-  $report->add_data($row);
+  if ( !$form->{report_generator_csv_options_for_import} ) {
+    $report->add_separator();
+    $report->add_data($row);
+  }
 
   my $raw_bottom_info_text;
 
@@ -514,15 +631,25 @@ sub generate_report {
 
   $report->set_options('raw_bottom_info_text' => $raw_bottom_info_text);
 
+  setup_gl_transactions_action_bar(num_rows => scalar(@{$form->{GL}}));
+
   $report->generate_with_headers();
 
   $main::lxdebug->leave_sub();
 }
 
+sub show_draft {
+  $::form->{transdate} = DateTime->today_local->to_kivitendo if !$::form->{transdate};
+  $::form->{gldate}    = $::form->{transdate} if !$::form->{gldate};
+  update();
+}
+
 sub update {
+  my %params = @_;
+
   $main::lxdebug->enter_sub();
 
-  $main::auth->assert('general_ledger');
+  $main::auth->assert('gl_transactions');
 
   my $form     = $main::form;
   my %myconfig = %main::myconfig;
@@ -537,77 +664,73 @@ sub update {
   my $creditcount = 0;
   my ($debitcredit, $amount);
 
+  my $dbh = SL::DB->client->dbh;
+  my ($notax_id) = selectrow_query($form, $dbh, "SELECT id FROM tax WHERE taxkey = 0 LIMIT 1", );
+  my $zerotaxes  = selectall_hashref_query($form, $dbh, "SELECT id FROM tax WHERE rate = 0", );
+
   my @flds =
     qw(accno debit credit projectnumber fx_transaction source memo tax taxchart);
 
   for my $i (1 .. $form->{rowcount}) {
+    $form->{"${_}_$i"} = $form->parse_amount(\%myconfig, $form->{"${_}_$i"}) for qw(debit credit tax);
 
-    unless (($form->{"debit_$i"} eq "") && ($form->{"credit_$i"} eq "")) {
-      for (qw(debit credit tax)) {
-        $form->{"${_}_$i"} =
-          $form->parse_amount(\%myconfig, $form->{"${_}_$i"});
-      }
+    next if !$form->{"debit_$i"} && !$form->{"credit_$i"} && !$params{keep_rows_without_amount};
 
-      push @a, {};
-      $debitcredit = ($form->{"debit_$i"} == 0) ? "0" : "1";
-      if ($debitcredit) {
-        $debitcount++;
-      } else {
-        $creditcount++;
-      }
+    push @a, {};
+    $debitcredit = ($form->{"debit_$i"} == 0) ? "0" : "1";
+    if ($debitcredit) {
+      $debitcount++;
+    } else {
+      $creditcount++;
+    }
 
-      if (($debitcount >= 2) && ($creditcount == 2)) {
-        $form->{"credit_$i"} = 0;
-        $form->{"tax_$i"}    = 0;
-        $creditcount--;
-        $form->{creditlock} = 1;
-      }
-      if (($creditcount >= 2) && ($debitcount == 2)) {
-        $form->{"debit_$i"} = 0;
-        $form->{"tax_$i"}   = 0;
-        $debitcount--;
-        $form->{debitlock} = 1;
-      }
-      if (($creditcount == 1) && ($debitcount == 2)) {
-        $form->{creditlock} = 1;
-      }
-      if (($creditcount == 2) && ($debitcount == 1)) {
-        $form->{debitlock} = 1;
-      }
-      if ($debitcredit && $credittax) {
-        $form->{"taxchart_$i"} = "0--0.00";
-      }
-      if (!$debitcredit && $debittax) {
-        $form->{"taxchart_$i"} = "0--0.00";
-      }
-      $amount =
-        ($form->{"debit_$i"} == 0)
-        ? $form->{"credit_$i"}
-        : $form->{"debit_$i"};
-      my $j = $#a;
-      if (($debitcredit && $credittax) || (!$debitcredit && $debittax)) {
-        $form->{"taxchart_$i"} = "0--0.00";
-        $form->{"tax_$i"}      = 0;
-      }
-      my ($taxkey, $rate) = split(/--/, $form->{"taxchart_$i"});
-      if ($taxkey > 1) {
-        if ($debitcredit) {
-          $debittax = 1;
-        } else {
-          $credittax = 1;
-        }
-        if ($form->{taxincluded}) {
-          $form->{"tax_$i"} = $amount / ($rate + 1) * $rate;
-        } else {
-          $form->{"tax_$i"} = $amount * $rate;
-        }
+    if (($debitcount >= 2) && ($creditcount == 2)) {
+      $form->{"credit_$i"} = 0;
+      $form->{"tax_$i"}    = 0;
+      $creditcount--;
+      $form->{creditlock} = 1;
+    }
+    if (($creditcount >= 2) && ($debitcount == 2)) {
+      $form->{"debit_$i"} = 0;
+      $form->{"tax_$i"}   = 0;
+      $debitcount--;
+      $form->{debitlock} = 1;
+    }
+    if (($creditcount == 1) && ($debitcount == 2)) {
+      $form->{creditlock} = 1;
+    }
+    if (($creditcount == 2) && ($debitcount == 1)) {
+      $form->{debitlock} = 1;
+    }
+    if ($debitcredit && $credittax) {
+      $form->{"taxchart_$i"} = "$notax_id--0.00";
+    }
+    if (!$debitcredit && $debittax) {
+      $form->{"taxchart_$i"} = "$notax_id--0.00";
+    }
+    $amount =
+      ($form->{"debit_$i"} == 0)
+      ? $form->{"credit_$i"}
+      : $form->{"debit_$i"};
+    my $j = $#a;
+    if (($debitcredit && $credittax) || (!$debitcredit && $debittax)) {
+      $form->{"taxchart_$i"} = "$notax_id--0.00";
+      $form->{"tax_$i"}      = 0;
+    }
+    my ($taxkey, $rate) = split(/--/, $form->{"taxchart_$i"});
+    my $iswithouttax = grep { $_->{id} == $taxkey } @{ $zerotaxes };
+    if (!$iswithouttax) {
+      if ($debitcredit) {
+        $debittax = 1;
       } else {
-        $form->{"tax_$i"} = 0;
+        $credittax = 1;
       }
+    };
+    my ($tmpnetamount,$tmpdiff);
+    ($tmpnetamount,$form->{"tax_$i"},$tmpdiff) = $form->calculate_tax($amount,$rate,$form->{taxincluded} *= 1,2);
 
-      for (@flds) { $a[$j]->{$_} = $form->{"${_}_$i"} }
-      $count++;
-    }
+    for (@flds) { $a[$j]->{$_} = $form->{"${_}_$i"} }
+    $count++;
   }
 
   for my $i (1 .. $count) {
@@ -619,9 +742,9 @@ sub update {
     for (@flds) { delete $form->{"${_}_$i"} }
   }
 
-  $form->{rowcount} = $count + 1;
+  $form->{rowcount} = $count + ($params{dont_add_new_row} ? 0 : 1);
 
-  &display_form;
+  display_form();
   $main::lxdebug->leave_sub();
 
 }
@@ -630,7 +753,7 @@ sub display_form {
   my ($init) = @_;
   $main::lxdebug->enter_sub();
 
-  $main::auth->assert('general_ledger');
+  $main::auth->assert('gl_transactions');
 
   my $form     = $main::form;
   my %myconfig = %main::myconfig;
@@ -653,12 +776,14 @@ sub display_rows {
   my ($init) = @_;
   $main::lxdebug->enter_sub();
 
-  $main::auth->assert('general_ledger');
+  $main::auth->assert('gl_transactions');
 
   my $form     = $main::form;
   my %myconfig = %main::myconfig;
   my $cgi      = $::request->{cgi};
 
+  my %balances = GL->get_chart_balances(map { $_->{id} } @{ $form->{ALL_CHARTS} });
+
   $form->{debit_1}     = 0 if !$form->{"debit_1"};
   $form->{totaldebit}  = 0;
   $form->{totalcredit} = 0;
@@ -670,31 +795,9 @@ sub display_rows {
     $project_labels{$item->{"id"}} = $item->{"projectnumber"};
   }
 
-  my %chart_labels = ();
-  my @chart_values = ();
-  my %charts = ();
-  my $taxchart_init;
-  foreach my $item (@{ $form->{ALL_CHARTS} }) {
-    if ($item->{charttype} eq 'H'){ #falls überschrift
-      next;                         #überspringen (Bug 1150)
-    }
-    my $key = $item->{accno} . "--" . $item->{tax_id};
-    $taxchart_init = $item->{tax_id} unless (@chart_values);
-    push(@chart_values, $key);
-    $chart_labels{$key} = $item->{accno} . "--" . $item->{description};
-    $charts{$item->{accno}} = $item;
-  }
-
-  my %taxchart_labels = ();
-  my @taxchart_values = ();
-  my %taxcharts = ();
-  foreach my $item (@{ $form->{ALL_TAXCHARTS} }) {
-    my $key = $item->{id} . "--" . $item->{rate};
-    $taxchart_init = $key if ($taxchart_init == $item->{id});
-    push(@taxchart_values, $key);
-    $taxchart_labels{$key} = $item->{taxdescription} . " " . $item->{rate} * 100 . ' %';
-    $taxcharts{$item->{id}} = $item;
-  }
+  my %charts_by_id  = map { ($_->{id} => $_) } @{ $::form->{ALL_CHARTS} };
+  my $default_chart = $::form->{ALL_CHARTS}[0];
+  my $transdate     = $::form->{transdate} ? DateTime->from_kivitendo($::form->{transdate}) : DateTime->today_local;
 
   my ($source, $memo, $source_hidden, $memo_hidden);
   for my $i (1 .. $form->{rowcount}) {
@@ -710,43 +813,38 @@ sub display_rows {
       <input type="hidden" name="memo_$i" value="$form->{"memo_$i"}" size="16">|;
     }
 
-    my $selected_accno_full;
-    my ($accno_row) = split(/--/, $form->{"accno_$i"});
-    my $item = $charts{$accno_row};
-    $selected_accno_full = "$item->{accno}--$item->{tax_id}";
+    my %taxchart_labels = ();
+    my @taxchart_values = ();
 
-    my $selected_taxchart = $form->{"taxchart_$i"};
-    my ($selected_accno, $selected_tax_id) = split(/--/, $selected_accno_full);
-    my ($previous_accno, $previous_tax_id) = split(/--/, $form->{"previous_accno_$i"});
+    my $accno_id = $::form->{"accno_id_$i"};
+    my $chart    = $charts_by_id{$accno_id} // $default_chart;
+    $accno_id    = $chart->{id};
+    my ($first_taxchart, $default_taxchart, $taxchart_to_use);
 
-    if ($previous_accno &&
-        ($previous_accno eq $selected_accno) &&
-        ($previous_tax_id ne $selected_tax_id)) {
-      my $item = $taxcharts{$selected_tax_id};
-      $selected_taxchart = "$item->{id}--$item->{rate}";
+    foreach my $item ( GL->get_active_taxes_for_chart($accno_id, $transdate) ) {
+      my $key             = $item->id . "--" . $item->rate;
+      $first_taxchart   //= $item;
+      $default_taxchart   = $item if $item->{is_default};
+      $taxchart_to_use    = $item if $key eq $form->{"taxchart_$i"};
+
+      push(@taxchart_values, $key);
+      $taxchart_labels{$key} = $item->taxdescription . " " . $item->rate * 100 . ' %';
     }
 
-    $selected_accno      = '' if ($init);
-    $selected_taxchart ||= $taxchart_init;
+    $taxchart_to_use    //= $default_taxchart // $first_taxchart;
+    my $selected_taxchart = $taxchart_to_use->id . '--' . $taxchart_to_use->rate;
 
     my $accno = qq|<td>| .
-      NTI($cgi->popup_menu('-name' => "accno_$i",
-                           '-id' => "accno_$i",
-                           '-onChange' => "setTaxkey($i)",
-                           '-style' => 'width:200px',
-                           '-values' => \@chart_values,
-                           '-labels' => \%chart_labels,
-                           '-default' => $selected_accno_full))
-      . $cgi->hidden('-name' => "previous_accno_$i",
-                     '-default' => $selected_accno_full)
+      $::request->presenter->chart_picker("accno_id_$i", $accno_id, style => "width: 300px") .
+      $::request->presenter->hidden_tag("previous_accno_id_$i", $accno_id)
       . qq|</td>|;
     my $tax_ddbox = qq|<td>| .
       NTI($cgi->popup_menu('-name' => "taxchart_$i",
-                           '-id' => "taxchart_$i",
-                           '-style' => 'width:200px',
-                           '-values' => \@taxchart_values,
-                           '-labels' => \%taxchart_labels,
-                           '-default' => $selected_taxchart))
+            '-id' => "taxchart_$i",
+            '-style' => 'width:200px',
+            '-values' => \@taxchart_values,
+            '-labels' => \%taxchart_labels,
+            '-default' => $selected_taxchart))
       . qq|</td>|;
 
     my ($fx_transaction, $checked);
@@ -814,10 +912,11 @@ sub display_rows {
     <input type="hidden" name="project_id_$i" value="$form->{"project_id_$i"}">|;
 
     my $copy2credit = $i == 1 ? 'onkeyup="copy_debit_to_credit()"' : '';
+    my $balance     = $form->format_amount(\%::myconfig, $balances{$accno_id} // 0, 2, 'DRCR');
 
     print qq|<tr valign=top>
     $accno
-    <td id="chart_balance_$i" align="right">&nbsp;</td>
+    <td id="chart_balance_$i" align="right">${balance}</td>
     $fx_transaction
     <td><input name="debit_$i" size="8" value="$form->{"debit_$i"}" accesskey=$i $copy2credit $debitreadonly></td>
     <td><input name="credit_$i" size=8 value="$form->{"credit_$i"}" $creditreadonly></td>
@@ -848,427 +947,194 @@ sub display_rows {
 
 }
 
-sub form_header {
-  my ($init) = @_;
-  $main::lxdebug->enter_sub();
-
-  $main::auth->assert('general_ledger');
-
-  my $form     = $main::form;
-  my %myconfig = %main::myconfig;
-  my $locale   = $main::locale;
-
-  my @old_project_ids = ();
-  map({ push(@old_project_ids, $form->{"project_id_$_"})
-          if ($form->{"project_id_$_"}); } (1..$form->{"rowcount"}));
-
-  $form->get_lists("projects"  => { "key"       => "ALL_PROJECTS",
-                                    "all"       => 0,
-                                    "old_id"    => \@old_project_ids },
-                   "charts"    => { "key"       => "ALL_CHARTS",
-                                    "transdate" => $form->{transdate} },
-                   "taxcharts" => "ALL_TAXCHARTS");
-
-  GL->get_chart_balances('charts' => $form->{ALL_CHARTS});
-
-  my $title      = $form->{title};
-  $form->{title} = $locale->text("$title General Ledger Transaction");
-  my $readonly   = ($form->{id}) ? "readonly" : "";
-
-  my $show_details_checked   = $form->{show_details}   ? "checked" : '';
-  my $ob_transaction_checked = $form->{ob_transaction} ? "checked" : '';
-  my $cb_transaction_checked = $form->{cb_transaction} ? "checked" : '';
-
-  # $locale->text('Add General Ledger Transaction')
-  # $locale->text('Edit General Ledger Transaction')
-
-  map { $form->{$_} =~ s/\"/&quot;/g }
-    qw(reference description chart taxchart);
-
-  $form->{javascript} = qq|<script type="text/javascript">
-  <!--
-  function setTaxkey(row) {
-    var accno  = document.getElementById('accno_' + row);
-    var taxkey = accno.options[accno.selectedIndex].value;
-    var reg = /--([0-9]*)/;
-    var found = reg.exec(taxkey);
-    var index = found[1];
-    index = parseInt(index);
-    var tax = 'taxchart_' + row;
-    for (var i = 0; i < document.getElementById(tax).options.length; ++i) {
-      var reg2 = new RegExp("^"+ index, "");
-      if (reg2.exec(document.getElementById(tax).options[i].value)) {
-        document.getElementById(tax).options[i].selected = true;
-        break;
-      }
-    }
-  };
-
-  function copy_debit_to_credit() {
-    var txt = document.getElementsByName('debit_1')[0].value;
-    document.getElementsByName('credit_2')[0].value = txt;
-  };
-  //-->
-  </script>
-  <script type="text/javascript" src="js/show_form_details.js"></script>
-  <script type="text/javascript" src="js/jquery.js"></script>
-|;
-
-  $form->{selectdepartment} =~ s/ selected//;
-  $form->{selectdepartment} =~
-    s/option>\Q$form->{department}\E/option selected>$form->{department}/;
-
-  my $description;
-  if ((my $rows = $form->numtextrows($form->{description}, 50)) > 1) {
-    $description =
-      qq|<textarea name=description rows=$rows cols=50 wrap=soft $readonly >$form->{description}</textarea>|;
-  } else {
-    $description =
-      qq|<input name=description size=50 value="$form->{description}" $readonly>|;
-  }
-
-  my $taxincluded = ($form->{taxincluded}) ? "checked" : "";
-
-  if ($init) {
-    $taxincluded = "checked";
-  }
-
-  my $department;
-  $department = qq|
-        <tr>
-          <th align=right nowrap>| . $locale->text('Department') . qq|</th>
-          <td colspan=3><select name=department>$form->{selectdepartment}</select></td>
-          <input type=hidden name=selectdepartment value="$form->{selectdepartment}">
-        </tr>
-| if $form->{selectdepartment};
-  if ($init) {
-    $form->{fokus} = "gl.reference";
-  } else {
-    $form->{fokus} = qq|gl.accno_$form->{rowcount}|;
-  }
-
-  # use JavaScript Calendar or not
-  $form->{jsscript} = 1;
-  my $jsscript = "";
-  my ($button1, $button2);
-  if ($form->{jsscript}) {
-
-    # with JavaScript Calendar
-    $button1 = qq|
-       <td><input name=transdate id=transdate size=11 title="$myconfig{dateformat}" value="$form->{transdate}" $readonly onBlur=\"check_right_date_format(this)\">
-       <input type=button name=transdate id="trigger1" value=|
-      . $locale->text('button') . qq|></td>
-       |;
-
-    #write Trigger
-    $jsscript =
-      Form->write_trigger(\%myconfig, "1", "transdate", "BL", "trigger1");
-  } else {
+sub _get_radieren {
+  return ($::instance_conf->get_gl_changeable == 2) ? ($::form->current_date(\%::myconfig) eq $::form->{gldate}) : ($::instance_conf->get_gl_changeable == 1);
+}
 
-    # without JavaScript Calendar
-    $button1 =
-      qq|<td><input name=transdate id=transdate size=11 title="$myconfig{dateformat}" value="$form->{transdate}" $readonly onBlur=\"check_right_date_format(this)\"></td>|;
+sub setup_gl_action_bar {
+  my %params = @_;
+  my $form   = $::form;
+  my $change_never            = $::instance_conf->get_gl_changeable == 0;
+  my $change_on_same_day_only = $::instance_conf->get_gl_changeable == 2 && ($form->current_date(\%::myconfig) ne $form->{gldate});
+
+  for my $bar ($::request->layout->get('actionbar')) {
+    $bar->add(
+      action => [
+        t8('Update'),
+        submit    => [ '#form', { action => 'update' } ],
+        id        => 'update_button',
+        accesskey => 'enter',
+      ],
+      action => [
+        t8('Post'),
+        submit   => [ '#form', { action => 'post' } ],
+        disabled => $form->{locked}                           ? t8('The billing period has already been locked.')
+                  : $form->{storno}                           ? t8('A canceled general ledger transaction cannot be posted.')
+                  : ($form->{id} && $change_never)            ? t8('Changing general ledger transaction has been disabled in the configuration.')
+                  : ($form->{id} && $change_on_same_day_only) ? t8('General ledger transactions can only be changed on the day they are posted.')
+                  :                                             undef,
+        ],
+      combobox => [
+        action => [ t8('Storno'),
+          submit   => [ '#form', { action => 'storno' } ],
+          confirm  => t8('Do you really want to cancel this general ledger transaction?'),
+          disabled => !$form->{id} ? t8('This general ledger transaction has not been posted yet.') : undef,
+        ],
+        action => [ t8('Delete'),
+          submit   => [ '#form', { action => 'delete' } ],
+          confirm  => t8('Do you really want to delete this object?'),
+          disabled => !$form->{id}             ? t8('This invoice has not been posted yet.')
+                    : $form->{locked}          ? t8('The billing period has already been locked.')
+                    : $change_never            ? t8('Changing invoices has been disabled in the configuration.')
+                    : $change_on_same_day_only ? t8('Invoices can only be changed on the day they are posted.')
+                    :                            undef,
+        ],
+      ], # end of combobox "Storno"
+
+      combobox => [
+        action => [ t8('more') ],
+        action => [
+          t8('History'),
+          call     => [ 'set_history_window', $form->{id} * 1, 'id' ],
+          disabled => !$form->{id} ? t8('This invoice has not been posted yet.') : undef,
+        ],
+        action => [
+          t8('Follow-Up'),
+          call     => [ 'follow_up_window' ],
+          disabled => !$form->{id} ? t8('This invoice has not been posted yet.') : undef,
+        ],
+        action => [
+          t8('Record templates'),
+          call => [ 'kivi.RecordTemplate.popup', 'gl_transaction' ],
+        ],
+        action => [
+          t8('Drafts'),
+          call     => [ 'kivi.Draft.popup', 'gl', 'unknown', $form->{draft_id}, $form->{draft_description} ],
+          disabled => $form->{id}     ? t8('This invoice has already been posted.')
+                    : $form->{locked} ? t8('The billing period has already been locked.')
+                    :                   undef,
+        ],
+      ], # end of combobox "more"
+    );
   }
+}
 
-  $form->{previous_id}     ||= "--";
-  $form->{previous_gldate} ||= "--";
-
-  $jsscript .= $form->parse_html_template('gl/form_header_chart_balances_js');
-
-  $form->header;
-
-  print qq|
-<body onLoad="focus()">
-
-<script type="text/javascript" src="js/follow_up.js"></script>
-
-<form method=post name="gl" action=gl.pl>
-|;
+sub setup_gl_search_action_bar {
+  my %params = @_;
 
-  $form->hide_form(qw(id closedto locked storno storno_id previous_id previous_gldate));
-
-  print qq|
-<input type=hidden name=title value="$title">
-
-<input type="hidden" name="follow_up_trans_id_1" value="| . H($form->{id}) . qq|">
-<input type="hidden" name="follow_up_trans_type_1" value="gl_transaction">
-<input type="hidden" name="follow_up_trans_info_1" value="| . H($form->{id}) . qq|">
-<input type="hidden" name="follow_up_rowcount" value="1">
-
-<table width=100%>
-  <tr>
-    <th class=listtop>$form->{title}</th>
-  </tr>| .
-
-  ($form->{saved_message} ? qq|
-  <tr>
-    <td>$form->{saved_message}</th>
-  </tr>| : '') .
-
-qq|
-  <tr height="5"></tr>
-  <tr>
-    <td>
-      <table width=100%>
-        <tr>
-          <td colspan="6" align="left">|
-    . $locale->text("Previous transnumber text")
-    . " $form->{previous_id} "
-    . $locale->text("Previous transdate text")
-    . " $form->{previous_gldate}"
-    . qq|</td>
-        </tr>
-        <tr>
-          <th align=right>| . $locale->text('Reference') . qq|</th>
-          <td><input name=reference size=20 value="$form->{reference}" $readonly></td>
-          <td align=left>
-            <table>
-              <tr>
-                <th align=right nowrap>| . $locale->text('Date') . qq|</th>
-                $button1
-              </tr>
-            </table>
-          </td>
-        </tr>|;
-  if ($form->{id}) {
-    print qq|
-        <tr>
-          <th align=right>| . $locale->text('Belegnummer') . qq|</th>
-          <td><input name=id size=20 value="$form->{id}" $readonly></td>
-          <td align=left>
-          <table>
-              <tr>
-                <th align=right width=50%>| . $locale->text('Buchungsdatum') . qq|</th>
-                <td align=left><input name=gldate size=11 title="$myconfig{dateformat}" value=$form->{gldate} $readonly onBlur=\"check_right_date_format(this)\"></td>
-              </tr>
-            </table>
-          </td>
-        </tr>|;
-  }
-  print qq|
-        $department|;
-  if ($form->{id}) {
-    print qq|
-        <tr>
-          <th align=right width=1%>| . $locale->text('Description') . qq|</th>
-          <td width=1%>$description</td>
-          <td>
-            <table>
-              <tr>
-                <th align=left>| . $locale->text('MwSt. inkl.') . qq|</th>
-                <td><input type=checkbox name=taxincluded value=1 $taxincluded></td>
-              </tr>
-            </table>
-         </td>
-          <td align=left>
-            <table width=100%>
-              <tr>
-                <th align=right width=50%>| . $locale->text('Mitarbeiter') . qq|</th>
-                <td align=left><input name=employee size=20  value="| . H($form->{employee}) . qq|" readonly></td>
-              </tr>
-            </table>
-          </td>
-        </tr>|;
-  } else {
-    print qq|
-        <tr>
-          <th align=left width=1%>| . $locale->text('Description') . qq|</th>
-          <td width=1%>$description</td>
-          <td>
-            <table>
-              <tr>
-                <th align=left>| . $locale->text('MwSt. inkl.') . qq|</th>
-                <td><input type=checkbox name=taxincluded value=1 $taxincluded></td>
-              </tr>
-            </table>
-         </td>
-        </tr>|;
-  }
-
-  print qq|
-      <tr>
-      <tr><td colspan=4><table><tr>
-       <td>
-        | . $locale->text('OB Transaction') . qq|<input type="checkbox" name="ob_transaction" value="1" $ob_transaction_checked>
-       </td>
-       <td>
-        | . $locale->text('CB Transaction') . qq|<input type="checkbox" name="cb_transaction" value="1" $cb_transaction_checked>
-       </td>
-      </tr></table></td></tr>
-      <tr>
-       <td width="1%" align="right" nowrap>| . $locale->text('Show details') . qq|</td>
-       <td width="1%"><input type="checkbox" onclick="show_form_details();" name="show_details" value="1" $show_details_checked></td>
-      </tr>|;
-
-  print qq|
-      <tr>
-      <td colspan=4>
-          <table width=100%>
-           <tr class=listheading>
-          <th class=listheading style="width:15%">|
-    . $locale->text('Account') . qq|</th>
-          <th class=listheading style="width:10%">| . $locale->text('Chart balance') . qq|</th>
-          <th class=listheading style="width:10%">|
-    . $locale->text('Debit') . qq|</th>
-          <th class=listheading style="width:10%">|
-    . $locale->text('Credit') . qq|</th>
-          <th class=listheading style="width:10%">|
-    . $locale->text('Tax') . qq|</th>
-          <th class=listheading style="width:5%">|
-    . $locale->text('Taxkey') . qq|</th>|;
-
-  if ($form->{show_details}) {
-    print qq|
-          <th class=listheading style="width:20%">| . $locale->text('Source') . qq|</th>
-          <th class=listheading style="width:20%">| . $locale->text('Memo') . qq|</th>
-          <th class=listheading style="width:20%">| . $locale->text('Project Number') . qq|</th>
-|;
+  for my $bar ($::request->layout->get('actionbar')) {
+    $bar->add(
+      action => [
+        t8('Search'),
+        submit    => [ '#form', { action => 'continue', nextsub => 'generate_report' } ],
+        accesskey => 'enter',
+      ],
+    );
   }
-
-  print qq|
-        </tr>
-
-$jsscript
-|;
-  $main::lxdebug->leave_sub();
-
 }
 
-sub form_footer {
-  $main::lxdebug->enter_sub();
-
-  $main::auth->assert('general_ledger');
-
-  my $form     = $main::form;
-  my %myconfig = %main::myconfig;
-  my $locale   = $main::locale;
-  my $cgi      = $::request->{cgi};
-
-  my $follow_ups_block;
-  if ($form->{id}) {
-    my $follow_ups = FU->follow_ups('trans_id' => $form->{id});
-
-    if (@{ $follow_ups} ) {
-      my $num_due       = sum map { $_->{due} * 1 } @{ $follow_ups };
-      $follow_ups_block = qq|<p>| . $locale->text("There are #1 unfinished follow-ups of which #2 are due.", scalar @{ $follow_ups }, $num_due) . qq|</p>|;
-    }
+sub setup_gl_transactions_action_bar {
+  my %params = @_;
+
+  for my $bar ($::request->layout->get('actionbar')) {
+    $bar->add(
+      combobox => [
+        action => [ $::locale->text('Create new') ],
+        action => [
+          $::locale->text('GL Transaction'),
+          submit => [ '#create_new_form', { action => 'gl_transaction' } ],
+        ],
+        action => [
+          $::locale->text('AR Transaction'),
+          submit => [ '#create_new_form', { action => 'ar_transaction' } ],
+        ],
+        action => [
+          $::locale->text('AP Transaction'),
+          submit => [ '#create_new_form', { action => 'ap_transaction' } ],
+        ],
+        action => [
+          $::locale->text('Sales Invoice'),
+          submit => [ '#create_new_form', { action => 'sales_invoice'  } ],
+        ],
+        action => [
+          $::locale->text('Vendor Invoice'),
+          submit => [ '#create_new_form', { action => 'vendor_invoice' } ],
+        ],
+      ], # end of combobox "Create new"
+    );
   }
+}
 
-  my ($dec) = ($form->{totaldebit} =~ /\.(\d+)/);
-  $dec = length $dec;
-  my $decimalplaces = ($dec > 2) ? $dec : 2;
-  my $radieren = ($form->current_date(\%myconfig) eq $form->{gldate}) ? 1 : 0;
-
-  map {
-    $form->{$_} = $form->format_amount(\%myconfig, $form->{$_}, 2, "&nbsp;")
-  } qw(totaldebit totalcredit);
-
-  print qq|
-    <tr class=listtotal>
-    <th colspan="3" align=right class=listtotal> $form->{totaldebit}</th>
-    <th align=right class=listtotal> $form->{totalcredit}</th>
-    <td colspan=6></td>
-    </tr>
-  </table>
-  </td>
-  </tr>
-</table>
+sub form_header {
+  $::lxdebug->enter_sub;
+  $::auth->assert('gl_transactions');
 
-<input name=callback type=hidden value="$form->{callback}">
+  my ($init) = @_;
 
-$follow_ups_block
+  $::request->layout->add_javascripts("autocomplete_chart.js", "kivi.File.js", "kivi.GL.js", "kivi.RecordTemplate.js");
 
-<br>
-|;
+  my @old_project_ids = grep { $_ } map{ $::form->{"project_id_$_"} } 1..$::form->{rowcount};
 
-  my $transdate = $form->datetonum($form->{transdate}, \%myconfig);
-  my $closedto  = $form->datetonum($form->{closedto},  \%myconfig);
+  $::form->get_lists("projects"  => { "key"       => "ALL_PROJECTS",
+                                    "all"       => 0,
+                                    "old_id"    => \@old_project_ids },
 
-  if ($form->{id}) {
+                   "charts"    => { "key"       => "ALL_CHARTS",
+                                    "transdate" => $::form->{transdate} });
 
-    if (!$form->{storno}) {
-      print qq|<input class=submit type=submit name=action value="| . $locale->text('Storno') . qq|">|;
-    }
+  $::form->{ALL_DEPARTMENTS} = SL::DB::Manager::Department->get_all;
 
-    # Löschen und Ändern von Buchungen nicht mehr möglich (GoB) nur am selben Tag möglich
-    if (!$form->{locked} && $radieren) {
-      print qq|
-        <input class=submit type=submit name=action value="| . $locale->text('Post') . qq|" accesskey="b">
-        <input class=submit type=submit name=action value="| . $locale->text('Delete') . qq|">|;
-    }
+  my $title      = $::form->{title};
+  $::form->{title} = $::locale->text("$title General Ledger Transaction");
+  # $locale->text('Add General Ledger Transaction')
+  # $locale->text('Edit General Ledger Transaction')
 
-    print qq|
-        <input class=submit type=submit name=action id=update_button value="| . $locale->text('Update') . qq|">
-        <input type="button" class="submit" onclick="follow_up_window()" value="|
-      . $locale->text('Follow-Up')
-      . qq|"> |;
+  map { $::form->{$_} =~ s/\"/&quot;/g }
+    qw(chart taxchart);
 
+  if ($init) {
+    $::request->{layout}->focus("#reference");
+    $::form->{taxincluded} = "1";
   } else {
-    if ($form->{draft_id}) {
-      my $remove_draft_checked = $form->{remove_draft} ? 'checked' : '';
-      print qq|<p>\n|
-        . qq|  <input name="remove_draft" id="remove_draft" type="checkbox" class="checkbox" ${remove_draft_checked}>|
-        . qq|  <label for="remove_draft">| . $locale->text('Remove Draft') . qq|</label>\n|
-        . qq|</p>\n|;
-    }
-
-    print qq|
-        <input class=submit type=submit name=action id=update_button value="| . $locale->text('Update') . qq|">
-        <input class=submit type=submit name=action value="| . $locale->text('Post') . qq|"> |
-        . NTI($cgi->submit('-name' => 'action', '-value' => $locale->text('Save draft'), '-class' => 'submit'))
-        . $cgi->hidden('-name' => 'draft_id',          '-default' => [$form->{draft_id}])
-        . $cgi->hidden('-name' => 'draft_description', '-default' => [$form->{draft_description}]);
+    $::request->{layout}->focus("#accno_id_$::form->{rowcount}_name");
   }
 
-  print "
-  </form>
-
-</body>
-</html>
-";
-  $main::lxdebug->leave_sub();
+  $::form->{previous_id}     ||= "--";
+  $::form->{previous_gldate} ||= "--";
 
-}
-
-sub delete {
-  $main::lxdebug->enter_sub();
-
-  my $form     = $main::form;
-  my $locale   = $main::locale;
+  setup_gl_action_bar();
 
-  $form->header;
+  $::form->header;
+  print $::form->parse_html_template('gl/form_header', {
+    hide_title => $title,
+    readonly   => $::form->{id} && ($::form->{locked} || !_get_radieren()),
+  });
 
-  print qq|
-<body>
+  $::lxdebug->leave_sub;
 
-<form method=post action=gl.pl>
-|;
+}
 
-  map { $form->{$_} =~ s/\"/&quot;/g } qw(reference description);
+sub form_footer {
+  $::lxdebug->enter_sub;
+  $::auth->assert('gl_transactions');
 
-  delete $form->{header};
+  my ($follow_ups, $follow_ups_due);
 
-  foreach my $key (keys %$form) {
-    next if (($key eq 'login') || ($key eq 'password') || ('' ne ref $form->{$key}));
-    print qq|<input type="hidden" name="$key" value="$form->{$key}">\n|;
+  if ($::form->{id}) {
+    $follow_ups     = FU->follow_ups('trans_id' => $::form->{id}, 'not_done' => 1);
+    $follow_ups_due = sum map { $_->{due} * 1 } @{ $follow_ups || [] };
   }
 
-  print qq|
-<h2 class=confirm>| . $locale->text('Confirm!') . qq|</h2>
-
-<h4>|
-    . $locale->text('Are you sure you want to delete Transaction')
-    . qq| $form->{reference}</h4>
-
-<input name=action class=submit type=submit value="|
-    . $locale->text('Yes') . qq|">
-</form>
-|;
-  $main::lxdebug->leave_sub();
+  print $::form->parse_html_template('gl/form_footer', {
+    radieren       => _get_radieren(),
+    follow_ups     => $follow_ups,
+    follow_ups_due => $follow_ups_due,
+  });
 
+  $::lxdebug->leave_sub;
 }
 
-sub yes {
+sub delete {
   $main::lxdebug->enter_sub();
 
   my $form     = $main::form;
@@ -1278,8 +1144,9 @@ sub yes {
   if (GL->delete_transaction(\%myconfig, \%$form)){
     # saving the history
       if(!exists $form->{addition} && $form->{id} ne "") {
-        $form->{snumbers} = qq|ordnumber_| . $form->{ordnumber};
+        $form->{snumbers} = qq|gltransaction_| . $form->{id};
         $form->{addition} = "DELETED";
+        $form->{what_done} = "gl_transaction";
         $form->save_history;
       }
     # /saving the history
@@ -1314,6 +1181,10 @@ sub post_transaction {
   my $debitcredit;
   my %split_safety = ();
 
+  my $dbh = SL::DB->client->dbh;
+  my ($notax_id) = selectrow_query($form, $dbh, "SELECT id FROM tax WHERE taxkey = 0 LIMIT 1", );
+  my $zerotaxes  = selectall_hashref_query($form, $dbh, "SELECT id FROM tax WHERE rate = 0", );
+
   my @flds = qw(accno debit credit projectnumber fx_transaction source memo tax taxchart);
 
   for my $i (1 .. $form->{rowcount}) {
@@ -1354,36 +1225,36 @@ sub post_transaction {
       $form->{debitlock} = 1;
     }
     if ($debitcredit && $credittax) {
-      $form->{"taxchart_$i"} = "0--0.00";
+      $form->{"taxchart_$i"} = "$notax_id--0.00";
     }
     if (!$debitcredit && $debittax) {
-      $form->{"taxchart_$i"} = "0--0.00";
+      $form->{"taxchart_$i"} = "$notax_id--0.00";
     }
     my $amount = ($form->{"debit_$i"} == 0)
             ? $form->{"credit_$i"}
             : $form->{"debit_$i"};
     my $j = $#a;
     if (($debitcredit && $credittax) || (!$debitcredit && $debittax)) {
-      $form->{"taxchart_$i"} = "0--0.00";
+      $form->{"taxchart_$i"} = "$notax_id--0.00";
       $form->{"tax_$i"}      = 0;
     }
     my ($taxkey, $rate) = split(/--/, $form->{"taxchart_$i"});
-    if ($taxkey > 1) {
+    my $iswithouttax = grep { $_->{id} == $taxkey } @{ $zerotaxes };
+    if (!$iswithouttax) {
       if ($debitcredit) {
         $debittax = 1;
       } else {
         $credittax = 1;
       }
-      if ($form->{taxincluded}) {
-        $form->{"tax_$i"} = $amount / ($rate + 1) * $rate;
-        if ($debitcredit) {
-          $form->{"debit_$i"} = $form->{"debit_$i"} - $form->{"tax_$i"};
-        } else {
-          $form->{"credit_$i"} = $form->{"credit_$i"} - $form->{"tax_$i"};
-        }
+
+      my ($tmpnetamount,$tmpdiff);
+      ($tmpnetamount,$form->{"tax_$i"},$tmpdiff) = $form->calculate_tax($amount,$rate,$form->{taxincluded} *= 1,2);
+      if ($debitcredit) {
+        $form->{"debit_$i"} = $tmpnetamount;
       } else {
-        $form->{"tax_$i"} = $amount * $rate;
+        $form->{"credit_$i"} = $tmpnetamount;
       }
+
     } else {
       $form->{"tax_$i"} = 0;
     }
@@ -1393,7 +1264,8 @@ sub post_transaction {
   }
 
   if ($split_safety{-1} > 1 && $split_safety{1} > 1) {
-    $::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."));
+    $::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 kivitendo does not allow these."));
   }
 
   for my $i (1 .. $count) {
@@ -1421,6 +1293,9 @@ sub post_transaction {
   $form->{taxincluded} = 0 if !$taxtotal;
 
   # this is just for the wise guys
+
+  $form->error($locale->text('Cannot post transaction above the maximum future booking date!'))
+    if ($form->date_max_future($form->{"transdate"}, \%myconfig));
   $form->error($locale->text('Cannot post transaction for a closed period!'))
     if ($form->date_closed($form->{"transdate"}, \%myconfig));
   if ($form->round_amount($debit, 2) != $form->round_amount($credit, 2)) {
@@ -1443,9 +1318,9 @@ sub post_transaction {
   undef($form->{callback});
   # saving the history
   if(!exists $form->{addition} && $form->{id} ne "") {
-    $form->{snumbers} = qq|ordnumber_| . $form->{ordnumber};
-    $form->{addition} = "SAVED";
-    $form->{what_done} = $locale->text("Buchungsnummer") . " = " . $form->{id};
+    $form->{snumbers} = qq|gltransaction_| . $form->{id};
+    $form->{addition} = "POSTED";
+    $form->{what_done} = "gl transaction";
     $form->save_history;
   }
   # /saving the history
@@ -1456,12 +1331,12 @@ sub post_transaction {
 sub post {
   $main::lxdebug->enter_sub();
 
-  $main::auth->assert('general_ledger');
+  $main::auth->assert('gl_transactions');
 
   my $form     = $main::form;
   my $locale   = $main::locale;
 
-  if ($::myconfig{mandatory_departments} && !$form->{department}) {
+  if ($::myconfig{mandatory_departments} && !$form->{department_id}) {
     $form->{saved_message} = $::locale->text('You have to specify a department.');
     update();
     exit;
@@ -1471,11 +1346,14 @@ sub post {
   $form->{storno} = 0;
 
   post_transaction();
+  if ($::instance_conf->get_webdav) {
+    SL::Webdav->new(type     => 'general_ledger',
+                    number   => $form->{id},
+                   )->webdav_path;
+  }
 
-  remove_draft() if $form->{remove_draft};
-
-  $form->{callback} = build_std_url("action=add&DONT_LOAD_DRAFT=1", "show_details");
-  $form->redirect($form->{callback});
+  $form->{callback} = build_std_url("action=add", "show_details");
+  $form->redirect($::locale->text("General ledger transaction '#1' posted", $form->{reference}));
 
   $main::lxdebug->leave_sub();
 }
@@ -1483,7 +1361,7 @@ sub post {
 sub post_as_new {
   $main::lxdebug->enter_sub();
 
-  $main::auth->assert('general_ledger');
+  $main::auth->assert('gl_transactions');
 
   my $form     = $main::form;
 
@@ -1496,7 +1374,7 @@ sub post_as_new {
 sub storno {
   $main::lxdebug->enter_sub();
 
-  $main::auth->assert('general_ledger');
+  $main::auth->assert('gl_transactions');
 
   my $form     = $main::form;
   my %myconfig = %main::myconfig;
@@ -1512,8 +1390,9 @@ sub storno {
 
   # saving the history
   if(!exists $form->{addition} && $form->{id} ne "") {
-    $form->{snumbers} = "ordnumber_$form->{ordnumber}";
+    $form->{snumbers} = qq|gltransaction_| . $form->{id};
     $form->{addition} = "STORNO";
+    $form->{what_done} = "gl_transaction";
     $form->save_history;
   }
   # /saving the history
@@ -1527,4 +1406,19 @@ sub continue {
   call_sub($main::form->{nextsub});
 }
 
+sub get_tax_dropdown {
+  my $transdate    = $::form->{transdate} ? DateTime->from_kivitendo($::form->{transdate}) : DateTime->today_local;
+  my @tax_accounts = GL->get_active_taxes_for_chart($::form->{accno_id}, $transdate);
+  my $html         = $::form->parse_html_template("gl/update_tax_accounts", { TAX_ACCOUNTS => \@tax_accounts });
+
+  print $::form->ajax_response_header, $html;
+}
+
+sub get_chart_balance {
+  my %balances = GL->get_chart_balances($::form->{accno_id});
+  my $balance  = $::form->format_amount(\%::myconfig, $balances{ $::form->{accno_id} }, 2, 'DRCR');
+
+  print $::form->ajax_response_header, $balance;
+}
+
 1;