Bug 19532: Database and installer stuff
[koha.git] / Koha / BackgroundJob / BatchDeleteItem.pm
1 package Koha::BackgroundJob::BatchDeleteItem;
2
3 # This file is part of Koha.
4 #
5 # Koha is free software; you can redistribute it and/or modify it
6 # under the terms of the GNU General Public License as published by
7 # the Free Software Foundation; either version 3 of the License, or
8 # (at your option) any later version.
9 #
10 # Koha is distributed in the hope that it will be useful, but
11 # WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # GNU General Public License for more details.
14 #
15 # You should have received a copy of the GNU General Public License
16 # along with Koha; if not, see <http://www.gnu.org/licenses>.
17
18 =head1 NAME
19
20 Koha::BackgroundJob::BatchDeleteItem - Background job derived class to process item deletion in batch
21
22 =cut
23
24 use Modern::Perl;
25 use JSON qw( encode_json decode_json );
26 use List::MoreUtils qw( uniq );
27 use Try::Tiny;
28
29 use Koha::BackgroundJobs;
30 use Koha::DateUtils qw( dt_from_string );
31 use Koha::Items;
32
33 use base 'Koha::BackgroundJob';
34
35 =head1 API
36
37 =head2 Class methods
38
39 =head3 job_type
40
41 Return the job type 'batch_item_record_deletion'.
42
43 =cut
44
45 sub job_type {
46     return 'batch_item_record_deletion';
47 }
48
49 =head3 process
50
51     Koha::BackgroundJobs->find($id)->process(
52         {
53             record_ids => \@itemnumbers,
54             deleted_biblios => 0|1,
55         }
56     );
57
58 Will delete all the items that have been passed for deletion.
59
60 When deleted_biblios is passed, if we deleted the last item of a biblio,
61 the bibliographic record will be deleted as well.
62
63 The search engine's index will be updated according to the changes made
64 to the deleted bibliographic recods.
65
66 The generated report will be:
67   {
68     deleted_itemnumbers => \@list_of_itemnumbers,
69     not_deleted_itemnumbers => \@list_of_itemnumbers,
70     deleted_biblionumbers=> \@list_of_biblionumbers,
71   }
72
73 =cut
74
75 sub process {
76     my ( $self, $args ) = @_;
77
78     my $job_type = $args->{job_type};
79
80     my $job = Koha::BackgroundJobs->find( $args->{job_id} );
81
82     if ( !exists $args->{job_id} || !$job || $job->status eq 'cancelled' ) {
83         return;
84     }
85
86     # FIXME If the job has already been started, but started again (worker has been restart for instance)
87     # Then we will start from scratch and so double delete the same records
88
89     my $job_progress = 0;
90     $job->started_on(dt_from_string)->progress($job_progress)
91       ->status('started')->store;
92
93     my @record_ids     = @{ $args->{record_ids} };
94     my $delete_biblios = $args->{delete_biblios};
95
96     my $report = {
97         total_records => scalar @record_ids,
98         total_success => 0,
99     };
100     my @messages;
101     my $schema = Koha::Database->new->schema;
102     my ( @deleted_itemnumbers, @not_deleted_itemnumbers,
103         @deleted_biblionumbers );
104
105     try {
106         my $schema = Koha::Database->new->schema;
107         $schema->txn_do(
108             sub {
109                 my (@biblionumbers);
110                 for my $record_id ( sort { $a <=> $b } @record_ids ) {
111
112                     last if $job->get_from_storage->status eq 'cancelled';
113
114                     my $item = Koha::Items->find($record_id) || next;
115
116                     my $return = $item->safe_delete;
117                     unless ( $return ) {
118
119                         # FIXME Do we need to rollback the whole transaction if a deletion failed?
120                         push @not_deleted_itemnumbers, $item->itemnumber;
121                         push @messages,
122                           {
123                             type         => 'error',
124                             code         => 'item_not_deleted',
125                             itemnumber   => $item->itemnumber,
126                             biblionumber => $item->biblionumber,
127                             barcode      => $item->barcode,
128                             title        => $item->biblio->title,
129                             reason       => @{$return->messages}[0]->message,
130                           };
131
132                         next;
133                     }
134
135                     push @deleted_itemnumbers, $item->itemnumber;
136                     push @biblionumbers,       $item->biblionumber;
137
138                     $report->{total_success}++;
139                     $job->progress( ++$job_progress )->store;
140                 }
141
142                 # If there are no items left, delete the biblio
143                 if ( $delete_biblios && @biblionumbers ) {
144                     for my $biblionumber ( uniq @biblionumbers ) {
145                         my $items_count =
146                           Koha::Biblios->find($biblionumber)->items->count;
147                         if ( $items_count == 0 ) {
148                             my $error = C4::Biblio::DelBiblio( $biblionumber,
149                                 { skip_record_index => 1 } );
150                             unless ($error) {
151                                 push @deleted_biblionumbers, $biblionumber;
152                             }
153                         }
154                     }
155
156                     if (@deleted_biblionumbers) {
157                         my $indexer = Koha::SearchEngine::Indexer->new(
158                             { index => $Koha::SearchEngine::BIBLIOS_INDEX } );
159
160                         $indexer->index_records( \@deleted_biblionumbers,
161                             'recordDelete', "biblioserver", undef );
162                     }
163                 }
164             }
165         );
166     }
167     catch {
168
169         warn $_;
170
171         push @messages,
172           {
173             type  => 'error',
174             code  => 'unknown',
175             error => $_,
176           };
177
178         die "Something terrible has happened!"
179           if ( $_ =~ /Rollback failed/ );    # Rollback failed
180     };
181
182     $report->{deleted_itemnumbers}     = \@deleted_itemnumbers;
183     $report->{not_deleted_itemnumbers} = \@not_deleted_itemnumbers;
184     $report->{deleted_biblionumbers}   = \@deleted_biblionumbers;
185
186     my $job_data = decode_json $job->data;
187     $job_data->{messages} = \@messages;
188     $job_data->{report}   = $report;
189
190     $job->ended_on(dt_from_string)->data( encode_json $job_data);
191     $job->status('finished') if $job->status ne 'cancelled';
192     $job->store;
193 }
194
195 =head3 enqueue
196
197     Koha::BackgroundJob::BatchDeleteItem->new->enqueue(
198         {
199             record_ids => \@itemnumbers,
200             deleted_biblios => 0|1,
201         }
202     );
203
204 Enqueue the job.
205
206 =cut
207
208 sub enqueue {
209     my ( $self, $args ) = @_;
210
211     # TODO Raise exception instead
212     return unless exists $args->{record_ids};
213
214     my @record_ids = @{ $args->{record_ids} };
215     my $delete_biblios = $args->{delete_biblios} || 0;
216
217     $self->SUPER::enqueue(
218         {
219             job_size => scalar @record_ids,
220             job_args => {
221                 record_ids     => \@record_ids,
222                 delete_biblios => $delete_biblios,
223             }
224         }
225     );
226 }
227
228 1;