Projekt

Allgemein

Profil

Herunterladen (17 KB) Statistiken
| Zweig: | Markierung: | Revision:
b7e394f2 Jan Büren
package SL::Controller::MassInvoiceCreatePrint;

use strict;

use parent qw(SL::Controller::Base);

use File::Slurp ();
use List::MoreUtils qw(all uniq);
use List::Util qw(first min);

use SL::BackgroundJob::MassRecordCreationAndPrinting;
use SL::Controller::Helper::GetModels;
use SL::DB::DeliveryOrder;
use SL::DB::Order;
use SL::DB::Printer;
54ce5144 Martin Helmling
use SL::Helper::MassPrintCreatePDF qw(:all);
b7e394f2 Jan Büren
use SL::Helper::CreatePDF qw(:all);
141b46df Werner Hahn
use SL::Helper::File qw(store_pdf append_general_pdf_attachments doc_storage_enabled);
b7e394f2 Jan Büren
use SL::Helper::Flash;
use SL::Locale::String;
use SL::SessionFile;
use SL::System::TaskServer;
use Rose::Object::MakeMethods::Generic
(
fb5900bf Bernd Bleßmann
'scalar --get_set_init' => [ qw(invoice_models invoice_ids sales_delivery_order_models printers default_printer_id today all_businesses) ],
b7e394f2 Jan Büren
);

__PACKAGE__->run_before('setup');

#
# actions
#

sub action_list_sales_delivery_orders {
my ($self) = @_;

# default is usually no show, exception here
my $show = ($::form->{noshow} ? 0 : 1);
delete $::form->{noshow};

# if a filter is choosen, the filter info should be visible
$self->make_filter_summary;
31f98925 Moritz Bunkus
$self->setup_list_sales_delivery_orders_action_bar(show_creation_buttons => $show, num_rows => scalar(@{ $self->sales_delivery_order_models->get }));
b7e394f2 Jan Büren
$self->render('mass_invoice_create_print_from_do/list_sales_delivery_orders',
noshow => $show,
title => $::locale->text('Open sales delivery orders'));
}

sub action_create_invoices {
my ($self) = @_;

my @sales_delivery_order_ids = @{ $::form->{id} || [] };
if (!@sales_delivery_order_ids) {
# should never be executed, double catch via js
flash_later('error', t8('No delivery orders have been selected.'));
return $self->redirect_to(action => 'list_sales_delivery_orders');
}

my $db = SL::DB::Invoice->new->db;
96670fe8 Moritz Bunkus
my @invoices;
b7e394f2 Jan Büren
96670fe8 Moritz Bunkus
if (!$db->with_transaction(sub {
b7e394f2 Jan Büren
foreach my $id (@sales_delivery_order_ids) {
my $delivery_order = SL::DB::DeliveryOrder->new(id => $id)->load;

my $invoice = $delivery_order->convert_to_invoice() || die $db->error;
push @invoices, $invoice;
}

1;
})) {
$::lxdebug->message(LXDebug::WARN(), "Error: " . $db->error);
$::form->error($db->error);
}
96670fe8 Moritz Bunkus
my $key = sprintf('%d-%d', Time::HiRes::gettimeofday());
$::auth->set_session_value("MassInvoiceCreatePrint::ids-${key}" => [ map { $_->id } @invoices ]);

flash_later('info', t8('The invoices have been created. They\'re pre-selected below.'));
$self->redirect_to(action => 'list_invoices', ids => $key);
b7e394f2 Jan Büren
}

sub action_list_invoices {
my ($self) = @_;

my $show = $::form->{noshow} ? 0 : 1;
delete $::form->{noshow};

if ($::form->{ids}) {
my $key = 'MassInvoiceCreatePrint::ids-' . $::form->{ids};
$self->invoice_ids($::auth->get_session_value($key) || []);
$self->invoice_models->add_additional_url_params(ids => $::form->{ids});
}

my %selected_ids = map { +($_ => 1) } @{ $self->invoice_ids };

$::form->{printer_id} ||= $self->default_printer_id;

fdbdd0b1 Moritz Bunkus
$self->setup_list_invoices_action_bar(num_rows => scalar(@{ $self->invoice_models->get }));

b7e394f2 Jan Büren
$self->render('mass_invoice_create_print_from_do/list_invoices',
title => $::locale->text('Open invoice'),
noshow => $show,
selected_ids => \%selected_ids);
}

sub action_print {
my ($self) = @_;

my @invoices = map { SL::DB::Invoice->new(id => $_)->load } @{ $::form->{id} || [] };
if (!@invoices) {
flash_later('error', t8('No invoices have been selected.'));
return $self->redirect_to(action => 'list_invoices');
}

8298c2ea Bernd Bleßmann
$self->download_or_print_documents(printer_id => $::form->{printer_id}, invoices => \@invoices, bothsided => $::form->{bothsided});
b7e394f2 Jan Büren
}

sub action_create_print_all_start {
my ($self) = @_;

$self->sales_delivery_order_models->disable_plugin('paginated');

b74a00e5 Jan Büren
my @records = @{ $self->sales_delivery_order_models->get };
b7e394f2 Jan Büren
my $num = min(scalar(@records), $::form->{number_of_invoices} // scalar(@records));

my $job = SL::DB::BackgroundJob->new(
type => 'once',
active => 1,
package_name => 'MassRecordCreationAndPrinting',

)->set_data(
record_ids => [ map { $_->id } @records[0..$num - 1] ],
printer_id => $::form->{printer_id},
b74a00e5 Jan Büren
copy_printer_id => $::form->{copy_printer_id},
54ce5144 Martin Helmling
bothsided => ($::form->{bothsided}?1:0),
b74a00e5 Jan Büren
transdate => $::form->{transdate},
b7e394f2 Jan Büren
status => SL::BackgroundJob::MassRecordCreationAndPrinting->WAITING_FOR_EXECUTION(),
num_created => 0,
num_printed => 0,
invoice_ids => [ ],
conversion_errors => [ ],
print_errors => [ ],
ee75e598 Martin Helmling
session_id => $::auth->get_session_id,
b7e394f2 Jan Büren
)->update_next_run_at;

SL::System::TaskServer->new->wake_up;

my $html = $self->render('mass_invoice_create_print_from_do/_create_print_all_status', { output => 0 }, job => $job);

$self->js
->html('#create_print_all_dialog', $html)
->run('kivi.MassInvoiceCreatePrint.massConversionStarted')
->render;
}

sub action_create_print_all_status {
my ($self) = @_;
my $job = SL::DB::BackgroundJob->new(id => $::form->{job_id})->load;
my $html = $self->render('mass_invoice_create_print_from_do/_create_print_all_status', { output => 0 }, job => $job);

$self->js->html('#create_print_all_dialog', $html);
$self->js->run('kivi.MassInvoiceCreatePrint.massConversionFinished') if $job->data_as_hash->{status} == SL::BackgroundJob::MassRecordCreationAndPrinting->DONE();
$self->js->render;
}

sub action_create_print_all_download {
my ($self) = @_;
my $job = SL::DB::BackgroundJob->new(id => $::form->{job_id})->load;

my $sfile = SL::SessionFile->new($job->data_as_hash->{pdf_file_name}, mode => 'r');
die $! if !$sfile->fh;

my $merged_pdf = do { local $/; my $fh = $sfile->fh; <$fh> };
$sfile->fh->close;

my $type = 'Invoices';
54ce5144 Martin Helmling
my $file_name = t8($type) . '-' . DateTime->now_local->strftime('%Y%m%d%H%M%S') . '.pdf';
b7e394f2 Jan Büren
$file_name =~ s{[^\w\.]+}{_}g;

return $self->send_file(
\$merged_pdf,
type => 'application/pdf',
name => $file_name,
);
}

#
# filters
#

sub init_printers { SL::DB::Manager::Printer->get_all_sorted }
54ce5144 Martin Helmling
#sub init_att { require SL::Controller::Attachments; SL::Controller::Attachments->new() }
b7e394f2 Jan Büren
sub init_invoice_ids { [] }
b74a00e5 Jan Büren
sub init_today { DateTime->today_local }
b7e394f2 Jan Büren
sub init_sales_delivery_order_models {
my ($self) = @_;
return $self->_init_sales_delivery_order_models(sortby => 'donumber');
}

sub _init_sales_delivery_order_models {
my ($self, %params) = @_;

SL::Controller::Helper::GetModels->new(
controller => $_[0],
model => 'DeliveryOrder',
# model => 'Order',
sorted => {
_default => {
by => $params{sortby},
dir => 1,
},
customer => t8('Customer'),
employee => t8('Employee'),
0070a250 Andreas Rudin
transdate => t8('Delivery Order Date'),
b7e394f2 Jan Büren
donumber => t8('Delivery Order Number'),
ordnumber => t8('Order Number'),
},
with_objects => [ qw(customer employee) ],
query => [
'!customer_id' => undef,
or => [ closed => undef, closed => 0 ],
],
);
}


sub init_invoice_models {
my ($self) = @_;
my @invoice_ids = @{ $self->invoice_ids };

SL::Controller::Helper::GetModels->new(
controller => $_[0],
model => 'Invoice',
(paginated => 0,) x !!@invoice_ids,
sorted => {
_default => {
by => 'transdate',
dir => 0,
},
customer => t8('Customer'),
invnumber => t8('Invoice Number'),
employee => t8('Employee'),
donumber => t8('Delivery Order Number'),
ordnumber => t8('Order Number'),
reqdate => t8('Delivery Date'),
transdate => t8('Date'),
},
with_objects => [ qw(customer employee) ],
query => [
'!customer_id' => undef,
(id => \@invoice_ids) x !!@invoice_ids,
],
);
}


sub init_default_printer_id {
my $pr = SL::DB::Manager::Printer->find_by(printer_description => $::locale->text("sales_invoice_printer"));
return $pr ? $pr->id : undef;
}

fb5900bf Bernd Bleßmann
sub init_all_businesses {
return SL::DB::Manager::Business->get_all_sorted;
}

b7e394f2 Jan Büren
sub setup {
my ($self) = @_;
$::auth->assert('invoice_edit');

$::request->layout->use_javascript("${_}.js") for qw(kivi.MassInvoiceCreatePrint);
}

#
# helpers
#


sub download_or_print_documents {
my ($self, %params) = @_;

my @pdf_file_names;

eval {
my %pdf_params = (
54ce5144 Martin Helmling
documents => $params{invoices},
b7e394f2 Jan Büren
variables => {
type => 'invoice',
formname => 'invoice',
format => 'pdf',
media => $params{printer_id} ? 'printer' : 'file',
54ce5144 Martin Helmling
printer_id => $params{printer_id},
b7e394f2 Jan Büren
});

@pdf_file_names = $self->create_pdfs(%pdf_params);
8298c2ea Bernd Bleßmann
my $merged_pdf = $self->merge_pdfs(file_names => \@pdf_file_names, bothsided => $params{bothsided});
b7e394f2 Jan Büren
unlink @pdf_file_names;

if (!$params{printer_id}) {
54ce5144 Martin Helmling
my $file_name = t8("Invoices") . '-' . DateTime->now_local->strftime('%Y%m%d%H%M%S') . '.pdf';
b7e394f2 Jan Büren
$file_name =~ s{[^\w\.]+}{_}g;

return $self->send_file(
\$merged_pdf,
type => 'application/pdf',
name => $file_name,
);
}

my $printer = SL::DB::Printer->new(id => $params{printer_id})->load;
f08036d7 Moritz Bunkus
$printer->print_document(content => $merged_pdf);
b7e394f2 Jan Büren
flash_later('info', t8('The documents have been sent to the printer \'#1\'.', $printer->printer_description));
return $self->redirect_to(action => 'list_invoices', printer_id => $params{printer_id});

} or do {
unlink @pdf_file_names;
$::form->error(t8("Creating the PDF failed:") . " " . $@);
};
}

sub make_filter_summary {
my ($self) = @_;

my $filter = $::form->{filter} || {};
my @filter_strings;

my @filters = (
[ $filter->{customer}{"name:substr::ilike"}, t8('Customer') ],
0070a250 Andreas Rudin
[ $filter->{"transdate:date::ge"}, t8('Delivery Order Date') . " " . t8('From Date') ],
[ $filter->{"transdate:date::le"}, t8('Delivery Order Date') . " " . t8('To Date') ],
b7e394f2 Jan Büren
);

for (@filters) {
push @filter_strings, "$_->[1]: " . ($_->[2] ? $_->[2]->() : $_->[0]) if $_->[0];
}

$self->{filter_summary} = join ', ', @filter_strings;
}
fdbdd0b1 Moritz Bunkus
sub setup_list_invoices_action_bar {
my ($self, %params) = @_;

for my $bar ($::request->layout->get('actionbar')) {
$bar->add(
action => [
f372dca8 Moritz Bunkus
t8('Update'),
fdbdd0b1 Moritz Bunkus
submit => [ '#search_form', { action => 'MassInvoiceCreatePrint/list_invoices' } ],
accesskey => 'enter',
],
action => [
$::locale->text('Print'),
call => [ 'kivi.MassInvoiceCreatePrint.showMassPrintOptionsOrDownloadDirectly' ],
disabled => !$params{num_rows} ? $::locale->text('The report doesn\'t contain entries.') : undef,
],
);
}
}

31f98925 Moritz Bunkus
sub setup_list_sales_delivery_orders_action_bar {
my ($self, %params) = @_;

for my $bar ($::request->layout->get('actionbar')) {
$bar->add(
action => [
f372dca8 Moritz Bunkus
$params{show_creation_buttons} ? t8('Update') : t8('Search'),
31f98925 Moritz Bunkus
submit => [ '#search_form', { action => 'MassInvoiceCreatePrint/list_sales_delivery_orders' } ],
accesskey => 'enter',
],

combobox => [
action => [
t8('Invoices'),
tooltip => t8("Create and print invoices")
],
action => [
c9947d8e Moritz Bunkus
t8("Create and print invoices for all selected delivery orders"),
d8930664 Bernd Bleßmann
submit => [ 'form', { action => 'MassInvoiceCreatePrint/create_invoices' } ],
b060528d Bernd Bleßmann
disabled => !$params{num_rows} ? $::locale->text('The report doesn\'t contain entries.') : undef,
only_if => $params{show_creation_buttons},
checks => [ 'kivi.MassInvoiceCreatePrint.checkDeliveryOrderSelection' ],
only_once => 1,
31f98925 Moritz Bunkus
],

action => [
c9947d8e Moritz Bunkus
t8("Create and print invoices for all delivery orders matching the filter"),
31f98925 Moritz Bunkus
call => [ 'kivi.MassInvoiceCreatePrint.createPrintAllInitialize' ],
disabled => !$params{num_rows} ? $::locale->text('The report doesn\'t contain entries.') : undef,
only_if => $params{show_creation_buttons},
],
],
);
}
}

b7e394f2 Jan Büren
1;

__END__

=pod

=encoding utf8

=head1 NAME

SL::Controller::MassInvoiceCreatePrint - Controller for Mass Create Print Sales Invoice from Delivery Order

=head2 OVERVIEW

Controller class for the conversion and processing (printing) of objects.


Inherited from the base controller class, this controller implements the Sales Mass Invoice Creation.
In general there are two major distinctions:
This class implements the conversion and the printing via clickable action AND triggers the same
conversion towards a Background-Job with a good user interaction.

Analysis hints: All this is more or less boilerplate code around the great convert_to_invoice method
in DeliverOrder.pm. If you need to debug stuff, take a look at the corresponding test case
($ t/test.pl t/db_helper/convert_invoices.t). There are some redundant code parts in this controller
and in the background job, i.e. compare the actions create and print.
From a reverse engineering point of view the actions in this controller were written before the
background job existed, therefore if anything goes boom take a look at the single steps done via gui
in this controller and after that take a deeper look at the MassRecordCreationAndPrinting job.

=head1 FUNCTIONS

=over 2

=item C<action_list_sales_delivery_orders>

List all open sales delivery orders. The filter can be in two states show or "no show" the
original, probably gorash idea, is to increase performance and not to be forced to click on the
next button (like in all other reports). Therefore use this option and this filter for a good
project default and hide it again. Filters can be added in _filter.html. Take a look at
SL::Controlle::Helper::GetModels::Filtered.pm and SL::DB::Helper::Filtered.

=item C<action_create_invoices>

Creates or to be more correctly converts delivery orders to invoices. All items are
converted 1:1 and after conversion the delivery order(s) are closed.

=item C<action_list_invoices>

List the created invoices, if created via gui (see action above)

=item C<action_print>

Print the selected invoices. Yes, it really is all boring linear (see action above).
Calls download_or_print method.

=item C<action_create_print_all_start>

Initialises the webform for the creation and printing via background job. Now we get to
the more fun part ... Mosu did a great user interaction job here, we can choose how many
objects are converted in one strike and if or if not they are downloadable or will be sent to
a printer (if defined as a printing command) right away.
Background job is started and status is watched via js and the next action.

=item C<action_create_print_all_status>

Action for watching status, default is refreshing every 5 seconds

=item C<action_create_print_all_download>

If the above is done (did I already said: boring linear?). Documents will
be either printed or downloaded.

=item C<init_printers>

Gets all printer commands

=item C<init_invoice_ids>

Gets a list of (empty) invoice ids

b74a00e5 Jan Büren
=item C<init_today>

Gets the current day. Currently used in custom code.
Has to be initialised (get_set_init) and can be used as default for
a date tag like C<[% L.date_tag("transdate", SELF.today, id=transdate) %]>.

b7e394f2 Jan Büren
=item C<init_sales_delivery_order_models>

Calls _init_sales_delivery_order_models with a param

=item C<_init_sales_delivery_order_models>

Private function, called by init_sales_delivery_order_models.
Gets all open sales delivery orders.

=item C<init_invoice_models>

Gets all invoice_models via the ids in invoice_ids (at the beginning no ids exist)

=item C<init_default_printer_id>

b74a00e5 Jan Büren
Gets the default printer for sales_invoices. Currently this function is not called, but
might be useful in the next version.Calling template code and Controller already expect a default:
C<L.select_tag("", printers, title_key="description", default=SELF.default_printer_id, id="cpa_printer_id") %]>
b7e394f2 Jan Büren
=item C<setup>

Currently sets / checks only the access right.

=item C<create_pdfs>

=item C<download_or_print_documents>

Backend function for printing or downloading docs. Only used for gui processing (called
via action_print).

=item C<make_filter_summary>
Creates the filter option summary in the header. By the time of writing three filters are
supported: Customer and date from/to of the Delivery Order (database field transdate).

=back

=head1 TODO

31f98925 Moritz Bunkus
pShould be more generalized. Right now just one conversion (delivery order to invoice) is supported.
b7e394f2 Jan Büren
Using BackgroundJobs to mass create / transfer stuff is the way to do it. The original idea
was taken from one client project (mosu) with some extra (maybe not standard compliant) customized
stuff (using cvars for extra filters and a very compressed Controller for linking (ODSalesOrder.pm)).


=head1 AUTHOR

Moritz Bunkus E<lt>m.bunkus@linet-services.deE<gt>

Jan Büren E<lt>jan@kivitendo-premium.deE<gt>
b1a25bd3 Sven Schöling
b7e394f2 Jan Büren
=cut