Merge branch 'b-3.6.1' of ../kivitendo-erp_20220811
[kivitendo-erp.git] / bin / mozilla / is.pl
index 46abc9e..d0e2506 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.
 #======================================================================
 #
 # Inventory invoicing module
 
 use SL::FU;
 use SL::IS;
-use SL::PE;
 use SL::OE;
+use SL::Helper::UserPreferences::DisplayPreferences;
+use SL::MoreCommon qw(restore_form save_form);
+use SL::RecordLinks;
+
 use Data::Dumper;
 use DateTime;
-use List::MoreUtils qw(uniq);
+use List::MoreUtils qw(any uniq);
 use List::Util qw(max sum);
 use List::UtilsBy qw(sort_by);
 use English qw(-no_match_vars);
 
+use SL::DB::BankTransactionAccTrans;
 use SL::DB::Default;
 use SL::DB::Customer;
+use SL::DB::Department;
+use SL::DB::Invoice;
 use SL::DB::PaymentTerm;
 
+require "bin/mozilla/common.pl";
 require "bin/mozilla/io.pl";
-require "bin/mozilla/invoice_io.pl";
-require "bin/mozilla/arap.pl";
-require "bin/mozilla/drafts.pl";
 
 use strict;
 
@@ -57,6 +62,21 @@ use strict;
 
 # end of main
 
+sub _may_view_or_edit_this_invoice {
+  return 1 if  $::auth->assert('invoice_edit', 1);       # may edit all invoices
+  return 0 if !$::form->{id};                            # creating new invoices isn't allowed without invoice_edit
+  return 1 if  $::auth->assert('sales_invoice_view', 1); # viewing is allowed with this right
+  return 0 if !$::form->{globalproject_id};              # existing records without a project ID are not allowed
+  return SL::DB::Project->new(id => $::form->{globalproject_id})->load->may_employee_view_project_invoices(SL::DB::Manager::Employee->current);
+}
+
+sub _assert_access {
+  my $cache = $::request->cache('is.pl::_assert_access');
+
+  $cache->{_may_view_or_edit_this_invoice} = _may_view_or_edit_this_invoice()                              if !exists $cache->{_may_view_or_edit_this_invoice};
+  $::form->show_generic_error($::locale->text("You do not have the permissions to access this function.")) if !       $cache->{_may_view_or_edit_this_invoice};
+}
+
 sub add {
   $main::lxdebug->enter_sub();
 
@@ -65,7 +85,7 @@ sub add {
 
   $main::auth->assert('invoice_edit');
 
-  return $main::lxdebug->leave_sub() if (load_draft_maybe());
+  $form->{show_details} = $::myconfig{show_form_details};
 
   if ($form->{type} eq "credit_note") {
     $form->{title} = $locale->text('Add Credit Note');
@@ -73,6 +93,13 @@ sub add {
     if ($form->{storno}) {
       $form->{title} = $locale->text('Add Storno Credit Note');
     }
+
+  } elsif ($form->{type} eq "invoice_for_advance_payment") {
+    $form->{title} = $locale->text('Add Invoice for Advance Payment');
+
+  } elsif ($form->{type} eq "final_invoice") {
+    $form->{title} = $locale->text('Add Final Invoice');
+
   } else {
     $form->{title} = $locale->text('Add Sales Invoice');
 
@@ -81,7 +108,7 @@ sub add {
 
   $form->{callback} = "$form->{script}?action=add&type=$form->{type}" unless $form->{callback};
 
-  &invoice_links;
+  invoice_links(is_new => 1);
   &prepare_invoice;
   &display_form;
 
@@ -91,11 +118,14 @@ sub add {
 sub edit {
   $main::lxdebug->enter_sub();
 
+  # Delay access check to after the invoice's been loaded in
+  # "invoice_links" so that project-specific invoice rights can be
+  # evaluated.
+
   my $form     = $main::form;
   my $locale   = $main::locale;
 
-  $main::auth->assert('invoice_edit');
-
+  $form->{show_details}                = $::myconfig{show_form_details};
   $form->{taxincluded_changed_by_user} = 1;
 
   # show history button
@@ -113,6 +143,14 @@ sub edit {
   if ($form->{type} eq "credit_note") {
     $form->{title} = $locale->text('Edit Credit Note');
     $form->{title} = $locale->text('Edit Storno Credit Note') if $form->{storno};
+
+  } elsif ($form->{type} eq "invoice_for_advance_payment") {
+    $form->{title} = $locale->text('Edit Invoice for Advance Payment');
+    $form->{title} = $locale->text('Edit Storno Invoice for Advance Payment') if $form->{storno};
+
+  } elsif ($form->{type} eq "final_invoice") {
+    $form->{title} = $locale->text('Edit Final Invoice');
+
   } else {
     $form->{title} = $locale->text('Edit Sales Invoice');
     $form->{title} = $locale->text('Edit Storno Invoice')     if $form->{storno};
@@ -132,24 +170,19 @@ sub edit {
 sub invoice_links {
   $main::lxdebug->enter_sub();
 
+  # Delay access check to after the invoice's been loaded so that
+  # project-specific invoice rights can be evaluated.
+
+  my %params   = @_;
   my $form     = $main::form;
   my %myconfig = %main::myconfig;
 
-  $main::auth->assert('invoice_edit');
-
   $form->{vc} = 'customer';
 
   # create links
-  $form->{webdav}   = $::instance_conf->get_webdav;
-
   $form->create_links("AR", \%myconfig, "customer");
 
-  if ($form->{all_customer}) {
-    unless ($form->{customer_id}) {
-      $form->{customer_id} = $form->{all_customer}->[0]->{id};
-      $form->{salesman_id} = $form->{all_customer}->[0]->{salesman_id};
-    }
-  }
+  _assert_access();
 
   my $editing = $form->{id};
 
@@ -159,10 +192,7 @@ sub invoice_links {
 
   IS->get_customer(\%myconfig, \%$form);
 
-  #quote all_customer Bug 133
-  foreach my $ref (@{ $form->{all_customer} }) {
-    $ref->{name} = $form->quote($ref->{name});
-  }
+  $form->{billing_address_id} = $form->{default_billing_address_id} if $params{is_new};
 
   $form->restore_vars(qw(id));
 
@@ -172,23 +202,6 @@ sub invoice_links {
   $form->restore_vars(qw(taxincluded)) if $form->{id};
   $form->restore_vars(qw(salesman_id)) if $editing;
 
-
-  # build vendor/customer drop down compatibility... don't ask
-  if (@{ $form->{"all_customer"} }) {
-    $form->{"selectcustomer"} = 1;
-    $form->{customer}         = qq|$form->{customer}--$form->{"customer_id"}|;
-  }
-
-  $form->{"oldcustomer"}  = $form->{customer};
-
-  if ($form->{"oldcustomer"} !~ m/--\d+$/ && $form->{"customer_id"}) {
-    $form->{"oldcustomer"} .= qq|--$form->{"customer_id"}|
-  }
-
-
-#  $form->{oldcustomer} = "$form->{customer}--$form->{customer_id}";
-#  $form->{selectcustomer} = 1;
-
   $form->{employee} = "$form->{employee}--$form->{employee_id}";
 
   # forex
@@ -235,14 +248,26 @@ sub invoice_links {
 sub prepare_invoice {
   $main::lxdebug->enter_sub();
 
+  _assert_access();
+
   my $form     = $main::form;
   my %myconfig = %main::myconfig;
 
-  $main::auth->assert('invoice_edit');
-
   if ($form->{type} eq "credit_note") {
     $form->{type}     = "credit_note";
     $form->{formname} = "credit_note";
+
+  } elsif ($form->{type} eq "invoice_for_advance_payment") {
+    $form->{type}     = "invoice_for_advance_payment";
+    $form->{formname} = "invoice_for_advance_payment";
+
+  } elsif ($form->{type} eq "final_invoice") {
+    $form->{type}     = "final_invoice";
+    $form->{formname} = "final_invoice";
+
+  } elsif ($form->{formname} eq "proforma" ) {
+    $form->{type}     = "invoice";
+
   } else {
     $form->{type}     = "invoice";
     $form->{formname} = "invoice";
@@ -279,34 +304,273 @@ sub prepare_invoice {
   $main::lxdebug->leave_sub();
 }
 
+sub setup_is_action_bar {
+  my ($tmpl_var)              = @_;
+  my $form                    = $::form;
+  my $change_never            = $::instance_conf->get_is_changeable == 0;
+  my $change_on_same_day_only = $::instance_conf->get_is_changeable == 2 && ($form->current_date(\%::myconfig) ne $form->{gldate});
+  my $payments_balanced       = ($::form->{oldtotalpaid} == 0);
+  my $has_storno              = ($::form->{storno} && !$::form->{storno_id});
+  my $may_edit_create         = $::auth->assert('invoice_edit', 1);
+  my $factur_x_enabled        = $tmpl_var->{invoice_obj} && $tmpl_var->{invoice_obj}->customer->create_zugferd_invoices_for_this_customer;
+  my ($is_linked_bank_transaction, $warn_unlinked_delivery_order);
+    if ($::form->{id}
+        && SL::DB::Default->get->payments_changeable != 0
+        && SL::DB::Manager::BankTransactionAccTrans->find_by(ar_id => $::form->{id})) {
+
+      $is_linked_bank_transaction = 1;
+    }
+  if ($::instance_conf->get_warn_no_delivery_order_for_invoice && !$form->{id}) {
+    $warn_unlinked_delivery_order = 1 unless $form->{convert_from_do_ids};
+  }
+
+  my $has_further_invoice_for_advance_payment;
+  if ($form->{id} && $form->{type} eq "invoice_for_advance_payment") {
+    my $invoice_obj = SL::DB::Invoice->load_cached($form->{id});
+    my $lr          = $invoice_obj->linked_records(direction => 'to', to => ['Invoice']);
+    $has_further_invoice_for_advance_payment = any {'SL::DB::Invoice' eq ref $_ && "invoice_for_advance_payment" eq $_->type} @$lr;
+  }
+
+  my $has_final_invoice;
+  if ($form->{id} && $form->{type} eq "invoice_for_advance_payment") {
+    my $invoice_obj = SL::DB::Invoice->load_cached($form->{id});
+    my $lr          = $invoice_obj->linked_records(direction => 'to', to => ['Invoice']);
+    $has_final_invoice = any {'SL::DB::Invoice' eq ref $_ && "final_invoice" eq $_->invoice_type} @$lr;
+  }
+
+  my $is_invoice_for_advance_payment_from_order;
+  if ($form->{id} && $form->{type} eq "invoice_for_advance_payment") {
+    my $invoice_obj = SL::DB::Invoice->load_cached($form->{id});
+    my $lr          = $invoice_obj->linked_records(direction => 'from', from => ['Order']);
+    $is_invoice_for_advance_payment_from_order = scalar @$lr >= 1;
+  }
+
+  for my $bar ($::request->layout->get('actionbar')) {
+    $bar->add(
+      action => [
+        t8('Update'),
+        submit    => [ '#form', { action => "update" } ],
+        disabled  => !$may_edit_create ? t8('You must not change this invoice.')
+                   : $form->{locked}   ? t8('The billing period has already been locked.')
+                   :                     undef,
+        id        => 'update_button',
+        accesskey => 'enter',
+      ],
+
+      combobox => [
+        action => [
+          t8('Post'),
+          submit   => [ '#form', { action => "post" } ],
+          checks   => [ 'kivi.validate_form' ],
+          confirm  => t8('The invoice is not linked with a sales delivery order. Post anyway?') x !!$warn_unlinked_delivery_order,
+          disabled => !$may_edit_create                         ? t8('You must not change this invoice.')
+                    : $form->{locked}                           ? t8('The billing period has already been locked.')
+                    : $form->{storno}                           ? t8('A canceled invoice cannot be posted.')
+                    : ($form->{id} && $change_never)            ? t8('Changing invoices has been disabled in the configuration.')
+                    : ($form->{id} && $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.')
+                    :                                             undef,
+        ],
+        action => [
+          t8('Post Payment'),
+          submit   => [ '#form', { action => "post_payment" } ],
+          checks   => [ 'kivi.validate_form' ],
+          disabled => !$may_edit_create           ? t8('You must not change this invoice.')
+                    : !$form->{id}                ? t8('This invoice has not been posted yet.')
+                    : $is_linked_bank_transaction ? t8('This transaction is linked with a bank transaction. Please undo and redo the bank transaction booking if needed.')
+                    :                               undef,
+          only_if  => $form->{type} ne "invoice_for_advance_payment",
+        ],
+        action => [ t8('Mark as paid'),
+          submit   => [ '#form', { action => "mark_as_paid" } ],
+          confirm  => t8('This will remove the invoice from showing as unpaid even if the unpaid amount does not match the amount. Proceed?'),
+          disabled => !$may_edit_create ? t8('You must not change this invoice.')
+                    : !$form->{id}      ? t8('This invoice has not been posted yet.')
+                    :                     undef,
+          only_if  => ($::instance_conf->get_is_show_mark_as_paid && $form->{type} ne "invoice_for_advance_payment") || $form->{type} eq 'final_invoice',
+        ],
+      ], # end of combobox "Post"
+
+      combobox => [
+        action => [ t8('Storno'),
+          submit   => [ '#form', { action => "storno" } ],
+          confirm  => t8('Do you really want to cancel this invoice?'),
+          checks   => [ 'kivi.validate_form' ],
+          disabled => !$may_edit_create   ? t8('You must not change this invoice.')
+                    : !$form->{id}        ? t8('This invoice has not been posted yet.')
+                    : $form->{storno}     ? t8('Cannot storno storno invoice!')
+                    : $form->{locked}     ? t8('The billing period has already been locked.')
+                    : !$payments_balanced ? t8('Cancelling is disallowed. Either undo or balance the current payments until the open amount matches the invoice amount')
+                    : undef,
+        ],
+        action => [ t8('Delete'),
+          submit   => [ '#form', { action => "delete" } ],
+          confirm  => t8('Do you really want to delete this object?'),
+          checks   => [ 'kivi.validate_form' ],
+          disabled => !$may_edit_create        ? t8('You must not change this invoice.')
+                    : !$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.')
+                    : $has_storno              ? t8('Can only delete the "Storno zu" part of the cancellation pair.')
+                    :                            undef,
+        ],
+      ], # end of combobox "Storno"
+
+      'separator',
+
+      combobox => [
+        action => [ t8('Workflow') ],
+        action => [
+          t8('Use As New'),
+          submit   => [ '#form', { action => "use_as_new" } ],
+          checks   => [ 'kivi.validate_form' ],
+          disabled => !$may_edit_create ? t8('You must not change this invoice.')
+                    : !$form->{id}      ? t8('This invoice has not been posted yet.')
+                    :                     undef,
+        ],
+        action => [
+          t8('Further Invoice for Advance Payment'),
+          submit   => [ '#form', { action => "further_invoice_for_advance_payment" } ],
+          checks   => [ 'kivi.validate_form' ],
+          disabled => !$may_edit_create                          ? t8('You must not change this invoice.')
+                    : !$form->{id}                               ? t8('This invoice has not been posted yet.')
+                    : $has_further_invoice_for_advance_payment   ? t8('This invoice has already a further invoice for advanced payment.')
+                    : $has_final_invoice                         ? t8('This invoice has already a final invoice.')
+                    : $is_invoice_for_advance_payment_from_order ? t8('This invoice was added from an order. See there.')
+                    :                                              undef,
+          only_if  => $form->{type} eq "invoice_for_advance_payment",
+        ],
+        action => [
+          t8('Final Invoice'),
+          submit   => [ '#form', { action => "final_invoice" } ],
+          checks   => [ 'kivi.validate_form' ],
+          disabled => !$may_edit_create                          ? t8('You must not change this invoice.')
+                    : !$form->{id}                               ? t8('This invoice has not been posted yet.')
+                    : $has_further_invoice_for_advance_payment   ? t8('This invoice has a further invoice for advanced payment.')
+                    : $has_final_invoice                         ? t8('This invoice has already a final invoice.')
+                    : $is_invoice_for_advance_payment_from_order ? t8('This invoice was added from an order. See there.')
+                    :                                              undef,
+          only_if  => $form->{type} eq "invoice_for_advance_payment",
+        ],
+        action => [
+          t8('Credit Note'),
+          submit   => [ '#form', { action => "credit_note" } ],
+          checks   => [ 'kivi.validate_form' ],
+          disabled => !$may_edit_create              ? t8('You must not change this invoice.')
+                    : $form->{type} eq "credit_note" ? t8('Credit notes cannot be converted into other credit notes.')
+                    : !$form->{id}                   ? t8('This invoice has not been posted yet.')
+                    : $form->{storno}                ? t8('A canceled invoice cannot be used. Please undo the cancellation first.')
+                    :                                  undef,
+        ],
+        action => [
+          t8('Sales Order'),
+          submit   => [ '#form', { action => "order" } ],
+          checks   => [ 'kivi.validate_form' ],
+          disabled => !$form->{id} ? t8('This invoice has not been posted yet.') : undef,
+        ],
+      ], # end of combobox "Workflow"
+
+      combobox => [
+        action => [ t8('Export') ],
+        action => [
+          ($form->{id} ? t8('Print') : t8('Preview')),
+          call     => [ 'kivi.SalesPurchase.show_print_dialog', $form->{id} ? 'print' : 'preview' ],
+          checks   => [ 'kivi.validate_form' ],
+          disabled => !$may_edit_create               ? t8('You must not print this invoice.')
+                    : !$form->{id} && $form->{locked} ? t8('The billing period has already been locked.')
+                    :                                   undef,
+        ],
+        action => [ t8('Print and Post'),
+          call     => [ 'kivi.SalesPurchase.show_print_dialog', 'print_and_post' ],
+          checks   => [ 'kivi.validate_form' ],
+          confirm  => t8('The invoice is not linked with a sales delivery order. Post anyway?') x !!$warn_unlinked_delivery_order,
+          disabled => !$may_edit_create                         ? t8('You must not change this invoice.')
+                    : $form->{locked}                           ? t8('The billing period has already been locked.')
+                    : $form->{storno}                           ? t8('A canceled invoice cannot be posted.')
+                    : ($form->{id} && $change_never)            ? t8('Changing invoices has been disabled in the configuration.')
+                    : ($form->{id} && $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.')
+                    :                                             undef,
+        ],
+        action => [ t8('E Mail'),
+          call     => [ 'kivi.SalesPurchase.show_email_dialog' ],
+          checks   => [ 'kivi.validate_form' ],
+          disabled => !$may_edit_create       ? t8('You must not print this invoice.')
+                    : !$form->{id}            ? t8('This invoice has not been posted yet.')
+                    : $form->{postal_invoice} ? t8('This customer wants a postal invoices.')
+                    :                     undef,
+        ],
+        action => [ t8('Factur-X/ZUGFeRD'),
+          submit   => [ '#form', { action => "download_factur_x_xml" } ],
+          checks   => [ 'kivi.validate_form' ],
+          disabled => !$may_edit_create  ? t8('You must not print this invoice.')
+                    : !$form->{id}       ? t8('This invoice has not been posted yet.')
+                    : !$factur_x_enabled ? t8('Creating Factur-X/ZUGFeRD invoices is not enabled for this customer.')
+                    :                      undef,
+        ],
+      ], # end of combobox "Export"
+
+      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('Drafts'),
+          call     => [ 'kivi.Draft.popup', 'is', 'invoice', $form->{draft_id}, $form->{draft_description} ],
+          disabled => !$may_edit_create ? t8('You must not change this invoice.')
+                    :  $form->{id}      ? t8('This invoice has already been posted.')
+                    : $form->{locked}   ? t8('The billing period has already been locked.')
+                    :                     undef,
+        ],
+      ], # end of combobox "more"
+    );
+  }
+  $::request->layout->add_javascripts('kivi.Validator.js');
+}
+
 sub form_header {
   $main::lxdebug->enter_sub();
 
+  _assert_access();
+
   my $form     = $main::form;
   my %myconfig = %main::myconfig;
   my $locale   = $main::locale;
   my $cgi      = $::request->{cgi};
 
-  $main::auth->assert('invoice_edit');
-
   my %TMPL_VAR = ();
   my @custom_hiddens;
 
-  if ($form->{id}) {
-    require SL::DB::Invoice;
-    $TMPL_VAR{invoice_obj} = SL::DB::Invoice->new(id => $form->{id})->load;
-  }
-  $form->{employee_id} = $form->{old_employee_id} if $form->{old_employee_id};
-  $form->{salesman_id} = $form->{old_salesman_id} if $form->{old_salesman_id};
+  $TMPL_VAR{customer_obj} = SL::DB::Customer->load_cached($form->{customer_id}) if $form->{customer_id};
+  $TMPL_VAR{invoice_obj}  = SL::DB::Invoice->load_cached($form->{id})           if $form->{id};
+
+  # only print, no mail
+  $form->{postal_invoice} = $TMPL_VAR{customer_obj}->postal_invoice if ref $TMPL_VAR{customer_obj} eq 'SL::DB::Customer';
+
+  my $current_employee   = SL::DB::Manager::Employee->current;
+  $form->{employee_id}   = $form->{old_employee_id} if $form->{old_employee_id};
+  $form->{salesman_id}   = $form->{old_salesman_id} if $form->{old_salesman_id};
+  $form->{employee_id} ||= $current_employee->id;
+  $form->{salesman_id} ||= $current_employee->id;
 
   $form->{defaultcurrency} = $form->get_default_currency(\%myconfig);
 
   $form->get_lists("taxzones"      => ($form->{id} ? "ALL_TAXZONES" : "ALL_ACTIVE_TAXZONES"),
                    "currencies"    => "ALL_CURRENCIES",
-                   "customers"     => "ALL_CUSTOMERS",
-                   "departments"   => "all_departments",
                    "price_factors" => "ALL_PRICE_FACTORS");
 
+  $form->{ALL_DEPARTMENTS} = SL::DB::Manager::Department->get_all_sorted;
+  $form->{ALL_LANGUAGES}   = SL::DB::Manager::Language->get_all_sorted;
+
   # Projects
   my @old_project_ids = uniq grep { $_ } map { $_ * 1 } ($form->{"globalproject_id"}, map { $form->{"project_id_$_"} } 1..$form->{"rowcount"});
   my @old_ids_cond    = @old_project_ids ? (id => \@old_project_ids) : ();
@@ -340,15 +604,6 @@ sub form_header {
       ]
     ]
   ]);
-  $TMPL_VAR{department_labels}     = sub { "$_[0]->{description}--$_[0]->{id}" };
-
-  # customer
-  $TMPL_VAR{vc_keys} = sub { "$_[0]->{name}--$_[0]->{id}" };
-  $TMPL_VAR{vclimit} = $myconfig{vclimit};
-  $TMPL_VAR{vc_select} = "customer_or_vendor_selection_window('customer', '', 0, 0)";
-  push @custom_hiddens, "customer_id";
-  push @custom_hiddens, "oldcustomer";
-  push @custom_hiddens, "selectcustomer";
 
   # currencies and exchangerate
   my @values = map { $_       } @{ $form->{ALL_CURRENCIES} };
@@ -365,38 +620,37 @@ sub form_header {
   $TMPL_VAR{creditwarning} = ($form->{creditlimit} != 0) && ($form->{creditremaining} < 0) && !$form->{update};
   $TMPL_VAR{is_credit_remaining_negativ} = $form->{creditremaining} =~ /-/;
 
-  my $follow_up_vc         =  $form->{customer};
-  $follow_up_vc            =~ s/--\d*\s*$//;
-  $TMPL_VAR{customer_name} = $follow_up_vc;
-
 # set option selected
   foreach my $item (qw(AR)) {
     $form->{"select$item"} =~ s/ selected//;
     $form->{"select$item"} =~ s/option>\Q$form->{$item}\E/option selected>$form->{$item}/;
   }
 
-  $TMPL_VAR{is_type_credit_note} = $form->{type}   eq "credit_note";
-  $TMPL_VAR{is_format_html}      = $form->{format} eq 'html';
-  $TMPL_VAR{dateformat}          = $myconfig{dateformat};
-  $TMPL_VAR{numberformat}        = $myconfig{numberformat};
+  $TMPL_VAR{is_type_normal_invoice}                 = $form->{type} eq "invoice";
+  $TMPL_VAR{is_type_credit_note}                    = $form->{type}   eq "credit_note";
+  $TMPL_VAR{is_format_html}                         = $form->{format} eq 'html';
+  $TMPL_VAR{dateformat}                             = $myconfig{dateformat};
+  $TMPL_VAR{numberformat}                           = $myconfig{numberformat};
+  $TMPL_VAR{longdescription_dialog_size_percentage} = SL::Helper::UserPreferences::DisplayPreferences->new()->get_longdescription_dialog_size_percentage();
 
   # hiddens
   $TMPL_VAR{HIDDENS} = [qw(
-    id action type media format queued printed emailed title vc discount
+    id type queued printed emailed vc discount
     title creditlimit creditremaining tradediscount business closedto locked shipped storno storno_id
     max_dunning_level dunning_amount dunning_description
-    shiptoname shiptostreet shiptozipcode shiptocity shiptocountry  shiptocontact shiptophone shiptofax
-    shiptoemail shiptodepartment_1 shiptodepartment_2  shiptocp_gender message email subject cc bcc taxaccounts cursor_fokus
+    taxaccounts cursor_fokus
     convert_from_do_ids convert_from_oe_ids convert_from_ar_ids useasnew
     invoice_id
     show_details
   ), @custom_hiddens,
-  map { $_.'_rate', $_.'_description', $_.'_taxnumber' } split / /, $form->{taxaccounts}];
+  map { $_.'_rate', $_.'_description', $_.'_taxnumber', $_.'_tax_id' } split / /, $form->{taxaccounts}];
 
-  $::request->{layout}->use_javascript(map { "${_}.js" } qw(kivi.SalesPurchase ckeditor/ckeditor ckeditor/adapters/jquery kivi.io autocomplete_customer autocomplete_part client_js));
+  $::request->{layout}->use_javascript(map { "${_}.js" } qw(kivi.Draft kivi.File kivi.SalesPurchase kivi.Part kivi.CustomerVendor kivi.Validator ckeditor/ckeditor ckeditor/adapters/jquery kivi.io client_js));
 
   $TMPL_VAR{payment_terms_obj} = get_payment_terms_for_invoice();
-  $form->{duedate}             = $TMPL_VAR{payment_terms_obj}->calc_date(reference_date => $form->{invdate}, due_date => $form->{due_due})->to_kivitendo if $TMPL_VAR{payment_terms_obj};
+  $form->{duedate}             = $TMPL_VAR{payment_terms_obj}->calc_date(reference_date => $form->{invdate}, due_date => $form->{duedate})->to_kivitendo if $TMPL_VAR{payment_terms_obj};
+
+  setup_is_action_bar(\%TMPL_VAR);
 
   $form->header();
 
@@ -427,26 +681,20 @@ sub _sort_payments {
 sub form_footer {
   $main::lxdebug->enter_sub();
 
+  _assert_access();
+
   my $form     = $main::form;
   my %myconfig = %main::myconfig;
   my $locale   = $main::locale;
 
-  $main::auth->assert('invoice_edit');
-
   $form->{invtotal}    = $form->{invsubtotal};
 
-  # note rows
-  $form->{rows} = max 2,
-    $form->numtextrows($form->{notes},    26, 8),
-    $form->numtextrows($form->{intnotes}, 35, 8);
-
-
   # tax, total and subtotal calculations
   my ($tax, $subtotal);
   $form->{taxaccounts_array} = [ split(/ /, $form->{taxaccounts}) ];
 
   if( $form->{customer_id} && !$form->{taxincluded_changed_by_user} ) {
-    my $customer = SL::DB::Customer->new(id => $form->{customer_id})->load();
+    my $customer = SL::DB::Customer->load_cached($form->{customer_id});
     $form->{taxincluded} = defined($customer->taxincluded_checked) ? $customer->taxincluded_checked : $myconfig{taxincluded_checked};
   }
 
@@ -463,9 +711,16 @@ sub form_footer {
     }
   }
 
+  my $grossamount = $form->{invtotal};
+  $form->{invtotal} = $form->round_amount( $form->{invtotal}, 2, 1 );
+  $form->{rounding} = $form->round_amount(
+    $form->{invtotal} - $form->round_amount($grossamount, 2),
+    2
+  );
+
   # follow ups
   if ($form->{id}) {
-    $form->{follow_ups}            = FU->follow_ups('trans_id' => $form->{id}) || [];
+    $form->{follow_ups}            = FU->follow_ups('trans_id' => $form->{id}, 'not_done' => 1) || [];
     $form->{follow_ups_unfinished} = ( sum map { $_->{due} * 1 } @{ $form->{follow_ups} } ) || 0;
   }
 
@@ -490,6 +745,11 @@ sub form_footer {
                                   ($form->current_date(\%myconfig) eq $form->{"gldate_$i"}));
     }
 
+    #deaktivieren von gebuchten Zahlungen ausserhalb der Bücherkontrolle, vorher prüfen ob heute eingegeben
+    if ($form->date_closed($form->{"gldate_$i"})) {
+      $form->{"changeable_$i"} = 0;
+    }
+
     $form->{"selectAR_paid_$i"} = $form->{selectAR_paid};
     if (!$form->{"AR_paid_$i"}) {
       $form->{"selectAR_paid_$i"} =~ s/option>$accno_arap--(.*?)</option selected>$accno_arap--$1</;
@@ -504,16 +764,25 @@ sub form_footer {
 
   $form->{ALL_DELIVERY_TERMS} = SL::DB::Manager::DeliveryTerm->get_all_sorted();
 
+  my $shipto_cvars       = SL::DB::Shipto->new->cvars_by_config;
+  foreach my $var (@{ $shipto_cvars }) {
+    my $name = "shiptocvar_" . $var->config->name;
+    $var->value($form->{$name}) if exists $form->{$name};
+  }
+
   print $form->parse_html_template('is/form_footer', {
-    is_type_credit_note => ($form->{type} eq "credit_note"),
-    totalpaid           => $totalpaid,
-    paid_missing        => $form->{invtotal} - $totalpaid,
-    print_options       => print_options(inline => 1),
-    show_storno         => $form->{id} && !$form->{storno} && !IS->has_storno(\%myconfig, $form, "ar") && !$totalpaid,
-    show_delete         => ($::instance_conf->get_is_changeable == 2)
-                             ? ($form->current_date(\%myconfig) eq $form->{gldate})
-                             : ($::instance_conf->get_is_changeable == 1),
-    today               => DateTime->today,
+    is_type_normal_invoice              => ($form->{type} eq "invoice"),
+    is_type_credit_note                 => ($form->{type} eq "credit_note"),
+    totalpaid                           => $totalpaid,
+    paid_missing                        => $form->{invtotal} - $totalpaid,
+    print_options                       => setup_sales_purchase_print_options(),
+    show_storno                         => $form->{id} && !$form->{storno} && !IS->has_storno(\%myconfig, $form, "ar") && !$totalpaid,
+    show_delete                         => ($::instance_conf->get_is_changeable == 2)
+                                             ? ($form->current_date(\%myconfig) eq $form->{gldate})
+                                             : ($::instance_conf->get_is_changeable == 1),
+    today                               => DateTime->today,
+    vc_obj                              => $form->{customer_id} ? SL::DB::Customer->load_cached($form->{customer_id}) : undef,
+    shipto_cvars                        => $shipto_cvars,
   });
 ##print $form->parse_html_template('is/_payments'); # parser
 ##print $form->parse_html_template('webdav/_list'); # parser
@@ -522,33 +791,40 @@ sub form_footer {
 }
 
 sub mark_as_paid {
-  $main::lxdebug->enter_sub();
+  $::auth->assert('invoice_edit');
 
-  my $form     = $main::form;
-  my %myconfig = %main::myconfig;
-
-  $main::auth->assert('invoice_edit');
+  SL::DB::Invoice->new(id => $::form->{id})->load->mark_as_paid;
 
-  &mark_as_paid_common(\%myconfig,"ar");
+  $::form->redirect($::locale->text("Marked as paid"));
+}
 
-  $main::lxdebug->leave_sub();
+sub show_draft {
+  # unless no lazy implementation of save draft without invdate
+  # set the current date like in version <= 3.4.1
+  $::form->{invdate}   = DateTime->today->to_lxoffice;
+  update();
 }
 
 sub update {
   $main::lxdebug->enter_sub();
 
+  _assert_access();
+
   my $form     = $main::form;
   my %myconfig = %main::myconfig;
 
-  $main::auth->assert('invoice_edit');
-
   my ($recursive_call) = @_;
 
   $form->{print_and_post} = 0         if $form->{second_run};
   my $taxincluded         = $form->{taxincluded} ? "checked" : '';
   $form->{update} = 1;
 
-  &check_name("customer");
+  if (($form->{previous_customer_id} || $form->{customer_id}) != $form->{customer_id}) {
+    $::form->{salesman_id} = SL::DB::Manager::Employee->current->id if exists $::form->{salesman_id};
+
+    IS->get_customer(\%myconfig, $form);
+    $::form->{billing_address_id} = $::form->{default_billing_address_id};
+  }
 
   $form->{taxincluded} ||= $taxincluded;
 
@@ -560,10 +836,7 @@ sub update {
 
   for my $i (1 .. $form->{paidaccounts}) {
     next unless $form->{"paid_$i"};
-    map { $form->{"${_}_$i"} = $form->parse_amount(\%myconfig, $form->{"${_}_$i"}) } qw(paid exchangerate);
-    if (!$form->{"forex_$i"}) {   #read exchangerate from input field (not hidden)
-      $form->{exchangerate} = $form->{"exchangerate_$i"};
-    }
+    map { $form->{"${_}_$i"}   = $form->parse_amount(\%myconfig, $form->{"${_}_$i"}) } qw(paid exchangerate);
     $form->{"forex_$i"}        = $form->check_exchangerate(\%myconfig, $form->{currency}, $form->{"datepaid_$i"}, 'buy');
     $form->{"exchangerate_$i"} = $form->{"forex_$i"} if $form->{"forex_$i"};
   }
@@ -597,7 +870,7 @@ sub update {
       if ($rows > 1) {
 
         select_item(mode => 'IS', pre_entered_qty => $form->{"qty_$i"});
-        ::end_of_request();
+        $::dispatcher->end_request;
 
       } else {
 
@@ -658,7 +931,7 @@ sub update {
       # ask if it is a part or service item
 
       if (   $form->{"partsgroup_$i"}
-          && ($form->{"partsnumber_$i"} eq "")
+          && ($form->{"partnumber_$i" } eq "")
           && ($form->{"description_$i"} eq "")) {
         $form->{rowcount}--;
         $form->{"discount_$i"} = "";
@@ -699,20 +972,15 @@ sub post_payment {
         $form->isblank("exchangerate_$i",
                        $locale->text('Exchangerate for payment missing!'));
       }
+      $form->error($locale->text('Cannot post transaction above the maximum future booking date!'))
+        if ($form->date_max_future($form->{"datepaid_$i"}, \%myconfig));
+
+      #Zusätzlich noch das Buchungsdatum in die Bücherkontrolle einbeziehen
+      # (Dient zur Prüfung ob ZE oder ZA geprüft werden soll)
+      $form->error($locale->text('Cannot post payment for a closed period!'))
+        if ($form->date_closed($form->{"datepaid_$i"})  && !$form->date_closed($form->{"gldate_$i"}, \%myconfig));
     }
   }
-  # Abgeschlossene Zeiträume nur für den letzten (aktuellen) Zahlungseingang prüfen
-  # Details s.a. Bug 1502
-  # Das Problem ist jetzt, dass man Zahlungseingänge nachträglich ändern kann
-  # Wobei dies für Installationen die sowieso nicht mit Bücherkontrolle arbeiten keinen
-  # keinen Unterschied macht.
-  # Optimal wäre, wenn gegen einen Zeitstempel des Zahlungsfelds geprüft würde ...
-  # Das Problem hierbei ist, dass in IS.pm post_invoice IMMER alle Zahlungseingänge aus $form
-  # erneut gespeichert werden. Prinzipiell wäre es besser NUR die Änderungen des Rechnungs-
-  # belegs (neue Zahlung aber nichts anderes) zu speichern ...
-  # Vielleicht könnte man ähnlich wie bei Rechnung löschen verfahren
-  $form->error($locale->text('Cannot post payment for a closed period!'))
-    if ($form->date_closed($form->{"datepaid_$form->{paidaccounts}"}, \%myconfig));
 
   ($form->{AR})      = split /--/, $form->{AR};
   ($form->{AR_paid}) = split /--/, $form->{AR_paid};
@@ -742,7 +1010,7 @@ sub post {
 
   $form->{defaultcurrency} = $form->get_default_currency(\%myconfig);
   $form->isblank("invdate",  $locale->text('Invoice Date missing!'));
-  $form->isblank("customer", $locale->text('Customer missing!'));
+  $form->isblank("customer_id", $locale->text('Customer missing!'));
   $form->error($locale->text('Cannot post invoice for a closed period!'))
         if ($form->date_closed($form->{"invdate"}, \%myconfig));
 
@@ -750,9 +1018,9 @@ sub post {
   $form->{invnumber} =~ s/\s*$//g;
 
   # if oldcustomer ne customer redo form
-  if (&check_name('customer')) {
+  if (($form->{previous_customer_id} || $form->{customer_id}) != $form->{customer_id}) {
     &update;
-    ::end_of_request();
+    $::dispatcher->end_request;
   }
 
   if ($myconfig{mandatory_departments} && !$form->{department_id}) {
@@ -778,6 +1046,14 @@ sub post {
 
   $form->isblank("exchangerate", $locale->text('Exchangerate missing!'))
     if ($form->{currency} ne $form->{defaultcurrency});
+  # advance payment allows only one tax
+  if ($form->{type} eq 'invoice_for_advance_payment') {
+    my @current_taxaccounts = (split(/ /, $form->{taxaccounts}));
+    $form->error($locale->text('Cannot post invoice for advance payment with more than one tax'))
+      if (scalar @current_taxaccounts > 1);
+    $form->error($locale->text('Cannot post invoice for advance payment with taxincluded'))
+      if ($form->{taxincluded});
+  }
 
   for my $i (1 .. $form->{paidaccounts}) {
     if ($form->parse_amount(\%myconfig, $form->{"paid_$i"})) {
@@ -785,8 +1061,13 @@ sub post {
 
       $form->isblank("datepaid_$i", $locale->text('Payment date missing!'));
 
+      $form->error($locale->text('Cannot post transaction above the maximum future booking date!'))
+        if ($form->date_max_future($form->{"datepaid_$i"}, \%myconfig));
+
+      #Zusätzlich noch das Buchungsdatum in die Bücherkontrolle einbeziehen
+      # (Dient zur Prüfung ob ZE oder ZA geprüft werden soll)
       $form->error($locale->text('Cannot post payment for a closed period!'))
-        if ($form->date_closed($form->{"datepaid_$i"}, \%myconfig));
+        if ($form->date_closed($form->{"datepaid_$i"})  && !$form->date_closed($form->{"gldate_$i"}, \%myconfig));
 
       if ($form->{currency} ne $form->{defaultcurrency}) {
         $form->{"exchangerate_$i"} = $form->{exchangerate}
@@ -817,7 +1098,7 @@ sub post {
   relink_accounts();
 
   my $terms        = get_payment_terms_for_invoice();
-  $form->{duedate} = $terms->calc_date(reference_date => $form->{invdate}, due_date => $form->{due_due})->to_kivitendo if $terms;
+  $form->{duedate} = $terms->calc_date(reference_date => $form->{invdate}, due_date => $form->{duedate})->to_kivitendo if $terms;
 
   # If transfer_out is requested, get rose db handle and do post and
   # transfer out in one transaction. Otherwise just post the invoice.
@@ -841,7 +1122,7 @@ sub post {
         1;
       }) {
         push @errors, $EVAL_ERROR;
-        die 'transaction error';
+        $form->error($locale->text('Cannot post invoice and/or transfer out! Error message:') . "\n" . join("\n", @errors));
       }
 
       1;
@@ -855,8 +1136,6 @@ sub post {
     }
   }
 
-  remove_draft() if $form->{remove_draft};
-
   if(!exists $form->{addition}) {
     $form->{snumbers}  =  'invnumber' .'_'. $form->{invnumber}; # ($form->{type} eq 'credit_note' ? 'cnnumber' : 'invnumber') .'_'. $form->{invnumber};
     $form->{what_done} = 'invoice';
@@ -869,9 +1148,8 @@ sub post {
   if (!$form->{no_redirect_after_post}) {
     $form->{action} = 'edit';
     $form->{script} = 'is.pl';
-    $form->{saved_message} = $form->{label} . " $form->{invnumber} " . $locale->text('posted!');
     $form->{callback} = build_std_url(qw(action edit id callback saved_message));
-    $form->redirect;
+    $form->redirect($form->{label} . " $form->{invnumber} " . $locale->text('posted!'));
   }
 
   $main::lxdebug->leave_sub();
@@ -920,6 +1198,80 @@ sub use_as_new {
   $main::lxdebug->leave_sub();
 }
 
+sub further_invoice_for_advance_payment {
+  my $form     = $main::form;
+  my %myconfig = %main::myconfig;
+
+  $main::auth->assert('invoice_edit');
+
+  delete @{ $form }{qw(printed emailed queued invnumber invdate exchangerate forex deliverydate datepaid_1 gldate_1 acc_trans_id_1 source_1 memo_1 paid_1 exchangerate_1 AP_paid_1 storno locked)};
+  $form->{convert_from_ar_ids} = $form->{id};
+  $form->{id}                  = '';
+  $form->{rowcount}--;
+  $form->{paidaccounts}        = 1;
+  $form->{invdate}             = $form->current_date(\%myconfig);
+  my $terms                    = get_payment_terms_for_invoice();
+  $form->{duedate}             = $terms ? $terms->calc_date(reference_date => $form->{invdate})->to_kivitendo : $form->{invdate};
+  $form->{employee_id}         = SL::DB::Manager::Employee->current->id;
+  $form->{forex}               = $form->check_exchangerate(\%myconfig, $form->{currency}, $form->{invdate}, 'buy');
+  $form->{exchangerate}        = $form->{forex} if $form->{forex};
+
+  $form->{"converted_from_invoice_id_$_"} = delete $form->{"invoice_id_$_"} for 1 .. $form->{"rowcount"};
+
+  &display_form;
+}
+
+sub final_invoice {
+  my $form     = $main::form;
+  my %myconfig = %main::myconfig;
+
+  $main::auth->assert('invoice_edit');
+
+  my $related_invoices = IS->_get_invoices_for_advance_payment($form->{id});
+
+  delete @{ $form }{qw(printed emailed queued invnumber invdate exchangerate forex deliverydate datepaid_1 gldate_1 acc_trans_id_1 source_1 memo_1 paid_1 exchangerate_1 AP_paid_1 storno locked)};
+
+  $form->{convert_from_ar_ids} = $form->{id};
+  $form->{id}                  = '';
+  $form->{type}                = 'final_invoice';
+  $form->{title}               = t8('Edit Final Invoice');
+  $form->{paidaccounts}        = 1;
+  $form->{invdate}             = $form->current_date(\%myconfig);
+  my $terms                    = get_payment_terms_for_invoice();
+  $form->{duedate}             = $terms ? $terms->calc_date(reference_date => $form->{invdate})->to_kivitendo : $form->{invdate};
+  $form->{employee_id}         = SL::DB::Manager::Employee->current->id;
+  $form->{forex}               = $form->check_exchangerate(\%myconfig, $form->{currency}, $form->{invdate}, 'buy');
+  $form->{exchangerate}        = $form->{forex} if $form->{forex};
+
+  foreach my $i (1 .. $form->{"rowcount"}) {
+    delete $form->{"id_$i"};
+    delete $form->{"invoice_id_$i"};
+    delete $form->{"parts_id_$i"};
+    delete $form->{"partnumber_$i"};
+    delete $form->{"description_$i"};
+  }
+
+  remove_emptied_rows(1);
+
+  my $i = 0;
+  foreach my $ri (@$related_invoices) {
+    foreach my $item (@{$ri->items_sorted}) {
+      $i++;
+      $form->{"id_$i"}         = $item->parts_id;
+      $form->{"partnumber_$i"} = $item->part->partnumber;
+      $form->{"discount_$i"}   = $item->discount*100.0;
+      $form->{"sellprice_$i"}  = $item->fxsellprice;
+      $form->{$_ . "_" . $i}   = $item->$_       for qw(description longdescription qty price_factor_id unit active_price_source active_discount_source);
+
+      $form->{$_ . "_" . $i}   = $form->format_amount(\%myconfig, $form->{$_ . "_" . $i}) for qw(qty sellprice discount);
+    }
+  }
+  $form->{rowcount} = $i;
+
+  update();
+  $::dispatcher->end_request;
+}
+
 sub storno {
   $main::lxdebug->enter_sub();
 
@@ -956,6 +1308,8 @@ sub storno {
   $form->{paidaccounts} = 0;
   map { my $key = $_; delete $form->{$key} if grep { $key =~ /^$_/ } qw(datepaid_ gldate_ acc_trans_id_ source_ memo_ paid_ exchangerate_ AR_paid_) } keys %{ $form };
 
+  # record link invoice to storno
+  $form->{convert_from_ar_ids} = $form->{id};
   $form->{storno_id} = $form->{id};
   $form->{storno} = 1;
   $form->{id} = "";
@@ -963,7 +1317,8 @@ sub storno {
   $form->{invdate}   = DateTime->today->to_lxoffice;
   $form->{rowcount}++;
   # set new ids for storno invoice
-  delete $form->{"invoice_id_$_"} for 1 .. $form->{"rowcount"};
+  # set new persistent ids for storno invoice items
+  $form->{"converted_from_invoice_id_$_"} = delete $form->{"invoice_id_$_"} for 1 .. $form->{"rowcount"};
 
   post();
   $main::lxdebug->leave_sub();
@@ -985,49 +1340,6 @@ sub preview {
 
 }
 
-sub delete {
-  $main::lxdebug->enter_sub();
-
-  my $form     = $main::form;
-  my $locale   = $main::locale;
-
-  $main::auth->assert('invoice_edit');
-
-  if ($form->{second_run}) {
-    $form->{print_and_post} = 0;
-  }
-  $form->header;
-
-  print qq|
-<form method="post" action="$form->{script}">
-|;
-
-  # delete action variable
-  map { delete $form->{$_} } qw(action header);
-
-  foreach my $key (keys %$form) {
-    next if (($key eq 'login') || ($key eq 'password') || ('' ne ref $form->{$key}));
-    $form->{$key} =~ s/\"/&quot;/g;
-    print qq|<input type="hidden" name="$key" value="$form->{$key}">\n|;
-  }
-
-  print qq|
-<h2 class="confirm">| . $locale->text('Confirm!') . qq|</h2>
-
-<h4>|
-    . $locale->text('Are you sure you want to delete Invoice Number')
-    . qq| $form->{invnumber}
-</h4>
-
-<p>
-<input name="action" class="submit" type="submit" value="|
-    . $locale->text('Yes') . qq|">
-</form>
-|;
-
-  $main::lxdebug->leave_sub();
-}
-
 sub credit_note {
   $main::lxdebug->enter_sub();
 
@@ -1089,59 +1401,60 @@ sub credit_note {
 
   &prepare_invoice;
 
-
   &display_form;
 
   $main::lxdebug->leave_sub();
 }
 
-sub yes {
-  $main::lxdebug->enter_sub();
+sub display_form {
+  $::lxdebug->enter_sub;
 
-  my $form     = $main::form;
-  my %myconfig = %main::myconfig;
-  my $locale   = $main::locale;
-
-  $main::auth->assert('invoice_edit');
+  _assert_access();
 
-  if (IS->delete_invoice(\%myconfig, \%$form)) {
-    # saving the history
-    if(!exists $form->{addition}) {
-      $form->{snumbers}  = 'invnumber' .'_'. $form->{invnumber}; # ($form->{type} eq 'credit_note' ? 'cnnumber' : 'invnumber') .'_'. $form->{invnumber};
-      $form->{what_done} = 'invoice';
-      $form->{addition}  = "DELETED";
-      $form->save_history;
-    }
-    # /saving the history
-    $form->redirect($locale->text('Invoice deleted!'));
-  }
-  $form->error($locale->text('Cannot delete invoice!'));
-
-  $main::lxdebug->leave_sub();
-}
+  relink_accounts();
 
-sub post_and_e_mail {
-  e_mail();
-};
+  my $new_rowcount = $::form->{"rowcount"} * 1 + 1;
+  $::form->{"project_id_${new_rowcount}"} = $::form->{"globalproject_id"};
 
-sub e_mail {
-  $main::lxdebug->enter_sub();
+  $::form->language_payment(\%::myconfig);
 
-  my $form     = $main::form;
+  Common::webdav_folder($::form);
 
-  $main::auth->assert('invoice_edit');
+  form_header();
+  display_row(++$::form->{rowcount});
+  form_footer();
 
-  if (!$form->{id}) {
-    $form->{no_redirect_after_post} = 1;
-
-    my $saved_form = save_form();
+  $::lxdebug->leave_sub;
+}
 
-    post();
+sub delete {
+  $::auth->assert('invoice_edit');
 
-    restore_form($saved_form, 0, qw(id invnumber));
+  if (IS->delete_invoice(\%::myconfig, $::form)) {
+    # saving the history
+    if(!exists $::form->{addition}) {
+      $::form->{snumbers}  = 'invnumber' .'_'. $::form->{invnumber};
+      $::form->{what_done} = 'invoice';
+      $::form->{addition}  = "DELETED";
+      $::form->save_history;
+    }
+    # /saving the history
+    $::form->redirect($::locale->text('Invoice deleted!'));
   }
+  $::form->error($::locale->text('Cannot delete invoice!'));
+}
 
-  edit_e_mail();
+sub dispatcher {
+  for my $action (qw(
+    print update ship_to storno post_payment use_as_new credit_note
+    delete post order preview post_and_e_mail print_and_post
+    mark_as_paid
+  )) {
+    if ($::form->{"action_$action"}) {
+      call_sub($action);
+      return;
+    }
+  }
 
-  $main::lxdebug->leave_sub();
+  $::form->error($::locale->text('No action defined.'));
 }