ebd20e13e8fc0a432b314a496e64ac7401bea021
[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::ARAP;
15 use SL::Locale::String qw(t8);
16 use SL::Helper::MassPrintCreatePDF qw(:all);
17 use SL::Helper::CreatePDF qw(:all);
18 use SL::Helper::File qw(store_pdf append_general_pdf_attachments doc_storage_enabled);
19
20 use constant WAITING_FOR_EXECUTION       => 0;
21 use constant CONVERTING_DELIVERY_ORDERS  => 1;
22 use constant PRINTING_INVOICES           => 2;
23 use constant DONE                        => 3;
24
25 # Data format:
26 # my $data             = {
27 #   record_ids          => [ 123, 124, 127, ],
28 #   printer_id         => 4711,
29 #   copy_printer_id    => 4711,
30 #   transdate          => $today || $custom_transdate,
31 #   num_created        => 0,
32 #   num_printed        => 0,
33 #   invoice_ids        => [ 234, 235, ],
34 #   conversion_errors  => [ { id => 124, number => 'A981723', message => "Stuff went boom" }, ],
35 #   print_errors       => [ { id => 234, number => 'L87123123', message => "Printer is out of coffee" }, ],
36 #   pdf_file_name      => 'qweqwe.pdf',
37 #   session_id         => $::auth->get_session_id,
38 # };
39
40 sub create_invoices {
41   my ($self)  = @_;
42
43   my $job_obj = $self->{job_obj};
44   my $db      = $job_obj->db;
45   my $dbh     = SL::DB->client->dbh;
46
47   $job_obj->set_data(status => CONVERTING_DELIVERY_ORDERS())->save;
48
49   foreach my $delivery_order_id (@{ $job_obj->data_as_hash->{record_ids} }) {
50     my $number = $delivery_order_id;
51     my $data   = $job_obj->data_as_hash;
52
53     eval {
54       my $sales_delivery_order = SL::DB::DeliveryOrder->new(id => $delivery_order_id)->load;
55       $number                  = $sales_delivery_order->donumber;
56       my %conversion_params    = $data->{transdate} ? ('attributes' => { transdate => $data->{transdate} }) : ();
57       my $invoice              = $sales_delivery_order->convert_to_invoice(%conversion_params);
58
59       die $db->error if !$invoice;
60
61       ARAP->close_orders_if_billed('dbh'     => $dbh,
62                                    'arap_id' => $invoice->id,
63                                    'table'   => 'ar',);
64
65       # update shop status
66       my @linked_shop_orders = $invoice->linked_records(
67         from      => 'ShopOrder',
68         via       => [ 'DeliveryOrder', 'Order' ],
69       );
70       #if (scalar @linked_shop_orders[0][0] >= 1){
71         #do update
72       my $shop_order = $linked_shop_orders[0][0];
73       if ($shop_order){
74       require SL::Shop;
75         my $shop_config = SL::DB::Manager::Shop->get_first( query => [ id => $shop_order->shop_id ] );
76         my $shop = SL::Shop->new( config => $shop_config );
77         $shop->connector->set_orderstatus($shop_order->shop_trans_id, "completed");
78       }
79
80       $data->{num_created}++;
81       push @{ $data->{invoice_ids} }, $invoice->id;
82       push @{ $self->{invoices}    }, $invoice;
83
84       1;
85     } or do {
86       push @{ $data->{conversion_errors} }, { id => $delivery_order_id, number => $number, message => $@ };
87     };
88
89     $job_obj->update_attributes(data_as_hash => $data);
90   }
91 }
92
93 sub convert_invoices_to_pdf {
94   my ($self) = @_;
95
96   return if !@{ $self->{invoices} };
97
98   my $job_obj = $self->{job_obj};
99   my $db      = $job_obj->db;
100
101   $job_obj->set_data(status => PRINTING_INVOICES())->save;
102   my $data = $job_obj->data_as_hash;
103
104   my $printer_id = $data->{printer_id};
105   if ( $data->{media} ne 'printer' ) {
106       undef $printer_id;
107       $data->{media} = 'file';
108   }
109   my %variables  = (
110     type         => 'invoice',
111     formname     => 'invoice',
112     format       => 'pdf',
113     media        => $printer_id ? 'printer' : 'file',
114     printer_id   => $printer_id,
115   );
116
117   my @pdf_file_names;
118
119   foreach my $invoice (@{ $self->{invoices} }) {
120
121     eval {
122       my @errors = ();
123       my %params = (
124         variables => \%variables,
125         return    => 'file_name',
126         document  => $invoice,
127         errors    => \@errors,
128       );
129       push @pdf_file_names, $self->create_massprint_pdf(%params);
130       $data->{num_printed}++;
131
132       if (scalar @errors) {
133         push @{ $data->{print_errors} }, { id => $invoice->id, number => $invoice->invnumber, message => join(', ', @errors) };
134       }
135
136       1;
137
138     } or do {
139       push @{ $data->{print_errors} }, { id => $invoice->id, number => $invoice->invnumber, message => $@ };
140     };
141
142     $job_obj->update_attributes(data_as_hash => $data);
143   }
144
145   $self->merge_massprint_pdf(file_names => \@pdf_file_names, type => 'invoice' ) if scalar(@pdf_file_names) > 0;
146 }
147
148 sub run {
149   my ($self, $job_obj) = @_;
150
151   $self->{job_obj}         = $job_obj;
152   $self->{invoices} = [];
153
154   $self->create_invoices;
155   $self->convert_invoices_to_pdf;
156   $self->print_pdfs;
157
158   $job_obj->set_data(status => DONE())->save;
159
160   return 1;
161 }
162
163 1;
164
165 __END__
166
167 =pod
168
169 =encoding utf8
170
171 =head1 NAME
172
173 SL::BackgroundJob::MassRecordCreationAndPrinting
174
175 =head1 SYNOPSIS
176
177 In controller:
178
179 use SL::BackgroundJob::MassRecordCreationAndPrinting
180
181 my $job              = SL::DB::BackgroundJob->new(
182     type               => 'once',
183     active             => 1,
184     package_name       => 'MassRecordCreationAndPrinting',
185
186   )->set_data(
187     record_ids         => [ map { $_->id } @records[0..$num - 1] ],
188     printer_id         => $::form->{printer_id},
189     copy_printer_id    => $::form->{copy_printer_id},
190     transdate          => $::form->{transdate} || undef,
191     status             => SL::BackgroundJob::MassRecordCreationAndPrinting->WAITING_FOR_EXECUTION(),
192     num_created        => 0,
193     num_printed        => 0,
194     invoice_ids        => [ ],
195     conversion_errors  => [ ],
196     print_errors       => [ ],
197
198   )->update_next_run_at;
199   SL::System::TaskServer->new->wake_up;
200
201 =head1 OVERVIEW
202
203 This background job has 4 states which are described by the four constants above.
204
205 =over 2
206
207 =item * WAITING_FOR_EXECUTION
208   Background has been initialised and needs to be picked up by the task_server
209
210 =item * CONVERTING_DELIVERY_ORDERS
211    Object conversion
212
213 =item * PRINTING_INVOICES
214   Printing, if done via print command
215
216 =item * DONE
217   To release the process and for the user information
218
219 =back
220
221 =head1 FUNCTIONS
222
223 =over 2
224
225 =item C<create_invoices>
226
227 Converts the source objects (DeliveryOrder) to destination objects (Invoice).
228 On success objects will be saved.
229 If param C<data->{transdate}> is set, this will be the transdate. No safety checks are done.
230 The original conversion from order to delivery order had a post_save_sanity_check
231 C<$delivery_order-E<gt>post_save_sanity_check; # just a hint at e8521eee (#90 od)>
232 The params of convert_to_invoice are created on the fly with a anonym sub, as a alternative check
233  perlsecret Enterprise ()x!!
234
235 =item C<convert_invoices_to_pdf>
236
237 Takes the new destination objects and merges them via print template in one pdf.
238
239 =item C<print_pdfs>
240
241 Sent the pdf to the printer command.
242 If param C<data->{copy_printer_id}> is set, the pdf will be sent to a second printer command.
243
244 =back
245
246 =head1 BUGS
247
248 Currently the calculation from the gui (form) differs from the calculation via convert (PTC).
249 Furthermore mass conversion with foreign currencies could lead to problems (daily rate check).
250
251 =head1 TODO
252
253 It would be great to extend this Job for general background printing. The original project
254 code converted sales order to delivery orders (84e7c540) this could be merged in unstable.
255 The states should be CONVERTING_SOURCE_RECORDS, PRINTING_DESTINATION_RECORDS etc
256
257 =head1 AUTHOR
258
259 Moritz Bunkus E<lt>m.bunkus@linet-services.deE<gt>
260
261 Jan Büren E<lt>jan@kivitendo-premium.deE<gt>
262
263 =cut