MassRecordCreationAndPrinting: SessionFiles für TaskServer Fix
[kivitendo-erp.git] / SL / BackgroundJob / MassRecordCreationAndPrinting.pm
1 package SL::BackgroundJob::MassRecordCreationAndPrinting;
2
3 use strict;
4 use warnings;
5
6 use parent qw(SL::BackgroundJob::Base);
7
8 use SL::DB::DeliveryOrder;
9 use SL::DB::Order;  # origin order to delivery_order
10 use SL::DB::Invoice;
11 use SL::DB::Printer;
12 use SL::SessionFile;
13 use SL::Template;
14 use SL::Locale::String qw(t8);
15 use SL::Webdav;
16
17 use constant WAITING_FOR_EXECUTION       => 0;
18 use constant CONVERTING_DELIVERY_ORDERS  => 1;
19 use constant PRINTING_INVOICES           => 2;
20 use constant DONE                        => 3;
21 # Data format:
22 # my $data             = {
23 #   record_ids          => [ 123, 124, 127, ],
24 #   printer_id         => 4711,
25 #   copy_printer_id    => 4711,
26 #   transdate          => $today || $custom_transdate,
27 #   num_created        => 0,
28 #   num_printed        => 0,
29 #   invoice_ids        => [ 234, 235, ],
30 #   conversion_errors  => [ { id => 124, number => 'A981723', message => "Stuff went boom" }, ],
31 #   print_errors       => [ { id => 234, number => 'L87123123', message => "Printer is out of coffee" }, ],
32 #   pdf_file_name      => 'qweqwe.pdf',
33 #   session_id         => $::auth->get_session_id,
34 # };
35
36 sub create_invoices {
37   my ($self)  = @_;
38
39   my $job_obj = $self->{job_obj};
40   my $db      = $job_obj->db;
41
42   $job_obj->set_data(status => CONVERTING_DELIVERY_ORDERS())->save;
43
44   foreach my $delivery_order_id (@{ $job_obj->data_as_hash->{record_ids} }) {
45     my $number = $delivery_order_id;
46     my $data   = $job_obj->data_as_hash;
47
48     eval {
49       my $invoice;
50       my $sales_delivery_order = SL::DB::DeliveryOrder->new(id => $delivery_order_id)->load;
51       $number                  = $sales_delivery_order->donumber;
52
53       if (!$db->do_transaction(sub {
54         $invoice = $sales_delivery_order->convert_to_invoice(sub { $data->{transdate} ? ('attributes' => { transdate => $data->{transdate} }) :
55                                                                          undef }->() ) || die $db->error;
56         1;
57       })) {
58         die $db->error;
59       }
60
61       $data->{num_created}++;
62       push @{ $data->{invoice_ids} }, $invoice->id;
63       push @{ $self->{invoices}    }, $invoice;
64
65       1;
66     } or do {
67       push @{ $data->{conversion_errors} }, { id => $delivery_order_id, number => $number, message => $@ };
68     };
69
70     $job_obj->update_attributes(data_as_hash => $data);
71   }
72 }
73
74 sub convert_invoices_to_pdf {
75   my ($self) = @_;
76
77   return if !@{ $self->{invoices} };
78
79   my $job_obj = $self->{job_obj};
80   my $db      = $job_obj->db;
81
82   $job_obj->set_data(status => PRINTING_INVOICES())->save;
83
84   require SL::Controller::MassInvoiceCreatePrint;
85
86   my $printer_id = $job_obj->data_as_hash->{printer_id};
87   my $ctrl       = SL::Controller::MassInvoiceCreatePrint->new;
88   my %variables  = (
89     type         => 'invoice',
90     formname     => 'invoice',
91     format       => 'pdf',
92     media        => $printer_id ? 'printer' : 'file',
93   );
94
95   my @pdf_file_names;
96
97   foreach my $invoice (@{ $self->{invoices} }) {
98     my $data = $job_obj->data_as_hash;
99
100     eval {
101       my %create_params = (
102         template  => $ctrl->find_template(name => 'invoice', printer_id => $printer_id),
103         variables => Form->new(''),
104         return    => 'file_name',
105         variable_content_types => { longdescription => 'html',
106                                     partnotes       => 'html',
107                                     notes           => 'html',}
108       );
109
110
111
112       $create_params{variables}->{$_} = $variables{$_} for keys %variables;
113
114       $invoice->flatten_to_form($create_params{variables}, format_amounts => 1);
115       $create_params{variables}->prepare_for_printing;
116
117       push @pdf_file_names, $ctrl->create_pdf(%create_params);
118
119       # copy file to webdav folder
120       if ($::instance_conf->get_webdav_documents) {
121         my $webdav = SL::Webdav->new(
122           type     => 'invoice',
123           number   => $invoice->invnumber,
124         );
125         my $webdav_file = SL::Webdav::File->new(
126           webdav   => $webdav,
127           filename => t8('Invoice') . '_' . $invoice->invnumber . '.pdf',
128         );
129         eval {
130           $webdav_file->store(file => $pdf_file_names[-1]);
131           1;
132         } or do {
133           push @{ $data->{print_errors} }, { id => $invoice->id, number => $invoice->invnumber, message => $@ };
134         }
135       }
136
137       $data->{num_printed}++;
138
139       1;
140
141     } or do {
142       push @{ $data->{print_errors} }, { id => $invoice->id, number => $invoice->invnumber, message => $@ };
143     };
144
145     $job_obj->update_attributes(data_as_hash => $data);
146   }
147
148   if (@pdf_file_names) {
149     my $data = $job_obj->data_as_hash;
150
151     eval {
152       $self->{merged_pdf} = $ctrl->merge_pdfs(file_names => \@pdf_file_names);
153       unlink @pdf_file_names;
154
155       if (!$printer_id) {
156         my $file_name = 'mass_invoice' . $job_obj->id . '.pdf';
157         my $sfile     = SL::SessionFile->new($file_name, mode => 'w', session_id => $data->{session_id});
158         $sfile->fh->print($self->{merged_pdf});
159         $sfile->fh->close;
160
161         $data->{pdf_file_name} = $file_name;
162       }
163
164       1;
165
166     } or do {
167       push @{ $data->{print_errors} }, { message => $@ };
168     };
169
170     $job_obj->update_attributes(data_as_hash => $data);
171   }
172 }
173
174 sub print_pdfs {
175   my ($self)     = @_;
176
177   my $job_obj         = $self->{job_obj};
178   my $data            = $job_obj->data_as_hash;
179   my $printer_id      = $data->{printer_id};
180   my $copy_printer_id = $data->{copy_printer_id};
181
182   return if !$printer_id;
183
184   my $out;
185
186   foreach  my $local_printer_id ($printer_id, $copy_printer_id) {
187     next unless $local_printer_id;
188     my $printer = SL::DB::Printer->new(id => $local_printer_id)->load;
189     my $command = SL::Template::create(type => 'ShellCommand', form => Form->new(''))->parse($printer->printer_command);
190     if (!open $out, '|-', $command) {
191       push @{ $data->{print_errors} }, { message => $::locale->text('Could not execute printer command: #1', $!) };
192       $job_obj->update_attributes(data_as_hash => $data);
193       return;
194     }
195     binmode $out;
196     print $out $self->{merged_pdf};
197     close $out;
198   }
199
200 }
201
202 sub run {
203   my ($self, $job_obj) = @_;
204
205   $self->{job_obj}         = $job_obj;
206   $self->{invoices} = [];
207
208   $self->create_invoices;
209   $self->convert_invoices_to_pdf;
210   $self->print_pdfs;
211
212   $job_obj->set_data(status => DONE())->save;
213
214   return 1;
215 }
216
217 1;
218
219 __END__
220
221 =pod
222
223 =encoding utf8
224
225 =head1 NAME
226
227 SL::BackgroundJob::MassRecordCreationAndPrinting
228
229 =head1 SYNOPSIS
230
231 In controller:
232
233 use SL::BackgroundJob::MassRecordCreationAndPrinting
234
235 my $job              = SL::DB::BackgroundJob->new(
236     type               => 'once',
237     active             => 1,
238     package_name       => 'MassRecordCreationAndPrinting',
239
240   )->set_data(
241     record_ids         => [ map { $_->id } @records[0..$num - 1] ],
242     printer_id         => $::form->{printer_id},
243     copy_printer_id    => $::form->{copy_printer_id},
244     transdate          => $::form->{transdate} || undef,
245     status             => SL::BackgroundJob::MassRecordCreationAndPrinting->WAITING_FOR_EXECUTION(),
246     num_created        => 0,
247     num_printed        => 0,
248     invoice_ids        => [ ],
249     conversion_errors  => [ ],
250     print_errors       => [ ],
251
252   )->update_next_run_at;
253   SL::System::TaskServer->new->wake_up;
254
255 =head1 OVERVIEW
256
257 This background job has 4 states which are described by the four constants above.
258
259 =over 2
260
261 =item * WAITING_FOR_EXECUTION
262   Background has been initialised and needs to be picked up by the task_server
263
264 =item * CONVERTING_DELIVERY_ORDERS
265    Object conversion
266
267 =item * PRINTING_INVOICES
268   Printing, if done via print command
269
270 =item * DONE
271   To release the process and for the user information
272
273 =back
274
275 =head1 FUNCTIONS
276
277 =over 2
278
279 =item C<create_invoices>
280
281 Converts the source objects (DeliveryOrder) to destination objects (Invoice).
282 On success objects will be saved.
283 If param C<data->{transdate}> is set, this will be the transdate. No safety checks are done.
284 The original conversion from order to delivery order had a post_save_sanity_check
285 C<$delivery_order-E<gt>post_save_sanity_check; # just a hint at e8521eee (#90 od)>
286 The params of convert_to_invoice are created on the fly with a anonym sub, as a alternative check
287  perlsecret Enterprise ()x!!
288
289 =item C<convert_invoices_to_pdf>
290
291 Takes the new destination objects and merges them via print template in one pdf.
292
293 =item C<print_pdfs>
294
295 Sent the pdf to the printer command.
296 If param C<data->{copy_printer_id}> is set, the pdf will be sent to a second printer command.
297
298 =back
299
300 =head1 BUGS
301
302 Currently the calculation from the gui (form) differs from the calculation via convert (PTC).
303 Furthermore mass conversion with foreign currencies could lead to problems (daily rate check).
304
305 =head1 TODO
306
307 It would be great to extend this Job for general background printing. The original project
308 code converted sales order to delivery orders (84e7c540) this could be merged in unstable.
309 The states should be CONVERTING_SOURCE_RECORDS, PRINTING_DESTINATION_RECORDS etc
310
311 =head1 AUTHOR
312
313 Moritz Bunkus E<lt>m.bunkus@linet-services.deE<gt>
314
315 Jan Büren E<lt>jan@kivitendo-premium.deE<gt>
316
317 =cut