Bug 32351: Fix Members.t
[koha.git] / Koha / BackgroundJob / BatchDeleteBiblio.pm
1 package Koha::BackgroundJob::BatchDeleteBiblio;
2
3 use Modern::Perl;
4
5 use C4::Biblio;
6
7 use Koha::BackgroundJob::BatchUpdateBiblioHoldsQueue;
8 use Koha::DateUtils qw( dt_from_string );
9 use Koha::SearchEngine;
10 use Koha::SearchEngine::Indexer;
11
12 use base 'Koha::BackgroundJob';
13
14 =head1 NAME
15
16 Koha::BackgroundJob::BatchDeleteBiblio - Batch delete bibliographic records
17
18 This is a subclass of Koha::BackgroundJob.
19
20 =head1 API
21
22 =head2 Class methods
23
24 =head3 job_type
25
26 Define the job type of this job: batch_biblio_record_deletion
27
28 =cut
29
30 sub job_type {
31     return 'batch_biblio_record_deletion';
32 }
33
34 =head3 process
35
36 Process the job.
37
38 =cut
39
40 sub process {
41     my ( $self, $args ) = @_;
42
43     if ( $self->status eq 'cancelled' ) {
44         return;
45     }
46
47     # FIXME If the job has already been started, but started again (worker has been restart for instance)
48     # Then we will start from scratch and so double delete the same records
49
50     my $job_progress = 0;
51     $self->started_on(dt_from_string)
52         ->progress($job_progress)
53         ->status('started')
54         ->store;
55
56     my $mmtid = $args->{mmtid};
57     my @record_ids = @{ $args->{record_ids} };
58
59     my $report = {
60         total_records => scalar @record_ids,
61         total_success => 0,
62     };
63     my @messages;
64     my $schema = Koha::Database->new->schema;
65     RECORD_IDS: for my $record_id ( sort { $a <=> $b } @record_ids ) {
66
67         last if $self->get_from_storage->status eq 'cancelled';
68
69         next unless $record_id;
70
71         $schema->storage->txn_begin;
72
73         my $biblionumber = $record_id;
74         # First, checking if issues exist.
75         # If yes, nothing to do
76         my $biblio = Koha::Biblios->find( $biblionumber );
77
78         # TODO Replace with $biblio->get_issues->count
79         if ( C4::Biblio::CountItemsIssued( $biblionumber ) ) {
80             push @messages, {
81                 type => 'warning',
82                 code => 'item_issued',
83                 biblionumber => $biblionumber,
84             };
85             $schema->storage->txn_rollback;
86             $self->progress( ++$job_progress )->store;
87             next;
88         }
89
90         # Cancel reserves
91         my $holds = $biblio->holds;
92         while ( my $hold = $holds->next ) {
93             eval{
94                 $hold->cancel({ skip_holds_queue => 1 });
95             };
96             if ( $@ ) {
97                 push @messages, {
98                     type => 'error',
99                     code => 'reserve_not_cancelled',
100                     biblionumber => $biblionumber,
101                     reserve_id => $hold->reserve_id,
102                     error => "$@",
103                 };
104                 $schema->storage->txn_rollback;
105                 $self->progress( ++$job_progress )->store;
106                 next RECORD_IDS;
107             }
108         }
109
110         # Delete items
111         my $items = Koha::Items->search({ biblionumber => $biblionumber });
112         while ( my $item = $items->next ) {
113             my $deleted = $item->safe_delete({ skip_record_index => 1, skip_holds_queue => 1 });
114             unless ( $deleted ) {
115                 push @messages, {
116                     type => 'error',
117                     code => 'item_not_deleted',
118                     biblionumber => $biblionumber,
119                     itemnumber => $item->itemnumber,
120                     error => @{$deleted->messages}[0]->message,
121                 };
122                 $schema->storage->txn_rollback;
123                 $self->progress( ++$job_progress )->store;
124                 next RECORD_IDS;
125             }
126         }
127
128         # Finally, delete the biblio
129         my $error = eval {
130             C4::Biblio::DelBiblio( $biblionumber, { skip_record_index => 1, skip_holds_queue => 1 } );
131         };
132         if ( $error or $@ ) {
133             push @messages, {
134                 type => 'error',
135                 code => 'biblio_not_deleted',
136                 biblionumber => $biblionumber,
137                 error => ($@ ? $@ : $error),
138             };
139             $schema->storage->txn_rollback;
140             $self->progress( ++$job_progress )->store;
141             next;
142         }
143
144         push @messages, {
145             type => 'success',
146             code => 'biblio_deleted',
147             biblionumber => $biblionumber,
148         };
149         $report->{total_success}++;
150         $schema->storage->txn_commit;
151         $self->progress( ++$job_progress )->store;
152     }
153
154     my @deleted_biblionumbers =
155       map { $_->{code} eq 'biblio_deleted' ? $_->{biblionumber} : () }
156           @messages;
157
158     if ( @deleted_biblionumbers ) {
159         my $indexer = Koha::SearchEngine::Indexer->new({ index => $Koha::SearchEngine::BIBLIOS_INDEX });
160         $indexer->index_records( \@deleted_biblionumbers, "recordDelete", "biblioserver" );
161
162         Koha::BackgroundJob::BatchUpdateBiblioHoldsQueue->new->enqueue(
163             {
164                 biblio_ids => \@deleted_biblionumbers
165             }
166         ) if C4::Context->preference('RealTimeHoldsQueue');
167     }
168
169     my $json = $self->json;
170     my $job_data = $json->decode($self->data);
171     $job_data->{messages} = \@messages;
172     $job_data->{report} = $report;
173
174     $self->ended_on(dt_from_string)
175         ->data($json->encode($job_data));
176     $self->status('finished') if $self->status ne 'cancelled';
177     $self->store;
178 }
179
180 =head3 enqueue
181
182 Enqueue the new job
183
184 =cut
185
186 sub enqueue {
187     my ( $self, $args) = @_;
188
189     # TODO Raise exception instead
190     return unless exists $args->{record_ids};
191
192     my @record_ids = @{ $args->{record_ids} };
193
194     $self->SUPER::enqueue({
195         job_size => scalar @record_ids,
196         job_args => {record_ids => \@record_ids,},
197         queue    => 'long_tasks',
198     });
199 }
200
201 1;