Dunning: Email und Zahlungsbedingung in Mahnlaufbericht aufgenommen
[kivitendo-erp.git] / SL / DN.pm
index 46d3c8e..76ee1b8 100644 (file)
--- a/SL/DN.pm
+++ b/SL/DN.pm
@@ -54,6 +54,7 @@ use SL::DB;
 use SL::Webdav;
 
 use File::Copy;
+use File::Slurp qw(read_file);
 
 use strict;
 
@@ -317,11 +318,30 @@ sub save_dunning {
   my ($self, $myconfig, $form, $rows) = @_;
   $main::lxdebug->enter_sub();
 
-  my $rc = SL::DB->client->with_transaction(\&_save_dunning, $self, $myconfig, $form, $rows);
+  $form->{DUNNING_PDFS_STORAGE} = [];
+
+  # Catch any error, either exception or a call to form->error
+  # and return it to the calling function.
+  my ($error, $rc);
+  eval {
+    local $form->{__ERROR_HANDLER} = sub { die @_ };
+    $rc = SL::DB->client->with_transaction(\&_save_dunning, $self, $myconfig, $form, $rows);
+    1;
+  } or do {
+    $error = $@;
+  };
 
-  if (!$rc) {
-    die SL::DB->client->error
+  # Save PDFs in filemanagement and webdav after transation succeeded,
+  # because otherwise files in the storage may exists if the transaction
+  # failed. Ignore all errros.
+  # Todo: Maybe catch errros and display them as warnings or non fatal errors in the status.
+  if (!$error && $form->{DUNNING_PDFS_STORAGE} && scalar @{ $form->{DUNNING_PDFS_STORAGE} }) {
+    _store_pdf_to_webdav_and_filemanagement($_->{dunning_id}, $_->{path}, $_->{name}) for @{ $form->{DUNNING_PDFS_STORAGE} };
   }
+
+  $error       = 'unknown errror' if !$error && !$rc;
+  $rc->{error} = $error           if $error;
+
   $::lxdebug->leave_sub;
 
   return $rc;
@@ -403,7 +423,7 @@ sub _save_dunning {
   }
   # die this transaction, because for this customer only credit notes are
   # selected ...
-  return unless $customer_id;
+  die "only credit notes are selected for this customer\n" unless $customer_id;
 
   $h_update_ar->finish();
   $h_insert_dunning->finish();
@@ -425,7 +445,7 @@ sub _save_dunning {
     $self->send_email($myconfig, $form, $dunning_id, $dbh);
   }
 
-  return 1;
+  return ({dunning_id => $dunning_id, print_original_invoice => $print_invoice, send_email => $send_email});
 }
 
 sub send_email {
@@ -467,13 +487,17 @@ sub send_email {
     } or die $main::locale->text('No email for user with login #1 defined.', $ref->{invoice_employee_login});
   }
 
+  my $html_template = SL::Template::create(type => 'HTML',      form => $form, myconfig => $myconfig);
+  $html_template->set_tag_style('<%', '%>');
+
   my $template     = SL::Template::create(type => 'PlainText', form => $form, myconfig => $myconfig);
   my $mail         = Mailer->new();
   $mail->{bcc}     = $form->get_bcc_defaults($myconfig, $form->{bcc});
   $mail->{from}    = $from;
   $mail->{to}      = $ref->{recipient};
   $mail->{subject} = $template->parse_block($ref->{email_subject});
-  $mail->{message} = $template->parse_block($ref->{email_body});
+  $mail->{message} = $html_template->parse_block($ref->{email_body});
+  $mail->{content_type} = 'text/html';
   my $sign_backup  = $::myconfig{signature};
   $::myconfig{signature} = $sign if $sign;
   $mail->{message} .= $form->create_email_signature();
@@ -485,6 +509,11 @@ sub send_email {
     $mail->{attachments} = $form->{DUNNING_PDFS_EMAIL};
   }
 
+  $query  = qq|SELECT id FROM dunning WHERE dunning_id = ?|;
+  my @ids = selectall_array_query($form, $dbh, $query, $dunning_id);
+  $mail->{record_id}   = \@ids;
+  $mail->{record_type} = 'dunning';
+
   $mail->send();
 
   $main::lxdebug->leave_sub();
@@ -628,8 +657,9 @@ sub get_invoices {
          ct.name AS customername, a.customer_id, a.duedate,
          a.amount - a.paid AS open_amount,
          a.direct_debit,
+         pt.description as payment_term,
          dep.description as departmentname,
-
+         ct.invoice_mail AS cv_email,
          cfg.dunning_description, cfg.dunning_level,
 
          d.transdate AS dunning_date, d.duedate AS dunning_duedate,
@@ -647,6 +677,7 @@ sub get_invoices {
 
        LEFT JOIN customer ct ON (a.customer_id = ct.id)
        LEFT JOIN department dep ON (a.department_id = dep.id)
+       LEFT JOIN payment_terms pt ON (a.payment_id = pt.id)
        LEFT JOIN dunning_config cfg ON (a.dunning_config_id = cfg.id)
        LEFT JOIN dunning_config nextcfg ON
          (nextcfg.id =
@@ -727,6 +758,11 @@ sub get_dunning {
     push(@values, like($form->{$key}));
   }
 
+  if ($form->{dunning_id}) {
+    $where .= qq| AND da.dunning_id = ?|;
+    push(@values, conv_i($form->{dunning_id}));
+  }
+
   if ($form->{dunning_level}) {
     $where .= qq| AND a.dunning_config_id = ?|;
     push(@values, conv_i($form->{dunning_level}));
@@ -770,25 +806,27 @@ sub get_dunning {
   }
 
   my %sort_columns = (
-    'dunning_description' => [ qw(dn.dunning_description customername invnumber) ],
-    'customername'        => [ qw(customername invnumber) ],
+    'dunning_description' => [ qw(dn.dunning_description da.dunning_id customername invnumber) ],
+    'customername'        => [ qw(customername da.dunning_id invnumber) ],
     'invnumber'           => [ qw(a.invnumber) ],
     'transdate'           => [ qw(a.transdate a.invnumber) ],
     'duedate'             => [ qw(a.duedate a.invnumber) ],
-    'dunning_date'        => [ qw(dunning_date a.invnumber) ],
-    'dunning_duedate'     => [ qw(dunning_duedate a.invnumber) ],
+    'dunning_date'        => [ qw(dunning_date da.dunning_id a.invnumber) ],
+    'dunning_duedate'     => [ qw(dunning_duedate da.dunning_id a.invnumber) ],
+    'dunning_id'          => [ qw(dunning_id a.invnumber) ],
     'salesman'            => [ qw(salesman) ],
     );
 
   my $sortdir   = !defined $form->{sortdir}    ? 'ASC'         : $form->{sortdir} ? 'ASC' : 'DESC';
   my $sortkey   = $sort_columns{$form->{sort}} ? $form->{sort} : 'customername';
-  my $sortorder = join ', ', map { "$_ $sortdir" } (@{ $sort_columns{$sortkey} }, 'da.dunning_id');
+  my $sortorder = join ', ', map { "$_ $sortdir" } @{ $sort_columns{$sortkey} };
 
   my $query =
     qq|SELECT a.id, a.ordnumber, a.invoice, a.transdate, a.invnumber, a.amount, a.language_id,
          ct.name AS customername, ct.id AS customer_id, a.duedate, da.fee,
          da.interest, dn.dunning_description, dn.dunning_level, da.transdate AS dunning_date,
          da.duedate AS dunning_duedate, da.dunning_id, da.dunning_config_id,
+         da.id AS dunning_table_id,
          e2.name AS salesman
        FROM ar a
        JOIN customer ct ON (a.customer_id = ct.id)
@@ -810,7 +848,7 @@ sub melt_pdfs {
 
   $main::lxdebug->enter_sub();
 
-  my ($self, $myconfig, $form, $copies) = @_;
+  my ($self, $myconfig, $form, $copies, %params) = @_;
 
   # Don't allow access outside of $spool.
   map { $_ =~ s|.*/||; } @{ $form->{DUNNING_PDFS} };
@@ -826,23 +864,30 @@ sub melt_pdfs {
   my $in = IO::File->new($::lx_office_conf{applications}->{ghostscript} . " -dBATCH -dNOPAUSE -q -sDEVICE=pdfwrite -sOutputFile=- $inputfiles |");
   $form->error($main::locale->text('Could not spawn ghostscript.')) unless $in;
 
-  if ($form->{media} eq 'printer') {
-    $form->get_printer_code($myconfig);
-    my $out;
-    if ($form->{printer_command}) {
-      $out = IO::File->new("| $form->{printer_command}");
-    }
+  my $dunning_filename    = $form->get_formname_translation('dunning');
+  my $attachment_filename = "${dunning_filename}_${dunning_id}.pdf";
+  my $content;
+  if ($params{return_content}) {
+    $content = read_file($in);
 
-    $::locale->with_raw_io($out, sub { $out->print($_) while <$in> });
+  } else {
+    if ($form->{media} eq 'printer') {
+      $form->get_printer_code($myconfig);
+      my $out;
+      if ($form->{printer_command}) {
+        $out = IO::File->new("| $form->{printer_command}");
+      }
 
-    $form->error($main::locale->text('Could not spawn the printer command.')) unless $out;
+      $form->error($main::locale->text('Could not spawn the printer command.')) unless $out;
 
-  } else {
-    my $dunning_filename = $form->get_formname_translation('dunning');
-    print qq|Content-Type: Application/PDF\n| .
-          qq|Content-Disposition: attachment; filename="${dunning_filename}_${dunning_id}.pdf"\n\n|;
+      $::locale->with_raw_io($out, sub { $out->print($_) while <$in> });
+
+    } else {
+      print qq|Content-Type: Application/PDF\n| .
+            qq|Content-Disposition: attachment; filename=$attachment_filename\n\n|;
 
-    $::locale->with_raw_io(\*STDOUT, sub { print while <$in> });
+      $::locale->with_raw_io(\*STDOUT, sub { print while <$in> });
+    }
   }
 
   $in->close();
@@ -850,6 +895,7 @@ sub melt_pdfs {
   map { unlink("$spool/$_") } @{ $form->{DUNNING_PDFS} };
 
   $main::lxdebug->leave_sub();
+  return ($attachment_filename, $content) if $params{return_content};
 }
 
 sub print_dunning {
@@ -923,7 +969,7 @@ sub print_dunning {
          c.country,           c.department_1, c.department_2, c.email,     c.customernumber,
          c.greeting,          c.contact,      c.phone,        c.fax,       c.homepage,
          c.email,             c.taxincluded,  c.business_id,  c.taxnumber, c.iban,
-         c.ustid,
+         c.ustid,             c.currency_id,  curr.name as currency,
          ar.id AS invoice_id,
          co.*
        FROM dunning d
@@ -931,6 +977,7 @@ sub print_dunning {
        LEFT JOIN customer c  ON (ar.customer_id = c.id)
        LEFT JOIN contacts co ON (ar.cp_id = co.cp_id)
        LEFT JOIN employee e  ON (ar.salesman_id = e.id)
+       LEFT JOIN currencies curr ON (c.currency_id = curr.id)
        WHERE (d.dunning_id = ?)
        LIMIT 1|;
   my $ref = selectfirst_hashref_query($form, $dbh, $query, $dunning_id);
@@ -998,10 +1045,6 @@ sub print_dunning {
 
   delete $form->{tmpfile};
 
-  push @{ $form->{DUNNING_PDFS} }, $filename;
-  push @{ $form->{DUNNING_PDFS_EMAIL} }, { 'path' => "${spool}/$filename",
-                                           'name'     => $form->get_formname_translation('dunning') . "_${dunning_id}.pdf" };
-
   my $employee_id = ($::instance_conf->get_dunning_creator eq 'invoice_employee') ?
                       $form->{employee_id}                                        :
                       SL::DB::Manager::Employee->current->id;
@@ -1019,29 +1062,12 @@ sub print_dunning {
   # this generates the file in the spool directory
   $form->parse_template($myconfig);
 
-  # save dunning pdf in filemanagement/webdav
-  if ($::instance_conf->get_doc_storage) {
-    SL::File->save(
-      object_id   => $dunning_id,
-      object_type => 'dunning',
-      mime_type   => 'application/pdf',
-      source      => 'created',
-      file_type   => 'document',
-      file_name   => $form->{attachment_filename},
-      file_path   => "${spool}/$filename",
-    );
-  }
-  if ($::instance_conf->get_webdav_documents) {
-    my $webdav = SL::Webdav->new(
-      type     => 'dunning',
-      number   => $dunning_id,
-    );
-    my $webdav_file = SL::Webdav::File->new(
-      webdav   => $webdav,
-      filename => $form->{attachment_filename},
-    );
-    $webdav_file->store(file => "${spool}/$filename");
-  }
+  push @{ $form->{DUNNING_PDFS} }        , $filename;
+  push @{ $form->{DUNNING_PDFS_EMAIL} }  , { 'path'       => "${spool}/$filename",
+                                             'name'       => $form->get_formname_translation('dunning') . "_${dunning_id}.pdf" };
+  push @{ $form->{DUNNING_PDFS_STORAGE} }, { 'dunning_id' => $dunning_id,
+                                             'path'       => "${spool}/$filename",
+                                             'name'       => $form->get_formname_translation('dunning') . "_${dunning_id}.pdf" };
 
   $main::lxdebug->leave_sub();
 }
@@ -1123,7 +1149,7 @@ sub print_invoice_for_fees {
   $self->set_customer_cvars($myconfig, $form);
   $self->set_template_options($myconfig, $form);
 
-  my $filename = Common::unique_id() . "dunning_invoice_${dunning_id}.pdf";
+  my $filename = Common::unique_id() . "dunning_invoice_" . $form->{invnumber} . ".pdf";
 
   my $spool             = $::lx_office_conf{paths}->{spool};
   $form->{OUT}          = "$spool/$filename";
@@ -1132,7 +1158,7 @@ sub print_invoice_for_fees {
 
   map { delete $form->{$_} } grep /^[a-z_]+_\d+$/, keys %{ $form };
 
-  my $attachment_filename      = $form->get_formname_translation('dunning_invoice') . "_${dunning_id}.pdf";
+  my $attachment_filename      = $form->get_formname_translation('dunning_invoice') . "_" . $form->{invnumber} . ".pdf";
   $form->{attachment_filename} = $attachment_filename;
   $form->{attachment_type}     = "dunning";
   $form->{attachment_id}       = $invoice_id;
@@ -1140,33 +1166,12 @@ sub print_invoice_for_fees {
 
   restore_form($saved_form);
 
-  push @{ $form->{DUNNING_PDFS} }, $filename;
-  push @{ $form->{DUNNING_PDFS_EMAIL} }, { 'path' => "${spool}/$filename",
-                                           'name' => $attachment_filename };
-
-  # save dunning fee pdf in filemanagement/webdav
-  if ($::instance_conf->get_doc_storage) {
-    SL::File->save(
-      object_id   => $dunning_id,
-      object_type => 'dunning',
-      mime_type   => 'application/pdf',
-      source      => 'created',
-      file_type   => 'document',
-      file_name   => $attachment_filename,
-      file_path   => "${spool}/$filename",
-    );
-  }
-  if ($::instance_conf->get_webdav_documents) {
-    my $webdav = SL::Webdav->new(
-      type     => 'dunning',
-      number   => $dunning_id,
-    );
-    my $webdav_file = SL::Webdav::File->new(
-      webdav   => $webdav,
-      filename => $attachment_filename,
-    );
-    $webdav_file->store(file => "${spool}/$filename");
-  }
+  push @{ $form->{DUNNING_PDFS} },         $filename;
+  push @{ $form->{DUNNING_PDFS_EMAIL} },   { 'path'       => "${spool}/$filename",
+                                             'name'       => $attachment_filename };
+  push @{ $form->{DUNNING_PDFS_STORAGE} }, { 'dunning_id' => $dunning_id,
+                                             'path'       => "${spool}/$filename",
+                                             'name'       => $attachment_filename };
 
   $main::lxdebug->leave_sub();
 }
@@ -1219,6 +1224,7 @@ sub print_original_invoice {
                      longdescription => 'html',
                      partnotes       => 'html',
                      notes           => 'html',
+                     $print_form->get_variable_content_types_for_cvars,
                    },
   );
 
@@ -1234,36 +1240,56 @@ sub print_original_invoice {
 
   my $attachment_filename    = $form->get_formname_translation('invoice') . "_" . $invoice->invnumber . ".pdf";
 
-  push @{ $form->{DUNNING_PDFS} }, $file_name;
-  push @{ $form->{DUNNING_PDFS_EMAIL} }, { 'path' => "${spool}/$file_name",
-                                           'name' => $attachment_filename };
+  push @{ $form->{DUNNING_PDFS} },         $file_name;
+  push @{ $form->{DUNNING_PDFS_EMAIL} },   { 'path'       => "${spool}/$file_name",
+                                             'name'       => $attachment_filename };
+  push @{ $form->{DUNNING_PDFS_STORAGE} }, { 'dunning_id' => $dunning_id,
+                                             'path'       => "${spool}/$file_name",
+                                             'name'       => $attachment_filename };
 
   $form->{recipient_locale}  = $saved_reicpient_locale;
+}
+
+sub _store_pdf_to_webdav_and_filemanagement {
+  my ($dunning_id, $path, $name) =@_;
+
+  my @errors;
 
-  # save original invoice pdf in filemanagement/webdav for dunning
   if ($::instance_conf->get_doc_storage) {
-    SL::File->save(
-      object_id   => $dunning_id,
-      object_type => 'dunning',
-      mime_type   => 'application/pdf',
-      source      => 'created',
-      file_type   => 'document',
-      file_name   => $attachment_filename,
-      file_path   => "${spool}/$file_name",
-    );
+    eval {
+      SL::File->save(
+        object_id   => $dunning_id,
+        object_type => 'dunning',
+        mime_type   => 'application/pdf',
+        source      => 'created',
+        file_type   => 'document',
+        file_name   => $name,
+        file_path   => $path,
+      );
+      1;
+    } or do {
+      push @errors, $::locale->text('Storing PDF in storage backend failed: #1', $@);
+    };
   }
+
   if ($::instance_conf->get_webdav_documents) {
-    my $webdav = SL::Webdav->new(
-      type     => 'dunning',
-      number   => $dunning_id,
-    );
-    my $webdav_file = SL::Webdav::File->new(
-      webdav   => $webdav,
-      filename => $attachment_filename,
-    );
-    $webdav_file->store(file => "${spool}/$file_name");
+    eval {
+      my $webdav = SL::Webdav->new(
+        type     => 'dunning',
+        number   => $dunning_id,
+      );
+      my $webdav_file = SL::Webdav::File->new(
+        webdav   => $webdav,
+        filename => $name,
+      );
+      $webdav_file->store(file => $path);
+    } or do {
+      push @errors, $::locale->text('Storing PDF to webdav folder failed: #1', $@);
+    };
   }
 
+  return @errors;
 }
 
+
 1;