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