1 package SL::BackgroundJob::CreatePeriodicInvoices;
5 use parent qw(SL::BackgroundJob::Base);
8 use English qw(-no_match_vars);
13 use SL::DB::PeriodicInvoice;
14 use SL::DB::PeriodicInvoicesConfig;
18 $_[0]->create_standard_job('0 3 1 * *'); # first day of month at 3:00 am
23 $self->{db_obj} = shift;
25 my $configs = SL::DB::Manager::PeriodicInvoicesConfig->get_all(query => [ active => 1 ]);
27 foreach my $config (@{ $configs }) {
28 my $new_end_date = $config->handle_automatic_extension;
29 _log_msg("Periodic invoice configuration ID " . $config->id . " extended through " . $new_end_date->strftime('%d.%m.%Y') . "\n") if $new_end_date;
32 my (@new_invoices, @invoices_to_print);
34 _log_msg("Number of configs: " . scalar(@{ $configs}));
36 foreach my $config (@{ $configs }) {
37 # A configuration can be set to inactive by
38 # $config->handle_automatic_extension. Therefore the check in
39 # ...->get_all() does not suffice.
40 _log_msg("Config " . $config->id . " active " . $config->active);
41 next unless $config->active;
43 my @dates = _calculate_dates($config);
45 _log_msg("Dates: " . join(' ', map { $_->to_lxoffice } @dates));
47 foreach my $date (@dates) {
48 my $invoice = $self->_create_periodic_invoice($config, $date);
51 _log_msg("Invoice " . $invoice->invnumber . " posted for config ID " . $config->id . ", period start date " . $::locale->format_date(\%::myconfig, $date) . "\n");
52 push @new_invoices, $invoice;
53 push @invoices_to_print, [ $invoice, $config ] if $config->print;
59 map { _print_invoice(@{ $_ }) } @invoices_to_print;
61 _send_email(\@new_invoices, [ map { $_->[0] } @invoices_to_print ]) if @new_invoices;
67 # my $message = join('', @_);
68 # $message .= "\n" unless $message =~ m/\n$/;
69 # $::lxdebug->message(0, $message);
72 sub _generate_time_period_variables {
74 my $period_start_date = shift;
75 my $period_end_date = $period_start_date->clone->truncate(to => 'month')->add(months => $config->get_period_length)->subtract(days => 1);
77 my @month_names = ('',
78 'Januar', 'Februar', 'März', 'April', 'Mai', 'Juni',
79 'Juli', 'August', 'September', 'Oktober', 'November', 'Dezember');
81 my $vars = { current_quarter => $period_start_date->quarter,
82 previous_quarter => $period_start_date->clone->subtract(months => 3)->quarter,
83 next_quarter => $period_start_date->clone->add( months => 3)->quarter,
85 current_month => $period_start_date->month,
86 previous_month => $period_start_date->clone->subtract(months => 1)->month,
87 next_month => $period_start_date->clone->add( months => 1)->month,
89 current_year => $period_start_date->year,
90 previous_year => $period_start_date->year - 1,
91 next_year => $period_start_date->year + 1,
93 period_start_date => $::locale->format_date(\%::myconfig, $period_start_date),
94 period_end_date => $::locale->format_date(\%::myconfig, $period_end_date),
97 map { $vars->{"${_}_month_long"} = $month_names[ $vars->{"${_}_month"} ] } qw(current previous next);
106 my $str = $object->$sub;
109 $str =~ s|<\%${key}\%>|$value|g while ($key, $value) = each %{ $vars };
113 sub _create_periodic_invoice {
116 my $period_start_date = shift;
118 my $time_period_vars = _generate_time_period_variables($config, $period_start_date);
120 my $invdate = DateTime->today_local;
122 my $order = $config->order;
124 if (!$self->{db_obj}->db->do_transaction(sub {
125 1; # make Emacs happy
127 $invoice = SL::DB::Invoice->new_from($order);
129 my $intnotes = $invoice->intnotes ? $invoice->intnotes . "\n\n" : '';
130 $intnotes .= "Automatisch am " . $invdate->to_lxoffice . " erzeugte Rechnung";
132 $invoice->assign_attributes(deliverydate => $period_start_date,
133 intnotes => $intnotes,
136 map { _replace_vars($invoice, $time_period_vars, $_) } qw(notes intnotes transaction_description);
138 foreach my $item (@{ $invoice->items }) {
139 map { _replace_vars($item, $time_period_vars, $_) } qw(description longdescription);
142 $invoice->post(ar_id => $config->ar_chart_id) || die;
144 $order->link_to_record($invoice);
146 SL::DB::PeriodicInvoice->new(config_id => $config->id,
147 ar_id => $invoice->id,
148 period_start_date => $period_start_date)
151 # die $invoice->transaction_description;
153 $::lxdebug->message(LXDebug->WARN(), "_create_invoice failed: " . join("\n", (split(/\n/, $self->{db_obj}->db->error))[0..2]));
160 sub _calculate_dates {
163 my $cur_date = $config->start_date;
164 my $start_date = $config->get_previous_invoice_date || DateTime->new(year => 1970, month => 1, day => 1);
165 my $end_date = $config->end_date || DateTime->new(year => 2100, month => 1, day => 1);
166 my $tomorrow = DateTime->today_local->add(days => 1);
167 my $period_len = $config->get_period_length;
169 $end_date = $tomorrow if $end_date > $tomorrow;
174 last if $cur_date >= $end_date;
176 push @dates, $cur_date->clone if $cur_date > $start_date;
178 $cur_date->add(months => $period_len);
185 my ($posted_invoices, $printed_invoices) = @_;
187 my %config = %::lx_office_conf;
189 return if !$config{periodic_invoices} || !$config{periodic_invoices}->{send_email_to} || !scalar @{ $posted_invoices };
191 my $user = SL::DB::Manager::AuthUser->find_by(login => $config{periodic_invoices}->{send_email_to});
192 my $email = $user ? $user->get_config_value('email') : undef;
194 return unless $email;
196 my $template = Template->new({ 'INTERPOLATE' => 0,
202 return unless $template;
204 my $email_template = $config{periodic_invoices}->{email_template};
205 my $filename = $email_template || ( ($user->get_config_value('templates') || "templates/webpages") . "/periodic_invoices_email.txt" );
206 my %params = ( POSTED_INVOICES => $posted_invoices,
207 PRINTED_INVOICES => $printed_invoices );
210 $template->process($filename, \%params, \$output);
212 my $mail = Mailer->new;
213 $mail->{from} = $config{periodic_invoices}->{email_from};
214 $mail->{to} = $email;
215 $mail->{subject} = $config{periodic_invoices}->{email_subject};
216 $mail->{content_type} = $filename =~ m/.html$/ ? 'text/html' : 'text/plain';
217 $mail->{message} = $output;
223 my ($invoice, $config) = @_;
225 return unless $config->print && $config->printer_id && $config->printer->printer_command;
227 my $form = Form->new;
228 $invoice->flatten_to_form($form, format_amounts => 1);
230 $form->{printer_code} = $config->printer->template_code;
231 $form->{copies} = $config->copies;
232 $form->{formname} = $form->{type};
233 $form->{format} = 'pdf';
234 $form->{media} = 'printer';
235 $form->{OUT} = "| " . $config->printer->printer_command;
237 $form->prepare_for_printing;
239 $form->throw_on_error(sub {
241 $form->parse_template(\%::myconfig);
243 } || die $EVAL_ERROR->getMessage;
257 SL::BackgroundJob::CleanBackgroundJobHistory - Create periodic
262 Iterate over all periodic invoice configurations, extend them if
263 applicable, calculate the dates for which invoices have to be posted
264 and post those invoices by converting the order into an invoice for
273 Strings like month names are hardcoded to German in this file.
279 Moritz Bunkus E<lt>m.bunkus@linet-services.deE<gt>