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