9b374f65727b6dcb2f8fc9345bdc1451d8c04a57
[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 #   num_created        => 0,
26 #   num_printed        => 0,
27 #   invoice_ids        => [ 234, 235, ],
28 #   conversion_errors  => [ { id => 124, number => 'A981723', message => "Stuff went boom" }, ],
29 #   print_errors       => [ { id => 234, number => 'L87123123', message => "Printer is out of coffee" }, ],
30 #   pdf_file_name      => 'qweqwe.pdf',
31 # };
32
33 sub create_invoices {
34   my ($self)  = @_;
35
36   my $job_obj = $self->{job_obj};
37   my $db      = $job_obj->db;
38
39   $job_obj->set_data(status => CONVERTING_DELIVERY_ORDERS())->save;
40
41   foreach my $delivery_order_id (@{ $job_obj->data_as_hash->{record_ids} }) {
42     my $number = $delivery_order_id;
43     my $data   = $job_obj->data_as_hash;
44
45     eval {
46       my $invoice;
47       my $sales_delivery_order = SL::DB::DeliveryOrder->new(id => $delivery_order_id)->load;
48       $number                  = $sales_delivery_order->donumber;
49
50       if (!$db->do_transaction(sub {
51         $invoice = $sales_delivery_order->convert_to_invoice(item_filter => \&delivery_order_item_filter, queue_sort => 1) || die $db->error;
52         # $delivery_order->post_save_sanity_check; # just a hint at e8521eee (#90 od)
53         1;
54       })) {
55         die $db->error;
56       }
57
58       $data->{num_created}++;
59       push @{ $data->{invoice_ids} }, $invoice->id;
60       push @{ $self->{invoices}    }, $invoice;
61
62       1;
63     } or do {
64       push @{ $data->{conversion_errors} }, { id => $delivery_order_id, number => $number, message => $@ };
65     };
66
67     $job_obj->update_attributes(data_as_hash => $data);
68   }
69 }
70
71 sub convert_invoices_to_pdf {
72   my ($self) = @_;
73
74   return if !@{ $self->{invoices} };
75
76   my $job_obj = $self->{job_obj};
77   my $db      = $job_obj->db;
78
79   $job_obj->set_data(status => PRINTING_INVOICES())->save;
80
81   require SL::Controller::MassInvoiceCreatePrint;
82
83   my $printer_id = $job_obj->data_as_hash->{printer_id};
84   my $ctrl       = SL::Controller::MassInvoiceCreatePrint->new;
85   my %variables  = (
86     type         => 'invoice',
87     formname     => 'invoice',
88     format       => 'pdf',
89     media        => $printer_id ? 'printer' : 'file',
90   );
91
92   my @pdf_file_names;
93
94   foreach my $invoice (@{ $self->{invoices} }) {
95     my $data = $job_obj->data_as_hash;
96
97     eval {
98       my %create_params = (
99         template  => $ctrl->find_template(name => 'invoice', printer_id => $printer_id),
100         variables => Form->new(''),
101         return    => 'file_name',
102       );
103
104       $create_params{variables}->{$_} = $variables{$_} for keys %variables;
105
106       $invoice->flatten_to_form($create_params{variables}, format_amounts => 1);
107       $create_params{variables}->prepare_for_printing;
108
109       push @pdf_file_names, $ctrl->create_pdf(%create_params);
110
111       # copy file to webdav folder
112       if ($::instance_conf->get_webdav_documents) {
113         my $webdav = SL::Webdav->new(
114           type     => 'invoice',
115           number   => $invoice->invnumber,
116         );
117         my $webdav_file = SL::Webdav::File->new(
118           webdav   => $webdav,
119           filename => t8('Invoice') . '_' . $invoice->invnumber . '.pdf',
120         );
121         eval {
122           $webdav_file->store(file => $pdf_file_names[-1]);
123           1;
124         } or do {
125           push @{ $data->{print_errors} }, { id => $invoice->id, number => $invoice->invnumber, message => $@ };
126         }
127       }
128
129       $data->{num_printed}++;
130
131       1;
132
133     } or do {
134       push @{ $data->{print_errors} }, { id => $invoice->id, number => $invoice->invnumber, message => $@ };
135     };
136
137     $job_obj->update_attributes(data_as_hash => $data);
138   }
139
140   if (@pdf_file_names) {
141     my $data = $job_obj->data_as_hash;
142
143     eval {
144       $self->{merged_pdf} = $ctrl->merge_pdfs(file_names => \@pdf_file_names);
145       unlink @pdf_file_names;
146
147       if (!$printer_id) {
148         my $file_name = 'mass_invoice' . $job_obj->id . '.pdf';
149         my $sfile     = SL::SessionFile->new($file_name, mode => 'w');
150         $sfile->fh->print($self->{merged_pdf});
151         $sfile->fh->close;
152
153         $data->{pdf_file_name} = $file_name;
154       }
155
156       1;
157
158     } or do {
159       push @{ $data->{print_errors} }, { message => $@ };
160     };
161
162     $job_obj->update_attributes(data_as_hash => $data);
163   }
164 }
165
166 sub print_pdfs {
167   my ($self)     = @_;
168
169   my $job_obj    = $self->{job_obj};
170   my $data       = $job_obj->data_as_hash;
171   my $printer_id = $data->{printer_id};
172
173   return if !$printer_id;
174
175   my $printer = SL::DB::Printer->new(id => $printer_id)->load;
176   my $command = SL::Template::create(type => 'ShellCommand', form => Form->new(''))->parse($printer->printer_command);
177   my $out;
178
179   if (!open $out, '|-', $command) {
180     push @{ $data->{print_errors} }, { message => $::locale->text('Could not execute printer command: #1', $!) };
181     $job_obj->update_attributes(data_as_hash => $data);
182     return;
183   }
184
185   binmode $out;
186   print $out $self->{merged_pdf};
187   close $out;
188 }
189
190 sub run {
191   my ($self, $job_obj) = @_;
192
193   $self->{job_obj}         = $job_obj;
194   $self->{invoices} = [];
195
196   $self->create_invoices;
197   $self->convert_invoices_to_pdf;
198   $self->print_pdfs;
199
200   $job_obj->set_data(status => DONE())->save;
201
202   return 1;
203 }
204
205 1;
206
207 __END__
208
209 =pod
210
211 =encoding utf8
212
213 =head1 NAME
214
215 SL::BackgroundJob::MassRecordCreationAndPrinting
216
217 =head1 SYNOPSIS
218
219 In controller:
220
221 use SL::BackgroundJob::MassRecordCreationAndPrinting
222
223 my $job              = SL::DB::BackgroundJob->new(
224     type               => 'once',
225     active             => 1,
226     package_name       => 'MassRecordCreationAndPrinting',
227
228   )->set_data(
229     record_ids         => [ map { $_->id } @records[0..$num - 1] ],
230     printer_id         => $::form->{printer_id},
231     status             => SL::BackgroundJob::MassRecordCreationAndPrinting->WAITING_FOR_EXECUTION(),
232     num_created        => 0,
233     num_printed        => 0,
234     invoice_ids        => [ ],
235     conversion_errors  => [ ],
236     print_errors       => [ ],
237
238   )->update_next_run_at;
239   SL::System::TaskServer->new->wake_up;
240
241 =head1 OVERVIEW
242
243 This background job has 4 states which are described by the four constants above.
244
245 =over 2
246
247 =item * WAITING_FOR_EXECUTION
248   Background has been initialised and needs to be picked up by the task_server
249
250 =item * CONVERTING_DELIVERY_ORDERS
251    Object conversion
252
253 =item * PRINTING_INVOICES
254   Printing, if done via print command
255
256 =item * DONE
257   To release the process and for the user information
258
259 =back
260
261 =head1 FUNCTIONS
262
263 =over 2
264
265 =item C<create_invoices>
266
267 Converts the source objects (DeliveryOrder) to destination objects (Invoice).
268 On success objects will be saved.
269
270 =item C<convert_invoices_to_pdf>
271
272 Takes the new destination objects and merges them via print template in one pdf.
273
274 =item C<print_pdfs>
275
276 Sent the pdf to the printer command (if checked).
277
278 =back
279
280 =head1 BUGS
281
282 Currently the calculation from the gui (form) differs from the calculation via convert (PTC).
283 Furthermore mass conversion with foreign currencies could lead to problems (daily rate check).
284
285 =head1 AUTHOR
286
287 Moritz Bunkus E<lt>m.bunkus@linet-services.deE<gt>
288
289 Jan Büren E<lt>jan@kivitendo-premium.deE<gt>
290
291 =cut