X-Git-Url: http://wagnertech.de/git?a=blobdiff_plain;f=SL%2FController%2FOrder.pm;h=211ff09b1d606ca40273755af6477720b7b3e9b4;hb=f7305c1d4f5c2e9d1a2f2d4e370b1e26a73a8842;hp=b51802f629835ba7f9852e650fe6e1b7d3f9c876;hpb=4dc48e117cfdb6e59c2d8b9d8087ef8a3bc245a4;p=kivitendo-erp.git diff --git a/SL/Controller/Order.pm b/SL/Controller/Order.pm index b51802f62..211ff09b1 100644 --- a/SL/Controller/Order.pm +++ b/SL/Controller/Order.pm @@ -13,10 +13,12 @@ use SL::File; use SL::MIME; use SL::Util qw(trim); use SL::YAML; +use SL::DB::History; use SL::DB::Order; use SL::DB::Default; use SL::DB::Unit; use SL::DB::Part; +use SL::DB::PartClassification; use SL::DB::PartsGroup; use SL::DB::Printer; use SL::DB::Language; @@ -43,7 +45,7 @@ use Sort::Naturally; use Rose::Object::MakeMethods::Generic ( scalar => [ qw(item_ids_to_delete is_custom_shipto_to_delete) ], - 'scalar --get_set_init' => [ qw(order valid_types type cv p multi_items_models all_price_factors search_cvpartnumber show_update_button) ], + 'scalar --get_set_init' => [ qw(order valid_types type cv p all_price_factors search_cvpartnumber show_update_button part_picker_classification_ids) ], ); @@ -69,7 +71,12 @@ sub action_add { $self->order->transdate(DateTime->now_local()); my $extra_days = $self->{type} eq 'sales_quotation' ? $::instance_conf->get_reqdate_interval : $self->{type} eq 'sales_order' ? $::instance_conf->get_delivery_date_interval : 1; - $self->order->reqdate(DateTime->today_local->next_workday(extra_days => $extra_days)) if !$self->order->reqdate; + + if ( ($self->{type} eq 'sales_order' && $::instance_conf->get_deliverydate_on) + || ($self->{type} eq 'sales_quotation' && $::instance_conf->get_reqdate_on) + && (!$self->order->reqdate)) { + $self->order->reqdate(DateTime->today_local->next_workday(extra_days => $extra_days)); + } $self->pre_render(); @@ -218,11 +225,17 @@ sub action_save_as_new { ? DateTime->today_local : $order->transdate; - # Set new reqdate unless changed + # Set new reqdate unless changed if it is enabled in client config if ($order->reqdate == $saved_order->reqdate) { my $extra_days = $self->{type} eq 'sales_quotation' ? $::instance_conf->get_reqdate_interval : $self->{type} eq 'sales_order' ? $::instance_conf->get_delivery_date_interval : 1; - $new_attrs{reqdate} = DateTime->today_local->next_workday(extra_days => $extra_days); + + if ( ($self->{type} eq 'sales_order' && !$::instance_conf->get_deliverydate_on) + || ($self->{type} eq 'sales_quotation' && !$::instance_conf->get_reqdate_on)) { + $new_attrs{reqdate} = ''; + } else { + $new_attrs{reqdate} = DateTime->today_local->next_workday(extra_days => $extra_days); + } } else { $new_attrs{reqdate} = $order->reqdate; } @@ -314,38 +327,16 @@ sub action_print { $self->js->flash('info', t8('The PDF has been printed')); } - # copy file to webdav folder - if ($self->order->number && $::instance_conf->get_webdav_documents) { - my $webdav = SL::Webdav->new( - type => $self->type, - number => $self->order->number, - ); - my $webdav_file = SL::Webdav::File->new( - webdav => $webdav, - filename => $pdf_filename, - ); - eval { - $webdav_file->store(data => \$pdf); - 1; - } or do { - $self->js->flash('error', t8('Storing PDF to webdav folder failed: #1', $@)); - } + my @warnings = store_pdf_to_webdav_and_filemanagement($self->order, $pdf, $pdf_filename); + if (scalar @warnings) { + $self->js->flash('warning', $_) for @warnings; } - if ($self->order->number && $::instance_conf->get_doc_storage) { - eval { - SL::File->save(object_id => $self->order->id, - object_type => $self->type, - mime_type => 'application/pdf', - source => 'created', - file_type => 'document', - file_name => $pdf_filename, - file_contents => $pdf); - 1; - } or do { - $self->js->flash('error', t8('Storing PDF in storage backend failed: #1', $@)); - } - } - $self->js->render; + + $self->save_history('PRINTED'); + + $self->js + ->run('kivi.ActionBar.setEnabled', '#save_and_email_action') + ->render; } # open the email dialog @@ -441,6 +432,11 @@ sub action_send_email { return $self->js->flash('error', t8('Conversion to PDF failed: #1', $errors[0]))->render($self); } + my @warnings = store_pdf_to_webdav_and_filemanagement($self->order, $pdf, $::form->{attachment_filename}); + if (scalar @warnings) { + flash_later('warning', $_) for @warnings; + } + my $sfile = SL::SessionFile::Random->new(mode => "w"); $sfile->fh->print($pdf); $sfile->fh->close; @@ -465,6 +461,8 @@ sub action_send_email { $self->order->update_attributes(intnotes => $intnotes); + $self->save_history('MAILED'); + flash_later('info', t8('The email has been sent.')); my @redirect_params = ( @@ -598,6 +596,16 @@ sub action_save_and_invoice { ); } +# workflow from sales order to sales quotation +sub action_sales_quotation { + $_[0]->workflow_sales_or_request_for_quotation(); +} + +# workflow from sales order to sales quotation +sub action_request_for_quotation { + $_[0]->workflow_sales_or_request_for_quotation(); +} + # workflow from sales quotation to sales order sub action_sales_order { $_[0]->workflow_sales_or_purchase_order(); @@ -791,38 +799,11 @@ sub action_add_item { $self->js->render(); } -# open the dialog for entering multiple items at once -sub action_show_multi_items_dialog { - $_[0]->render('order/tabs/_multi_items_dialog', { layout => 0 }, - all_partsgroups => SL::DB::Manager::PartsGroup->get_all); -} - -# update the filter results in the multi item dialog -sub action_multi_items_update_result { - my $max_count = 100; - - $::form->{multi_items}->{filter}->{obsolete} = 0; - - my $count = $_[0]->multi_items_models->count; - - if ($count == 0) { - my $text = SL::Presenter::EscapedText->new(text => $::locale->text('No results.')); - $_[0]->render($text, { layout => 0 }); - } elsif ($count > $max_count) { - my $text = SL::Presenter::EscapedText->new(text => $::locale->text('Too many results (#1 from #2).', $count, $max_count)); - $_[0]->render($text, { layout => 0 }); - } else { - my $multi_items = $_[0]->multi_items_models->get; - $_[0]->render('order/tabs/_multi_items_result', { layout => 0 }, - multi_items => $multi_items); - } -} - # add item rows for multiple items at once sub action_add_multi_items { my ($self) = @_; - my @form_attr = grep { $_->{qty_as_number} } @{ $::form->{add_multi_items} }; + my @form_attr = grep { $_->{qty_as_number} } @{ $::form->{add_items} }; return $self->js->render() unless scalar @form_attr; my @items; @@ -867,7 +848,7 @@ sub action_add_multi_items { } $self->js - ->run('kivi.Order.close_multi_items_dialog') + ->run('kivi.Part.close_picker_dialogs') ->run('kivi.Order.init_row_handlers') ->run('kivi.Order.renumber_positions') ->focus('#add_item_parts_id_name'); @@ -1183,28 +1164,17 @@ sub init_order { $_[0]->make_order; } -# model used to filter/display the parts in the multi-items dialog -sub init_multi_items_models { - SL::Controller::Helper::GetModels->new( - controller => $_[0], - model => 'Part', - with_objects => [ qw(unit_obj) ], - disable_plugin => 'paginated', - source => $::form->{multi_items}, - sorted => { - _default => { - by => 'partnumber', - dir => 1, - }, - partnumber => t8('Partnumber'), - description => t8('Description')} - ); -} - sub init_all_price_factors { SL::DB::Manager::PriceFactor->get_all; } +sub init_part_picker_classification_ids { + my ($self) = @_; + my $attribute = 'used_for_' . ($self->type =~ m{sales} ? 'sale' : 'purchase'); + + return [ map { $_->id } @{ SL::DB::Manager::PartClassification->get_all(where => [ $attribute => 1 ]) } ]; +} + sub check_auth { my ($self) = @_; @@ -1572,6 +1542,8 @@ sub delete { my $spool = $::lx_office_conf{paths}->{spool}; unlink map { "$spool/$_" } @spoolfiles if $spool; + $self->save_history('DELETED'); + 1; }) || push(@{$errors}, $db->error); @@ -1619,12 +1591,58 @@ sub save { } } } + + $self->save_history('SAVED'); + 1; }) || push(@{$errors}, $db->error); return $errors; } +sub workflow_sales_or_request_for_quotation { + my ($self) = @_; + + # always save + my $errors = $self->save(); + + if (scalar @{ $errors }) { + $self->js->flash('error', $_) for @{ $errors }; + return $self->js->render(); + } + + my $destination_type = $::form->{type} eq sales_order_type() ? sales_quotation_type() : request_quotation_type(); + + $self->order(SL::DB::Order->new_from($self->order, destination_type => $destination_type)); + $self->{converted_from_oe_id} = delete $::form->{id}; + + # set item ids to new fake id, to identify them as new items + foreach my $item (@{$self->order->items_sorted}) { + $item->{new_fake_id} = join('_', 'new', Time::HiRes::gettimeofday(), int rand 1000000000000); + } + + # change form type + $::form->{type} = $destination_type; + $self->type($self->init_type); + $self->cv ($self->init_cv); + $self->check_auth; + + $self->recalc(); + $self->get_unalterable_data(); + $self->pre_render(); + + # trigger rendering values for second row 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 }; + + $self->render( + 'order/form', + title => $self->get_title_for('edit'), + %{$self->{template_args}} + ); +} + sub workflow_sales_or_purchase_order { my ($self) = @_; @@ -1750,7 +1768,7 @@ sub pre_render { $self->get_item_cvpartnumber($_) for @{$self->order->items_sorted}; $::request->{layout}->use_javascript("${_}.js") for qw(kivi.SalesPurchase kivi.Order kivi.File ckeditor/ckeditor ckeditor/adapters/jquery - edit_periodic_invoices_config calculate_qty kivi.Validator follow_up); + edit_periodic_invoices_config calculate_qty kivi.Validator follow_up show_history); $self->setup_edit_action_bar; } @@ -1783,6 +1801,16 @@ sub setup_edit_action_bar { action => [ t8('Workflow'), ], + action => [ + t8('Save and Quotation'), + submit => [ '#order_form', { action => "Order/sales_quotation" } ], + only_if => (any { $self->type eq $_ } (sales_order_type())), + ], + action => [ + t8('Save and RFQ'), + submit => [ '#order_form', { action => "Order/request_for_quotation" } ], + only_if => (any { $self->type eq $_ } (purchase_order_type())), + ], action => [ t8('Save and Sales Order'), submit => [ '#order_form', { action => "Order/sales_order" } ], @@ -1824,6 +1852,7 @@ sub setup_edit_action_bar { ], action => [ t8('Save and E-mail'), + id => 'save_and_email_action', call => [ 'kivi.Order.save', 'save_and_show_email_dialog', $::instance_conf->get_order_warn_duplicate_parts ], disabled => !$self->order->id ? t8('This object has not been saved yet.') : undef, ], @@ -1853,6 +1882,11 @@ sub setup_edit_action_bar { disabled => !$self->order->id ? t8('This object has not been saved yet.') : undef, only_if => $::auth->assert('productivity', 1), ], + action => [ + t8('History'), + call => [ 'set_history_window', $self->order->id, 'id' ], + disabled => !$self->order->id ? t8('This record has not been saved yet.') : undef, + ], ], # end of combobox "more" ); } @@ -2080,6 +2114,61 @@ sub save_and_redirect_to { $self->redirect_to(%params, id => $self->order->id); } +sub save_history { + my ($self, $addition) = @_; + + my $number_type = $self->order->type =~ m{order} ? 'ordnumber' : 'quonumber'; + my $snumbers = $number_type . '_' . $self->order->$number_type; + + SL::DB::History->new( + trans_id => $self->order->id, + employee_id => SL::DB::Manager::Employee->current->id, + what_done => $self->order->type, + snumbers => $snumbers, + addition => $addition, + )->save; +} + +sub store_pdf_to_webdav_and_filemanagement { + my($order, $content, $filename) = @_; + + my @errors; + + # copy file to webdav folder + if ($order->number && $::instance_conf->get_webdav_documents) { + my $webdav = SL::Webdav->new( + type => $order->type, + number => $order->number, + ); + my $webdav_file = SL::Webdav::File->new( + webdav => $webdav, + filename => $filename, + ); + eval { + $webdav_file->store(data => \$content); + 1; + } or do { + push @errors, t8('Storing PDF to webdav folder failed: #1', $@); + }; + } + if ($order->id && $::instance_conf->get_doc_storage) { + eval { + SL::File->save(object_id => $order->id, + object_type => $order->type, + mime_type => 'application/pdf', + source => 'created', + file_type => 'document', + file_name => $filename, + file_contents => $content); + 1; + } or do { + push @errors, t8('Storing PDF in storage backend failed: #1', $@); + }; + } + + return @errors; +} + 1; __END__ @@ -2175,14 +2264,6 @@ One row for already entered items Displaying tax information -=item * C