]> wagnertech.de Git - kivitendo-erp.git/blobdiff - SL/Controller/Order.pm
Auftrags-Controller: Mini-Refactoring: js-save-Funktionen zusammengefasst
[kivitendo-erp.git] / SL / Controller / Order.pm
index ac619d5bdc3de6cf93a97c16c297c5c577d0d19a..8ee73f9c8fb0addeaf6e532719a3cd7026100a62 100644 (file)
@@ -10,13 +10,14 @@ use SL::SessionFile::Random;
 use SL::PriceSource;
 use SL::Webdav;
 use SL::File;
-
+use SL::Util qw(trim);
 use SL::DB::Order;
 use SL::DB::Default;
 use SL::DB::Unit;
 use SL::DB::Part;
 use SL::DB::Printer;
 use SL::DB::Language;
+use SL::DB::RecordLink;
 
 use SL::Helper::CreatePDF qw(:all);
 use SL::Helper::PrintOptions;
@@ -41,10 +42,10 @@ use Rose::Object::MakeMethods::Generic
 __PACKAGE__->run_before('_check_auth');
 
 __PACKAGE__->run_before('_recalc',
-                        only => [ qw(save save_and_delivery_order save_and_invoice print create_pdf send_email) ]);
+                        only => [ qw(save save_as_new save_and_delivery_order save_and_invoice print create_pdf send_email) ]);
 
 __PACKAGE__->run_before('_get_unalterable_data',
-                        only => [ qw(save save_and_delivery_order save_and_invoice print create_pdf send_email) ]);
+                        only => [ qw(save save_as_new save_and_delivery_order save_and_invoice print create_pdf send_email) ]);
 
 #
 # actions
@@ -60,9 +61,7 @@ sub action_add {
   $self->_pre_render();
   $self->render(
     'order/form',
-    title => $self->type eq _sales_order_type()    ? $::locale->text('Add Sales Order')
-           : $self->type eq _purchase_order_type() ? $::locale->text('Add Purchase Order')
-           : '',
+    title => $self->_get_title_for('add'),
     %{$self->{template_args}}
   );
 }
@@ -76,9 +75,7 @@ sub action_edit {
   $self->_pre_render();
   $self->render(
     'order/form',
-    title => $self->type eq _sales_order_type()    ? $::locale->text('Edit Sales Order')
-           : $self->type eq _purchase_order_type() ? $::locale->text('Edit Purchase Order')
-           : '',
+    title => $self->_get_title_for('edit'),
     %{$self->{template_args}}
   );
 }
@@ -94,7 +91,13 @@ sub action_delete {
     return $self->js->render();
   }
 
-  flash_later('info', $::locale->text('The order has been deleted'));
+  my $text = $self->type eq _sales_order_type()       ? $::locale->text('The order has been deleted')
+           : $self->type eq _purchase_order_type()    ? $::locale->text('The order has been deleted')
+           : $self->type eq _sales_quotation_type()   ? $::locale->text('The quotation has been deleted')
+           : $self->type eq _request_quotation_type() ? $::locale->text('The rfq has been deleted')
+           : '';
+  flash_later('info', $text);
+
   my @redirect_params = (
     action => 'add',
     type   => $self->type,
@@ -114,7 +117,13 @@ sub action_save {
     return $self->js->render();
   }
 
-  flash_later('info', $::locale->text('The order has been saved'));
+  my $text = $self->type eq _sales_order_type()       ? $::locale->text('The order has been saved')
+           : $self->type eq _purchase_order_type()    ? $::locale->text('The order has been saved')
+           : $self->type eq _sales_quotation_type()   ? $::locale->text('The quotation has been saved')
+           : $self->type eq _request_quotation_type() ? $::locale->text('The rfq has been saved')
+           : '';
+  flash_later('info', $text);
+
   my @redirect_params = (
     action => 'edit',
     type   => $self->type,
@@ -124,6 +133,41 @@ sub action_save {
   $self->redirect_to(@redirect_params);
 }
 
+# save the order as new document an open it for edit
+sub action_save_as_new {
+  my ($self) = @_;
+
+  if (!$self->order->id) {
+    $self->js->flash('error', t8('This object has not been saved yet.'));
+    return $self->js->render();
+  }
+
+  delete $::form->{$_} for qw(closed delivered converted_from_oe_id converted_from_orderitems_ids);
+
+  my $src_order = SL::DB::Order->new(id => $self->order->id)->load;
+
+  # Lets assign a new number if the user hasn't changed the previous one.
+  # If it has been changed manually then use it as-is.
+  if (trim($self->order->number) eq $src_order->number) {
+    $self->order->number('');
+  }
+
+  # Clear reqdate and transdate unless changed
+  if ($self->order->transdate == $src_order->transdate) {
+    $self->order->transdate(DateTime->today_local)
+  }
+  if ($self->order->reqdate == $src_order->reqdate) {
+    my $extra_days = $self->type eq _sales_quotation_type() ? $::instance_conf->get_reqdate_interval : 1;
+    $self->order->reqdate(DateTime->today_local->next_workday(extra_days => $extra_days));
+  }
+
+  # Update employee
+  $self->order->employee(SL::DB::Manager::Employee->current);
+
+  # save
+  $self->action_save();
+}
+
 # print the order
 #
 # This is called if "print" is pressed in the print dialog.
@@ -458,7 +502,13 @@ sub action_save_and_delivery_order {
     $self->js->flash('error', $_) foreach @{ $errors };
     return $self->js->render();
   }
-  flash_later('info', $::locale->text('The order has been saved'));
+
+  my $text = $self->type eq _sales_order_type()       ? $::locale->text('The order has been saved')
+           : $self->type eq _purchase_order_type()    ? $::locale->text('The order has been saved')
+           : $self->type eq _sales_quotation_type()   ? $::locale->text('The quotation has been saved')
+           : $self->type eq _request_quotation_type() ? $::locale->text('The rfq has been saved')
+           : '';
+  flash_later('info', $text);
 
   my @redirect_params = (
     controller => 'oe.pl',
@@ -480,7 +530,13 @@ sub action_save_and_invoice {
     $self->js->flash('error', $_) foreach @{ $errors };
     return $self->js->render();
   }
-  flash_later('info', $::locale->text('The order has been saved'));
+
+  my $text = $self->type eq _sales_order_type()       ? $::locale->text('The order has been saved')
+           : $self->type eq _purchase_order_type()    ? $::locale->text('The order has been saved')
+           : $self->type eq _sales_quotation_type()   ? $::locale->text('The quotation has been saved')
+           : $self->type eq _request_quotation_type() ? $::locale->text('The rfq has been saved')
+           : '';
+  flash_later('info', $text);
 
   my @redirect_params = (
     controller => 'oe.pl',
@@ -491,6 +547,16 @@ sub action_save_and_invoice {
   $self->redirect_to(@redirect_params);
 }
 
+# workflow from sales quotation to sales order
+sub action_sales_order {
+  $_[0]->_workflow_sales_or_purchase_order();
+}
+
+# workflow from rfq to purchase order
+sub action_purchase_order {
+  $_[0]->_workflow_sales_or_purchase_order();
+}
+
 # set form elements in respect to a changed customer or vendor
 #
 # This action is called on an change of the customer/vendor picker.
@@ -856,7 +922,7 @@ sub _js_redisplay_amounts_and_taxes {
 #
 
 sub init_valid_types {
-  [ _sales_order_type(), _purchase_order_type() ];
+  [ _sales_order_type(), _purchase_order_type(), _sales_quotation_type(), _request_quotation_type() ];
 }
 
 sub init_type {
@@ -872,8 +938,8 @@ sub init_type {
 sub init_cv {
   my ($self) = @_;
 
-  my $cv = $self->type eq _sales_order_type()    ? 'customer'
-         : $self->type eq _purchase_order_type() ? 'vendor'
+  my $cv = (any { $self->type eq $_ } (_sales_order_type(),    _sales_quotation_type()))   ? 'customer'
+         : (any { $self->type eq $_ } (_purchase_order_type(), _request_quotation_type())) ? 'vendor'
          : die "Not a valid type for order";
 
   return $cv;
@@ -1007,7 +1073,8 @@ sub _make_order {
   # order here solves this problem.
   my $order;
   $order   = SL::DB::Manager::Order->find_by(id => $::form->{id}) if $::form->{id};
-  $order ||= SL::DB::Order->new(orderitems => []);
+  $order ||= SL::DB::Order->new(orderitems => [],
+                                quotation  => (any { $self->type eq $_ } (_sales_quotation_type(), _request_quotation_type())));
 
   my $form_orderitems               = delete $::form->{order}->{orderitems};
   my $form_periodic_invoices_config = delete $::form->{order}->{periodic_invoices_config};
@@ -1058,9 +1125,9 @@ sub _make_item {
   $item ||= SL::DB::OrderItem->new(custom_variables => []);
 
   $item->assign_attributes(%$attr);
-  $item->longdescription($item->part->notes)   if $is_new && !defined $attr->{longdescription};
-  $item->project_id($record->globalproject_id) if $is_new && !defined $attr->{project_id};
-  $item->lastcost($item->part->lastcost)       if $is_new && !defined $attr->{lastcost_as_number};
+  $item->longdescription($item->part->notes)                     if $is_new && !defined $attr->{longdescription};
+  $item->project_id($record->globalproject_id)                   if $is_new && !defined $attr->{project_id};
+  $item->lastcost($record->is_sales ? $item->part->lastcost : 0) if $is_new && !defined $attr->{lastcost_as_number};
 
   return $item;
 }
@@ -1116,7 +1183,7 @@ sub _new_item {
   $new_attr{active_discount_source} = $discount_src;
   $new_attr{longdescription}        = $part->notes           if ! defined $attr->{longdescription};
   $new_attr{project_id}             = $record->globalproject_id;
-  $new_attr{lastcost}               = $part->lastcost;
+  $new_attr{lastcost}               = $record->is_sales ? $part->lastcost : 0;
 
   # add_custom_variables adds cvars to an orderitem with no cvars for saving, but
   # they cannot be retrieved via custom_variables until the order/orderitem is
@@ -1201,11 +1268,63 @@ sub _save {
   $db->with_transaction(sub {
     SL::DB::OrderItem->new(id => $_)->delete for @{$self->item_ids_to_delete};
     $self->order->save(cascade => 1);
+
+    # link records
+    if ($::form->{converted_from_oe_id}) {
+      SL::DB::Order->new(id => $::form->{converted_from_oe_id})->load->link_to_record($self->order);
+
+      if (scalar @{ $::form->{converted_from_orderitems_ids} || [] }) {
+        my $idx = 0;
+        foreach (@{ $self->order->items_sorted }) {
+          my $from_id = $::form->{converted_from_orderitems_ids}->[$idx];
+          next if !$from_id;
+          SL::DB::RecordLink->new(from_table => 'orderitems',
+                                  from_id    => $from_id,
+                                  to_table   => 'orderitems',
+                                  to_id      => $_->id
+          )->save;
+          $idx++;
+        }
+      }
+    }
+    1;
   }) || push(@{$errors}, $db->error);
 
   return $errors;
 }
 
+sub _workflow_sales_or_purchase_order {
+  my ($self) = @_;
+
+  my $destination_type = $::form->{type} eq _sales_quotation_type()   ? _sales_order_type()
+                       : $::form->{type} eq _request_quotation_type() ? _purchase_order_type()
+                       : '';
+
+  $self->order(SL::DB::Order->new_from($self->order, destination_type => $destination_type));
+  $self->{converted_from_oe_id} = delete $::form->{id};
+
+  # change form type
+  $::form->{type} = $destination_type;
+  $self->init_type;
+  $self->_check_auth;
+
+  $self->_recalc();
+  $self->_get_unalterable_data();
+  $self->_pre_render();
+
+  # trigger rendering values for second row/longdescription as hidden,
+  # because they are loaded only on demand. So we need to keep the values
+  # from the source.
+  $_->{render_second_row}      = 1 for @{ $self->order->items_sorted };
+  $_->{render_longdescription} = 1 for @{ $self->order->items_sorted };
+
+  $self->render(
+    'order/form',
+    title => $self->_get_title_for('edit'),
+    %{$self->{template_args}}
+  );
+}
+
 
 sub _pre_render {
   my ($self) = @_;
@@ -1226,6 +1345,7 @@ sub _pre_render {
   $self->{all_delivery_terms}       = SL::DB::Manager::DeliveryTerm->get_all_sorted();
   $self->{current_employee_id}      = SL::DB::Manager::Employee->current->id;
   $self->{periodic_invoices_status} = $self->_get_periodic_invoices_status($self->order->periodic_invoices_config);
+  $self->{order_probabilities}      = [ map { { title => ($_ * 10) . '%', id => $_ * 10 } } (0..10) ];
 
   my $print_form = Form->new('');
   $print_form->{type}      = $self->type;
@@ -1266,31 +1386,53 @@ sub _pre_render {
 sub _setup_edit_action_bar {
   my ($self, %params) = @_;
 
-  my $deletion_allowed = (($self->cv eq 'customer') && $::instance_conf->get_sales_order_show_delete)
-                      || (($self->cv eq 'vendor')   && $::instance_conf->get_purchase_order_show_delete);
+  my $deletion_allowed = (any { $self->type eq $_ } (_sales_quotation_type(), _request_quotation_type()))
+                      || (($self->type eq _sales_order_type())    && $::instance_conf->get_sales_order_show_delete)
+                      || (($self->type eq _purchase_order_type()) && $::instance_conf->get_purchase_order_show_delete);
 
   for my $bar ($::request->layout->get('actionbar')) {
     $bar->add(
       combobox => [
         action => [
           t8('Save'),
-          call      => [ 'kivi.Order.save', $::instance_conf->get_order_warn_duplicate_parts ],
+          call      => [ 'kivi.Order.save', 'save', $::instance_conf->get_order_warn_duplicate_parts ],
+          checks    => [ 'kivi.Order.check_save_active_periodic_invoices' ],
+        ],
+        action => [
+          t8('Save as new'),
+          call      => [ 'kivi.Order.save', 'save_as_new', $::instance_conf->get_order_warn_duplicate_parts ],
           checks    => [ 'kivi.Order.check_save_active_periodic_invoices' ],
-          accesskey => 'enter',
+          disabled  => !$self->order->id ? t8('This object has not been saved yet.') : undef,
         ],
         action => [
           t8('Save and Delivery Order'),
-          call      => [ 'kivi.Order.save_and_delivery_order', $::instance_conf->get_order_warn_duplicate_parts ],
+          call      => [ 'kivi.Order.save', 'save_and_delivery_order', $::instance_conf->get_order_warn_duplicate_parts ],
           checks    => [ 'kivi.Order.check_save_active_periodic_invoices' ],
+          only_if   => (any { $self->type eq $_ } (_sales_order_type(), _purchase_order_type()))
         ],
         action => [
           t8('Save and Invoice'),
-          call      => [ 'kivi.Order.save_and_invoice', $::instance_conf->get_order_warn_duplicate_parts ],
+          call      => [ 'kivi.Order.save', 'save_and_invoice', $::instance_conf->get_order_warn_duplicate_parts ],
           checks    => [ 'kivi.Order.check_save_active_periodic_invoices' ],
         ],
-
       ], # end of combobox "Save"
 
+      combobox => [
+        action => [
+          t8('Workflow'),
+        ],
+        action => [
+          t8('Sales Order'),
+          submit  => [ '#order_form', { action => "Order/sales_order" } ],
+          only_if => (any { $self->type eq $_ } (_sales_quotation_type())),
+        ],
+        action => [
+          t8('Purchase Order'),
+          submit  => [ '#order_form', { action => "Order/purchase_order" } ],
+          only_if   => (any { $self->type eq $_ } (_request_quotation_type())),
+        ],
+      ], # end of combobox "Workflow"
+
       combobox => [
         action => [
           t8('Export'),
@@ -1427,6 +1569,29 @@ sub _get_periodic_invoices_status {
   return $active ? t8('active') : t8('inactive');
 }
 
+sub _get_title_for {
+  my ($self, $action) = @_;
+
+  return '' if none { lc($action)} qw(add edit);
+
+  # for locales:
+  # $::locale->text("Add Sales Order");
+  # $::locale->text("Add Purchase Order");
+  # $::locale->text("Add Quotation");
+  # $::locale->text("Add Request for Quotation");
+  # $::locale->text("Edit Sales Order");
+  # $::locale->text("Edit Purchase Order");
+  # $::locale->text("Edit Quotation");
+  # $::locale->text("Edit Request for Quotation");
+
+  $action = ucfirst(lc($action));
+  return $self->type eq _sales_order_type()       ? $::locale->text("$action Sales Order")
+       : $self->type eq _purchase_order_type()    ? $::locale->text("$action Purchase Order")
+       : $self->type eq _sales_quotation_type()   ? $::locale->text("$action Quotation")
+       : $self->type eq _request_quotation_type() ? $::locale->text("$action Request for Quotation")
+       : '';
+}
+
 sub _sales_order_type {
   'sales_order';
 }
@@ -1435,6 +1600,14 @@ sub _purchase_order_type {
   'purchase_order';
 }
 
+sub _sales_quotation_type {
+  'sales_quotation';
+}
+
+sub _request_quotation_type {
+  'request_quotation';
+}
+
 1;
 
 __END__