@@ -, +, @@ - a new pl/tt files tools/batch_delete_records - a new permission: tools > records_batchdel - using the basket: do a search, add some biblio to your basket, open the basket and click on the "Action" button > "Delete" - generating a list from a report - biblios with issues cannot be deleted (checkbox disabled and line in red). - information is correct. - sort functions work on each columns. - the items, reserves and issues values are correct. - reserves, items and biblio have successful been deleted. - if an error occurs, the tool display an error message. - authorities are display with the summary. - the count usage (used in X biblios) is correct. - The authorities have successful been deleted. - if an error occurs, the tool display an error message. --- .../intranet-tmpl/prog/en/includes/tools-menu.inc | 3 + .../intranet-tmpl/prog/en/modules/basket/basket.tt | 6 + .../prog/en/modules/tools/batch_delete_records.tt | 236 +++++++++++++++++++++ .../prog/en/modules/tools/tools-home.tt | 7 +- tools/batch_delete_records.pl | 231 ++++++++++++++++++++ 5 files changed, 482 insertions(+), 1 deletion(-) create mode 100644 koha-tmpl/intranet-tmpl/prog/en/modules/tools/batch_delete_records.tt create mode 100755 tools/batch_delete_records.pl --- a/koha-tmpl/intranet-tmpl/prog/en/includes/tools-menu.inc +++ a/koha-tmpl/intranet-tmpl/prog/en/includes/tools-menu.inc @@ -56,6 +56,9 @@ [% IF ( CAN_user_tools_items_batchmod ) %]
  • Batch item modification
  • [% END %] + [% IF CAN_user_tools_records_batchdel %] +
  • Batch record deletion
  • + [% END %] [% IF ( CAN_user_tools_export_catalog ) %]
  • Export data
  • [% END %] --- a/koha-tmpl/intranet-tmpl/prog/en/modules/basket/basket.tt +++ a/koha-tmpl/intranet-tmpl/prog/en/modules/basket/basket.tt @@ -101,6 +101,12 @@ function placeHold () { [% END %] +
    + Actions + +
    Print Empty and close Hide window --- a/koha-tmpl/intranet-tmpl/prog/en/modules/tools/batch_delete_records.tt +++ a/koha-tmpl/intranet-tmpl/prog/en/modules/tools/batch_delete_records.tt @@ -0,0 +1,236 @@ +[% PROCESS 'authorities-search-results.inc' %] +[% INCLUDE 'doc-head-open.inc' %] +Koha › Tools › Batch record deletion +[% INCLUDE 'doc-head-close.inc' %] + +[% INCLUDE 'datatables.inc' %] + + + + +[% INCLUDE 'header.inc' %] +[% INCLUDE 'cat-search.inc' %] + + + +
    +
    +
    +
    +

    Batch record deletion

    + [% FOREACH message IN messages %] + [% IF message.type == 'success' %] +
    + [% ELSIF message.type == 'warning' %] +
    + [% ELSIF message.type == 'error' %] +
    + [% END %] + [% IF message.code == 'biblio_not_exists' %] + The biblionumber [% message.biblionumber %] does not exist in the database. + [% ELSIF message.code == 'authority_not_exists' %] + The authority id [% message.authid %] does not exist in the database. + [% ELSIF message.code == 'item_issued' %] + At least one item issued for the biblio [% message.biblionumber %]. + [% ELSIF message.code == 'reserve_not_cancelled' %] + The biblio [% message.biblionumber %] has not been deleted. A reserve (reserve_id [% message.reserve_id %]) caused an error on cancel. + [% ELSIF message.code == 'item_not_deleted' %] + The biblio [% message.biblionumber %] has not been deleted. An item (itemnumber [% message.itemnumber %]) caused an error on delete. + [% ELSIF message.code == 'biblio_not_deleted' %] + The biblio [% message.biblionumber %] has not been deleted. An error occurred on deleting it. + [% ELSIF message.code == 'authority_not_deleted' %] + The authority [% message.authid %] has not been deleted. An error occurred on deleting it. + [% ELSIF message.code == 'biblio_deleted' %] + The biblio [% message.biblionumber %] has successfully been deleted. + [% ELSIF message.code == 'authority_deleted' %] + The authority [% message.authid %] has successfully been deleted. + [% END %] + [% IF message.error %] + (The error was: [% message.error%], see the Koha logfile for more information). + [% END %] +
    + [% END %] + [% IF op == 'form' %] +
    +
    + Record type +
      +
    1. +
    2. +
    +
    +
    + Use a file +
      +
    1. +
    +
    +
    + Or enter a list of record numbers +
      +
    1. + + +
    2. +
    +
    +
    + + + Cancel +
    +
    + [% ELSIF op == 'list' %] + [% IF records %] + [% IF recordtype == 'biblio' %] + +
    + + + + + + + + + + + + + [% FOR biblio IN records %] + + + + + + + + + [% END %] + +
    BiblionumberTitleItemsReservesIssues
    [% biblio.biblionumber %][% biblio.title %][% biblio.itemnumbers.size %][% biblio.reserves.size %][% biblio.issues_count %]
    +
    Reminder: this action will delete all selected biblios, attached subscriptions, existing holds and items!
    + [% ELSE %] + + + + + + + + + + + + + [% FOR authority IN records %] + + + + + + + [% END %] + +
    AuthidSummaryUsed in
    [% authority.authid %][% PROCESS authresult summary=authority.summary %][% authority.count_usage %] biblio(s)
    +
    Reminder: this action will delete all selected authorities!
    + [% END %] +
    + + + + Cancel +
    +
    + [% ELSE %] + There is no record ids defined. + [% END %] + [% ELSIF op == 'report' %] + [% IF report.total_records == report.total_success %] + All records have successfully been deleted! + [% ELSIF report.total_success == 0 %] + No record has been deleted, some errors occurred. + [% ELSE %] + [% report.total_success %] / [% report.total_records %] records have successfully been deleted but some errors occurred. + [% END %] +

    New batch record deletion

    + [% ELSE %] + No action defined for the template. + [% END %] +
    +
    +
    + [% INCLUDE 'tools-menu.inc' %] +
    +
    +[% INCLUDE 'intranet-bottom.inc' %] --- a/koha-tmpl/intranet-tmpl/prog/en/modules/tools/tools-home.tt +++ a/koha-tmpl/intranet-tmpl/prog/en/modules/tools/tools-home.tt @@ -121,7 +121,12 @@
    Batch item modification
    Modify items in a batch
    [% END %] - + + [% IF CAN_user_tools_records_batchdel %] +
    Batch record deletion
    +
    Delete a batch of records (biblios or authorities)
    + [% END %] + [% IF ( CAN_user_tools_export_catalog ) %]
    Export data
    Export bibliographic, holdings, and authority records
    --- a/tools/batch_delete_records.pl +++ a/tools/batch_delete_records.pl @@ -0,0 +1,231 @@ +#!/usr/bin/perl + +# This file is part of Koha. +# +# Copyright 2013 BibLibre +# +# Koha is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as +# published by the Free Software Foundation; either version 3 +# of the License, or (at your option) any later version. +# +# Koha is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General +# Public License along with Koha; if not, see +# + +use Modern::Perl; + +use CGI; +use List::MoreUtils qw( uniq ); + +use C4::Auth; +use C4::Output; +use C4::AuthoritiesMarc; +use C4::Biblio; + +my $input = new CGI; +my $dbh = C4::Context->dbh; +my $op = $input->param('op') // q|form|; +my $recordtype = $input->param('recordtype') // 'biblio'; + +my ($template, $loggedinuser, $cookie) = get_template_and_user({ + template_name => 'tools/batch_delete_records.tt', + query => $input, + type => "intranet", + authnotrequired => 0, + flagsrequired => { tools => 'biblio_batchdel' }, +}); + +my @records; +my @messages; +if ( $op eq 'form' ) { + # Display the form + $template->param( op => 'form' ); +} elsif ( $op eq 'list' ) { + # List all records to process + my @record_ids; + if ( my $bib_list = $input->param('bib_list') ) { + # Come from the basket + @record_ids = split /\//, $bib_list; + $recordtype = 'biblio'; + } elsif ( my $uploadfile = $input->param('uploadfile') ) { + # A file of id is given + while ( my $content = <$uploadfile> ) { + next unless $content; + $content =~ s/[\r\n]*$//; + push @record_ids, $content if $content; + } + } else { + # The user enters manually the list of id + push @record_ids, split( /\s\n/, $input->param('recordnumber_list') ); + } + + for my $record_id ( uniq @record_ids ) { + if ( $recordtype eq 'biblio' ) { + # Retrieve biblio information + my $biblio = C4::Biblio::GetBiblio( $record_id ); + unless ( $biblio ) { + push @messages, { + type => 'warning', + code => 'biblio_not_exists', + biblionumber => $record_id, + }; + next; + } + $biblio->{itemnumbers} = C4::Items::GetItemnumbersForBiblio( $record_id ); + $biblio->{reserves} = C4::Reserves::GetReservesFromBiblionumber({ biblionumber => $record_id }); + $biblio->{issues_count} = C4::Biblio::CountItemsIssued( $record_id ); + push @records, $biblio; + } else { + # Retrieve authority information + my $authority = C4::AuthoritiesMarc::GetAuthority( $record_id ); + unless ( $authority ) { + push @messages, { + type => 'warning', + code => 'authority_not_exists', + authid => $record_id, + }; + next; + } + + $authority = { + authid => $record_id, + summary => C4::AuthoritiesMarc::BuildSummary( $authority, $record_id ), + count_usage => C4::AuthoritiesMarc::CountUsage( $record_id ), + }; + push @records, $authority; + } + } + $template->param( + records => \@records, + op => 'list', + ); +} elsif ( $op eq 'delete' ) { + # We want to delete selected records! + my @record_ids = $input->param('record_id'); + my $dbh = C4::Context->dbh; + $dbh->{AutoCommit} = 0; + $dbh->{RaiseError} = 1; + + my $error; + my $report = { + total_records => 0, + total_success => 0, + }; + RECORD_IDS: for my $record_id ( sort { $a <=> $b } @record_ids ) { + $report->{total_records}++; + next unless $record_id; + if ( $recordtype eq 'biblio' ) { + # Biblios + my $biblionumber = $record_id; + # First, checking if issues exist. + # If yes, nothing to do + if ( C4::Biblio::CountItemsIssued( $biblionumber ) ) { + push @messages, { + type => 'warning', + code => 'item_issued', + biblionumber => $biblionumber, + }; + $dbh->rollback; + next; + } + + # Cancel reserves + my $reserves = C4::Reserves::GetReservesFromBiblionumber({ biblionumber => $biblionumber }); + for my $reserve ( @$reserves ) { + eval{ + C4::Reserves::CancelReserve( { reserve_id => $reserve->{reserve_id} } ); + }; + if ( $@ ) { + push @messages, { + type => 'error', + code => 'reserve_not_cancelled', + biblionumber => $biblionumber, + reserve_id => $reserve->{reserve_id}, + error => $@, + }; + $dbh->rollback; + next RECORD_IDS; + } + } + + # Delete items + my @itemnumbers = @{ C4::Items::GetItemnumbersForBiblio( $biblionumber ) }; + ITEMNUMBER: for my $itemnumber ( @itemnumbers ) { + my $error = eval { C4::Items::DelItemCheck( $dbh, $biblionumber, $itemnumber ) }; + if ( $error != 1 or $@ ) { + push @messages, { + type => 'error', + code => 'item_not_deleted', + biblionumber => $biblionumber, + itemnumber => $itemnumber, + error => ($@ ? $@ : $error), + }; + $dbh->rollback; + next BIBLIONUMBER; + } + } + + # Finally, delete the biblio + my $error = eval { + C4::Biblio::DelBiblio( $biblionumber ); + }; + if ( $error or $@ ) { + push @messages, { + type => 'error', + code => 'biblio_not_deleted', + biblionumber => $biblionumber, + error => ($@ ? $@ : $error), + }; + $dbh->rollback; + next; + } + + push @messages, { + type => 'success', + code => 'biblio_deleted', + biblionumber => $biblionumber, + }; + $report->{total_success}++; + $dbh->commit; + } else { + # Authorities + my $authid = $record_id; + my $r = eval { C4::AuthoritiesMarc::DelAuthority( $authid ) }; + if ( $r eq '0E0' or $@ ) { + push @messages, { + type => 'error', + code => 'authority_not_deleted', + authid => $authid, + error => ($@ ? $@ : 0), + }; + $dbh->rollback; + next; + } else { + push @messages, { + type => 'success', + code => 'authority_deleted', + authid => $authid, + }; + $report->{total_success}++; + $dbh->commit; + } + } + } + $template->param( + op => 'report', + report => $report, + ); +} + +$template->param( + messages => \@messages, + recordtype => $recordtype, +); + +output_html_with_http_headers $input, $cookie, $template->output; --