Dateimanagement: Alle Anhänge per E-Mail versendbar machen
authorMartin Helmling martin.helmling@octosoft.eu <martin.helmling@octosoft.eu>
Mon, 16 Jan 2017 16:28:31 +0000 (17:28 +0100)
committerMartin Helmling martin.helmling@octosoft.eu <martin.helmling@octosoft.eu>
Wed, 1 Feb 2017 07:52:39 +0000 (08:52 +0100)
Es können an eine E-Mail alle Anhänge eines Dokumentes,
sowie die Anhänge am Kunden/Liefranten sowie die Anhänge an Artikeln
mitgesendet werden.

Falls ein Dokument bereits existiert muss es nicht noch neu erzeugt werden.

Als MIME Types werden die bereits in der Datenbank abgespeicherten Typen verwendet.

Es werden in Perl nun MIME::Entity und MIME::Parser verwendet,
deshalb ist der installationcheck erweitet.

SL/Controller/EmailJournal.pm
SL/DB/MetaSetup/EmailJournalAttachment.pm
SL/Form.pm
SL/InstallationCheck.pm
SL/Mailer.pm
bin/mozilla/io.pl
js/locale/de.js
locale/de/all
sql/Pg-upgrade2/email_journal_attachments_add_fileid.sql [new file with mode: 0644]
templates/webpages/generic/edit_email.html

index 551214e..6dfed90 100644 (file)
@@ -60,8 +60,12 @@ sub action_download_attachment {
   if (!$self->can_view_all && ($attachment->email_journal->sender_id != SL::DB::Manager::Employee->current->id)) {
     $::form->error(t8('You do not have permission to access this entry.'));
   }
-
-  $self->send_file(\$attachment->content, name => $attachment->name, type => $attachment->mime_type);
+  my $ref = \$attachment->content;
+  if ( $attachment->file_id > 0 ) {
+    my $file = SL::File->get(id => $attachment->file_id );
+    $ref = SL::File->get_content(dbfile => $file) if $file;
+  }
+  $self->send_file($ref, name => $attachment->name, type => $attachment->mime_type);
 }
 
 #
index e804477..591e799 100644 (file)
@@ -11,6 +11,7 @@ __PACKAGE__->meta->table('email_journal_attachments');
 __PACKAGE__->meta->columns(
   content          => { type => 'bytea', not_null => 1 },
   email_journal_id => { type => 'integer', not_null => 1 },
+  file_id          => { type => 'integer', default => '0', not_null => 1 },
   id               => { type => 'serial', not_null => 1 },
   itime            => { type => 'timestamp', default => 'now()', not_null => 1 },
   mime_type        => { type => 'text', not_null => 1 },
index 84349d2..bfc8827 100644 (file)
@@ -1102,100 +1102,179 @@ sub parse_template {
 
   if ( !$self->{preview} && $ext_for_format eq 'pdf' && $::instance_conf->get_doc_storage) {
     $self->{attachment_filename} ||= $self->generate_attachment_filename;
-    $self->store_pdf($self);
+    $self->{print_file_id} = $self->store_pdf($self);
   }
   if ($self->{media} eq 'email') {
+    if ( getcwd() eq $self->{"tmpdir"} ) {
+      # in the case of generating pdf we are in the tmpdir, but WHY ???
+      $self->{tmpfile} = $userspath."/".$self->{tmpfile};
+      chdir("$self->{cwd}");
+    }
+    $self->send_email(\%::myconfig,$ext_for_format);
+  }
+  else {
+    $self->{OUT}      = $out;
+    $self->{OUT_MODE} = $out_mode;
+    $self->output_file($template->get_mime_type,$command_formatter);
+  }
+  delete $self->{print_file_id};
 
-    my $mail = Mailer->new;
-
-    map { $mail->{$_} = $self->{$_} }
-      qw(cc bcc subject message version format);
-    $mail->{to} = $self->{EMAIL_RECIPIENT} ? $self->{EMAIL_RECIPIENT} : $self->{email};
-    $mail->{from}   = qq|"$myconfig->{name}" <$myconfig->{email}>|;
-    $mail->{fileid} = time() . '.' . $$ . '.';
-    my $full_signature     =  $self->create_email_signature();
-    $full_signature        =~ s/\r//g;
-
-    # if we send html or plain text inline
-    if (($self->{format} eq 'html') && ($self->{sendmode} eq 'inline')) {
-      $mail->{contenttype}    =  "text/html";
-      $mail->{message}        =~ s/\r//g;
-      $mail->{message}        =~ s/\n/<br>\n/g;
-      $full_signature         =~ s/\n/<br>\n/g;
-      $mail->{message}       .=  $full_signature;
-
-      open(IN, "<:encoding(UTF-8)", $self->{tmpfile})
-        or $self->error($self->cleanup . "$self->{tmpfile} : $!");
-      $mail->{message} .= $_ while <IN>;
-      close(IN);
+  $self->cleanup;
 
-    } else {
+  chdir("$self->{cwd}");
+  $main::lxdebug->leave_sub();
+}
 
-      if (!$self->{"do_not_attach"}) {
-        my $attachment_name  =  $self->{attachment_filename} || $self->{tmpfile};
-        $attachment_name     =~ s/\.(.+?)$/.${ext_for_format}/ if ($ext_for_format);
-        $mail->{attachments} =  [{ "filename" => $self->{tmpfile},
-                                   "name"     => $attachment_name }];
-      }
+sub get_bcc_defaults {
+  my ($self, $myconfig, $mybcc) = @_;
+#  if (SL::DB::Default->get->bcc_to_login) {
+#    $mybcc .= ", " if $mybcc;
+#    $mybcc .= $myconfig->{email};
+#  }
+  my $otherbcc = SL::DB::Default->get->global_bcc;
+  if ($otherbcc) {
+    $mybcc .= ", " if $mybcc;
+    $mybcc .= $otherbcc;
+  }
+  return $mybcc;
+}
 
-      $mail->{message} .= $full_signature;
-    }
+sub send_email {
+  $main::lxdebug->enter_sub();
+  my ($self, $myconfig, $ext_for_format) = @_;
+  my $mail = Mailer->new;
 
-    my $err = $mail->send();
-    $self->error($self->cleanup . "$err") if ($err);
+  map { $mail->{$_} = $self->{$_} }
+    qw(cc subject message version format);
 
-  } else {
+  $mail->{bcc}    = $self->get_bcc_defaults($myconfig, $self->{bcc});
+  $mail->{to}     = $self->{EMAIL_RECIPIENT} ? $self->{EMAIL_RECIPIENT} : $self->{email};
+  $mail->{from}   = qq|"$myconfig->{name}" <$myconfig->{email}>|;
+  $mail->{fileid} = time() . '.' . $$ . '.';
+  my $full_signature     =  $self->create_email_signature();
+  $full_signature        =~ s/\r//g;
 
-    $self->{OUT}      = $out;
-    $self->{OUT_MODE} = $out_mode;
+  $mail->{attachments} =  [];
+  my @attfiles;
+  # if we send html or plain text inline
+  if (($self->{format} eq 'html') && ($self->{sendmode} eq 'inline')) {
+    $mail->{contenttype}    =  "text/html";
+    $mail->{message}        =~ s/\r//g;
+    $mail->{message}        =~ s/\n/<br>\n/g;
+    $full_signature         =~ s/\n/<br>\n/g;
+    $mail->{message}       .=  $full_signature;
 
-    my $numbytes = (-s $self->{tmpfile});
     open(IN, "<", $self->{tmpfile})
       or $self->error($self->cleanup . "$self->{tmpfile} : $!");
-    binmode IN;
+    $mail->{message} .= $_ while <IN>;
+    close(IN);
 
-    $self->{copies} = 1 unless $self->{media} eq 'printer';
+  } else {
+    $main::lxdebug->message(LXDebug->DEBUG2(),"action_oldfile=" . $self->{action_oldfile}." action_nofile=".$self->{action_nofile});
+    if (!$self->{"do_not_attach"} && !$self->{action_nofile}) {
+      my $attachment_name  =  $self->{attachment_filename}  || $self->{tmpfile};
+      $attachment_name     =~ s/\.(.+?)$/.${ext_for_format}/ if ($ext_for_format);
+      if ( $self->{action_oldfile} ) {
+        $main::lxdebug->message(LXDebug->DEBUG2(),"object_id =>". $self->{id}." object_type =>". $self->{formname});
+        my ( $attfile ) = SL::File->get_all(object_id   => $self->{id},
+                                            object_type => $self->{formname},
+                                            file_type   => 'document');
+        $main::lxdebug->message(LXDebug->DEBUG2(), "old file obj=".$attfile);
+        push @attfiles, $attfile if $attfile;
+      } else {
+        push @{ $mail->{attachments} }, { path => $self->{tmpfile},
+                                          id   => $self->{print_file_id},
+                                          type => "application/pdf",
+                                          name => $attachment_name };
+      }
+    }
+  }
+  if (!$self->{"do_not_attach"}) {
+    for my $i (1 .. $self->{attfile_count}) {
+      if (  $self->{"attsel_$i"} ) {
+        my $attfile = SL::File->get(id => $self->{"attfile_$i"});
+        $main::lxdebug->message(LXDebug->DEBUG2(), "att file=".$self->{"attfile_$i"}." obj=".$attfile);
+        push @attfiles, $attfile if $attfile;
+      }
+    }
+    for my $i (1 .. $self->{attfile_cv_count}) {
+      if (  $self->{"attsel_cv_$i"} ) {
+        my $attfile = SL::File->get(id => $self->{"attfile_cv_$i"});
+        $main::lxdebug->message(LXDebug->DEBUG2(), "att file=".$self->{"attfile_$i"}." obj=".$attfile);
+        push @attfiles, $attfile if $attfile;
+      }
+    }
+    for my $i (1 .. $self->{attfile_part_count}) {
+      if (  $self->{"attsel_part_$i"} ) {
+        my $attfile = SL::File->get(id => $self->{"attfile_part_$i"});
+        $main::lxdebug->message(LXDebug->DEBUG2(), "att file=".$self->{"attfile_$i"}." obj=".$attfile);
+        push @attfiles, $attfile if $attfile;
+      }
+    }
+    foreach my $attfile ( @attfiles ) {
+      push @{ $mail->{attachments} }, { path => SL::File->get_file_path(dbfile => $attfile),
+                                        id   => $attfile->id,
+                                        type => $attfile->file_mime_type,
+                                        name => $attfile->file_name };
+    }
+  }
+  $mail->{message}  =~ s/\r//g;
+  $mail->{message} .= $full_signature;
+  $self->{emailerr} = $mail->send();
+  # $self->error($self->cleanup . "$err") if $self->{emailerr};
+  $self->{email_journal_id} = $mail->{journalentry};
 
-    chdir("$self->{cwd}");
-    #print(STDERR "Kopien $self->{copies}\n");
-    #print(STDERR "OUT $self->{OUT}\n");
-    for my $i (1 .. $self->{copies}) {
-      if ($self->{OUT}) {
-        $self->{OUT} = $command_formatter->($self->{OUT_MODE}, $self->{OUT});
+  #write back for message info and mail journal
+  $self->{cc}  = $mail->{cc};
+  $self->{bcc} = $mail->{bcc};
+  $self->{email} = $mail->{to};
 
-        open  OUT, $self->{OUT_MODE}, $self->{OUT} or $self->error($self->cleanup . "$self->{OUT} : $!");
-        print OUT $_ while <IN>;
-        close OUT;
-        seek  IN, 0, 0;
+  $main::lxdebug->leave_sub();
+}
 
-      } else {
-        my %headers = ('-type'       => $template->get_mime_type,
-                       '-connection' => 'close',
-                       '-charset'    => 'UTF-8');
-
-        $self->{attachment_filename} ||= $self->generate_attachment_filename;
-
-        if ($self->{attachment_filename}) {
-          %headers = (
-            %headers,
-            '-attachment'     => $self->{attachment_filename},
-            '-content-length' => $numbytes,
-            '-charset'        => '',
-          );
-        }
+sub output_file {
+  $main::lxdebug->enter_sub();
 
-        print $::request->cgi->header(%headers);
+  my ($self,$mimeType,$command_formatter) = @_;
+  my $numbytes = (-s $self->{tmpfile});
+  open(IN, "<", $self->{tmpfile})
+    or $self->error($self->cleanup . "$self->{tmpfile} : $!");
+  binmode IN;
 
-        $::locale->with_raw_io(\*STDOUT, sub { print while <IN> });
-      }
-    }
+  $self->{copies} = 1 unless $self->{media} eq 'printer';
 
-    close(IN);
-  }
+  chdir("$self->{cwd}");
+  for my $i (1 .. $self->{copies}) {
+    if ($self->{OUT}) {
+      $self->{OUT} = $command_formatter->($self->{OUT_MODE}, $self->{OUT});
 
-  $self->cleanup;
+      open  OUT, $self->{OUT_MODE}, $self->{OUT} or $self->error($self->cleanup . "$self->{OUT} : $!");
+      print OUT $_ while <IN>;
+      close OUT;
+      seek  IN, 0, 0;
 
-  chdir("$self->{cwd}");
+    } else {
+      my %headers = ('-type'       => $mimeType,
+                     '-connection' => 'close',
+                     '-charset'    => 'UTF-8');
+
+      $self->{attachment_filename} ||= $self->generate_attachment_filename;
+
+      if ($self->{attachment_filename}) {
+        %headers = (
+          %headers,
+          '-attachment'     => $self->{attachment_filename},
+          '-content-length' => $numbytes,
+          '-charset'        => '',
+        );
+      }
+
+      print $::request->cgi->header(%headers);
+
+      $::locale->with_raw_io(\*STDOUT, sub { print while <IN> });
+    }
+  }
+  close(IN);
   $main::lxdebug->leave_sub();
 }
 
index adb9494..2ce6257 100644 (file)
@@ -29,6 +29,8 @@ BEGIN {
   { name => "Digest::SHA",                         url => "http://search.cpan.org/~mshelor/",   debian => 'libdigest-sha-perl' },
   { name => "Email::Address",                      url => "http://search.cpan.org/~rjbs/",      debian => 'libemail-address-perl' },
   { name => "Email::MIME",                         url => "http://search.cpan.org/~rjbs/",      debian => 'libemail-mime-perl' },
+  { name => "MIME::Entity",                        url => "http://search.cpan.org/~dskoll/",    debian => '' },
+  { name => "MIME::Parser",                        url => "http://search.cpan.org/~dskoll/",    debian => '' },
   { name => "FCGI",            version => '0.72',  url => "http://search.cpan.org/~mstrout/",   debian => 'libfcgi-perl' },
   { name => "File::Copy::Recursive",               url => "http://search.cpan.org/~dmuey/",     debian => 'libfile-copy-recursive-perl' },
   { name => "GD",                                  url => "http://search.cpan.org/~lds/",       debian => 'libgd-gd2-perl', },
index eb94ae3..6bb3517 100644 (file)
@@ -24,7 +24,9 @@
 package Mailer;
 
 use Email::Address;
-use Email::MIME::Creator;
+use MIME::Entity;
+use MIME::Parser;
+use File::MimeInfo::Magic;
 use File::Slurp;
 use List::UtilsBy qw(bundle_by);
 
@@ -32,12 +34,13 @@ use SL::Common;
 use SL::DB::EmailJournal;
 use SL::DB::EmailJournalAttachment;
 use SL::DB::Employee;
-use SL::MIME;
 use SL::Template;
 
 use strict;
+use Encode;
 
 my $num_sent = 0;
+my $parser;
 
 my %mail_delivery_modules = (
   sendmail => 'SL::Mailer::Sendmail',
@@ -47,6 +50,8 @@ my %mail_delivery_modules = (
 sub new {
   my ($type, %params) = @_;
   my $self = { %params };
+  $parser = new MIME::Parser;
+  $parser->output_under("users");
 
   bless $self, $type;
 }
@@ -117,7 +122,7 @@ sub _create_address_headers {
         $addr_obj->phrase($phrase);
       }
 
-      push @header_addresses, $addr_obj->format;
+      push @header_addresses, encode('MIME-Header',$addr_obj->format);
     }
 
     push @{ $self->{headers} }, ( ucfirst($item) => join(', ', @header_addresses) ) if @header_addresses;
@@ -128,64 +133,80 @@ sub _create_attachment_part {
   my ($self, $attachment) = @_;
 
   my %attributes = (
-    disposition  => 'attachment',
-    encoding     => 'base64',
+    Disposition  => 'attachment',
+    Encoding     => 'base64',
   );
 
+  my $file_id = 0;
   my $attachment_content;
+  my $email_journal = $::instance_conf->get_email_journal;
 
+  $main::lxdebug->message(LXDebug->DEBUG2(), "mail5 att=".$attachment." email_journal=". $email_journal." id=".$attachment->{id});
   if (ref($attachment) eq "HASH") {
-    $attributes{filename} = $attachment->{name};
-    $attachment_content   = $attachment->{content} // eval { read_file($attachment->{filename}) };
+    $attributes{Path}     = $attachment->{path} || $attachment->{filename};
+    $attributes{Filename} = $attachment->{name};
+    $file_id              = $attachment->{id}   || '0';
+    $attributes{Type}     = $attachment->{type} || 'application/pdf';
+    $attachment_content   = eval { read_file($attachment->{path}) } if $email_journal > 1;
 
   } else {
     # strip path
-    $attributes{filename} =  $attachment;
-    $attributes{filename} =~ s:.*\Q$self->{fileid}\E:: if $self->{fileid};
-    $attributes{filename} =~ s:.*/::g;
-    $attachment_content   =  eval { read_file($attachment) };
+    $attributes{Path}     =  $attachment;
+    $attributes{Filename} =  $attachment;
+    $attributes{Filename} =~ s:.*\Q$self->{fileid}\E:: if $self->{fileid};
+    $attributes{Filename} =~ s:.*/::g;
+
+    my $application     = ($attachment =~ /(^\w+$)|\.(html|text|txt|sql)$/) ? 'text' : 'application';
+    $attributes{Type}   = File::MimeInfo::Magic::magic($attachment);
+    $attributes{Type} ||= "${application}/$self->{format}" if $self->{format};
+    $attributes{Type} ||= 'application/octet-stream';
+    $attachment_content = eval { read_file($attachment) } if $email_journal > 1;
   }
 
-  return undef if !defined $attachment_content;
+  return undef if $email_journal > 1 && !defined $attachment_content;
+  $attachment_content ||= ' ';
+  $main::lxdebug->message(LXDebug->DEBUG2(), "mail6 mtype=".$attributes{Type}." path=".
+                            $attributes{Path}." filename=".$attributes{Filename});
 
-  my $application             = ($attachment =~ /(^\w+$)|\.(html|text|txt|sql)$/) ? 'text' : 'application';
-  $attributes{content_type}   = SL::MIME->mime_type_from_ext($attributes{filename});
-  $attributes{content_type} ||= "${application}/$self->{format}" if $self->{format};
-  $attributes{content_type} ||= 'application/octet-stream';
-  $attributes{charset}        = $self->{charset} if lc $application eq 'text' && $self->{charset};
+# $attributes{Charset}  = $self->{charset} if lc $application eq 'text' && $self->{charset};
+  $attributes{Charset}  = $self->{charset} if $self->{charset};
 
-  return Email::MIME->create(
-    attributes => \%attributes,
-    body       => $attachment_content,
+  my $ent;
+  if ( $attributes{Type} eq 'message/rfc822' ) {
+    my $fh = IO::File->new($attributes{Path}, "r");
+    if (! defined $fh) {
+      return undef;
+    }
+    $ent = $parser->parse($fh);
+    undef $fh;
+    my $head = $ent->head;
+    $head->replace('Content-disposition','attachment; filename='.$attributes{Filename});
+  } else {
+    $ent = MIME::Entity->build(%attributes);
+  }
+  push @{ $self->{mail_attachments}} , SL::DB::EmailJournalAttachment->new(
+    name      => $attributes{Filename},
+    mime_type => $attributes{Type},
+    content   => ( $email_journal > 1 ? $attachment_content : ' '),
+    file_id   => $file_id,
   );
+  return $ent;
 }
 
 sub _create_message {
   my ($self) = @_;
 
-  my @parts;
-
+  push @{ $self->{headers} }, ('Type'    =>"multipart/mixed" );
+  my  $top = MIME::Entity->build(@{$self->{headers}});
   if ($self->{message}) {
-    push @parts, Email::MIME->create(
-      attributes => {
-        content_type => $self->{contenttype},
-        charset      => $self->{charset},
-        encoding     => 'quoted-printable',
-      },
-      body_str => $self->{message},
-    );
-
-    push @{ $self->{headers} }, (
-      'Content-Type' => qq|$self->{contenttype}; charset="$self->{charset}"|,
-    );
+    $top->attach(Data => encode($self->{charset},$self->{message}),
+                 Charset => $self->{charset},
+                 Type => $self->{contenttype},
+                 Encoding    => 'quoted-printable');
   }
 
-  push @parts, grep { $_ } map { $self->_create_attachment_part($_) } @{ $self->{attachments} || [] };
-
-  return Email::MIME->create(
-    header_str => $self->{headers},
-    parts      => \@parts,
-  );
+  map { $top->add_part($self->_create_attachment_part($_)) } @{ $self->{attachments} || [] };
+  return $top;
 }
 
 sub send {
@@ -202,10 +223,12 @@ sub send {
   $self->{charset}       =  'UTF-8';
   $self->{contenttype} ||=  "text/plain";
   $self->{headers}       =  [
-    Subject              => $self->{subject},
+    Subject              => encode('MIME-Header',$self->{subject}),
     'Message-ID'         => '<' . $self->_create_message_id . '>',
     'X-Mailer'           => "kivitendo $self->{version}",
   ];
+  $self->{mail_attachments} = [];
+  $self->{content_by_name}  = $::instance_conf->get_email_journal == 1 && $::instance_conf->get_doc_files;
 
   my $error;
   my $ok = eval {
@@ -215,10 +238,10 @@ sub send {
 
     my $email = $self->_create_message;
 
-    # $::lxdebug->message(0, "message: " . $email->as_string);
+    #$::lxdebug->message(0, "message: " . $email->as_string);
     # return "boom";
 
-    $self->{driver}->start_mail(from => $self->{from}, to => [ $self->_all_recipients ]);
+    $self->{driver}->start_mail(from => encode('MIME-Header',$self->{from}), to => [ $self->_all_recipients ]);
     $self->{driver}->print($email->as_string);
     $self->{driver}->send;
 
@@ -227,14 +250,14 @@ sub send {
 
   $error = $@ if !$ok;
 
-  $self->_store_in_journal;
+  $self->{journalentry} = $self->_store_in_journal;
+  $parser->filer->purge;
 
   return $ok ? '' : "send email: $error";
 }
 
 sub _all_recipients {
   my ($self) = @_;
-
   $self->{addresses} ||= {};
   return map { @{ $self->{addresses}->{$_} || [] } } qw(to cc bcc);
 }
@@ -251,22 +274,9 @@ sub _store_in_journal {
   $extended_status //= $self->{driver}->extended_status if $self->{driver};
   $extended_status //= 'unknown error';
 
-  my @attachments;
-
-  @attachments = grep { $_ } map {
-    my $part = $self->_create_attachment_part($_);
-    if ($part) {
-      SL::DB::EmailJournalAttachment->new(
-        name      => $part->filename,
-        mime_type => $part->content_type,
-        content   => $part->body,
-      )
-    }
-  } @{ $self->{attachments} || [] } if $journal_enable > 1;
-
   my $headers = join "\r\n", (bundle_by { join(': ', @_) } 2, @{ $self->{headers} || [] });
 
-  SL::DB::EmailJournal->new(
+  my $jentry = SL::DB::EmailJournal->new(
     sender          => SL::DB::Manager::Employee->current,
     from            => $self->{from}    // '',
     recipients      => join(', ', $self->_all_recipients),
@@ -274,10 +284,11 @@ sub _store_in_journal {
     headers         => $headers,
     body            => $self->{message} // '',
     sent_on         => DateTime->now_local,
-    attachments     => \@attachments,
+    attachments     => \@{ $self->{mail_attachments} },
     status          => $status,
     extended_status => $extended_status,
   )->save;
+  return $jentry->id;
 }
 
 1;
index f071871..b4f85af 100644 (file)
@@ -50,6 +50,7 @@ use SL::CT;
 use SL::Locale::String qw(t8);
 use SL::IC;
 use SL::IO;
+use SL::File;
 use SL::PriceSource;
 
 use SL::DB::Customer;
@@ -58,7 +59,7 @@ use SL::DB::Language;
 use SL::DB::Printer;
 use SL::DB::Vendor;
 use SL::Helper::CreatePDF;
-use SL::Helper::Flash qw(flash);
+use SL::Helper::Flash;
 
 require "bin/mozilla/common.pl";
 
@@ -1066,12 +1067,38 @@ sub edit_e_mail {
   @dont_hide_key{@dont_hide_key_list} = (1) x @dont_hide_key_list;
   @hidden_keys = sort grep { !$dont_hide_key{$_} } grep { !ref $form->{$_} } keys %$form;
 
+  my (@files, @vc_files, @part_files, $has_document);
+
+  if ($::instance_conf->get_doc_storage) {
+    @files = SL::File->get_all_versions(object_id => $form->{id}, object_type => $form->{type}, file_type => 'document');
+    $has_document = 1 if scalar(@files) > 0;
+    @files = SL::File->get_all(object_id => $form->{id}, object_type => $form->{type}, file_type => 'attachment');
+    @vc_files = SL::File->get_all(object_id => $form->{"$form->{vc}_id"}, object_type => $form->{vc})
+      if $form->{vc} && $form->{"$form->{vc}_id"};
+
+    my %part_id_map = map { $_->{id} => $_ } grep { $_->{id} } map {
+      {
+        'id'       => $form->{"id_$_"},
+        'partname' => $form->{"partnumber_$_"}
+      }
+    } (1 .. $form->{rowcount});
+
+    foreach my $partid (keys %part_id_map) {
+      my @pfiles = SL::File->get_all(object_id => $partid, object_type => 'part');
+      push @part_files, map { $_->{partname} = $part_id_map{$partid}->{partname}; $_ } @pfiles;
+    }
+  }
+
   print $form->parse_html_template('generic/edit_email',
                                    { title         => $title,
                                      a_filename    => $attachment_filename,
                                      subject       => $subject,
+                                     has_document  => $has_document,
                                      print_options => print_options('inline' => 1),
                                      action        => 'send_email',
+                                     FILES         => \@files,
+                                     VC_FILES      => \@vc_files,
+                                     PART_FILES    => \@part_files,
                                      HIDDEN        => [ map +{ name => $_, value => $form->{$_} }, @hidden_keys ],
                                      SHOW_BCC      => $::auth->assert('email_bcc', 'may fail') });
 
@@ -1088,10 +1115,28 @@ sub send_email {
 
   my $callback = $form->{script} . "?action=edit";
   map({ $callback .= "\&${_}=" . E($form->{$_}); } qw(type id));
+  if ( $form->{action_oldfile} || $form->{action_nofile} ) {
+    if (!$form->{email} || $form->{email} =~ /^\s*$/) {
+      flash('error', $::locale->text('E-mail address missing!'));
+    }
+    else {
+      $form->send_email(\%myconfig,'pdf');
+    }
+  }
+  else {
+    print_form("return");
+    $form->{addition} = "SCREENED";
+    $form->save_history;
+    $form->{addition} = "MAILED";
+  }
+
+  flash_later('info' , $::locale->text('E-Mail is sent to #1', $form->{email})) if !$form->{emailerr};
+  flash_later('error', $::locale->text($form->{emailerr})) if $form->{emailerr};
 
-  print_form("return");
+  delete $form->{emailerr};
 
   Common->save_email_status(\%myconfig, $form);
+  ##TODO andere SAVE HISTORY
 
   $form->{callback} = $callback;
   $form->redirect();
index 702d429..71749f7 100644 (file)
@@ -57,7 +57,6 @@ namespace("kivi").setupLocale({
 "If you switch to a different tab without saving you will lose the data you've entered in the current tab.":"Wenn Sie auf einen anderen Tab wechseln, ohne vorher zu speichern, so gehen die im aktuellen Tab eingegebenen Daten verloren.",
 "Map":"Karte",
 "No":"Nein",
-"No articles have been added yet.":"Es wurden noch keine Artikel hinzugefügt.",
 "No delievery orders selected, please set one checkbox!":"Kein Lieferschein selektiert, bitte eine Box anklicken!",
 "No delivery orders have been selected.":"Es wurden keine Lieferscheine ausgewählt.",
 "No entries have been selected.":"Es wurden keine Einträge ausgewählt.",
index af182ec..7e89c20 100644 (file)
@@ -767,6 +767,7 @@ $self->{texts} = {
   'Customer'                    => 'Kunde',
   'Customer (database ID)'      => 'Kunde (Datenbank-ID)',
   'Customer (name)'             => 'Kunde (Name)',
+  'Customer Attachments'        => 'Anhänge des Kunden',
   'Customer Discount'           => 'Kundenrabatt',
   'Customer Master Data'        => 'Kundenstammdaten',
   'Customer Name'               => 'Kundenname',
@@ -1042,6 +1043,7 @@ $self->{texts} = {
   'Duplicate in CSV file'       => 'Duplikat in CSV-Datei',
   'Duplicate in database'       => 'Duplikat in Datenbank',
   'During the next update a taxkey 0 with tax rate of 0 will automatically created.' => 'Beim nächsten Ausführen des Updates wird ein Steuerschlüssel 0 mit einem Steuersatz von 0% automatisch erzeugt.',
+  'E-Mail is sent to #1'        => 'E-Mail wurde an #1 gesendet',
   'E-mail'                      => 'E-Mail',
   'E-mail Statement to'         => 'Fälligkeitsabrechnung als E-Mail an',
   'E-mail address missing!'     => 'E-Mail-Adresse fehlt!',
@@ -1480,6 +1482,8 @@ $self->{texts} = {
   'Illegal characters have been removed from the following fields: #1' => 'Ungültige Zeichen wurden aus den folgenden Feldern entfernt: #1',
   'Illegal date'                => 'Ungültiges Datum',
   'Image'                       => 'Grafik',
+  'ImagePreview'                => 'Bildvorschau',
+  'Images'                      => 'Bilder',
   'Import'                      => 'Import',
   'Import AP from Scanner or Email' => 'Einkaufsbelege importieren vom Scanner oder von Email',
   'Import AR from Scanner or Email' => 'Verkaufsbelege importieren vom Scanner oder von Email',
@@ -1707,6 +1711,7 @@ $self->{texts} = {
   'Make (vendor\'s database ID, number or name; with X being a number)' => 'Lieferant (Datenbank-ID, Nummer oder Name des Lieferanten; X ist eine fortlaufende Zahl)',
   'Make compatible for import'  => 'Für den Import kompatibel machen',
   'Make default profile'        => 'Zu Standardprofil machen',
+  'Make new document'           => 'Erzeuge ein neue Datei',
   'Makemodel Price'             => 'Lieferantenpreis',
   'Manage Custom Variables'     => 'Benutzerdefinierte Variablen',
   'Mandantennummer'             => 'Mandantennummer',
@@ -1840,6 +1845,7 @@ $self->{texts} = {
   'No delivery orders have been selected.' => 'Es wurden keine Lieferscheine ausgewählt.',
   'No delivery term has been created yet.' => 'Es wurden noch keine Lieferbedingungen angelegt',
   'No department has been created yet.' => 'Es wurde noch keine Abteilung erfasst.',
+  'No document'                 => 'Kein Datei mitschicken (ggf. Anhänge)',
   'No draft was found.'         => 'Kein Entwurf gefunden.',
   'No dunnings have been selected for printing.' => 'Es wurden keine Mahnungen zum Drucken ausgew&auml;hlt.',
   'No end date given, setting to today' => 'Kein Enddatum gegeben, setze Enddatum auf heute',
@@ -2037,6 +2043,7 @@ $self->{texts} = {
   'Part "#1" has chargenumber or best before date set. So it cannot be transfered automatically.' => 'Bei Artikel "#1" ist eine Chargenummer oder ein Mindesthaltbarkeitsdatum vergeben. Deshalb kann dieser Artikel nicht automatisch ausgelagert werden.',
   'Part (database ID)'          => 'Artikel (Datenbank-ID)',
   'Part (typeabbreviation)'     => 'W',
+  'Part Attachments'            => 'Anhänge der Artikel',
   'Part Classification'         => 'Artikel-Klassifizierung',
   'Part Description'            => 'Artikelbeschreibung',
   'Part Description missing!'   => 'Artikelbezeichnung fehlt!',
@@ -3596,6 +3603,14 @@ $self->{texts} = {
   'Zip, City'                   => 'PLZ, Ort',
   'Zipcode'                     => 'PLZ',
   '[email]'                     => '[email]',
+  'a_credit_note'               => '',
+  'a_purchase_delivery_order'   => '',
+  'a_purchase_invoice'          => '',
+  'a_purchase_order'            => '',
+  'a_request_quotation'         => '',
+  'a_sales_delivery_order'      => '',
+  'a_sales_order'               => '',
+  'a_sales_quotation'           => '',
   'absolute'                    => 'absolut',
   'account_description'         => 'Beschreibung',
   'accrual'                     => 'Soll-Versteuerung',
@@ -3603,6 +3618,7 @@ $self->{texts} = {
   'active'                      => 'aktiv',
   'all'                         => 'Alle',
   'all entries'                 => 'alle Einträge',
+  'an_invoice'                  => '',
   'and'                         => 'und',
   'ap_aging_list'               => 'liste_offene_verbindlichkeiten',
   'ar_aging_list'               => 'liste_offene_forderungen',
@@ -3718,6 +3734,7 @@ $self->{texts} = {
   'list_of_payments'            => 'zahlungsausgaenge',
   'list_of_receipts'            => 'zahlungseingaenge',
   'list_of_transactions'        => 'buchungsliste',
+  'mail_greeting #1'            => '',
   'male'                        => 'männlich',
   'mark as paid'                => 'als bezahlt markieren',
   'max filesize'                => 'maximale Dateigröße',
@@ -3752,6 +3769,7 @@ $self->{texts} = {
   'only OB Transactions'        => 'nur EB-Buchungen',
   'open'                        => 'Offen',
   'order'                       => 'Reihenfolge',
+  'other Document Attachments'  => 'Weitere Dateianhänge',
   'our vendor number at customer' => 'Unsere Lieferanten-Nr. beim Kunden',
   'parsing csv'                 => 'Parse CSV Daten',
   'part'                        => 'Ware',
@@ -3838,6 +3856,8 @@ $self->{texts} = {
   'unconfigured'                => 'unkonfiguriert',
   'unnamed record template'     => 'unbenannte Belegvorlage',
   'until'                       => 'bis',
+  'uploaded'                    => 'Hochgeladen',
+  'use actual document'         => 'Verwende aktuelle Datei',
   'use program settings'        => 'benutze Programmeinstellungen',
   'use user config'             => 'Verwende Benutzereinstellung',
   'used'                        => 'Verbraucht',
diff --git a/sql/Pg-upgrade2/email_journal_attachments_add_fileid.sql b/sql/Pg-upgrade2/email_journal_attachments_add_fileid.sql
new file mode 100644 (file)
index 0000000..9465df5
--- /dev/null
@@ -0,0 +1,4 @@
+-- @tag: email_journal_attachments_add_fileid
+-- @description: attachments mit file_id
+-- @depends: email_journal filemanagement_feature files
+ALTER TABLE email_journal_attachments ADD COLUMN file_id integer default 0 NOT NULL;
index 48f6131..e3b03ac 100644 (file)
@@ -6,7 +6,7 @@
 
 <table width="100%">
   <tr>
-    <td>
+    <td style="width: 500px">
       <table>
         <tr>
           <th align="right" nowrap>[% 'To' | $T8 %]</th>
         </tr>
       </table>
     </td>
+[%- IF INSTANCE_CONF.get_doc_storage %]
+    <td align="left" rowspan="2">
+      <table>
+[%- USE ATT_it = Iterator(FILES) %]
+[% FOREACH file = ATT_it %]
+[% END %]
+[%- IF ATT_it.size > 0 %]  
+        <tr class="listheading">
+          <th colspan="3" align="left" nowrap>[% LxERP.t8('other Document Attachments') %]</th>
+          <input type="hidden" name="attfile_count" id="m_attfile_count" value="[% ATT_it.size %]">
+        </tr>
+         <tr class="">
+           <th align="left" nowrap></th>
+           <th align="left" nowrap>[% LxERP.t8('Filename') %]</th>
+           <th align="left" nowrap></th>
+        </tr>
+        <tr><td colspan="3"><hr size="1" style="height:1px;background-color:#000;" noshade></td></tr>
+       [% FOREACH file = ATT_it %]
+         <tr class="listrow">
+          <td></td><td>[% file.file_name %]
+            <input type="hidden" name="attfile_[% ATT_it.count %]" value="[% file.id %]">
+            <td><input name="attsel_[% ATT_it.count %]" type="checkbox" class="checkbox" ></td>
+         </tr>
+        [% END %]
+[%- END %]
+[%- USE ATT_it = Iterator(VC_FILES) %]
+[% FOREACH file = ATT_it %]
+[% END %]
+[%- IF ATT_it.size > 0 %]  
+        <tr><td colspan="3"><hr size="1" noshade></td></tr>
+         <tr class="listheading">
+           <th colspan="3" align="left" nowrap>
+             <input type="hidden" name="attfile_cv_count" id="m_attfile_cv_count" value="[% ATT_it.size %]">
+             [% LxERP.t8('Customer Attachments') %]
+           </th>
+         </tr>
+        <tr class="">
+           <th align="left" nowrap></th>
+           <th align="left" nowrap>[% LxERP.t8('Filename') %]</th>
+           <th align="left" nowrap></th>
+        </tr>
+        <tr><td colspan="3"><hr size="1" style="height:1px;background-color:#000;" noshade></td></tr>
+        [% FOREACH file = ATT_it %]
+         <tr class="listrow">
+          <td></td><td>[% file.file_name %]
+            <input type="hidden" name="attfile_cv_[% ATT_it.count %]" value="[% file.id %]">
+            <td><input name="attsel_cv_[% ATT_it.count %]" type="checkbox" class="checkbox" ></td>
+         </tr>
+        [% END %]
+[%- END %]
+[%- USE ATT_it = Iterator(PART_FILES) %]
+[%- SET lastpartid = '' %]  
+[%- FOREACH file = ATT_it %]
+[%- END %]
+[%- IF ATT_it.size > 0 %]  
+        <tr><td colspan="3"><hr size="1" noshade></td></tr>
+         <tr class="listheading">
+          <th colspan="3" align="left" nowrap>
+            <input type="hidden" name="attfile_part_count" id="m_attfile_part_count" value="[% ATT_it.size %]">
+            [% LxERP.t8('Part Attachments') %]
+          </th>
+        </tr>
+        <tr class="">
+           <th align="left" nowrap>[% LxERP.t8('Part Number') %]</th>
+           <th align="left" nowrap>[% LxERP.t8('Filename') %]</th>
+           <th align="left" nowrap></th>
+        </tr>
+        [% FOREACH file = ATT_it %]
+          [%- IF lastpartid != file.trans_id %]
+             [%- SET lastpartid = file.trans_id %][%- SET partname = file.partname %]
+        <tr><td colspan="3"><hr size="1" style="height:1px;background-color:#000;" noshade></td></tr>
+          [%- ELSE %][%- SET partname = '' %][% END %]          
+        <tr class="listrow">
+          <td>[% partname %]</td>
+          <td>[% file.file_name %]
+            <input type="hidden" name="attfile_part_[% ATT_it.count %]" value="[% file.id %]">
+            <td><input name="attsel_part_[% ATT_it.count %]" type="checkbox" class="checkbox" ></td>
+         </tr>
+        [% END %]
+[%- END %]
+      </table>
+    </td>
+[%- ELSE %]
+    <td rowspan="2">
+    </td>
+[%- END %]
   </tr>
 
   <tr>
     </td>
   </tr>
   <tr>
-    <td>
+    <td colspan="2">
 
 [% print_options %]
 [% FOREACH row = HIDDEN %]<input type="hidden" name="[% row.name %]" value="[% HTML.escape(row.value) %]">
   </tr>
 
   <tr>
-    <td><hr size="3" noshade></td>
+    <td colspan="2"><hr size="3" noshade></td>
   </tr>
 </table>
 
 [% L.hidden_tag('action', action) %]
 
 <br>
-[% L.submit_tag('dummy_action', LxERP.t8('Continue'), onclick="return check_prerequisites();") %]
+[% L.submit_tag('action_newfile', LxERP.t8('Make new document'), onclick="return check_prerequisites();") %]
+[%- IF has_document %]  
+[% L.submit_tag('action_oldfile', LxERP.t8('use actual document'), onclick="return check_prerequisites();") %]
+[%- END %]
+[% L.submit_tag('action_nofile',  LxERP.t8('No document'), onclick="return check_prerequisites();") %]
 </form>
 
 <script type="text/javascript">