Merge branch 'b-3.6.1' of ../kivitendo-erp_20220811
[kivitendo-erp.git] / SL / Controller / MassInvoiceCreatePrint.pm
index 7fc3388..626a663 100644 (file)
@@ -13,15 +13,17 @@ use SL::Controller::Helper::GetModels;
 use SL::DB::DeliveryOrder;
 use SL::DB::Order;
 use SL::DB::Printer;
+use SL::Helper::MassPrintCreatePDF qw(:all);
 use SL::Helper::CreatePDF qw(:all);
+use SL::Helper::File qw(store_pdf append_general_pdf_attachments doc_storage_enabled);
 use SL::Helper::Flash;
 use SL::Locale::String;
 use SL::SessionFile;
+use SL::ARAP;
 use SL::System::TaskServer;
-
 use Rose::Object::MakeMethods::Generic
 (
-  'scalar --get_set_init' => [ qw(invoice_models invoice_ids sales_delivery_order_models printers default_printer_id today) ],
+  'scalar --get_set_init' => [ qw(invoice_models invoice_ids sales_delivery_order_models printers default_printer_id today all_businesses) ],
 );
 
 __PACKAGE__->run_before('setup');
@@ -39,7 +41,7 @@ sub action_list_sales_delivery_orders {
 
   # if a filter is choosen, the filter info should be visible
   $self->make_filter_summary;
-  $self->sales_delivery_order_models->get;
+  $self->setup_list_sales_delivery_orders_action_bar(show_creation_buttons => $show, num_rows => scalar(@{ $self->sales_delivery_order_models->get }));
   $self->render('mass_invoice_create_print_from_do/list_sales_delivery_orders',
                 noshow  => $show,
                 title   => $::locale->text('Open sales delivery orders'));
@@ -56,27 +58,66 @@ sub action_create_invoices {
   }
 
   my $db = SL::DB::Invoice->new->db;
+  my $dbh = $db->dbh;
+  my @invoices;
+  my @already_closed_delivery_orders;
 
-  if (!$db->do_transaction(sub {
-    my @invoices;
+  if (!$db->with_transaction(sub {
     foreach my $id (@sales_delivery_order_ids) {
       my $delivery_order    = SL::DB::DeliveryOrder->new(id => $id)->load;
 
-      my $invoice = $delivery_order->convert_to_invoice() || die $db->error;
-      push @invoices, $invoice;
-    }
+      # Only process open delivery orders. In this list should only be open
+      # delivery orders, but if the user clicked browser back, a new creation
+      # of invoices for delivery orders which are closed now can be triggered.
+      # Prevent this.
+      if ($delivery_order->closed) {
+        push @already_closed_delivery_orders, $delivery_order;
+
+      } else {
+        my $invoice = $delivery_order->convert_to_invoice() || die $db->error;
 
-    my $key = sprintf('%d-%d', Time::HiRes::gettimeofday());
-    $::auth->set_session_value("MassInvoiceCreatePrint::ids-${key}" => [ map { $_->id } @invoices ]);
+        ARAP->close_orders_if_billed('dbh'     => $dbh,
+                                     'arap_id' => $invoice->id,
+                                     'table'   => 'ar',);
 
-    flash_later('info', t8('The invoices have been created. They\'re pre-selected below.'));
-    $self->redirect_to(action => 'list_invoices', ids => $key);
+        push @invoices, $invoice;
+      }
+    }
 
     1;
   })) {
     $::lxdebug->message(LXDebug::WARN(), "Error: " . $db->error);
     $::form->error($db->error);
   }
+
+  foreach my $invoice( @invoices ) {
+    # update shop status
+    my @linked_shop_orders = $invoice->linked_records(
+      from      => 'ShopOrder',
+      via       => [ 'DeliveryOrder', 'Order' ],
+    );
+    #if (scalar @linked_shop_orders[0][0] >= 1){
+      #do update
+    my $shop_order = $linked_shop_orders[0][0];
+    if ($shop_order){
+    require SL::Shop;
+      my $shop_config = SL::DB::Manager::Shop->get_first( query => [ id => $shop_order->shop_id ] );
+      my $shop = SL::Shop->new( config => $shop_config );
+      $shop->connector->set_orderstatus($shop_order->shop_trans_id, "completed");
+    }
+  }
+
+  my $key = sprintf('%d-%d', Time::HiRes::gettimeofday());
+  $::auth->set_session_value("MassInvoiceCreatePrint::ids-${key}" => [ map { $_->id } @invoices ]);
+
+  if (@already_closed_delivery_orders) {
+    my $dos_list = join ' ', map { $_->donumber } @already_closed_delivery_orders;
+    flash_later('error', t8('The following delivery orders could not be processed because they are already closed: #1', $dos_list));
+  }
+
+  flash_later('info', t8('The invoices have been created. They\'re pre-selected below.')) if @invoices;
+
+  $self->redirect_to(action => 'list_invoices', ids => $key);
 }
 
 sub action_list_invoices {
@@ -88,6 +129,11 @@ sub action_list_invoices {
   if ($::form->{ids}) {
     my $key = 'MassInvoiceCreatePrint::ids-' . $::form->{ids};
     $self->invoice_ids($::auth->get_session_value($key) || []);
+
+    # Prevent models->get to retrieve any invoices if session key is there
+    # but no ids are given.
+    $self->invoice_ids([0]) if !@{$self->invoice_ids};
+
     $self->invoice_models->add_additional_url_params(ids => $::form->{ids});
   }
 
@@ -95,6 +141,8 @@ sub action_list_invoices {
 
   $::form->{printer_id} ||= $self->default_printer_id;
 
+  $self->setup_list_invoices_action_bar(num_rows => scalar(@{ $self->invoice_models->get }));
+
   $self->render('mass_invoice_create_print_from_do/list_invoices',
                 title        => $::locale->text('Open invoice'),
                 noshow       => $show,
@@ -110,7 +158,7 @@ sub action_print {
     return $self->redirect_to(action => 'list_invoices');
   }
 
-  $self->download_or_print_documents(printer_id => $::form->{printer_id}, invoices => \@invoices);
+  $self->download_or_print_documents(printer_id => $::form->{printer_id}, invoices => \@invoices, bothsided => $::form->{bothsided});
 }
 
 sub action_create_print_all_start {
@@ -130,6 +178,7 @@ sub action_create_print_all_start {
     record_ids         => [ map { $_->id } @records[0..$num - 1] ],
     printer_id         => $::form->{printer_id},
     copy_printer_id    => $::form->{copy_printer_id},
+    bothsided          => ($::form->{bothsided}?1:0),
     transdate          => $::form->{transdate},
     status             => SL::BackgroundJob::MassRecordCreationAndPrinting->WAITING_FOR_EXECUTION(),
     num_created        => 0,
@@ -137,6 +186,7 @@ sub action_create_print_all_start {
     invoice_ids        => [ ],
     conversion_errors  => [ ],
     print_errors       => [ ],
+    session_id         => $::auth->get_session_id,
 
   )->update_next_run_at;
 
@@ -171,7 +221,7 @@ sub action_create_print_all_download {
   $sfile->fh->close;
 
   my $type      = 'Invoices';
-  my $file_name =  t8($type) . '-' . DateTime->today_local->strftime('%Y%m%d%H%M%S') . '.pdf';
+  my $file_name =  t8($type) . '-' . DateTime->now_local->strftime('%Y%m%d%H%M%S') . '.pdf';
   $file_name    =~ s{[^\w\.]+}{_}g;
 
   return $self->send_file(
@@ -186,6 +236,7 @@ sub action_create_print_all_download {
 #
 
 sub init_printers { SL::DB::Manager::Printer->get_all_sorted }
+#sub init_att      { require SL::Controller::Attachments; SL::Controller::Attachments->new() }
 sub init_invoice_ids { [] }
 sub init_today         { DateTime->today_local }
 
@@ -208,7 +259,7 @@ sub _init_sales_delivery_order_models {
       },
       customer     => t8('Customer'),
       employee     => t8('Employee'),
-      transdate    => t8('Date'),
+      transdate    => t8('Delivery Order Date'),
       donumber     => t8('Delivery Order Number'),
       ordnumber     => t8('Order Number'),
     },
@@ -256,6 +307,10 @@ sub init_default_printer_id {
   return $pr ? $pr->id : undef;
 }
 
+sub init_all_businesses {
+  return SL::DB::Manager::Business->get_all_sorted;
+}
+
 sub setup {
   my ($self) = @_;
   $::auth->assert('invoice_edit');
@@ -267,30 +322,6 @@ sub setup {
 # helpers
 #
 
-sub create_pdfs {
-  my ($self, %params) = @_;
-
-  my @pdf_file_names;
-  foreach my $invoice (@{ $params{invoices} }) {
-    my %create_params = (
-      template  => $self->find_template(name => 'invoice', printer_id => $params{printer_id}),
-      variables => Form->new(''),
-      return    => 'file_name',
-      variable_content_types => { longdescription => 'html',
-                                  partnotes       => 'html',
-                                  notes           => 'html',}
-    );
-
-    $create_params{variables}->{$_} = $params{variables}->{$_} for keys %{ $params{variables} };
-
-    $invoice->flatten_to_form($create_params{variables}, format_amounts => 1);
-    $create_params{variables}->prepare_for_printing;
-
-    push @pdf_file_names, $self->create_pdf(%create_params);
-  }
-
-  return @pdf_file_names;
-}
 
 sub download_or_print_documents {
   my ($self, %params) = @_;
@@ -299,21 +330,21 @@ sub download_or_print_documents {
 
   eval {
     my %pdf_params = (
-      invoices        => $params{invoices},
-      printer_id      => $params{printer_id},
+      documents       => $params{invoices},
       variables       => {
         type        => 'invoice',
         formname    => 'invoice',
         format      => 'pdf',
         media       => $params{printer_id} ? 'printer' : 'file',
+        printer_id  => $params{printer_id},
       });
 
     @pdf_file_names = $self->create_pdfs(%pdf_params);
-    my $merged_pdf  = $self->merge_pdfs(file_names => \@pdf_file_names);
+    my $merged_pdf  = $self->merge_pdfs(file_names => \@pdf_file_names, bothsided => $params{bothsided});
     unlink @pdf_file_names;
 
     if (!$params{printer_id}) {
-      my $file_name =  t8("Invoices") . '-' . DateTime->today_local->strftime('%Y%m%d%H%M%S') . '.pdf';
+      my $file_name =  t8("Invoices") . '-' . DateTime->now_local->strftime('%Y%m%d%H%M%S') . '.pdf';
       $file_name    =~ s{[^\w\.]+}{_}g;
 
       return $self->send_file(
@@ -324,12 +355,7 @@ sub download_or_print_documents {
     }
 
     my $printer = SL::DB::Printer->new(id => $params{printer_id})->load;
-    my $command = SL::Template::create(type => 'ShellCommand', form => Form->new(''))->parse($printer->printer_command);
-
-    open my $out, '|-', $command or die $!;
-    binmode $out;
-    print $out $merged_pdf;
-    close $out;
+    $printer->print_document(content => $merged_pdf);
 
     flash_later('info', t8('The documents have been sent to the printer \'#1\'.', $printer->printer_description));
     return $self->redirect_to(action => 'list_invoices', printer_id => $params{printer_id});
@@ -348,8 +374,8 @@ sub make_filter_summary {
 
   my @filters = (
     [ $filter->{customer}{"name:substr::ilike"}, t8('Customer') ],
-    [ $filter->{"transdate:date::ge"},           t8('Transdate') . " " . t8('From Date') ],
-    [ $filter->{"transdate:date::le"},           t8('Transdate') . " " . t8('To Date')   ],
+    [ $filter->{"transdate:date::ge"},           t8('Delivery Order Date') . " " . t8('From Date') ],
+    [ $filter->{"transdate:date::le"},           t8('Delivery Order Date') . " " . t8('To Date')   ],
   );
 
   for (@filters) {
@@ -358,6 +384,62 @@ sub make_filter_summary {
 
   $self->{filter_summary} = join ', ', @filter_strings;
 }
+
+sub setup_list_invoices_action_bar {
+  my ($self, %params) = @_;
+
+  for my $bar ($::request->layout->get('actionbar')) {
+    $bar->add(
+      action => [
+        t8('Update'),
+        submit    => [ '#search_form', { action => 'MassInvoiceCreatePrint/list_invoices' } ],
+        accesskey => 'enter',
+      ],
+      action => [
+        $::locale->text('Print'),
+        call     => [ 'kivi.MassInvoiceCreatePrint.showMassPrintOptionsOrDownloadDirectly' ],
+        disabled => !$params{num_rows} ? $::locale->text('The report doesn\'t contain entries.') : undef,
+      ],
+    );
+  }
+}
+
+sub setup_list_sales_delivery_orders_action_bar {
+  my ($self, %params) = @_;
+
+  for my $bar ($::request->layout->get('actionbar')) {
+    $bar->add(
+      action => [
+        $params{show_creation_buttons} ? t8('Update') : t8('Search'),
+        submit    => [ '#search_form', { action => 'MassInvoiceCreatePrint/list_sales_delivery_orders' } ],
+        accesskey => 'enter',
+      ],
+
+      combobox => [
+        action => [
+          t8('Invoices'),
+          tooltip => t8("Create and print invoices")
+        ],
+        action => [
+          t8("Create and print invoices for all selected delivery orders"),
+          submit    => [ 'form', { action => 'MassInvoiceCreatePrint/create_invoices' } ],
+          disabled  => !$params{num_rows} ? $::locale->text('The report doesn\'t contain entries.') : undef,
+          only_if   => $params{show_creation_buttons},
+          checks    => [ 'kivi.MassInvoiceCreatePrint.checkDeliveryOrderSelection' ],
+          only_once => 1,
+        ],
+
+        action => [
+          t8("Create and print invoices for all delivery orders matching the filter"),
+          call     => [ 'kivi.MassInvoiceCreatePrint.createPrintAllInitialize' ],
+          disabled => !$params{num_rows} ? $::locale->text('The report doesn\'t contain entries.') : undef,
+          only_if  => $params{show_creation_buttons},
+        ],
+      ],
+    );
+  }
+}
+
 1;
 
 __END__
@@ -483,7 +565,7 @@ supported: Customer and date from/to of the Delivery Order (database field trans
 
 =head1 TODO
 
-Should be more generalized. Right now just one conversion (delivery order to invoice) is supported.
+pShould be more generalized. Right now just one conversion (delivery order to invoice) is supported.
 Using BackgroundJobs to mass create / transfer stuff is the way to do it. The original idea
 was taken from one client project (mosu) with some extra (maybe not standard compliant) customized
 stuff (using cvars for extra filters and a very compressed Controller for linking (ODSalesOrder.pm)).