epic-ts
[kivitendo-erp.git] / bin / mozilla / gl.pl
index f4f87cc..faca8ce 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,25 @@ use utf8;
 use strict;
 
 use POSIX qw(strftime);
-use List::Util qw(sum);
+use List::Util qw(first sum);
 
+use SL::DB::ApGl;
+use SL::DB::RecordTemplate;
+use SL::DB::ReconciliationLink;
+use SL::DB::BankTransactionAccTrans;
+use SL::DB::Tax;
 use SL::FU;
 use SL::GL;
+use SL::Helper::Flash qw(flash flash_later);
 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);
+use SL::Presenter::Tag;
+use SL::Presenter::Chart;
 require "bin/mozilla/common.pl";
-require "bin/mozilla/drafts.pl";
 require "bin/mozilla/reportgenerator.pl";
 
 # this is for our long dates
@@ -75,16 +85,143 @@ 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;
+  my $payment_suggestion =  $::form->{form_defaults}->{amount_1};
+
+  # 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 show_details transaction_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, ($payment_suggestion ? $payment_suggestion : $item->amount1), 2) if $item->amount1 * 1;
+    $::form->{"credit_${row}"}            = $::form->format_amount(\%::myconfig, ($payment_suggestion ? $payment_suggestion : $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');
+
+
+  # bank transactions need amounts for assignment
+  my $can_save = 0;
+  $can_save    = 1 if ($::form->{credit_1} > 0 && $::form->{debit_2} > 0 && $::form->{credit_2} == 0 && $::form->{debit_1} == 0);
+  $can_save    = 1 if ($::form->{credit_2} > 0 && $::form->{debit_1} > 0 && $::form->{credit_1} == 0 && $::form->{debit_2} == 0);
+  return $js->flash('error', t8('Can only save template if amounts,i.e. 1 for debit and credit are set.'))->render unless $can_save;
+
+  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},
+    show_details            => $::form->{show_details},
+    transaction_description => $::form->{transaction_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 +236,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_sorted;
 
   $form->{show_details} = $myconfig{show_form_details} unless defined $form->{show_details};
 
@@ -120,7 +248,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 +257,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_sorted;
 
   my $i        = 1;
   my $tax      = 0;
@@ -158,7 +277,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 +286,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 +310,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 +321,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 +342,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_sorted;
 
-  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 +382,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,41 +428,40 @@ 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
-    credit         credit_accno     debit_tax      debit_tax_accno
-    credit_tax     credit_tax_accno projectnumbers balance employee
+    transdate     gldate              id                         reference
+    description   notes               transaction_description    source
+    doccnt        debit               debit_accno
+    credit        credit_accno        debit_tax                  debit_tax_accno
+    credit_tax    credit_tax_accno    balance                    projectnumbers
+    department    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 department_id transaction_description);
   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});
-  push @options,      $locale->text('Source')      . " : $form->{source}"                             if ($form->{source});
-  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('Account')                 . " : $form->{accno} $form->{account_description}" if ($form->{accno});
+  push @options,      $locale->text('Source')                  . " : $form->{source}"                             if ($form->{source});
+  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('Transaction description') . " : $form->{transaction_description}"            if $form->{transaction_description};
+  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,29 +472,33 @@ 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'), },
-    'reference'        => { 'text' => $locale->text('Reference'), },
-    'source'           => { 'text' => $locale->text('Source'), },
-    'description'      => { 'text' => $locale->text('Description'), },
-    'notes'            => { 'text' => $locale->text('Notes'), },
-    'debit'            => { 'text' => $locale->text('Debit'), },
-    'debit_accno'      => { 'text' => $locale->text('Debit Account'), },
-    'credit'           => { 'text' => $locale->text('Credit'), },
-    'credit_accno'     => { 'text' => $locale->text('Credit Account'), },
-    'debit_tax'        => { 'text' => $locale->text('Debit Tax'), },
-    'debit_tax_accno'  => { 'text' => $locale->text('Debit Tax Account'), },
-    'credit_tax'       => { 'text' => $locale->text('Credit Tax'), },
-    'credit_tax_accno' => { 'text' => $locale->text('Credit Tax Account'), },
-    'balance'          => { 'text' => $locale->text('Balance'), },
-    'projectnumbers'   => { 'text' => $locale->text('Project Numbers'), },
-    'employee'         => { 'text' => $locale->text('Employee'), },
+    'id'                      => { 'text' => $locale->text('ID'), },
+    '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'), },
+    'debit_accno'             => { 'text' => $locale->text('Debit Account'), },
+    'credit'                  => { 'text' => $locale->text('Credit'), },
+    'credit_accno'            => { 'text' => $locale->text('Credit Account'), },
+    'debit_tax'               => { 'text' => $locale->text('Debit Tax'), },
+    'debit_tax_accno'         => { 'text' => $locale->text('Debit Tax Account'), },
+    'credit_tax'              => { 'text' => $locale->text('Credit Tax'), },
+    'credit_tax_accno'        => { 'text' => $locale->text('Credit Tax Account'), },
+    'balance'                 => { 'text' => $locale->text('Balance'), },
+    'projectnumbers'          => { 'text' => $locale->text('Project Numbers'), },
+    'department'              => { 'text' => $locale->text('Department'), },
+    'employee'                => { 'text' => $locale->text('Employee'), },
+    'transaction_description' => { 'text' => $locale->text('Transaction description'), },
   );
 
-  foreach my $name (qw(id transdate gldate reference description debit_accno credit_accno debit_tax_accno credit_tax_accno)) {
+  foreach my $name (qw(id transdate gldate reference description debit_accno credit_accno debit_tax_accno credit_tax_accno department transaction_description)) {
     my $sortname                = $name =~ m/accno/ ? 'accno' : $name;
     my $sortdir                 = $sortname eq $form->{sort} ? 1 - $form->{sortdir} : $form->{sortdir};
     $column_defs{$name}->{link} = $callback . "&sort=$sortname&sortdir=$sortdir";
@@ -389,7 +518,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 +568,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";
@@ -452,7 +586,7 @@ sub generate_report {
     $row->{balance}->{data}        = $data;
     $row->{projectnumbers}->{data} = join ", ", sort { lc($a) cmp lc($b) } keys %{ $ref->{projectnumbers} };
 
-    map { $row->{$_}->{data} = $ref->{$_} } qw(id reference description notes gldate employee);
+    map { $row->{$_}->{data} = $ref->{$_} } qw(id reference description notes gldate employee department transaction_description);
 
     map { $row->{$_}->{data} = \@{ $rows{$_} }; } qw(transdate debit credit debit_accno credit_accno debit_tax_accno credit_tax_accno source);
 
@@ -466,7 +600,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 +611,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 +629,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 +649,25 @@ sub generate_report {
 
   $report->set_options('raw_bottom_info_text' => $raw_bottom_info_text);
 
+  setup_gl_transactions_action_bar();
+
   $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 +682,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);
+    qw(accno_id 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.00000";
+    }
+    if (!$debitcredit && $debittax) {
+      $form->{"taxchart_$i"} = "$notax_id--0.00000";
+    }
+    $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.00000";
+      $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 +760,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 +771,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,48 +794,22 @@ 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;
 
-  my %project_labels = ();
-  my @project_values = ("");
-  foreach my $item (@{ $form->{"ALL_PROJECTS"} }) {
-    push(@project_values, $item->{"id"});
-    $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 $deliverydate  = $::form->{deliverydate} ? DateTime->from_kivitendo($::form->{deliverydate}) : undef;
 
   my ($source, $memo, $source_hidden, $memo_hidden);
   for my $i (1 .. $form->{rowcount}) {
@@ -710,43 +825,44 @@ 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 $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);
+
+    my $used_tax_id;
+    if ( $form->{"taxchart_$i"} ) {
+      ($used_tax_id) = split(/--/, $form->{"taxchart_$i"});
+    }
 
-    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 $taxdate = $deliverydate ? $deliverydate : $transdate;
+    foreach my $item ( GL->get_active_taxes_for_chart($accno_id, $taxdate, $used_tax_id) ) {
+      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"};
 
-    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}";
+      push(@taxchart_values, $key);
+      $taxchart_labels{$key} = $item->taxkey . " - " . $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)
+      SL::Presenter::Chart::picker("accno_id_$i", $accno_id, style => "width: 300px") .
+      SL::Presenter::Tag::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);
@@ -805,19 +921,21 @@ sub display_rows {
       }
     }
 
-    my $projectnumber =
-      NTI($cgi->popup_menu('-name' => "project_id_$i",
-                           '-values' => \@project_values,
-                           '-labels' => \%project_labels,
-                           '-default' => $form->{"project_id_$i"} ));
-    my $projectnumber_hidden = qq|
-    <input type="hidden" name="project_id_$i" value="$form->{"project_id_$i"}">|;
+    my $projectnumber = SL::Presenter::Project::picker("project_id_$i", $form->{"project_id_$i"});
+    my $projectnumber_hidden = SL::Presenter::Tag::hidden_tag("project_id_$i", $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');
+
+    # if we have a bt_chart_id we disallow changing the amount of the bank account
+    if ($form->{bt_chart_id}) {
+      $debitreadonly = $creditreadonly = "readonly" if ($form->{"accno_id_$i"} eq $form->{bt_chart_id});
+      $copy2credit   = '' if $i == 1;   # and disallow copy2credit
+    }
 
     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 +966,238 @@ 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" : "";
+sub _get_radieren {
+  return ($::instance_conf->get_gl_changeable == 2) ? ($::form->current_date(\%::myconfig) eq $::form->{gldate}) : ($::instance_conf->get_gl_changeable == 1);
+}
 
-  if ($init) {
-    $taxincluded = "checked";
-  }
+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});
+  my ($is_linked_bank_transaction, $is_linked_ap_transaction, $is_reconciled_bank_transaction);
 
-  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}|;
+  if ($form->{id} && SL::DB::Manager::BankTransactionAccTrans->find_by(gl_id => $form->{id})) {
+    $is_linked_bank_transaction = 1;
   }
-
-  # 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 {
-
-    # 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>|;
+  if ($form->{id} && SL::DB::Manager::ApGl->find_by(gl_id => $form->{id})) {
+    $is_linked_ap_transaction = 1;
   }
-
-  $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>
-|;
-
-  $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>|;
+  # dont edit reconcilated bookings!
   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>|;
+    my @acc_trans = map { $_->acc_trans_id } @{ SL::DB::Manager::AccTransaction->get_all( where => [ trans_id => $form->{id} ] ) };
+    if (scalar @acc_trans && scalar @{ SL::DB::Manager::ReconciliationLink->get_all(where => [ acc_trans_id  => [ @acc_trans ] ]) }) {
+      $is_reconciled_bank_transaction = 1;
+    }
   }
-  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>|;
+  my $create_post_action = sub {
+    # $_[0]: description
+    # $_[1]: after_action
+    action => [
+      $_[0],
+      submit   => [ '#form', { action => 'post', after_action => $_[1] } ],
+      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.')
+                : $is_linked_bank_transaction               ? t8('This transaction is linked with a bank transaction. Please undo and redo the bank transaction booking if needed.')
+                : $is_linked_ap_transaction                 ? t8('This transaction is linked with a AP transaction. Please undo and redo the AP transaction booking if needed.')
+                : $is_reconciled_bank_transaction           ? t8('This transaction is reconciled with a bank transaction. Please undo the reconciliation if needed.')
+                : undef,
+    ],
+  };
+
+  my %post_entry;
+  if ($::instance_conf->get_gl_add_doc && $::instance_conf->get_doc_storage) {
+    %post_entry = (combobox => [ $create_post_action->(t8('Post'), 'doc-tab'),
+                                 $create_post_action->(t8('Post and new booking')) ]);
+  } elsif ($::instance_conf->get_doc_storage) {
+    %post_entry = (combobox => [ $create_post_action->(t8('Post')),
+                                 $create_post_action->(t8('Post and upload document'), 'doc-tab') ]);
   } 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>|;
+    %post_entry = $create_post_action->(t8('Post'));
   }
 
-  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('Update'),
+        submit    => [ '#form', { action => 'update' } ],
+        id        => 'update_button',
+        accesskey => 'enter',
+      ],
+      %post_entry,
+      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.')
+                    : $form->{storno}             ? t8('A canceled general ledger transaction cannot be canceled again.')
+                    : $is_linked_bank_transaction ? t8('This transaction is linked with a bank transaction. Please undo and redo the bank transaction booking if needed.')
+                    : $is_linked_ap_transaction   ? t8('This transaction is linked with a AP transaction. Please undo and redo the AP transaction booking if needed.')
+                    : $is_reconciled_bank_transaction ? t8('This transaction is reconciled with a bank transaction. Please undo the reconciliation if needed.')
+                    : 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.')
+                    : $is_linked_bank_transaction ? t8('This transaction is linked with a bank transaction. Please undo and redo the bank transaction booking if needed.')
+                    : $is_linked_ap_transaction   ? t8('This transaction is linked with a AP transaction. Please undo and redo the AP transaction booking if needed.')
+                    : $is_reconciled_bank_transaction ? t8('This transaction is reconciled with a bank transaction. Please undo the reconciliation if needed.')
+                    : $form->{storno}             ? t8('A canceled general ledger transaction cannot be deleted.')
+                    : undef,
+        ],
+      ], # end of combobox "Storno"
+
+      combobox => [
+        action => [ t8('more') ],
+        action => [
+          t8('History'),
+          call     => [ 'set_history_window', $form->{id} * 1, 'glid' ],
+          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"
+    );
   }
-
-  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};
+sub setup_gl_search_action_bar {
+  my %params = @_;
 
-  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>|;
-    }
+  for my $bar ($::request->layout->get('actionbar')) {
+    $bar->add(
+      action => [
+        t8('Search'),
+        submit    => [ '#form', { action => 'continue', nextsub => 'generate_report' } ],
+        accesskey => 'enter',
+      ],
+    );
   }
+}
 
-  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 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"
+    );
+  }
+}
 
-<input name=callback type=hidden value="$form->{callback}">
+sub form_header {
+  $::lxdebug->enter_sub;
+  $::auth->assert('gl_transactions');
 
-$follow_ups_block
+  my ($init) = @_;
 
-<br>
-|;
+  $::request->layout->add_javascripts("autocomplete_chart.js", "autocomplete_project.js", "kivi.File.js", "kivi.GL.js", "kivi.RecordTemplate.js", "kivi.Validator.js", "show_history.js");
 
-  my $transdate = $form->datetonum($form->{transdate}, \%myconfig);
-  my $closedto  = $form->datetonum($form->{closedto},  \%myconfig);
+  my @old_project_ids     = grep { $_ } map{ $::form->{"project_id_$_"} } 1..$::form->{rowcount};
+  my @conditions          = @old_project_ids ? (id => \@old_project_ids) : ();
+  $::form->{ALL_PROJECTS} = SL::DB::Manager::Project->get_all_sorted(query => [ or => [ active => 1, @conditions ]]);
 
-  if ($form->{id}) {
+  $::form->get_lists(
+    "charts"    => { "key" => "ALL_CHARTS", "transdate" => $::form->{transdate} },
+  );
 
-    if (!$form->{storno}) {
-      print qq|<input class=submit type=submit name=action value="| . $locale->text('Storno') . qq|">|;
-    }
+  # we cannot book on charttype header
+  @{ $::form->{ALL_CHARTS} } = grep { $_->{charttype} ne 'H' }  @{ $::form->{ALL_CHARTS} };
+  $::form->{ALL_DEPARTMENTS} = SL::DB::Manager::Department->get_all_sorted;
 
-    # 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();
+  setup_gl_action_bar();
 
-  my $form     = $main::form;
-  my $locale   = $main::locale;
-
-  $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 +1207,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
@@ -1298,9 +1228,10 @@ sub post_transaction {
   my $locale   = $main::locale;
 
   # check if there is something in reference and date
-  $form->isblank("reference",   $locale->text('Reference missing!'));
-  $form->isblank("transdate",   $locale->text('Transaction Date missing!'));
-  $form->isblank("description", $locale->text('Description missing!'));
+  $form->isblank("reference",               $locale->text('Reference missing!'));
+  $form->isblank("transdate",               $locale->text('Transaction Date missing!'));
+  $form->isblank("description",             $locale->text('Description missing!'));
+  $form->isblank("transaction_description", $locale->text('A transaction description is required.')) if $::instance_conf->get_require_transaction_description_ps;
 
   my $transdate = $form->datetonum($form->{transdate}, \%myconfig);
   my $closedto  = $form->datetonum($form->{closedto},  \%myconfig);
@@ -1314,7 +1245,11 @@ sub post_transaction {
   my $debitcredit;
   my %split_safety = ();
 
-  my @flds = qw(accno debit credit projectnumber fx_transaction source memo tax taxchart);
+  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_id debit credit projectnumber fx_transaction source memo tax taxchart);
 
   for my $i (1 .. $form->{rowcount}) {
     next if $form->{"debit_$i"} eq "" && $form->{"credit_$i"} eq "";
@@ -1354,36 +1289,36 @@ sub post_transaction {
       $form->{debitlock} = 1;
     }
     if ($debitcredit && $credittax) {
-      $form->{"taxchart_$i"} = "0--0.00";
+      $form->{"taxchart_$i"} = "$notax_id--0.00000";
     }
     if (!$debitcredit && $debittax) {
-      $form->{"taxchart_$i"} = "0--0.00";
+      $form->{"taxchart_$i"} = "$notax_id--0.00000";
     }
     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.00000";
       $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 +1328,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 +1357,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)) {
@@ -1431,24 +1370,72 @@ sub post_transaction {
     $form->error($locale->text('Empty transaction!'));
   }
 
-  if ((my $errno = GL->post_transaction(\%myconfig, \%$form)) <= -1) {
-    $errno *= -1;
-    my @err;
-    $err[1] = $locale->text('Cannot have a value in both Debit and Credit!');
-    $err[2] = $locale->text('Debit and credit out of balance!');
-    $err[3] = $locale->text('Cannot post a transaction without a value!');
 
-    $form->error($err[$errno]);
-  }
-  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->save_history;
-  }
-  # /saving the history
+  # start transaction (post + history + (optional) banktrans)
+  SL::DB->client->with_transaction(sub {
+
+    if ((my $errno = GL->post_transaction(\%myconfig, \%$form)) <= -1) {
+      $errno *= -1;
+      my @err;
+      $err[1] = $locale->text('Cannot have a value in both Debit and Credit!');
+      $err[2] = $locale->text('Debit and credit out of balance!');
+      $err[3] = $locale->text('Cannot post a transaction without a value!');
+
+      die $err[$errno];
+    }
+    # saving the history
+    if(!exists $form->{addition} && $form->{id} ne "") {
+      $form->{snumbers} = qq|gltransaction_| . $form->{id};
+      $form->{addition} = "POSTED";
+      $form->{what_done} = "gl transaction";
+      $form->save_history;
+    }
+
+    # Case BankTransaction: update RecordLink and BankTransaction
+    if ($form->{callback} =~ /BankTransaction/ && $form->{bt_id}) {
+      # set invoice_amount - we only rely on bt_id in form, do all other stuff ui independent
+      # die if we have a unlogic or NYI case and abort the whole transaction
+      my ($bt, $chart_id, $payment);
+      require SL::DB::Manager::BankTransaction;
+
+      $bt = SL::DB::Manager::BankTransaction->find_by(id => $::form->{bt_id});
+      die "No bank transaction found" unless $bt;
+
+      $chart_id = SL::DB::Manager::BankAccount->find_by(id => $bt->local_bank_account_id)->chart_id;
+      die "no chart id" unless $chart_id;
+
+      $payment = SL::DB::Manager::AccTransaction->get_all(where => [ trans_id => $::form->{id},
+                                                                     chart_link => { like => '%_paid%' },
+                                                                     chart_id => $chart_id                  ]);
+      die "guru meditation error: Can only assign amount to one bank account booking" if scalar @{ $payment } > 1;
+
+      # credit/debit * -1 matches the sign for bt.amount and bt.invoice_amount
+
+      die "Can only assign the full (partial) bank amount to a single general ledger booking: " . $bt->not_assigned_amount . " " .  ($payment->[0]->amount * -1)
+        unless (abs($bt->not_assigned_amount - ($payment->[0]->amount * -1)) < 0.001);
+
+      $bt->update_attributes(invoice_amount => $bt->invoice_amount + ($payment->[0]->amount * -1));
+
+      # create record_link
+      my %props = (
+        from_table => 'bank_transactions',
+        from_id    => $::form->{bt_id},
+        to_table   => 'gl',
+        to_id      => $::form->{id},
+      );
+      SL::DB::RecordLink->new(%props)->save;
+      # and tighten holy acc_trans_id for this bank_transaction
+      my  %props_acc = (
+        acc_trans_id        => $payment->[0]->acc_trans_id,
+        bank_transaction_id => $bt->id,
+        gl_id               => $payment->[0]->trans_id,
+      );
+      my $bta = SL::DB::BankTransactionAccTrans->new(%props_acc);
+      $bta->save;
+
+    }
+    1;
+  }) or do { die SL::DB->client->error };
 
   $main::lxdebug->leave_sub();
 }
@@ -1456,26 +1443,43 @@ 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}) {
-    $form->{saved_message} = $::locale->text('You have to specify a department.');
-    update();
-    exit;
+  if ($::myconfig{mandatory_departments} && !$form->{department_id}) {
+    $form->error($locale->text('You have to specify a department.'));
   }
 
   $form->{title}  = $locale->text("$form->{title} General Ledger Transaction");
   $form->{storno} = 0;
 
   post_transaction();
+  if ($::instance_conf->get_webdav) {
+    SL::Webdav->new(type     => 'general_ledger',
+                    number   => $form->{id},
+                   )->webdav_path;
+  }
+
+  my $msg = $::locale->text("General ledger transaction '#1' posted (ID: #2)", $form->{reference}, $form->{id});
+  if ($form->{callback} =~ /BankTransaction/ && $form->{bt_id}) {
+    $form->redirect($msg);
 
-  remove_draft() if $form->{remove_draft};
+  } elsif ('doc-tab' eq $form->{after_action}) {
+    # Redirect with callback containing a fragment does not work (by now)
+    # because the callback info is stored in the session an parsing the
+    # callback parameters does not support fragments (see SL::Form::redirect).
+    # So use flash_later for the message and redirect_headers for redirecting.
+    my $add_doc_url = build_std_url("script=gl.pl", 'action=edit', 'id=' . E($form->{id}), 'fragment=ui-tabs-docs');
+    SL::Helper::Flash::flash_later('info', $msg);
+    print $form->redirect_header($add_doc_url);
+    $::dispatcher->end_request;
 
-  $form->{callback} = build_std_url("action=add&DONT_LOAD_DRAFT=1", "show_details");
-  $form->redirect($form->{callback});
+  } else {
+    $form->{callback} = build_std_url("action=add", "show_details");
+    $form->redirect($msg);
+  }
 
   $main::lxdebug->leave_sub();
 }
@@ -1483,7 +1487,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 +1500,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 +1516,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 +1532,20 @@ sub continue {
   call_sub($main::form->{nextsub});
 }
 
+sub get_tax_dropdown {
+  my $transdate    = $::form->{transdate}    ? DateTime->from_kivitendo($::form->{transdate}) : DateTime->today_local;
+  my $deliverydate = $::form->{deliverydate} ? DateTime->from_kivitendo($::form->{deliverydate}) : undef;
+  my @tax_accounts = GL->get_active_taxes_for_chart($::form->{accno_id}, $deliverydate // $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;