Bug 33974: (QA follow-up) Remove superflous import
[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 List::MoreUtils qw( uniq );
26 use Try::Tiny;
27
28 use Koha::Items;
29
30 use base 'Koha::BackgroundJob';
31
32 =head1 API
33
34 =head2 Class methods
35
36 =head3 job_type
37
38 Return the job type 'batch_item_record_deletion'.
39
40 =cut
41
42 sub job_type {
43     return 'batch_item_record_deletion';
44 }
45
46 =head3 process
47
48     Koha::BackgroundJobs->find($id)->process(
49         {
50             record_ids => \@itemnumbers,
51             deleted_biblios => 0|1,
52         }
53     );
54
55 Will delete all the items that have been passed for deletion.
56
57 When deleted_biblios is passed, if we deleted the last item of a biblio,
58 the bibliographic record will be deleted as well.
59
60 The search engine's index will be updated according to the changes made
61 to the deleted bibliographic recods.
62
63 The generated report will be:
64   {
65     deleted_itemnumbers => \@list_of_itemnumbers,
66     not_deleted_itemnumbers => \@list_of_itemnumbers,
67     deleted_biblionumbers=> \@list_of_biblionumbers,
68   }
69
70 =cut
71
72 sub process {
73     my ( $self, $args ) = @_;
74
75     if ( $self->status eq 'cancelled' ) {
76         return;
77     }
78
79     # FIXME If the job has already been started, but started again (worker has been restart for instance)
80     # Then we will start from scratch and so double delete the same records
81
82     $self->start;
83
84     my @record_ids     = @{ $args->{record_ids} };
85     my $delete_biblios = $args->{delete_biblios};
86
87     my $report = {
88         total_records => scalar @record_ids,
89         total_success => 0,
90     };
91     my @messages;
92     my $schema = Koha::Database->new->schema;
93     my ( @deleted_itemnumbers, @not_deleted_itemnumbers,
94         @deleted_biblionumbers );
95
96     try {
97         my $schema = Koha::Database->new->schema;
98         $schema->txn_do(
99             sub {
100                 my (@biblionumbers);
101                 for my $record_id ( sort { $a <=> $b } @record_ids ) {
102
103                     last if $self->get_from_storage->status eq 'cancelled';
104
105                     my $item = Koha::Items->find($record_id) || next;
106
107                     my $return = $item->safe_delete({ skip_record_index => 1, skip_holds_queue => 1 });
108                     unless ( $return ) {
109
110                         # FIXME Do we need to rollback the whole transaction if a deletion failed?
111                         push @not_deleted_itemnumbers, $item->itemnumber;
112                         push @messages,
113                           {
114                             type         => 'error',
115                             code         => 'item_not_deleted',
116                             itemnumber   => $item->itemnumber,
117                             biblionumber => $item->biblionumber,
118                             barcode      => $item->barcode,
119                             title        => $item->biblio->title,
120                             reason       => @{$return->messages}[0]->message,
121                           };
122
123                         next;
124                     }
125
126                     push @deleted_itemnumbers, $item->itemnumber;
127                     push @biblionumbers,       $item->biblionumber;
128
129                     $report->{total_success}++;
130                     $self->step;
131                 }
132
133                 # If there are no items left, delete the biblio
134                 my @updated_biblionumbers;
135                 for my $biblionumber ( uniq @biblionumbers ) {
136                     my $items_count =
137                       Koha::Biblios->find($biblionumber)->items->count;
138                     if ( $delete_biblios && $items_count == 0 ) {
139                         my $error = C4::Biblio::DelBiblio( $biblionumber,
140                             { skip_record_index => 1, skip_holds_queue => 1 } );
141                         unless ($error) {
142                             push @deleted_biblionumbers, $biblionumber;
143                         }
144                     } else {
145                         push @updated_biblionumbers, $biblionumber;
146                     }
147                 }
148
149                 if (@deleted_biblionumbers) {
150                     my $indexer = Koha::SearchEngine::Indexer->new(
151                         { index => $Koha::SearchEngine::BIBLIOS_INDEX } );
152
153                     $indexer->index_records( \@deleted_biblionumbers,
154                         'recordDelete', "biblioserver", undef );
155
156                     Koha::BackgroundJob::BatchUpdateBiblioHoldsQueue->new->enqueue(
157                         {
158                             biblio_ids => \@deleted_biblionumbers
159                         }
160                     ) if C4::Context->preference('RealTimeHoldsQueue');
161                 }
162
163                 if (@updated_biblionumbers) {
164                     my $indexer = Koha::SearchEngine::Indexer->new(
165                         { index => $Koha::SearchEngine::BIBLIOS_INDEX } );
166
167                     $indexer->index_records( \@updated_biblionumbers,
168                         'specialUpdate', "biblioserver", undef );
169
170                     Koha::BackgroundJob::BatchUpdateBiblioHoldsQueue->new->enqueue(
171                         {
172                             biblio_ids => \@updated_biblionumbers
173                         }
174                     ) if C4::Context->preference('RealTimeHoldsQueue');
175                 }
176             }
177         );
178     }
179     catch {
180
181         warn $_;
182
183         push @messages,
184           {
185             type  => 'error',
186             code  => 'unknown',
187             error => $_,
188           };
189
190         die "Something terrible has happened!"
191           if ( $_ =~ /Rollback failed/ );    # Rollback failed
192     };
193
194     $report->{deleted_itemnumbers}     = \@deleted_itemnumbers;
195     $report->{not_deleted_itemnumbers} = \@not_deleted_itemnumbers;
196     $report->{deleted_biblionumbers}   = \@deleted_biblionumbers;
197
198     my $data = $self->decoded_data;
199     $data->{messages} = \@messages;
200     $data->{report} = $report;
201
202     $self->finish( $data );
203 }
204
205 =head3 enqueue
206
207     Koha::BackgroundJob::BatchDeleteItem->new->enqueue(
208         {
209             record_ids => \@itemnumbers,
210             deleted_biblios => 0|1,
211         }
212     );
213
214 Enqueue the job.
215
216 =cut
217
218 sub enqueue {
219     my ( $self, $args ) = @_;
220
221     # TODO Raise exception instead
222     return unless exists $args->{record_ids};
223
224     my @record_ids = @{ $args->{record_ids} };
225     my $delete_biblios = $args->{delete_biblios} || 0;
226
227     $self->SUPER::enqueue(
228         {
229             job_size => scalar @record_ids,
230             job_args => {
231                 record_ids     => \@record_ids,
232                 delete_biblios => $delete_biblios,
233             },
234             job_queue => 'long_tasks',
235         }
236     );
237 }
238
239 1;