1 package SL::BackgroundJob::SendFollowUpReminder;
5 use parent qw(SL::BackgroundJob::Base);
9 use SL::Locale::String qw(t8);
12 use SL::Util qw(trim);
15 use File::Slurp qw(slurp);
16 use List::Util qw(any);
21 $_[0]->create_standard_job('7 6 1-5 * *'); # every workday at 06:07
24 use Rose::Object::MakeMethods::Generic (
25 'scalar' => [ qw(params) ],
29 # If job does not throw an error,
30 # success in background_job_histories is 'success'.
31 # It is 'failure' otherwise.
33 # Return value goes to result in background_job_histories.
36 my ($self, $db_obj) = @_;
38 $self->{$_} = [] for qw(job_errors);
40 $self->initialize_params($db_obj->data_as_hash) if $db_obj;
42 my @follow_up_date_where = ();
43 push @follow_up_date_where, (follow_up_date => { ge => [ $self->params->{from_date} ]}) if $self->params->{from_date};
44 push @follow_up_date_where, (follow_up_date => { le => [ $self->params->{to_date} ]}) if $self->params->{to_date};
46 my $follow_ups = SL::DB::Manager::FollowUp->get_all(where => ['done.id' => undef,
47 @follow_up_date_where, ],
48 with_objects => ['done'],
49 sort_by => ['follow_up_date']);
51 # Collect follow ups for users with e-mail-address.
52 my $mail_to_by_employee_id;
53 foreach my $follow_up (@$follow_ups) {
56 $follow_up->{link} = URI->new_abs('fu.pl?action=edit&id=' . $follow_up->id, $::form->_get_request_uri);
58 foreach my $employee (@{ $follow_up->created_for_employees }) {
59 next if $employee->deleted;
61 if (!exists $mail_to_by_employee_id->{$employee->id}) {
62 my $user = SL::DB::Manager::AuthUser->find_by(login => $employee->login);
64 my $mail_to = trim($user->get_config_value('email'));
68 $mail_to_by_employee_id->{$employee->id}->{mail_to} = $mail_to;
72 if (exists $mail_to_by_employee_id->{$employee->id}) {
73 push @{ $mail_to_by_employee_id->{$employee->id}->{follow_ups} }, $follow_up;
78 foreach (keys %$mail_to_by_employee_id) {
79 my ($message, $content_type) = $self->generate_message($mail_to_by_employee_id->{$_}->{follow_ups});
81 my $mail = Mailer->new;
82 $mail->{from} = $self->params->{email_from};
83 $mail->{to} = $mail_to_by_employee_id->{$_}->{mail_to};
84 $mail->{bcc} = SL::DB::Default->get->global_bcc;
85 $mail->{subject} = $self->params->{email_subject};
86 $mail->{message} = $message;
87 $mail->{content_type} = $content_type;
89 my $error = $mail->send;
92 push @{ $self->{job_errors} }, $error;
96 my $msg = t8('Follow ups reminder sent.');
98 # die if errors exists
99 if (@{ $self->{job_errors} }) {
100 $msg .= t8('The following errors occurred:');
101 $msg .= join "\n", @{ $self->{job_errors} };
109 sub initialize_params {
110 my ($self, $data) = @_;
112 # valid parameters with default values
115 to_date => DateTime->today_local->to_kivitendo,
116 email_from => $::lx_office_conf{follow_up_reminder}->{email_from},
117 email_subject => $::lx_office_conf{follow_up_reminder}->{email_subject},
118 email_template => $::lx_office_conf{follow_up_reminder}->{email_template},
121 # check user input param names
122 foreach my $param (keys %$data) {
123 die "Not a valid parameter: $param" unless exists $valid_params{$param};
128 { map { ($_ => $data->{$_} // $valid_params{$_}) } keys %valid_params }
131 # convert date from string to object
132 my ($from_date, $to_date);
134 if ($self->params->{from_date}) {
135 $from_date = DateTime->from_kivitendo($self->params->{from_date});
136 # Not undef and no other type.
137 die unless ref $from_date eq 'DateTime';
139 if ($self->params->{to_date}) {
140 $to_date = DateTime->from_kivitendo($self->params->{to_date});
141 # Not undef and no other type.
142 die unless ref $to_date eq 'DateTime';
145 die t8("Cannot convert date.") ."\n" .
146 t8("Input from string: #1", $self->params->{from_date}) . "\n" .
147 t8("Input to string: #1", $self->params->{to_date}) . "\n" .
148 t8("Details: #1", $_);
151 $self->params->{from_date} = $from_date;
152 $self->params->{to_date} = $to_date;
154 $self->params->{email_from} = trim($self->params->{email_from});
155 die t8('No email sender address given.') if !$self->params->{email_from};
157 return $self->params;
160 sub generate_message {
161 my ($self, $follow_ups) = @_;
163 # Force scripts/locales.pl to parse this template:
164 # parse_html_template('fu/follow_up_reminder_mail')
165 my $email_template = $self->params->{email_template};
166 my $template = 'fu/follow_up_reminder_mail';
167 my $content_type = 'text/html';
168 my $render_type = 'html';
170 if ($email_template) {
173 $content = slurp $email_template;
176 $::lxdebug->message(LXDebug->WARN(), 'Cannot read template file. Error: ' . $_);
179 return $self->generate_message_simple_text($follow_ups) if !$content;
181 $template = \$content;
182 $content_type = $email_template =~ m/.html$/ ? 'text/html' : 'text/plain';
183 $render_type = $email_template =~ m/.html$/ ? 'html' : 'text';
186 my $output = SL::Presenter->get->render($template,
187 {type => $render_type},
188 follow_ups => $follow_ups);
190 return ($output, $content_type);
193 sub generate_message_simple_text {
194 my ($self, $follow_ups) = @_;
196 my $output = t8('Unfinished follow-ups') . ":\n";
197 foreach my $follow_up (@$follow_ups) {
198 $output .= t8('Follow-Up Date') . ': ' . $follow_up->follow_up_date_as_date;
199 $output .= ': ' . $follow_up->note->subject;
200 $output .= ': (' . t8('by') . ' ' . $follow_up->created_by_employee->safe_name . ')';
201 $output .= ' (' . $follow_up->{link} . ')';
207 return ($output, 'text/plain');
220 SL::BackgroundJob::SendFollowUpReminder - Send emails to employees to
221 remind them of due follow ups
225 Get all due follow ups. This are the ones that are not done yet and have a
226 follow up date until today (configurable, see below).
227 For each employee addreesed by this follow ups, an email is send (if the
228 employees email address is configured).
232 In the kivitendo configuration file (C<config/kivitendo.conf>) in the section
233 "follow_up_reminder" some settings can be made:
239 The senders email address. This information can be overwriten by
240 data provided to the background job.
242 =item C<email_subject>
244 The subject of the emails. This information can be overwriten by
245 data provided to the background job.
247 =item C<email_template>
249 A template file used to generate the emails content. It will be given an
250 array of the follow ups in the template variable C<follow_ups>.
251 You can provide a text or a html template.
252 If not given, it defaults to C<fu/follow_up_reminder_mail.html> in the
254 If given, but not found, a simple text version will be generated as
256 This information can be overwriten by data provided to the background job.
260 Also some data can be provided to configure this backgroung job.
261 If there is data provided and it cannot be validated the background job
266 from_date: 01.01.2022
274 The date from which on follow ups not done yet should be collected. It defaults
275 undef which means from the beginning on.
277 Example (format depends on your settings):
279 from_date: 01.01.2022
283 The date till which follow ups not done yet should be collected. It defaults
286 Example (format depends on your settings):
292 The senders email address. It defaults to the one given in the kivitendo
293 configuration file (see above). This information must be provided some
298 email_from: bernd@kivitendo.de
300 =item C<email_subject>
302 The subject of the emails.
303 It defaults to the one given in the kvitendo configuration file (see above).
307 =item C<email_template>
309 A template file used to generate the emails content. It will be given an
310 array of the follow ups in the template variable C<follow_ups>. You can
311 provide a text or a html template.
312 It defaults to the one given in the kvitendo configuration file (see above).
314 email_template: templates/my_templates/my_reminder_template.txt
320 Bernd Bleßmann E<lt>bernd@kivitendo-premium.deE<gt>