ActionBar: API-Umstellung fürs Hinzufügen
authorMoritz Bunkus <m.bunkus@linet-services.de>
Thu, 10 Nov 2016 12:08:20 +0000 (13:08 +0100)
committerMoritz Bunkus <m.bunkus@linet-services.de>
Tue, 28 Feb 2017 09:04:33 +0000 (10:04 +0100)
Designziele:

• möglichst wenig Funktionsaufrufe auf eine ActionBar-Instanz
• Hash-artige Struktur der Daten zwecks bekanntem Aussehen und leichter
  Verständlichkeit
• Leichter um neue Typen erweiterbar
• Rekursiv parsend (für Comboboxen)

SL/Layout/ActionBar.pm
SL/Layout/ActionBar/Action.pm
SL/Layout/ActionBar/ComboBox.pm
SL/Layout/ActionBar/Separator.pm
bin/mozilla/ir.pl
bin/mozilla/is.pl
bin/mozilla/oe.pl

index edb8365..a28e1a9 100644 (file)
@@ -3,7 +3,11 @@ package SL::Layout::ActionBar;
 use strict;
 use parent qw(SL::Layout::Base);
 
+use Carp;
+use Scalar::Util qw(blessed);
 use SL::Layout::ActionBar::Action;
+use SL::Layout::ActionBar::ComboBox;
+use SL::Layout::ActionBar::Separator;
 
 use constant HTML_CLASS => 'layout-actionbar';
 
@@ -11,6 +15,11 @@ use Rose::Object::MakeMethods::Generic (
   'scalar --get_set_init' => [ qw(actions) ],
 );
 
+my %class_descriptors = (
+  action    => { class => 'SL::Layout::ActionBar::Action',    num_params => 1, },
+  combobox  => { class => 'SL::Layout::ActionBar::ComboBox',  num_params => 1, },
+  separator => { class => 'SL::Layout::ActionBar::Separator', num_params => 0, },
+);
 
 ###### Layout overrides
 
@@ -32,22 +41,37 @@ sub javascripts {
 
 ###### interface
 
-sub add_actions {
+sub add {
   my ($self, @actions) = @_;
-  push @{ $self->actions }, map {
-       !ref $_ ? SL::Layout::ActionBar::Action->from_descriptor($_)
-     :  ref $_ && 'ARRAY' eq ref $_ ? SL::Layout::ActionBar::Action->simple($_)
-     :  ref $_ && $_->isa('SL::Layout::Action') ? $_
-     : do { die 'invalid action' };
-  } @actions;
-}
 
-sub init_actions {
-  []
+  push @{ $self->actions }, $self->parse_actions(@actions);
+
+  return $self->actions->[-1];
 }
 
+sub parse_actions {
+  my ($self_or_class, @actions) = @_;
+
+  my @parsed;
+
+  while (my $type = shift(@actions)) {
+    if (blessed($type) && $type->isa('SL::Layout::ActionBar::Action')) {
+      push @parsed, $type;
+      continue;
+    }
 
+    my $descriptor = $class_descriptors{lc $type} || croak("Unknown action type '${type}'");
+    my @params     = splice(@actions, 0, $descriptor->{num_params});
 
+    push @parsed, $descriptor->{class}->from_params(@params);
+  }
+
+  return @parsed;
+}
+
+sub init_actions {
+  []
+}
 
 1;
 
index 5d03b7c..bed33fe 100644 (file)
@@ -20,21 +20,8 @@ sub script {
   sprintf q|$('#%s').data('action', %s);|, $_[0]->id, JSON->new->allow_blessed->convert_blessed->encode($_[0]->params);
 }
 
-# static constructors
-
-sub from_descriptor {
-  my ($class, $descriptor) = @_;
-  require SL::Layout::ActionBar::Separator;
-  require SL::Layout::ActionBar::ComboBox;
-
-  return {
-     separator => SL::Layout::ActionBar::Separator->new,
-     combobox  => SL::Layout::ActionBar::ComboBox->new,
-  }->{$descriptor} || do { die 'unknown descriptor' };
-}
-
 # this is mostly so that outside consumer don't need to load subclasses themselves
-sub simple {
+sub from_params {
   my ($class, $data) = @_;
 
   my ($text, %params) = @$data;
@@ -83,4 +70,3 @@ on click call the specified function (is this a special case of checks?)
 TODO:
 
 - runtime disable/enable
-
index 5e5c863..9a54539 100644 (file)
@@ -9,17 +9,17 @@ use Rose::Object::MakeMethods::Generic (
   'scalar --get_set_init' => [ qw(actions) ],
 );
 
-sub parsed_actions {
-  $_[0]{parsed_actions} ||=
-    [ map { SL::Layout::ActionBar::Action->simple($_) } @{ $_[0]->actions || [] } ];
-}
+sub from_params {
+  my ($class, $actions) = @_;
+
+  my $combobox = $class->new;
+  push @{ $combobox->actions }, SL::Layout::ActionBar->parse_actions(@{ $actions });
 
-sub add_actions {
-  push @{$_[0]{actions} //= $_[0]->init_actions}, @_[1..$#_]
+  return $combobox;
 }
 
 sub render {
-  my ($first, @rest) = @{ $_[0]->parsed_actions };
+  my ($first, @rest) = @{ $_[0]->actions };
   $_[0]->p->html_tag('div',
     $_[0]->p->html_tag('div', $first->render . $_[0]->p->html_tag('span'), class => 'layout-actionbar-combobox-head') .
     $_[0]->p->html_tag('div', join('', map { $_->render } @rest), class => 'layout-actionbar-combobox-list'),
@@ -29,7 +29,7 @@ sub render {
 }
 
 sub script {
-  map { $_->script } @{ $_[0]->parsed_actions }
+  map { $_->script } @{ $_[0]->actions }
 }
 
 sub init_actions { [] }
index b6b7b94..cbcdf51 100644 (file)
@@ -3,6 +3,8 @@ package SL::Layout::ActionBar::Separator;
 use strict;
 use parent qw(SL::Layout::ActionBar::Action);
 
+sub from_params { $_[0]->new }
+
 sub render {
   $_[0]->p->html_tag('div', '', class => 'layout-actionbar-separator');
 }
index ecd2696..a5140d4 100644 (file)
@@ -222,66 +222,77 @@ sub setup_ir_action_bar {
   my $change_on_same_day_only = $::instance_conf->get_ir_changeable == 2 && ($form->current_date(\%::myconfig) ne $form->{gldate});
 
   for my $bar ($::request->layout->get('actionbar')) {
-    $bar->add_actions([ t8('Update'),
-      submit    => [ '#form', { action_update => 1 } ],
-      id        => 'update_button',
-      accesskey => 'enter',
-    ]);
-
-    $bar->add_actions("combobox");
-    $bar->actions->[-1]->add_actions([ t8('Post'),
-      submit   => [ '#form', { action_post => 1 } ],
-      disabled => $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.')
-                :                                             undef,
-    ]);
-    $bar->actions->[-1]->add_actions([ t8('Post Payment'),
-      submit   => [ '#form', { action_post_payment => 1 } ],
-      disabled => !$form->{id} ? t8('This invoice has not been posted yet.') : undef,
-    ]);
-    $bar->actions->[-1]->add_actions([ t8('mark as paid'),
-      submit   => [ '#form', { action_mark_as_paid => 1 } ],
-      confirm  => t8('This will remove the invoice from showing as unpaid even if the unpaid amount does not match the amount. Proceed?'),
-      disabled => !$form->{id} ? t8('This invoice has not been posted yet.') : undef,
-    ]) if $::instance_conf->get_ir_show_mark_as_paid;
-
-
-    $bar->add_actions("combobox");
-    $bar->actions->[-1]->add_actions([ t8('Storno'),
-      submit   => [ '#form', { action_storno => 1 } ],
-      confirm  => t8('Do you really want to cancel this invoice?'),
-      disabled => !$form->{id} ? t8('This invoice has not been posted yet.') : undef,
-    ]);
-    $bar->actions->[-1]->add_actions([ t8('Delete'),
-      submit   => [ '#form', { action_delete => 1 } ],
-      confirm  => t8('Do you really want to delete this object?'),
-      disabled => !$form->{id}             ? t8('This invoice has not been posted yet.')
-                : $form->{locked}          ? t8('The billing period has already been locked.')
-                : $change_never            ? t8('Changing invoices has been disabled in the configuration.')
-                : $change_on_same_day_only ? t8('Invoices can only be changed on the day they are posted.')
-                :                            undef,
-    ]);
-
-    $bar->add_actions('separator');
-
-    $bar->add_actions('combobox');
-    $bar->actions->[-1]->add_actions([ t8('more') ]);
-    $bar->actions->[-1]->add_actions([ t8('History'),
-      call     => [ 'set_history_window', $::form->{id} * 1, 'id', 'glid' ],
-      disabled => !$form->{id} ? t8('This invoice has not been posted yet.') : undef,
-    ]);
-    $bar->actions->[-1]->add_actions([ t8('Follow-Up'),
-      call     => [ 'follow_up_window' ],
-      disabled => !$form->{id} ? t8('This invoice has not been posted yet.') : undef,
-    ]);
-    $bar->actions->[-1]->add_actions([ t8('Drafts'),
-      call     => [ 'kivi.Draft.popup', 'ir', 'invoice', $::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,
-    ]);
+    $bar->add(
+      action => [
+        t8('Update'),
+        submit    => [ '#form', { action_update => 1 } ],
+        id        => 'update_button',
+        accesskey => 'enter',
+      ],
+
+      combobox => [
+        action => [
+          t8('Post'),
+          submit   => [ '#form', { action_post => 1 } ],
+          disabled => $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.')
+                    :                                             undef,
+        ],
+        action => [
+          t8('Post Payment'),
+          submit   => [ '#form', { action_post_payment => 1 } ],
+          disabled => !$form->{id} ? t8('This invoice has not been posted yet.') : undef,
+        ],
+        (action => [
+          t8('mark as paid'),
+          submit   => [ '#form', { action_mark_as_paid => 1 } ],
+          confirm  => t8('This will remove the invoice from showing as unpaid even if the unpaid amount does not match the amount. Proceed?'),
+          disabled => !$form->{id} ? t8('This invoice has not been posted yet.') : undef,
+        ]) x !!$::instance_conf->get_ir_show_mark_as_paid,
+      ], # end of combobox "Post"
+
+      combobox => [
+        action => [ t8('Storno'),
+          submit   => [ '#form', { action_storno => 1 } ],
+          confirm  => t8('Do you really want to cancel this invoice?'),
+          disabled => !$form->{id} ? t8('This invoice has not been posted yet.') : undef,
+        ],
+        action => [ t8('Delete'),
+          submit   => [ '#form', { action_delete => 1 } ],
+          confirm  => t8('Do you really want to delete this object?'),
+          disabled => !$form->{id}             ? t8('This invoice has not been posted yet.')
+                    : $form->{locked}          ? t8('The billing period has already been locked.')
+                    : $change_never            ? t8('Changing invoices has been disabled in the configuration.')
+                    : $change_on_same_day_only ? t8('Invoices can only be changed on the day they are posted.')
+                    :                            undef,
+        ],
+      ], # end of combobox "Storno"
+
+      'separator',
+
+      combobox => [
+        action => [ t8('more') ],
+        action => [
+          t8('History'),
+          call     => [ 'set_history_window', $::form->{id} * 1, 'id', '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', 'ir', 'invoice', $::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"
+    );
   }
 }
 
index 297a87d..8f8662f 100644 (file)
@@ -259,101 +259,118 @@ sub setup_is_action_bar {
   my @req_trans_desc          = qw(kivi.SalesPurchase.check_transaction_description) x!!$::instance_conf->get_require_transaction_description_ps;
 
   for my $bar ($::request->layout->get('actionbar')) {
-    $bar->add_actions([ t8('Update'),
-      submit    => [ '#form', { action_update => 1 } ],
-      disabled  => $form->{locked} ? t8('The billing period has already been locked.') : undef,
-      id        => 'update_button',
-      accesskey => 'enter',
-    ]);
-
-    $bar->add_actions("combobox");
-    $bar->actions->[-1]->add_actions([ t8('Post'),
-      submit   => [ '#form', { action_post => 1 } ],
-      checks   => [ @req_trans_desc ],
-      disabled => $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.')
-                :                                             undef,
-    ]);
-    $bar->actions->[-1]->add_actions([ t8('Post Payment'),
-      submit   => [ '#form', { action_post_payment => 1 } ],
-      checks   => [ @req_trans_desc ],
-      disabled => !$form->{id} ? t8('This invoice has not been posted yet.') : undef,
-    ]);
-    $bar->actions->[-1]->add_actions([ t8('mark as paid'),
-      submit   => [ '#form', { action_mark_as_paid => 1 } ],
-      confirm  => t8('This will remove the invoice from showing as unpaid even if the unpaid amount does not match the amount. Proceed?'),
-      disabled => !$form->{id} ? t8('This invoice has not been posted yet.') : undef,
-    ]) if $::instance_conf->get_is_show_mark_as_paid;
-
-
-    $bar->add_actions("combobox");
-    $bar->actions->[-1]->add_actions([ t8('Storno'),
-      submit   => [ '#form', { action_storno => 1 } ],
-      confirm  => t8('Do you really want to cancel this invoice?'),
-      checks   => [ @req_trans_desc ],
-      disabled => !$form->{id} ? t8('This invoice has not been posted yet.') : undef,
-    ]);
-    $bar->actions->[-1]->add_actions([ t8('Delete'),
-      submit   => [ '#form', { action_delete => 1 } ],
-      confirm  => t8('Do you really want to delete this object?'),
-      checks   => [ @req_trans_desc ],
-      disabled => !$form->{id}             ? t8('This invoice has not been posted yet.')
-                : $form->{locked}          ? t8('The billing period has already been locked.')
-                : $change_never            ? t8('Changing invoices has been disabled in the configuration.')
-                : $change_on_same_day_only ? t8('Invoices can only be changed on the day they are posted.')
-                :                            undef,
-    ]);
-
-    $bar->add_actions('separator');
-
-    $bar->add_actions('combobox');
-    $bar->actions->[-1]->add_actions([ t8('Workflow') ]);
-    $bar->actions->[-1]->add_actions([ t8('Use As New'),
-      submit   => [ '#form', { action_use_as_new    => 1 } ],
-      disabled => !$form->{id} ? t8('This invoice has not been posted yet.') : undef,
-    ]);
-    $bar->actions->[-1]->add_actions([ t8('Credit Note'),
-      submit   => [ '#form', { action_credit_note    => 1 } ],
-      checks   => [ @req_trans_desc ],
-      disabled => $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.')
-                :                                  undef,
-    ]);
-    $bar->actions->[-1]->add_actions([ t8('Sales Order'),
-      submit   => [ '#form', { action_sales_order   => 1 } ],
-      disabled => !$form->{id} ? t8('This invoice has not been posted yet.') : undef,
-    ]);
-
-    $bar->add_actions('combobox');
-    $bar->actions->[-1]->add_actions([ t8('Export') ]);
-    $bar->actions->[-1]->add_actions([ ($form->{id} ? t8('Print') : t8('Preview')),
-      submit   => [ '#form', { action_print => 1 } ],
-      checks   => [ @req_trans_desc ],
-      disabled => !$form->{id} && $form->{locked} ? t8('The billing period has already been locked.') : undef,
-    ]);
-    $bar->actions->[-1]->add_actions([ t8('E Mail'),
-      submit   => [ '#form', { action_print => 1 } ],
-      checks   => [ @req_trans_desc ],
-      disabled => !$form->{id} ? t8('This invoice has not been posted yet.') : undef,
-    ]);
-    $bar->add_actions('combobox');
-    $bar->actions->[-1]->add_actions([ t8('more') ]);
-    $bar->actions->[-1]->add_actions([ t8('History'),
-      call     => [ 'set_history_window', $form->{id} * 1, 'id' ],
-      disabled => !$form->{id} ? t8('This invoice has not been posted yet.') : undef,
-    ]);
-    $bar->actions->[-1]->add_actions([ t8('Follow-Up'),
-      call     => [ 'follow_up_window' ],
-      disabled => !$form->{id} ? t8('This invoice has not been posted yet.') : undef,
-    ]);
-    $bar->actions->[-1]->add_actions([ t8('Drafts'),
-      call     => [ 'kivi.Draft.popup', 'is', 'invoice', $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,
-    ]);
+    $bar->add(
+      action => [
+        t8('Update'),
+        submit    => [ '#form', { action_update => 1 } ],
+        disabled  => $form->{locked} ? t8('The billing period has already been locked.') : undef,
+        id        => 'update_button',
+        accesskey => 'enter',
+      ],
+
+      combobox => [
+        action => [
+          t8('Post'),
+          submit   => [ '#form', { action_post => 1 } ],
+          checks   => [ @req_trans_desc ],
+          disabled => $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.')
+                    :                                             undef,
+        ],
+        action => [
+          t8('Post Payment'),
+          submit   => [ '#form', { action_post_payment => 1 } ],
+          checks   => [ @req_trans_desc ],
+          disabled => !$form->{id} ? t8('This invoice has not been posted yet.') : undef,
+        ],
+        (action => [ t8('mark as paid'),
+          submit   => [ '#form', { action_mark_as_paid => 1 } ],
+          confirm  => t8('This will remove the invoice from showing as unpaid even if the unpaid amount does not match the amount. Proceed?'),
+          disabled => !$form->{id} ? t8('This invoice has not been posted yet.') : undef,
+        ]) x !!$::instance_conf->get_is_show_mark_as_paid,
+      ], # end of combobox "Post"
+
+      combobox => [
+        action => [ t8('Storno'),
+          submit   => [ '#form', { action_storno => 1 } ],
+          confirm  => t8('Do you really want to cancel this invoice?'),
+          checks   => [ @req_trans_desc ],
+          disabled => !$form->{id} ? t8('This invoice has not been posted yet.') : undef,
+        ],
+        action => [ t8('Delete'),
+          submit   => [ '#form', { action_delete => 1 } ],
+          confirm  => t8('Do you really want to delete this object?'),
+          checks   => [ @req_trans_desc ],
+          disabled => !$form->{id}             ? t8('This invoice has not been posted yet.')
+                    : $form->{locked}          ? t8('The billing period has already been locked.')
+                    : $change_never            ? t8('Changing invoices has been disabled in the configuration.')
+                    : $change_on_same_day_only ? t8('Invoices can only be changed on the day they are posted.')
+                    :                            undef,
+        ],
+      ], # end of combobox "Storno"
+
+      'separator',
+
+      combobox => [
+        action => [ t8('Workflow') ],
+        action => [
+          t8('Use As New'),
+          submit   => [ '#form', { action_use_as_new => 1 } ],
+          disabled => !$form->{id} ? t8('This invoice has not been posted yet.') : undef,
+        ],
+        action => [
+          t8('Credit Note'),
+          submit   => [ '#form', { action_credit_note => 1 } ],
+          checks   => [ @req_trans_desc ],
+          disabled => $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.')
+                    :                                  undef,
+        ],
+        action => [
+          t8('Sales Order'),
+          submit   => [ '#form', { action_sales_order   => 1 } ],
+          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')),
+          submit   => [ '#form', { action_print => 1 } ],
+          checks   => [ @req_trans_desc ],
+          disabled => !$form->{id} && $form->{locked} ? t8('The billing period has already been locked.') : undef,
+        ],
+        action => [ t8('E Mail'),
+          submit   => [ '#form', { action_print => 1 } ],
+          checks   => [ @req_trans_desc ],
+          disabled => !$form->{id} ? t8('This invoice has not been posted yet.') : undef,
+        ],
+      ], # end of combobox "Export"
+
+      combobox => [
+        action => [ t8('more') ],
+        action => [
+          t8('History'),
+          call     => [ 'set_history_window', $form->{id} * 1, 'id' ],
+          disabled => !$form->{id} ? t8('This invoice has not been posted yet.') : undef,
+        ],
+        action => [
+          t8('Follow-Up'),
+          call     => [ 'follow_up_window' ],
+          disabled => !$form->{id} ? t8('This invoice has not been posted yet.') : undef,
+        ],
+        action => [
+          t8('Drafts'),
+          call     => [ 'kivi.Draft.popup', 'is', 'invoice', $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"
+    );
   }
 }
 
index 6f5c547..d2e9b46 100644 (file)
@@ -317,84 +317,106 @@ sub setup_oe_action_bar {
   my @warn_p_invoice     = qw(kivi.SalesPurchase.oe_warn_save_active_periodic_invoice)  x!!$has_active_periodic_invoice;
 
   for my $bar ($::request->layout->get('actionbar')) {
-    $bar->add_actions([ t8('Update'),
-      submit    => [ '#form', { action_update => 1 } ],
-      id        => 'update_button',
-      accesskey => 'enter',
-    ]);
-
-    $bar->add_actions("combobox");
-    $bar->actions->[-1]->add_actions([ t8('Save'),
-      submit  => [ '#form', { action_save => 1 } ],
-      checks  => [ @req_trans_desc, @req_trans_cost_art, @warn_p_invoice ],
-    ]);
-    $bar->actions->[-1]->add_actions([ t8('Save as new'),
-      submit   => [ '#form', { action_save_as_new => 1 } ],
-      checks   => [ @req_trans_desc, @req_trans_cost_art ],
-      disabled => !$form->{id} ? t8('This record has not been saved yet.') : undef,
-    ]);
-     $bar->actions->[-1]->add_actions([ t8('Save and Close'),
-      submit  => [ '#form', { action_save_and_close => 1 } ],
-      checks  => [ @req_trans_desc, @req_trans_cost_art, @warn_p_invoice ],
-    ]);
-    $bar->add_actions([ t8('Delete'),
-      submit   => [ '#form', { action_delete => 1 } ],
-      confirm  => t8('Do you really want to delete this object?'),
-      disabled => !$form->{id}                                                                      ? t8('This record has not been saved yet.')
-                : (   ($params{is_sales_ord} && !$::instance_conf->get_sales_order_show_delete)
-                   || ($params{is_pur_ord}   && !$::instance_conf->get_purchase_order_show_delete)) ? t8('Deleting this type of record has been disabled in the configuration.')
-                :                                                                                     undef,
-    ]);
-
-    $bar->add_actions('separator');
-
-    $bar->add_actions('combobox');
-    $bar->actions->[-1]->add_actions([ t8('Workflow') ]);
-    $bar->actions->[-1]->add_actions([ t8('Sales Order'),
-      submit   => [ '#form', { action_sales_order => 1 } ],
-      disabled => !$form->{id} ? t8('This record has not been saved yet.') : undef,
-    ]) if $params{is_sales_quo};
-    $bar->actions->[-1]->add_actions([ t8('Purchase Order'),
-      submit   => [ '#form', { action_sales_order => 1 } ],
-      disabled => !$form->{id} ? t8('This record has not been saved yet.') : undef,
-    ]) if $params{is_req_quo};
-    $bar->actions->[-1]->add_actions([ t8('Delivery Order'),
-      submit   => [ '#form', { action_delivery_order => 1 } ],
-      disabled => !$form->{id} ? t8('This record has not been saved yet.') : undef,
-    ]) if $params{is_sales_ord} || $params{is_pur_ord};
-    $bar->actions->[-1]->add_actions([ t8('Invoice'),
-      submit   => [ '#form', { action_invoice => 1 } ],
-      disabled => !$form->{id} ? t8('This record has not been saved yet.') : undef,
-    ]) if $allow_invoice;
-    $bar->actions->[-1]->add_actions([ t8('Quotation'),
-      submit   => [ '#form', { action_quotation => 1 } ],
-      disabled => !$form->{id} ? t8('This record has not been saved yet.') : undef,
-    ]);
-    $bar->actions->[-1]->add_actions([ t8('Request for Quotation'),
-      submit   => [ '#form', { action_reqest_for_quotation => 1 } ],
-      disabled => !$form->{id} ? t8('This record has not been saved yet.') : undef,
-    ]);
-
-    $bar->add_actions('combobox');
-    $bar->actions->[-1]->add_actions([ t8('Export') ]);
-    $bar->actions->[-1]->add_actions([ t8('Print'),
-      submit => [ '#form', { action_print => 1 } ],
-      checks => [ @req_trans_desc ],
-    ]);
-    $bar->actions->[-1]->add_actions([ t8('E Mail'),
-      submit => [ '#form', { action_print => 1 } ],
-      checks => [ @req_trans_desc ],
-    ]);
-    $bar->add_actions('combobox');
-    $bar->actions->[-1]->add_actions([ t8('more') ]);
-    $bar->actions->[-1]->add_actions([ t8('History'),
-      call     => [ 'set_history_window', $form->{id} * 1, 'id' ],
-      disabled => !$form->{id} ? t8('This record has not been saved yet.') : undef,
-    ]);
-    $bar->actions->[-1]->add_actions([ t8('Follow-Up'),
-      call     => [ 'follow_up_window' ],
-      disabled => !$form->{id} ? t8('This record has not been saved yet.') : undef,
-    ]);
+    $bar->add(
+      action => [
+        t8('Update'),
+        submit    => [ '#form', { action_update => 1 } ],
+        id        => 'update_button',
+        accesskey => 'enter',
+      ],
+
+      combobox => [
+        action => [
+          t8('Save'),
+          submit  => [ '#form', { action_save => 1 } ],
+          checks  => [ @req_trans_desc, @req_trans_cost_art, @warn_p_invoice ],
+        ],
+        action => [
+          t8('Save as new'),
+          submit   => [ '#form', { action_save_as_new => 1 } ],
+          checks   => [ @req_trans_desc, @req_trans_cost_art ],
+          disabled => !$form->{id} ? t8('This record has not been saved yet.') : undef,
+        ],
+        action => [
+          t8('Save and Close'),
+          submit  => [ '#form', { action_save_and_close => 1 } ],
+          checks  => [ @req_trans_desc, @req_trans_cost_art, @warn_p_invoice ],
+        ],
+        action => [
+          t8('Delete'),
+          submit   => [ '#form', { action_delete => 1 } ],
+          confirm  => t8('Do you really want to delete this object?'),
+          disabled => !$form->{id}                                                                      ? t8('This record has not been saved yet.')
+                    : (   ($params{is_sales_ord} && !$::instance_conf->get_sales_order_show_delete)
+                       || ($params{is_pur_ord}   && !$::instance_conf->get_purchase_order_show_delete)) ? t8('Deleting this type of record has been disabled in the configuration.')
+                    :                                                                                     undef,
+        ],
+      ], # end of combobox "Save"
+
+      'separator',
+
+      combobox => [
+        action => [ t8('Workflow') ],
+        (action => [
+          t8('Sales Order'),
+          submit   => [ '#form', { action_sales_order => 1 } ],
+          disabled => !$form->{id} ? t8('This record has not been saved yet.') : undef,
+        ]) x !!$params{is_sales_quo},
+        (action => [
+          t8('Purchase Order'),
+          submit   => [ '#form', { action_sales_order => 1 } ],
+          disabled => !$form->{id} ? t8('This record has not been saved yet.') : undef,
+        ]) x !!$params{is_req_quo},
+        (action => [
+          t8('Delivery Order'),
+          submit   => [ '#form', { action_delivery_order => 1 } ],
+          disabled => !$form->{id} ? t8('This record has not been saved yet.') : undef,
+        ]) x ($params{is_sales_ord} || $params{is_pur_ord}),
+        (action => [
+          t8('Invoice'),
+          submit   => [ '#form', { action_invoice => 1 } ],
+          disabled => !$form->{id} ? t8('This record has not been saved yet.') : undef,
+        ]) x !!$allow_invoice,
+        action => [
+          t8('Quotation'),
+          submit   => [ '#form', { action_quotation => 1 } ],
+          disabled => !$form->{id} ? t8('This record has not been saved yet.') : undef,
+        ],
+        action => [
+          t8('Request for Quotation'),
+          submit   => [ '#form', { action_reqest_for_quotation => 1 } ],
+          disabled => !$form->{id} ? t8('This record has not been saved yet.') : undef,
+        ],
+      ], # end of combobox "Workflow"
+
+      combobox => [
+        action => [ t8('Export') ],
+        action => [
+          t8('Print'),
+          submit => [ '#form', { action_print => 1 } ],
+          checks => [ @req_trans_desc ],
+        ],
+        action => [
+          t8('E Mail'),
+          submit => [ '#form', { action_print => 1 } ],
+          checks => [ @req_trans_desc ],
+        ],
+      ], #end of combobox "Export"
+
+      combobox => [
+        action => [ t8('more') ],
+        action => [
+          t8('History'),
+          call     => [ 'set_history_window', $form->{id} * 1, 'id' ],
+          disabled => !$form->{id} ? t8('This record has not been saved yet.') : undef,
+        ],
+        action => [
+          t8('Follow-Up'),
+          call     => [ 'follow_up_window' ],
+          disabled => !$form->{id} ? t8('This record has not been saved yet.') : undef,
+        ],
+      ], # end of combobox "more"
+    );
   }
 }