use Config::Std;
use DateTime::Format::Strptime;
use English qw(-no_match_vars);
+use List::MoreUtils qw(uniq);
use SL::DB::AuthUser;
use SL::DB::Default;
use SL::DB::Invoice;
use SL::DB::PeriodicInvoice;
use SL::DB::PeriodicInvoicesConfig;
+use SL::Helper::CreatePDF qw(create_pdf find_template);
use SL::Mailer;
+use SL::Util qw(trim);
sub create_job {
$_[0]->create_standard_job('0 3 1 * *'); # first day of month at 3:00 am
my $self = shift;
$self->{db_obj} = shift;
+ $self->{job_errors} = [];
+
my $configs = SL::DB::Manager::PeriodicInvoicesConfig->get_all(query => [ active => 1 ]);
foreach my $config (@{ $configs }) {
_log_msg("Periodic invoice configuration ID " . $config->id . " extended through " . $new_end_date->strftime('%d.%m.%Y') . "\n") if $new_end_date;
}
- my (@new_invoices, @invoices_to_print);
+ my (@new_invoices, @invoices_to_print, @invoices_to_email);
_log_msg("Number of configs: " . scalar(@{ $configs}));
_log_msg("Dates: " . join(' ', map { $_->to_lxoffice } @dates));
foreach my $date (@dates) {
- my $invoice = $self->_create_periodic_invoice($config, $date);
- next unless $invoice;
+ my $data = $self->_create_periodic_invoice($config, $date);
+ next unless $data;
+
+ _log_msg("Invoice " . $data->{invoice}->invnumber . " posted for config ID " . $config->id . ", period start date " . $::locale->format_date(\%::myconfig, $date) . "\n");
- _log_msg("Invoice " . $invoice->invnumber . " posted for config ID " . $config->id . ", period start date " . $::locale->format_date(\%::myconfig, $date) . "\n");
- push @new_invoices, $invoice;
- push @invoices_to_print, [ $invoice, $config ] if $config->print;
+ push @new_invoices, $data;
+ push @invoices_to_print, $data if $config->print;
+ push @invoices_to_email, $data if $config->send_email;
# last;
}
}
- _print_invoice(@{ $_ }) for @invoices_to_print;
+ $self->_print_invoice($_) for @invoices_to_print;
+ $self->_email_invoice($_) for @invoices_to_email;
- _send_email(\@new_invoices, [ map { $_->[0] } @invoices_to_print ]) if @new_invoices;
+ $self->_send_summary_email(
+ [ map { $_->{invoice} } @new_invoices ],
+ [ map { $_->{invoice} } @invoices_to_print ],
+ [ map { $_->{invoice} } @invoices_to_email ],
+ );
+
+ if (@{ $self->{job_errors} }) {
+ my $msg = join "\n", @{ $self->{job_errors} };
+ _log_msg("Errors: $msg");
+ die $msg;
+ }
return 1;
}
sub _generate_time_period_variables {
my $config = shift;
my $period_start_date = shift;
- my $period_end_date = $period_start_date->clone->truncate(to => 'month')->add(months => $config->get_billing_period_length)->subtract(days => 1);
+ my $period_end_date = $period_start_date->clone->add(months => $config->get_billing_period_length)->subtract(days => 1);
my @month_names = ('',
$::locale->text('January'), $::locale->text('February'), $::locale->text('March'), $::locale->text('April'), $::locale->text('May'), $::locale->text('June'),
previous_year => [ $period_start_date->clone->truncate(to => 'year')->subtract(years => 1), sub { $_[0]->year } ],
next_year => [ $period_start_date->clone->truncate(to => 'year')->add( years => 1), sub { $_[0]->year } ],
- period_start_date => [ $period_start_date->clone->truncate(to => 'month'), sub { $::locale->format_date(\%::myconfig, $_[0]) } ],
- period_end_date => [ $period_end_date, sub { $::locale->format_date(\%::myconfig, $_[0]) } ],
+ period_start_date => [ $period_start_date->clone, sub { $::locale->format_date(\%::myconfig, $_[0]) } ],
+ period_end_date => [ $period_end_date, sub { $::locale->format_date(\%::myconfig, $_[0]) } ],
};
return $vars;
}
sub _create_periodic_invoice {
- $main::lxdebug->enter_sub();
-
my $self = shift;
my $config = shift;
my $period_start_date = shift;
my $order = $config->order;
my $invoice;
- if (!$self->{db_obj}->db->do_transaction(sub {
+ if (!$self->{db_obj}->db->with_transaction(sub {
1; # make Emacs happy
$invoice = SL::DB::Invoice->new_from($order);
$invoice->assign_attributes(deliverydate => $period_start_date,
intnotes => $intnotes,
employee => $order->employee, # new_from sets employee to import user
+ direct_debit => $config->direct_debit,
);
_replace_vars(object => $invoice, vars => $time_period_vars, attribute => $_, attribute_format => ($_ eq 'notes' ? 'html' : 'text')) for qw(notes intnotes transaction_description);
$invoice->post(ar_id => $config->ar_chart_id) || die;
- # like $form->add_shipto, but we don't need to check for a manual exception,
- # because we can already assume this (otherwise no shipto_id from order)
- if ($order->shipto_id) {
-
- my $shipto_oe = SL::DB::Manager::Shipto->find_by(shipto_id => $order->shipto_id);
- my $shipto_ar = $shipto_oe->clone_and_reset;
-
- $shipto_ar->module('AR'); # alter module OE -> AR
- $shipto_ar->trans_id($invoice->id); # alter trans_id -> new id from invoice
- $shipto_ar->save;
- }
-
$order->link_to_record($invoice);
foreach my $item (@{ $invoice->items }) {
_log_msg("_create_invoice created for period start date $period_start_date id " . $invoice->id . " number " . $invoice->invnumber . " netamount " . $invoice->netamount . " amount " . $invoice->amount);
# die $invoice->transaction_description;
+
+ 1;
})) {
$::lxdebug->message(LXDebug->WARN(), "_create_invoice failed: " . join("\n", (split(/\n/, $self->{db_obj}->db->error))[0..2]));
return undef;
}
- $main::lxdebug->leave_sub();
- return $invoice;
+
+ return {
+ config => $config,
+ period_start_date => $period_start_date,
+ invoice => $invoice,
+ time_period_vars => $time_period_vars,
+ };
}
sub _calculate_dates {
return $config->calculate_invoice_dates(end_date => DateTime->today_local);
}
-sub _send_email {
- my ($posted_invoices, $printed_invoices) = @_;
+sub _send_summary_email {
+ my ($self, $posted_invoices, $printed_invoices, $emailed_invoices) = @_;
my %config = %::lx_office_conf;
my $email_template = $config{periodic_invoices}->{email_template};
my $filename = $email_template || ( (SL::DB::Default->get->templates || "templates/webpages") . "/oe/periodic_invoices_email.txt" );
my %params = ( POSTED_INVOICES => $posted_invoices,
- PRINTED_INVOICES => $printed_invoices );
+ PRINTED_INVOICES => $printed_invoices,
+ EMAILED_INVOICES => $emailed_invoices );
my $output;
$template->process($filename, \%params, \$output);
}
sub _print_invoice {
- my ($invoice, $config) = @_;
+ my ($self, $data) = @_;
+
+ my $invoice = $data->{invoice};
+ my $config = $data->{config};
return unless $config->print && $config->printer_id && $config->printer->printer_command;
$form->{OUT} = $config->printer->printer_command;
$form->{OUT_MODE} = '|-';
+ $form->{TEMPLATE_DRIVER_OPTIONS} = {
+ variable_content_types => {
+ longdescription => 'html',
+ partnotes => 'html',
+ notes => 'html',
+ },
+ };
+
$form->prepare_for_printing;
$form->throw_on_error(sub {
eval {
$form->parse_template(\%::myconfig);
1;
- } || die $EVAL_ERROR->getMessage;
+ } or do {
+ push @{ $self->{job_errors} }, $EVAL_ERROR->getMessage;
+ };
});
}
+sub _email_invoice {
+ my ($self, $data) = @_;
+
+ $data->{config}->load;
+
+ return unless $data->{config}->send_email;
+
+ my @recipients =
+ uniq
+ map { lc }
+ grep { $_ }
+ map { trim($_) }
+ (split(m{,}, $data->{config}->email_recipient_address),
+ $data->{config}->email_recipient_contact ? ($data->{config}->email_recipient_contact->cp_email) : ());
+
+ return unless @recipients;
+
+ my %create_params = (
+ template => $self->find_template(name => 'invoice'),
+ variables => Form->new(''),
+ return => 'file_name',
+ variable_content_types => {
+ longdescription => 'html',
+ partnotes => 'html',
+ notes => 'html',
+ },
+ );
+
+ $data->{invoice}->flatten_to_form($create_params{variables}, format_amounts => 1);
+ $create_params{variables}->prepare_for_printing;
+
+ my $pdf_file_name;
+
+ eval {
+ $pdf_file_name = $self->create_pdf(%create_params);
+
+ for (qw(email_subject email_body)) {
+ _replace_vars(
+ object => $data->{config},
+ vars => $data->{time_period_vars},
+ attribute => $_,
+ attribute_format => 'text'
+ );
+ }
+
+ for my $recipient (@recipients) {
+ my $mail = Mailer->new;
+ $mail->{from} = $data->{config}->email_sender || $::lx_office_conf{periodic_invoices}->{email_from};
+ $mail->{to} = $recipient;
+ $mail->{subject} = $data->{config}->email_subject;
+ $mail->{message} = $data->{config}->email_body;
+ $mail->{attachments} = [{
+ filename => $pdf_file_name,
+ name => sprintf('%s %s.pdf', $::locale->text('Invoice'), $data->{invoice}->invnumber),
+ }];
+
+ my $error = $mail->send;
+
+ push @{ $self->{job_errors} }, $error if $error;
+ }
+
+ 1;
+
+ } or do {
+ push @{ $self->{job_errors} }, $EVAL_ERROR;
+ };
+
+ unlink $pdf_file_name if $pdf_file_name;
+}
+
1;
__END__