3 # Copyright 2009 PTFS, Inc.
5 # This file is part of Koha.
7 # Koha is free software; you can redistribute it and/or modify it
8 # under the terms of the GNU General Public License as published by
9 # the Free Software Foundation; either version 3 of the License, or
10 # (at your option) any later version.
12 # Koha is distributed in the hope that it will be useful, but
13 # WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 # GNU General Public License for more details.
17 # You should have received a copy of the GNU General Public License
18 # along with Koha; if not, see <http://www.gnu.org/licenses>.
22 use constant DEFAULT_ZEBRAQ_PURGEDAYS => 30;
23 use constant DEFAULT_MAIL_PURGEDAYS => 30;
24 use constant DEFAULT_IMPORT_PURGEDAYS => 60;
25 use constant DEFAULT_LOGS_PURGEDAYS => 180;
26 use constant DEFAULT_MESSAGES_PURGEDAYS => 365;
27 use constant DEFAULT_SEARCHHISTORY_PURGEDAYS => 30;
28 use constant DEFAULT_SHARE_INVITATION_EXPIRY_DAYS => 14;
29 use constant DEFAULT_DEBARMENTS_PURGEDAYS => 30;
30 use constant DEFAULT_JOBS_PURGEDAYS => 1;
31 use constant DEFAULT_JOBS_PURGETYPES => qw{ update_elastic_index };
32 use constant DEFAULT_EDIFACT_MSG_PURGEDAYS => 365;
34 use Getopt::Long qw( GetOptions );
36 use Koha::Script -cron;
38 use C4::Accounts qw( purge_zero_balance_fees );
40 use C4::Log qw( cronlogaction );
41 use C4::Search::History;
43 use Koha::BackgroundJobs;
45 use Koha::DateUtils qw( dt_from_string );
46 use Koha::Item::Transfers;
47 use Koha::Old::Biblioitems;
48 use Koha::Old::Biblios;
49 use Koha::Old::Checkouts;
52 use Koha::Old::Patrons;
53 use Koha::Patron::Debarments qw( DelDebarment );
54 use Koha::Patron::Messages;
55 use Koha::PseudonymizedTransactions;
56 use Koha::UploadedFiles;
60 Usage: $0 [-h|--help] [--confirm] [--sessions] [--sessdays DAYS] [-v|--verbose] [--zebraqueue DAYS] [-m|--mail] [--merged] [--import DAYS] [--logs DAYS] [--searchhistory DAYS] [--restrictions DAYS] [--all-restrictions] [--fees DAYS] [--temp-uploads] [--temp-uploads-days DAYS] [--uploads-missing 0|1 ] [--statistics DAYS] [--deleted-catalog DAYS] [--deleted-patrons DAYS] [--old-issues DAYS] [--old-reserves DAYS] [--transfers DAYS] [--labels DAYS] [--cards DAYS] [--bg-days DAYS [--bg-type TYPE] ] [--edifact-messages DAYS]
62 -h --help prints this help message, and exits, ignoring all
64 --confirm Confirmation flag, the script will be running in dry-run mode is not set.
65 --sessions purge the sessions table. If you use this while users
66 are logged into Koha, they will have to reconnect.
67 --sessdays DAYS purge only sessions older than DAYS days.
68 -v --verbose will cause the script to give you a bit more information
70 --zebraqueue DAYS purge completed zebraqueue entries older than DAYS days.
71 Defaults to 30 days if no days specified.
72 -m --mail DAYS purge items from the mail queue that are older than DAYS days.
73 Defaults to 30 days if no days specified.
74 --merged purged completed entries from need_merge_authorities.
75 --messages DAYS purge entries from messages table older than DAYS days.
76 Defaults to 365 days if no days specified.
77 --import DAYS purge records from import tables older than DAYS days.
78 Defaults to 60 days if no days specified.
79 --z3950 purge records from import tables that are the result
81 --fees DAYS purge entries accountlines older than DAYS days, where
82 amountoutstanding is 0 or NULL.
83 In the case of --fees, DAYS must be greater than
85 --log-module Specify which action log modules to trim. Repeatable.
86 --preserve-log Specify which action logs to exclude. Repeatable.
87 --log-action Specify which action log action entries to trim. Repeatable.
88 --logs DAYS purge entries from action_logs older than DAYS days.
89 Defaults to 180 days if no days specified.
90 --searchhistory DAYS purge entries from search_history older than DAYS days.
91 Defaults to 30 days if no days specified
92 --list-invites DAYS purge (unaccepted) list share invites older than DAYS days.
93 This parameter is prioritised over the
94 PurgeListShareInvitesOlderThan system preference.
95 Defaults to 14 days if no days specified for this parameter and
96 the PurgeListShareInvitesOlderThan system preference is empty.
97 --restrictions DAYS purge patrons restrictions expired since more than DAYS days.
98 Defaults to 30 days if no days specified.
99 --all-restrictions purge all expired patrons restrictions.
100 --del-exp-selfreg Delete expired self registration accounts
101 --del-unv-selfreg DAYS Delete unverified self registrations older than DAYS
102 --unique-holidays DAYS Delete all unique holidays older than DAYS
103 --temp-uploads Delete temporary uploads.
104 --temp-uploads-days DAYS Override the corresponding preference value.
105 --uploads-missing FLAG Delete upload records for missing files when FLAG is true, count them otherwise
106 --oauth-tokens Delete expired OAuth2 tokens
107 --statistics DAYS Purge statistics entries more than DAYS days old.
108 This table is used to build reports, make sure you are aware of the consequences of this before using it!
109 --deleted-catalog DAYS Purge catalog records deleted more then DAYS days ago
110 (from tables deleteditems, deletedbiblioitems, deletedbiblio_metadata and deletedbiblio).
111 --deleted-patrons DAYS Purge patrons deleted more than DAYS days ago.
112 --old-issues DAYS Purge checkouts (old_issues) returned more than DAYS days ago.
113 --old-reserves DAYS Purge reserves (old_reserves) more than DAYS old.
114 --transfers DAYS Purge transfers completed more than DAYS day ago.
115 --pseudo-transactions DAYS Purge the pseudonymized transactions that have been originally created more than DAYS days ago
116 DAYS is optional and can be replaced by:
117 --pseudo-transactions-from YYYY-MM-DD and/or --pseudo-transactions-to YYYY-MM-DD
118 --labels DAYS Purge item label batches last added to more than DAYS days ago.
119 --cards DAY Purge card creator batches last added to more than DAYS days ago.
120 --return-claims Purge all resolved return claims older than the number of days specified in
121 the system preference CleanUpDatabaseReturnClaims.
122 --jobs-days DAYS Purge all finished background jobs this many days old. Defaults to 1 if no DAYS provided.
123 --jobs-type TYPES What type of background job to purge. Defaults to "update_elastic_index" if omitted
124 Specifying "all" will purge all types. Repeatable.
125 --reports DAYS Purge reports data saved more than DAYS days ago. The data is created by running runreport.pl with the --store-results option.
126 --edifact-messages DAYS Purge process and failed EDIFACT messages handled more than DAYS days.
127 Defaults to 365 days if no days specified.
144 my $pListShareInvites;
151 my $special_holidays_days;
153 my $temp_uploads_days;
162 my ( $pPseudoTransactions, $pPseudoTransactionsFrom, $pPseudoTransactionsTo );
164 my $lock_days = C4::Context->preference('LockExpiredDelay');
173 my $edifact_msg_days;
175 my $command_line_options = join( " ", @ARGV );
179 'confirm' => \$confirm,
180 'sessions' => \$sessions,
181 'sessdays:i' => \$sess_days,
182 'v|verbose' => \$verbose,
183 'm|mail:i' => \$mail,
184 'zebraqueue:i' => \$zebraqueue_days,
185 'merged' => \$purge_merged,
186 'import:i' => \$pImport,
189 'log-module:s' => \@log_modules,
190 'preserve-log:s' => \@preserve_logs,
191 'log-action:s' => \@log_actions,
192 'messages:i' => \$pMessages,
193 'fees:i' => \$fees_days,
194 'searchhistory:i' => \$pSearchhistory,
195 'list-invites:i' => \$pListShareInvites,
196 'restrictions:i' => \$pDebarments,
197 'all-restrictions' => \$allDebarments,
198 'del-exp-selfreg' => \$pExpSelfReg,
199 'del-unv-selfreg:i' => \$pUnvSelfReg,
200 'unique-holidays:i' => \$special_holidays_days,
201 'temp-uploads' => \$temp_uploads,
202 'temp-uploads-days:i' => \$temp_uploads_days,
203 'uploads-missing:i' => \$uploads_missing,
204 'oauth-tokens' => \$oauth_tokens,
205 'statistics:i' => \$pStatistics,
206 'deleted-catalog:i' => \$pDeletedCatalog,
207 'deleted-patrons:i' => \$pDeletedPatrons,
208 'old-issues:i' => \$pOldIssues,
209 'old-reserves:i' => \$pOldReserves,
210 'transfers:i' => \$pTransfers,
211 'pseudo-transactions:i' => \$pPseudoTransactions,
212 'pseudo-transactions-from:s' => \$pPseudoTransactionsFrom,
213 'pseudo-transactions-to:s' => \$pPseudoTransactionsTo,
214 'labels' => \$labels,
216 'return-claims' => \$return_claims,
217 'jobs-type:s' => \@jobs_types,
218 'jobs-days:i' => \$jobs_days,
219 'reports:i' => \$reports,
220 'edifact-messages:i' => \$edifact_msg_days,
224 $sessions = 1 if $sess_days && $sess_days > 0;
225 $pImport = DEFAULT_IMPORT_PURGEDAYS if defined($pImport) && $pImport == 0;
226 $pLogs = DEFAULT_LOGS_PURGEDAYS if defined($pLogs) && $pLogs == 0;
227 $zebraqueue_days = DEFAULT_ZEBRAQ_PURGEDAYS if defined($zebraqueue_days) && $zebraqueue_days == 0;
228 $mail = DEFAULT_MAIL_PURGEDAYS if defined($mail) && $mail == 0;
229 $pSearchhistory = DEFAULT_SEARCHHISTORY_PURGEDAYS if defined($pSearchhistory) && $pSearchhistory == 0;
230 $pDebarments = DEFAULT_DEBARMENTS_PURGEDAYS if defined($pDebarments) && $pDebarments == 0;
231 $pMessages = DEFAULT_MESSAGES_PURGEDAYS if defined($pMessages) && $pMessages == 0;
232 $jobs_days = DEFAULT_JOBS_PURGEDAYS if defined($jobs_days) && $jobs_days == 0;
233 @jobs_types = (DEFAULT_JOBS_PURGETYPES) if $jobs_days && @jobs_types == 0;
234 $edifact_msg_days = DEFAULT_EDIFACT_MSG_PURGEDAYS if defined($edifact_msg_days) && $edifact_msg_days == 0;
236 # Choose the number of days at which to purge unaccepted list invites:
237 # - DAYS defined in the list-invites parameter is prioritised first
238 # - PurgeListShareInvitesOlderThan system preference is prioritised second
239 # - Default value of 14 days is prioritised last - if the list-invites parameter is passed without a DAYS value and the PurgeListShareInvitesOlderThan syspref is empty.
240 if ( !defined($pListShareInvites) ) {
241 if ( C4::Context->preference('PurgeListShareInvitesOlderThan') ) {
242 $pListShareInvites = C4::Context->preference('PurgeListShareInvitesOlderThan');
244 } elsif ( defined($pListShareInvites) && $pListShareInvites == 0 ) {
245 $pListShareInvites = DEFAULT_SHARE_INVITATION_EXPIRY_DAYS;
261 || $pListShareInvites
266 || $special_holidays_days
268 || defined $uploads_missing
276 || defined $pPseudoTransactions
277 || $pPseudoTransactionsFrom
278 || $pPseudoTransactionsTo
280 || defined $lock_days && $lock_days ne q{}
286 || $edifact_msg_days )
288 print "You did not specify any cleanup work for the script to do.\n\n";
292 if ( $pDebarments && $allDebarments ) {
293 print "You can not specify both --restrictions and --all-restrictions.\n\n";
297 say "Confirm flag not passed, running in dry-run mode..." unless $confirm;
299 cronlogaction( { info => $command_line_options } );
301 my $dbh = C4::Context->dbh();
305 if ( $sessions && !$sess_days ) {
307 say "Session purge triggered.";
308 $sth = $dbh->prepare(q{ SELECT COUNT(*) FROM sessions });
309 $sth->execute() or die $dbh->errstr;
310 my @count_arr = $sth->fetchrow_array;
311 say $confirm ? "$count_arr[0] entries will be deleted." : "$count_arr[0] entries would be deleted.";
314 $sth = $dbh->prepare(q{ TRUNCATE sessions });
315 $sth->execute() or die $dbh->errstr;
318 print "Done with session purge.\n";
320 } elsif ( $sessions && $sess_days > 0 ) {
321 print "Session purge triggered with days>$sess_days.\n" if $verbose;
322 RemoveOldSessions() if $confirm;
323 print "Done with session purge with days>$sess_days.\n" if $verbose;
326 if ($zebraqueue_days) {
328 print "Zebraqueue purge triggered for $zebraqueue_days days.\n" if $verbose;
329 $sth = $dbh->prepare(
331 SELECT id,biblio_auth_number,server,time
333 WHERE done=1 AND time < date_sub(curdate(), INTERVAL ? DAY)
336 $sth->execute($zebraqueue_days) or die $dbh->errstr;
337 $sth2 = $dbh->prepare(q{ DELETE FROM zebraqueue WHERE id=? });
338 while ( my $record = $sth->fetchrow_hashref ) {
340 $sth2->execute( $record->{id} ) or die $dbh->errstr;
345 say $confirm ? "$count records were deleted." : "$count records would have been deleted.";
346 say "Done with zebraqueue purge.";
352 print "Mail queue purge triggered for $mail days.\n" if $verbose;
353 $sth = $dbh->prepare(
355 DELETE FROM message_queue
356 WHERE time_queued < date_sub(curdate(), INTERVAL ? DAY)
360 $sth->execute($mail) or die $dbh->errstr;
365 ? "$count messages were deleted from the mail queue."
366 : "Message from message_queue would have been deleted";
367 say "Done with message_queue purge.";
372 print "Purging completed entries from need_merge_authorities.\n" if $verbose;
374 $sth = $dbh->prepare(q{ DELETE FROM need_merge_authorities WHERE done=1 });
375 $sth->execute() or die $dbh->errstr;
377 print "Done with purging need_merge_authorities.\n" if $verbose;
381 print "Purging records from import tables.\n" if $verbose;
382 PurgeImportTables() if $confirm;
383 print "Done with purging import tables.\n" if $verbose;
387 print "Purging Z39.50 records from import tables.\n" if $verbose;
388 PurgeZ3950() if $confirm;
389 print "Done with purging Z39.50 records from import tables.\n" if $verbose;
393 print "Purging records from action_logs.\n" if $verbose;
395 DELETE FROM action_logs
396 WHERE timestamp < date_sub(curdate(), INTERVAL ? DAY)
398 my @query_params = ();
399 if (@preserve_logs) {
400 $log_query .= " AND module NOT IN (" . join( ',', ('?') x @preserve_logs ) . ")";
401 push @query_params, @preserve_logs;
404 $log_query .= " AND module IN (" . join( ',', ('?') x @log_modules ) . ")";
405 push @query_params, @log_modules;
408 $log_query .= " AND action IN (" . join( ',', ('?') x @log_actions ) . ")";
409 push @query_params, @log_actions;
411 $sth = $dbh->prepare($log_query);
413 $sth->execute( $pLogs, @query_params ) or die $dbh->errstr;
415 print "Done with purging action_logs.\n" if $verbose;
419 print "Purging messages older than $pMessages days.\n" if $verbose;
420 my $messages = Koha::Patron::Messages->filter_by_last_update(
421 { timestamp_column_name => 'message_date', days => $pMessages } );
422 my $count = $messages->count;
423 $messages->delete if $confirm;
426 ? sprintf( "Done with purging %d messages", $count )
427 : sprintf( "%d messages would have been removed", $count );
432 print "Purging records from accountlines.\n" if $verbose;
433 purge_zero_balance_fees($fees_days) if $confirm;
434 print "Done purging records from accountlines.\n" if $verbose;
437 if ($pSearchhistory) {
438 print "Purging records older than $pSearchhistory from search_history.\n" if $verbose;
439 C4::Search::History::delete( { interval => $pSearchhistory } ) if $confirm;
440 print "Done with purging search_history.\n" if $verbose;
443 if ($pListShareInvites) {
444 print "Purging unaccepted list share invites older than $pListShareInvites days.\n" if $verbose;
445 $sth = $dbh->prepare(
447 DELETE FROM virtualshelfshares
448 WHERE invitekey IS NOT NULL
449 AND (sharedate + INTERVAL ? DAY) < NOW()
453 $sth->execute($pListShareInvites);
455 print "Done with purging unaccepted list share invites.\n" if $verbose;
459 print "Expired patrons restrictions purge triggered for $pDebarments days.\n" if $verbose;
460 my $count = PurgeDebarments( $pDebarments, $confirm );
462 say $confirm ? "$count restrictions were deleted." : "$count restrictions would have been deleted";
463 say "Done with restrictions purge.";
467 if ($allDebarments) {
468 print "All expired patrons restrictions purge triggered.\n" if $verbose;
469 my $count = PurgeDebarments( 0, $confirm );
471 say $confirm ? "$count restrictions were deleted." : "$count restrictions would have been deleted";
472 say "Done with all restrictions purge.";
476 # Lock expired patrons?
477 if ( defined $lock_days && $lock_days ne q{} ) {
478 say "Start locking expired patrons" if $verbose;
479 my $expired_patrons = Koha::Patrons->filter_by_expiration_date( { days => $lock_days } )
480 ->search( { login_attempts => { '!=' => -1 } } );
481 my $count = $expired_patrons->count;
482 $expired_patrons->lock( { remove => 1 } ) if $confirm;
484 say $confirm ? sprintf( "Locked %d patrons", $count ) : sprintf( "Found %d patrons", $count );
488 # Handle unsubscribe requests from GDPR consent form, depends on UnsubscribeReflectionDelay preference
489 say "Start lock unsubscribed, anonymize and delete" if $verbose;
490 my $unsubscribed_patrons = Koha::Patrons->search_unsubscribed;
491 my $count = $unsubscribed_patrons->count;
492 $unsubscribed_patrons->lock( { expire => 1, remove => 1 } ) if $confirm;
493 say $confirm ? sprintf( "Locked %d patrons", $count ) : sprintf( "%d patrons would have been locked", $count )
496 # Anonymize patron data, depending on PatronAnonymizeDelay
497 my $anonymize_candidates = Koha::Patrons->search_anonymize_candidates( { locked => 1 } );
498 $count = $anonymize_candidates->count;
499 $anonymize_candidates->anonymize if $confirm;
500 say $confirm ? sprintf( "Anonymized %d patrons", $count ) : sprintf( "%d patrons would have been anonymized", $count )
503 # Remove patron data, depending on PatronRemovalDelay (will raise an exception if problem encountered
504 my $anonymized_patrons = Koha::Patrons->search_anonymized;
505 $count = $anonymized_patrons->count;
507 $anonymized_patrons->delete( { move => 1 } );
513 say $confirm ? sprintf( "Deleted %d patrons", $count ) : sprintf( "%d patrons would have been deleted", $count );
516 # FIXME The output for dry-run mode needs to be improved
517 # But non trivial changes to C4::Members need to be done before.
520 DeleteExpiredSelfRegs();
522 say "self-registered borrowers may be deleted";
527 DeleteUnverifiedSelfRegs($pUnvSelfReg);
529 say "unverified self-registrations may be deleted";
533 if ($special_holidays_days) {
535 DeleteSpecialHolidays( abs($special_holidays_days) );
537 say "self-registered borrowers may be deleted";
543 # Delete temporary uploads, governed by a pref (unless you override)
544 print "Purging temporary uploads.\n" if $verbose;
546 Koha::UploadedFiles->delete_temporary(
548 defined($temp_uploads_days)
549 ? ( override_pref => $temp_uploads_days )
554 print "Done purging temporary uploads.\n" if $verbose;
557 if ( defined $uploads_missing ) {
558 print "Looking for missing uploads\n" if $verbose;
560 my $keep = $uploads_missing == 1 ? 0 : 1;
561 my $count = Koha::UploadedFiles->delete_missing( { keep_record => $keep } );
563 print "Counted $count missing uploaded files\n";
565 print "Removed $count records for missing uploads\n";
569 # FIXME need to create a filter_by_missing method (then call ->delete) instead of delete_missing
570 say "Dry-run mode cannot guess how many uploads would have been deleted";
575 require Koha::OAuthAccessTokens;
577 my $tokens = Koha::OAuthAccessTokens->search( { expires => { '<=', time } } );
578 my $count = $tokens->count;
579 $tokens->delete if $confirm;
582 ? sprintf( "Removed %d expired OAuth2 tokens", $count )
583 : sprintf( "%d expired OAuth tokens would have been removed", $count );
588 print "Purging statistics older than $pStatistics days.\n" if $verbose;
590 Koha::Statistics->filter_by_last_update( { timestamp_column_name => 'datetime', days => $pStatistics } );
591 my $count = $statistics->count;
592 $statistics->delete if $confirm;
595 ? sprintf( "Done with purging %d statistics", $count )
596 : sprintf( "%d statistics would have been removed", $count );
600 if ( $return_claims && ( my $days = C4::Context->preference('CleanUpDatabaseReturnClaims') ) ) {
601 print "Purging return claims older than $days days.\n" if $verbose;
603 $return_claims = Koha::Checkouts::ReturnClaims->filter_by_last_update(
605 timestamp_column_name => 'resolved_on',
610 my $count = $return_claims->count;
611 $return_claims->delete if $confirm;
615 ? sprintf "Done with purging %d resolved return claims.", $count
616 : sprintf "%d resolved return claims would have been purged.", $count;
620 if ($pDeletedCatalog) {
621 print "Purging deleted catalog older than $pDeletedCatalog days.\n"
623 my $old_items = Koha::Old::Items->filter_by_last_update( { days => $pDeletedCatalog } );
624 my $old_biblioitems = Koha::Old::Biblioitems->filter_by_last_update( { days => $pDeletedCatalog } );
625 my $old_biblios = Koha::Old::Biblios->filter_by_last_update( { days => $pDeletedCatalog } );
626 my ( $c_i, $c_bi, $c_b ) = ( $old_items->count, $old_biblioitems->count, $old_biblios->count );
629 $old_biblioitems->delete;
630 $old_biblios->delete;
635 ? "Done with purging deleted catalog (%d items, %d biblioitems, %d biblios)."
636 : "Deleted catalog would have been removed (%d items, %d biblioitems, %d biblios).",
642 if ($pDeletedPatrons) {
643 print "Purging deleted patrons older than $pDeletedPatrons days.\n" if $verbose;
644 my $old_patrons = Koha::Old::Patrons->filter_by_last_update(
645 { timestamp_column_name => 'updated_on', days => $pDeletedPatrons } );
646 my $count = $old_patrons->count;
647 $old_patrons->delete if $confirm;
650 ? sprintf "Done with purging %d deleted patrons.", $count
651 : sprintf "%d deleted patrons would have been purged.", $count;
656 print "Purging old checkouts older than $pOldIssues days.\n" if $verbose;
657 my $old_checkouts = Koha::Old::Checkouts->filter_by_last_update( { days => $pOldIssues } );
658 my $count = $old_checkouts->count;
659 $old_checkouts->delete if $confirm;
662 ? sprintf "Done with purging %d old checkouts.", $count
663 : sprintf "%d old checkouts would have been purged.", $count;
668 print "Purging old reserves older than $pOldReserves days.\n" if $verbose;
669 my $old_reserves = Koha::Old::Holds->filter_by_last_update( { days => $pOldReserves } );
670 my $count = $old_reserves->count;
671 $old_reserves->delete if $confirm;
674 ? sprintf "Done with purging %d old reserves.", $count
675 : sprintf "%d old reserves would have been purged.", $count;
680 print "Purging arrived item transfers older than $pTransfers days.\n" if $verbose;
681 my $transfers = Koha::Item::Transfers->filter_by_last_update(
683 timestamp_column_name => 'datearrived',
687 my $count = $transfers->count;
688 $transfers->delete if $confirm;
691 ? sprintf "Done with purging %d transfers.", $count
692 : sprintf "%d transfers would have been purged.", $count;
696 if ( defined $pPseudoTransactions or $pPseudoTransactionsFrom or $pPseudoTransactionsTo ) {
697 print "Purging pseudonymized transactions\n" if $verbose;
698 if ($pPseudoTransactionsTo) {
699 $pPseudoTransactionsTo = dt_from_string($pPseudoTransactionsTo);
700 if ( $pPseudoTransactionsTo->hour == 0 && $pPseudoTransactionsTo->minute == 0 ) {
701 $pPseudoTransactionsTo->set( hour => 23, minute => 59, second => 59 );
704 my $anonymized_transactions = Koha::PseudonymizedTransactions->filter_by_last_update(
706 timestamp_column_name => 'datetime',
707 ( defined $pPseudoTransactions ? ( days => $pPseudoTransactions ) : () ),
708 ( $pPseudoTransactionsFrom ? ( from => $pPseudoTransactionsFrom ) : () ),
709 ( $pPseudoTransactionsTo ? ( to => $pPseudoTransactionsTo ) : () ),
712 my $count = $anonymized_transactions->count;
713 $anonymized_transactions->delete if $confirm;
716 ? sprintf "Done with purging %d pseudonymized transactions.", $count
717 : sprintf "%d pseudonymized transactions would have been purged.", $count;
722 print "Purging item label batches last added to more than $labels days ago.\n" if $verbose;
723 my $count = PurgeCreatorBatches( $labels, 'labels', $confirm );
726 ? sprintf "Done with purging %d item label batches last added to more than %d days ago.\n", $count, $labels
727 : sprintf "%d item label batches would have been purged.", $count;
732 print "Purging card creator batches last added to more than $cards days ago.\n" if $verbose;
733 my $count = PurgeCreatorBatches( $labels, 'patroncards', $confirm );
736 ? sprintf "Done with purging %d card creator batches last added to more than %d days ago.\n", $count,
738 : sprintf "%d card creator batches would have been purged.", $count;
743 print "Purging background jobs more than $jobs_days days ago.\n"
745 my $jobs = Koha::BackgroundJobs->search(
747 status => 'finished',
748 ( $jobs_types[0] eq 'all' ? () : ( type => \@jobs_types ) )
750 )->filter_by_last_update(
752 timestamp_column_name => 'ended_on',
756 my $count = $jobs->count;
757 $jobs->delete if $confirm;
760 ? sprintf "Done with purging %d background jobs of type(s): %s added more than %d days ago.\n",
761 $count, join( ',', @jobs_types ), $jobs_days
762 : sprintf "%d background jobs of type(s): %s added more than %d days ago would have been purged.",
763 $count, join( ',', @jobs_types ), $jobs_days;
769 PurgeSavedReports($reports);
772 say "Purging reports data saved more than $reports days ago.\n";
776 if ($edifact_msg_days) {
777 print "Purging EDIFACT messages older than $edifact_msg_days days.\n" if $verbose;
778 my $count = PurgeEdifactMessages( $edifact_msg_days, $confirm );
781 ? sprintf( "Done with purging %d EDIFACT messages", $count )
782 : sprintf( "%d EDIFACT messages would have been removed", $count );
786 cronlogaction( { action => 'End', info => "COMPLETED" } );
790 sub RemoveOldSessions {
791 my ( $id, $a_session, $limit, $lasttime );
792 $limit = time() - 24 * 3600 * $sess_days;
794 $sth = $dbh->prepare(q{ SELECT id, a_session FROM sessions });
795 $sth->execute or die $dbh->errstr;
796 $sth->bind_columns( \$id, \$a_session );
797 $sth2 = $dbh->prepare(q{ DELETE FROM sessions WHERE id=? });
800 while ( $sth->fetch ) {
802 if ( $a_session =~ /lasttime:\s+'?(\d+)/ ) {
804 } elsif ( $a_session =~ /(ATIME|CTIME):\s+'?(\d+)/ ) {
807 if ( $lasttime && $lasttime < $limit ) {
808 $sth2->execute($id) or die $dbh->errstr;
813 print "$count sessions were deleted.\n";
817 sub PurgeImportTables {
819 #First purge import_records
820 #Delete cascades to import_biblios, import_items and import_record_matches
821 $sth = $dbh->prepare(
823 DELETE FROM import_records
824 WHERE upload_timestamp < date_sub(curdate(), INTERVAL ? DAY)
827 $sth->execute($pImport) or die $dbh->errstr;
829 # Now purge import_batches
830 # Timestamp cannot be used here without care, because records are added
831 # continuously to batches without updating timestamp (Z39.50 search).
832 # So we only delete older empty batches.
833 # This delete will therefore not have a cascading effect.
834 $sth = $dbh->prepare(
837 FROM import_batches ba
838 LEFT JOIN import_records re ON re.import_batch_id=ba.import_batch_id
839 WHERE re.import_record_id IS NULL AND
840 ba.upload_timestamp < date_sub(curdate(), INTERVAL ? DAY)
843 $sth->execute($pImport) or die $dbh->errstr;
847 $sth = $dbh->prepare(
849 DELETE FROM import_batches
850 WHERE batch_type = 'z3950'
853 $sth->execute() or die $dbh->errstr;
856 sub PurgeDebarments {
857 require Koha::Patron::Debarments;
858 my ( $days, $doit ) = @_;
860 $sth = $dbh->prepare(
862 SELECT borrower_debarment_id
863 FROM borrower_debarments
864 WHERE expiration < date_sub(curdate(), INTERVAL ? DAY)
867 $sth->execute($days) or die $dbh->errstr;
868 while ( my ($borrower_debarment_id) = $sth->fetchrow_array ) {
869 Koha::Patron::Debarments::DelDebarment($borrower_debarment_id) if $doit;
875 sub PurgeCreatorBatches {
876 require C4::Labels::Batch;
877 my ( $days, $creator, $doit ) = @_;
879 $sth = $dbh->prepare(
881 SELECT batch_id, branch_code FROM creator_batches
884 FROM (SELECT batch_id
888 HAVING max(timestamp) <= date_sub(curdate(),interval ? day)) a)
891 $sth->execute( $creator, $days ) or die $dbh->errstr;
892 while ( my ( $batch_id, $branch_code ) = $sth->fetchrow_array ) {
893 C4::Labels::Batch::delete(
894 batch_id => $batch_id,
895 branch_code => $branch_code
902 sub DeleteExpiredSelfRegs {
903 my $cnt = C4::Members::DeleteExpiredOpacRegistrations();
904 print "Removed $cnt expired self-registered borrowers\n" if $verbose;
907 sub DeleteUnverifiedSelfRegs {
908 my $cnt = C4::Members::DeleteUnverifiedOpacRegistrations( $_[0] );
909 print "Removed $cnt unverified self-registrations\n" if $verbose;
912 sub DeleteSpecialHolidays {
915 my $sth = $dbh->prepare(
917 DELETE FROM special_holidays
918 WHERE DATE( CONCAT( year, '-', month, '-', day ) ) < DATE_SUB( CAST(NOW() AS DATE), INTERVAL ? DAY );
921 my $count = $sth->execute($days) + 0;
922 print "Removed $count unique holidays\n" if $verbose;
925 sub PurgeSavedReports {
928 my $sth = $dbh->prepare(
930 DELETE FROM saved_reports
931 WHERE date(date_run) < DATE_SUB(CURDATE(),INTERVAL ? DAY );
934 $sth->execute($reports);
937 sub PurgeEdifactMessages {
938 my ( $days, $doit ) = @_;
940 my $schema = Koha::Database->new()->schema();
941 my $dtf = $schema->storage->datetime_parser;
942 my $resultset = $schema->resultset('EdifactMessage')->search(
944 transfer_date => { '<' => $dtf->format_datetime( dt_from_string->subtract( days => $days ) ) },
945 status => { '!=' => 'new' },
948 my $count = $resultset->count;
950 $resultset->delete if $doit;