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