1 package SL::BackgroundJob::ImportRecordEmails;
6 use parent qw(SL::BackgroundJob::Base);
9 use SL::DB::EmailJournal;
10 use SL::DB::Manager::EmailImport;
11 use SL::Helper::EmailProcessing;
12 use SL::Presenter::Tag qw(link_tag);
13 use SL::Locale::String qw(t8);
15 use List::MoreUtils qw(any);
16 use Params::Validate qw(:all);
19 sub sync_record_email_folder {
20 my ($self, $config) = @_;
23 foreach my $key (qw(enabled hostname port ssl username password)) {
24 if (defined $config->{$key}) {
25 $imap_config{$key} = $config->{$key};
29 my $imap_client = SL::IMAPClient->new(%imap_config);
31 my $email_import = $imap_client->update_emails_from_folder(
32 folder => $config->{folder},
33 email_journal_params => {
34 record_type => $config->{record_type},
37 return "No emails to import." unless $email_import;
38 if ($config->{imported_imap_flag}) {
39 foreach my $email_journal (@{$email_import->email_journals}) {
40 $imap_client->set_flag_for_email(
41 email_journal => $email_journal,
42 flag => $config->{imported_imap_flag},
46 my $result = "Created email import with id " . $email_import->id . " for ". scalar @{ $email_import->email_journals } . " emails.";
48 if ($config->{process_imported_emails}) {
50 ref $config->{process_imported_emails} eq 'ARRAY' ?
51 @{$config->{process_imported_emails}}
52 : ($config->{process_imported_emails});
53 foreach my $email_journal (@{$email_import->email_journals}) {
54 my $created_records = 0;
55 foreach my $function_name (@function_names) {
57 my $processed = SL::Helper::EmailProcessing->process_attachments($function_name, $email_journal);
58 $created_records += $processed;
61 # # TODO: link not shown as link
62 # my $email_journal_link = link_tag(
63 # $ENV{HTTP_ORIGIN} . $ENV{REQUEST_URI}
64 # . '?action=EmailJournal/show'
65 # . '&id=' . $email_journal->id
67 # , $email_journal->id
69 my $email_journal_id = $email_journal->id;
70 $result .= t8("Error while processing email journal ('#1') attachments with '#2': ", $email_journal_id, $function_name) . $@ . ".";
73 if ($created_records && $config->{processed_imap_flag}) {
74 $imap_client->set_flag_for_email(
75 email_journal => $email_journal,
76 flag => $config->{processed_imap_flag},
78 } elsif ($config->{not_processed_imap_flag}) {
79 $imap_client->set_flag_for_email(
80 email_journal => $email_journal,
81 flag => $config->{not_processed_imap_flag},
85 $result .= "Processed attachments with "
86 . join(', ', @function_names) . "."
87 if scalar @function_names;
93 sub delete_email_imports {
94 my ($self, $email_import_ids_to_delete) = @_;
96 my @not_found_email_import_ids;
97 my @deleted_email_import_ids;
98 foreach my $email_import_id (@$email_import_ids_to_delete) {
99 my $email_import = SL::DB::Manager::EmailImport->find_by(id => $email_import_id);
100 unless ($email_import) {
101 push @not_found_email_import_ids, $email_import_id;
104 $email_import->delete(cascade => 1);
105 push @deleted_email_import_ids, $email_import_id;
110 $result .= t8("Deleted email import(s): ")
111 . join(', ', @deleted_email_import_ids) . "."
112 if scalar @deleted_email_import_ids;
114 $result .= t8("Could not find email import(s): ")
115 . join(', ', @not_found_email_import_ids) . " for deletion."
116 if scalar @not_found_email_import_ids;
122 my ($self, $job_obj) = @_;
123 $self->{job_obj} = $job_obj;
128 $data = $job_obj->data_as_hash;
129 } catch { die t8("Invalid YAML Configuration for this job. Reason: malformed YAML Data: #1. Please consult: Program -> Documentation -> HTML -> Configuration of Background-Jobs.", $_ ); };
131 my @config_params = %{$data};
133 my %config = validate_with(
134 params => \@config_params,
142 default => 'catch_all',
144 'valid record type' => sub {
145 my $valid_record_types = SL::DB::EmailJournal->meta->{columns}->{record_type}->{check_in};
146 unless (any {$_[0] eq $_} @$valid_record_types) {
147 die "record_type '$_[0]' is not valid. Possible values:\n- " . join("\n- ", @$valid_record_types);
152 process_imported_emails => {
153 type => SCALAR | ARRAYREF,
156 'function is implemented' => sub {
157 foreach my $function_name (ref $_[0] eq 'ARRAY' ? @{$_[0]} : ($_[0])) {
158 !!SL::Helper::EmailProcessing->can_function($function_name) or
159 die "Function '$function_name' not implemented in SL::Helper::EmailProcessing";
165 processed_imap_flag => { type => SCALAR, optional => 1, },
166 not_processed_imap_flag => { type => SCALAR, optional => 1, },
167 email_import_ids_to_delete => { type => ARRAYREF, optional => 1, },
168 imported_imap_flag => { type => SCALAR, optional => 1, },
170 hostname => { type => SCALAR, },
171 port => { type => SCALAR, optional => 1},
172 ssl => { type => BOOLEAN, default => 1},
173 username => { type => SCALAR, },
174 password => { type => SCALAR, },
175 base_folder => { type => SCALAR, optional => 1},
178 called => "YAML Configuration for this Background Job invalid. Please consult: Program -> Documentation -> HTML -> Configuration of Background-Jobs.",
182 if (scalar $config{email_import_ids_to_delete}) {
183 push @results, $self->delete_email_imports($config{email_import_ids_to_delete});
186 push @results, $self->sync_record_email_folder(\%config);
188 return join("\n", grep { $_ ne ''} @results);
199 SL::BackgroundJob::ImportPurchaseInvoiceEmails - Background job for syncing
200 emails from a folder for records.
204 This background job imports emails from an imap folder. The emails are
205 imported into the email journal and can be processed with functions from
206 SL::Helper::EmailProcessing.
210 The data field in the backgroundjob config contains all configuration values:
216 required, hostname of IMAP server
220 required, login for IMAP server
224 required, password for login of IMAP server
228 optional Parameter IMAP port
232 required, The IMAP folder to import emails from. Sub folders are separated by a forward slash,
233 e.g. 'INBOX/Archive'. Subfolders are not synced. Default is 'INBOX'.
237 optional, The record type to set for each imported email in the email journal.
238 Default is catch-all. Valid types are the well-known types of kivitendo records, ie ar_transaction, ap_transaction
240 =item process_imported_emails
242 optional, more processing can be automatically done in the job.
243 Valid actions are defined in SL::Helper::EmailProcessing.pm
245 Take a look at currently supported actions with
247 perldoc SL/Helper/EmailProcessing.pm
250 =item processed_imap_flag
252 Optional, requires a valid value in process_imported_emails
254 If process_imported_emails is set and the process is successfully
255 executed this custom IMAP Flag is added to the processed email.
257 =item not_processed_imap_flag
259 Optional, requires a valid value in process_imported_emails
261 If process_imported_emails is set and the process is NOT
262 successfully executed this custom IMAP Flag is added
263 to the processed email.
265 =item imported_imap_flag
269 If the import is successfully executed this custom IMAP Flag
270 is added to the imported email.
275 =head1 YAML Configuration example with ZUGFeRD Processing
277 hostname: meinedomain.de
278 username: eingangsrechnung@meinedomain.de
280 folder: INBOX/vollimport
281 record_type: ap_transaction
282 process_imported_emails: zugferd
283 processed_imap_flag: $Label8
284 not_processed_imap_flag: $Label1
292 Tamino Steinert E<lt>tamino.steinert@tamino.stE<gt>