1 package C4::ImportBatch;
3 # Copyright (C) 2007 LibLime, 2012 C & P Bibliography Services
5 # This file is part of Koha.
7 # Koha is free software; you can redistribute it and/or modify it under the
8 # terms of the GNU General Public License as published by the Free Software
9 # Foundation; either version 2 of the License, or (at your option) any later
12 # Koha is distributed in the hope that it will be useful, but WITHOUT ANY
13 # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
14 # A PARTICULAR PURPOSE. See the GNU General Public License for more details.
16 # You should have received a copy of the GNU General Public License along
17 # with Koha; if not, write to the Free Software Foundation, Inc.,
18 # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
28 use C4::AuthoritiesMarc;
30 use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);
33 # set the version for version checking
34 $VERSION = 3.07.00.049;
41 GetImportRecordMarcXML
46 AddItemsToImportBiblio
57 GetStagedWebserviceBatches
58 GetImportBatchRangeDesc
59 GetNumberOfNonZ3950ImportBatches
61 GetItemNumbersFromImportBatch
65 GetImportBatchOverlayAction
66 SetImportBatchOverlayAction
67 GetImportBatchNoMatchAction
68 SetImportBatchNoMatchAction
69 GetImportBatchItemAction
70 SetImportBatchItemAction
73 GetImportRecordOverlayStatus
74 SetImportRecordOverlayStatus
77 GetImportRecordMatches
78 SetImportRecordMatches
84 C4::ImportBatch - manage batches of imported MARC records
92 =head2 GetZ3950BatchId
94 my $batchid = GetZ3950BatchId($z3950server);
96 Retrieves the ID of the import batch for the Z39.50
97 reservoir for the given target. If necessary,
98 creates the import batch.
102 sub GetZ3950BatchId {
103 my ($z3950server) = @_;
105 my $dbh = C4::Context->dbh;
106 my $sth = $dbh->prepare("SELECT import_batch_id FROM import_batches
107 WHERE batch_type = 'z3950'
109 $sth->execute($z3950server);
110 my $rowref = $sth->fetchrow_arrayref();
112 if (defined $rowref) {
115 my $batch_id = AddImportBatch( {
116 overlay_action => 'create_new',
117 import_status => 'staged',
118 batch_type => 'z3950',
119 file_name => $z3950server,
126 =head2 GetWebserviceBatchId
128 my $batchid = GetWebserviceBatchId();
130 Retrieves the ID of the import batch for webservice.
131 If necessary, creates the import batch.
135 my $WEBSERVICE_BASE_QRY = <<EOQ;
136 SELECT import_batch_id FROM import_batches
137 WHERE batch_type = 'webservice'
138 AND import_status = 'staged'
140 sub GetWebserviceBatchId {
143 my $dbh = C4::Context->dbh;
144 my $sql = $WEBSERVICE_BASE_QRY;
146 foreach my $field (qw(matcher_id overlay_action nomatch_action item_action)) {
147 if (my $val = $params->{$field}) {
148 $sql .= " AND $field = ?";
152 my $id = $dbh->selectrow_array($sql, undef, @args);
155 $params->{batch_type} = 'webservice';
156 $params->{import_status} = 'staged';
157 return AddImportBatch($params);
160 =head2 GetImportRecordMarc
162 my ($marcblob, $encoding) = GetImportRecordMarc($import_record_id);
166 sub GetImportRecordMarc {
167 my ($import_record_id) = @_;
169 my $dbh = C4::Context->dbh;
170 my $sth = $dbh->prepare("SELECT marc, encoding FROM import_records WHERE import_record_id = ?");
171 $sth->execute($import_record_id);
172 my ($marc, $encoding) = $sth->fetchrow();
174 return $marc, $encoding;
178 =head2 GetImportRecordMarcXML
180 my $marcxml = GetImportRecordMarcXML($import_record_id);
184 sub GetImportRecordMarcXML {
185 my ($import_record_id) = @_;
187 my $dbh = C4::Context->dbh;
188 my $sth = $dbh->prepare("SELECT marcxml FROM import_records WHERE import_record_id = ?");
189 $sth->execute($import_record_id);
190 my ($marcxml) = $sth->fetchrow();
196 =head2 AddImportBatch
198 my $batch_id = AddImportBatch($params_hash);
206 foreach (qw( matcher_id template_id branchcode
207 overlay_action nomatch_action item_action
208 import_status batch_type file_name comments record_type )) {
209 if (exists $params->{$_}) {
211 push @vals, $params->{$_};
214 my $dbh = C4::Context->dbh;
215 $dbh->do("INSERT INTO import_batches (".join( ',', @fields).")
216 VALUES (".join( ',', map '?', @fields).")",
219 return $dbh->{'mysql_insertid'};
222 =head2 GetImportBatch
224 my $row = GetImportBatch($batch_id);
226 Retrieve a hashref of an import_batches row.
233 my $dbh = C4::Context->dbh;
234 my $sth = $dbh->prepare_cached("SELECT * FROM import_batches WHERE import_batch_id = ?");
235 $sth->bind_param(1, $batch_id);
237 my $result = $sth->fetchrow_hashref;
243 =head2 AddBiblioToBatch
245 my $import_record_id = AddBiblioToBatch($batch_id, $record_sequence,
246 $marc_record, $encoding, $z3950random, $update_counts);
250 sub AddBiblioToBatch {
251 my $batch_id = shift;
252 my $record_sequence = shift;
253 my $marc_record = shift;
254 my $encoding = shift;
255 my $z3950random = shift;
256 my $update_counts = @_ ? shift : 1;
258 my $import_record_id = _create_import_record($batch_id, $record_sequence, $marc_record, 'biblio', $encoding, $z3950random, C4::Context->preference('marcflavour'));
259 _add_biblio_fields($import_record_id, $marc_record);
260 _update_batch_record_counts($batch_id) if $update_counts;
261 return $import_record_id;
264 =head2 ModBiblioInBatch
266 ModBiblioInBatch($import_record_id, $marc_record);
270 sub ModBiblioInBatch {
271 my ($import_record_id, $marc_record) = @_;
273 _update_import_record_marc($import_record_id, $marc_record, C4::Context->preference('marcflavour'));
274 _update_biblio_fields($import_record_id, $marc_record);
278 =head2 AddAuthToBatch
280 my $import_record_id = AddAuthToBatch($batch_id, $record_sequence,
281 $marc_record, $encoding, $z3950random, $update_counts, [$marc_type]);
286 my $batch_id = shift;
287 my $record_sequence = shift;
288 my $marc_record = shift;
289 my $encoding = shift;
290 my $z3950random = shift;
291 my $update_counts = @_ ? shift : 1;
292 my $marc_type = shift || C4::Context->preference('marcflavour');
294 $marc_type = 'UNIMARCAUTH' if $marc_type eq 'UNIMARC';
296 my $import_record_id = _create_import_record($batch_id, $record_sequence, $marc_record, 'auth', $encoding, $z3950random, $marc_type);
297 _add_auth_fields($import_record_id, $marc_record);
298 _update_batch_record_counts($batch_id) if $update_counts;
299 return $import_record_id;
302 =head2 ModAuthInBatch
304 ModAuthInBatch($import_record_id, $marc_record);
309 my ($import_record_id, $marc_record) = @_;
311 my $marcflavour = C4::Context->preference('marcflavour');
312 _update_import_record_marc($import_record_id, $marc_record, $marcflavour eq 'UNIMARC' ? 'UNIMARCAUTH' : 'USMARC');
316 =head2 BatchStageMarcRecords
318 ($batch_id, $num_records, $num_items, @invalid_records) =
319 BatchStageMarcRecords($record_type, $encoding, $marc_records, $file_name,
320 $comments, $branch_code, $parse_items,
322 $progress_interval, $progress_callback);
326 sub BatchStageMarcRecords {
327 my $record_type = shift;
328 my $encoding = shift;
329 my $marc_records = shift;
330 my $file_name = shift;
331 my $comments = shift;
332 my $branch_code = shift;
333 my $parse_items = shift;
334 my $leave_as_staging = shift;
336 # optional callback to monitor status
338 my $progress_interval = 0;
339 my $progress_callback = undef;
341 $progress_interval = shift;
342 $progress_callback = shift;
343 $progress_interval = 0 unless $progress_interval =~ /^\d+$/ and $progress_interval > 0;
344 $progress_interval = 0 unless 'CODE' eq ref $progress_callback;
347 my $batch_id = AddImportBatch( {
348 overlay_action => 'create_new',
349 import_status => 'staging',
350 batch_type => 'batch',
351 file_name => $file_name,
352 comments => $comments,
353 record_type => $record_type,
356 SetImportBatchItemAction($batch_id, 'always_add');
358 SetImportBatchItemAction($batch_id, 'ignore');
361 my $marc_type = C4::Context->preference('marcflavour');
362 $marc_type .= 'AUTH' if ($marc_type eq 'UNIMARC' && $record_type eq 'auth');
363 my @invalid_records = ();
366 # FIXME - for now, we're dealing only with bibs
368 foreach my $marc_blob (split(/\x1D/, $marc_records)) {
369 $marc_blob =~ s/^\s+//g;
370 $marc_blob =~ s/\s+$//g;
371 next unless $marc_blob;
373 if ($progress_interval and (0 == ($rec_num % $progress_interval))) {
374 &$progress_callback($rec_num);
376 my ($marc_record, $charset_guessed, $char_errors) =
377 MarcToUTF8Record($marc_blob, $marc_type, $encoding);
379 $encoding = $charset_guessed unless $encoding;
381 my $import_record_id;
382 if (scalar($marc_record->fields()) == 0) {
383 push @invalid_records, $marc_blob;
386 # Normalize the record so it doesn't have separated diacritics
387 SetUTF8Flag($marc_record);
390 if ($record_type eq 'biblio') {
391 $import_record_id = AddBiblioToBatch($batch_id, $rec_num, $marc_record, $encoding, int(rand(99999)), 0);
393 my @import_items_ids = AddItemsToImportBiblio($batch_id, $import_record_id, $marc_record, 0);
394 $num_items += scalar(@import_items_ids);
396 } elsif ($record_type eq 'auth') {
397 $import_record_id = AddAuthToBatch($batch_id, $rec_num, $marc_record, $encoding, int(rand(99999)), 0, $marc_type);
401 unless ($leave_as_staging) {
402 SetImportBatchStatus($batch_id, 'staged');
404 # FIXME branch_code, number of bibs, number of items
405 _update_batch_record_counts($batch_id);
406 return ($batch_id, $num_valid, $num_items, @invalid_records);
409 =head2 AddItemsToImportBiblio
411 my @import_items_ids = AddItemsToImportBiblio($batch_id,
412 $import_record_id, $marc_record, $update_counts);
416 sub AddItemsToImportBiblio {
417 my $batch_id = shift;
418 my $import_record_id = shift;
419 my $marc_record = shift;
420 my $update_counts = @_ ? shift : 0;
422 my @import_items_ids = ();
424 my $dbh = C4::Context->dbh;
425 my ($item_tag,$item_subfield) = &GetMarcFromKohaField("items.itemnumber",'');
426 foreach my $item_field ($marc_record->field($item_tag)) {
427 my $item_marc = MARC::Record->new();
428 $item_marc->leader("00000 a "); # must set Leader/09 to 'a'
429 $item_marc->append_fields($item_field);
430 $marc_record->delete_field($item_field);
431 my $sth = $dbh->prepare_cached("INSERT INTO import_items (import_record_id, status, marcxml)
433 $sth->bind_param(1, $import_record_id);
434 $sth->bind_param(2, 'staged');
435 $sth->bind_param(3, $item_marc->as_xml());
437 push @import_items_ids, $dbh->{'mysql_insertid'};
441 if ($#import_items_ids > -1) {
442 _update_batch_record_counts($batch_id) if $update_counts;
443 _update_import_record_marc($import_record_id, $marc_record, C4::Context->preference('marcflavour'));
445 return @import_items_ids;
448 =head2 BatchFindDuplicates
450 my $num_with_matches = BatchFindDuplicates($batch_id, $matcher,
451 $max_matches, $progress_interval, $progress_callback);
453 Goes through the records loaded in the batch and attempts to
454 find duplicates for each one. Sets the matching status
455 of each record to "no_match" or "auto_match" as appropriate.
457 The $max_matches parameter is optional; if it is not supplied,
460 The $progress_interval and $progress_callback parameters are
461 optional; if both are supplied, the sub referred to by
462 $progress_callback will be invoked every $progress_interval
463 records using the number of records processed as the
468 sub BatchFindDuplicates {
469 my $batch_id = shift;
471 my $max_matches = @_ ? shift : 10;
473 # optional callback to monitor status
475 my $progress_interval = 0;
476 my $progress_callback = undef;
478 $progress_interval = shift;
479 $progress_callback = shift;
480 $progress_interval = 0 unless $progress_interval =~ /^\d+$/ and $progress_interval > 0;
481 $progress_interval = 0 unless 'CODE' eq ref $progress_callback;
484 my $dbh = C4::Context->dbh;
486 my $sth = $dbh->prepare("SELECT import_record_id, record_type, marc
488 WHERE import_batch_id = ?");
489 $sth->execute($batch_id);
490 my $num_with_matches = 0;
492 while (my $rowref = $sth->fetchrow_hashref) {
494 if ($progress_interval and (0 == ($rec_num % $progress_interval))) {
495 &$progress_callback($rec_num);
497 my $marc_record = MARC::Record->new_from_usmarc($rowref->{'marc'});
499 if (defined $matcher) {
500 @matches = $matcher->get_matches($marc_record, $max_matches);
502 if (scalar(@matches) > 0) {
504 SetImportRecordMatches($rowref->{'import_record_id'}, @matches);
505 SetImportRecordOverlayStatus($rowref->{'import_record_id'}, 'auto_match');
507 SetImportRecordMatches($rowref->{'import_record_id'}, ());
508 SetImportRecordOverlayStatus($rowref->{'import_record_id'}, 'no_match');
512 return $num_with_matches;
515 =head2 BatchCommitRecords
517 my ($num_added, $num_updated, $num_items_added, $num_items_replaced, $num_items_errored, $num_ignored) =
518 BatchCommitRecords($batch_id, $framework,
519 $progress_interval, $progress_callback);
523 sub BatchCommitRecords {
524 my $batch_id = shift;
525 my $framework = shift;
527 # optional callback to monitor status
529 my $progress_interval = 0;
530 my $progress_callback = undef;
532 $progress_interval = shift;
533 $progress_callback = shift;
534 $progress_interval = 0 unless $progress_interval =~ /^\d+$/ and $progress_interval > 0;
535 $progress_interval = 0 unless 'CODE' eq ref $progress_callback;
541 my $num_items_added = 0;
542 my $num_items_replaced = 0;
543 my $num_items_errored = 0;
545 # commit (i.e., save, all records in the batch)
546 SetImportBatchStatus('importing');
547 my $overlay_action = GetImportBatchOverlayAction($batch_id);
548 my $nomatch_action = GetImportBatchNoMatchAction($batch_id);
549 my $item_action = GetImportBatchItemAction($batch_id);
552 my $dbh = C4::Context->dbh;
553 my $sth = $dbh->prepare("SELECT import_records.import_record_id, record_type, status, overlay_status, marc, encoding
555 LEFT JOIN import_auths ON (import_records.import_record_id=import_auths.import_record_id)
556 LEFT JOIN import_biblios ON (import_records.import_record_id=import_biblios.import_record_id)
557 WHERE import_batch_id = ?");
558 $sth->execute($batch_id);
559 my $marcflavour = C4::Context->preference('marcflavour');
561 while (my $rowref = $sth->fetchrow_hashref) {
562 $record_type = $rowref->{'record_type'};
564 if ($progress_interval and (0 == ($rec_num % $progress_interval))) {
565 &$progress_callback($rec_num);
567 if ($rowref->{'status'} eq 'error' or $rowref->{'status'} eq 'imported') {
573 if ($marcflavour eq 'UNIMARC' && $record_type eq 'auth') {
574 $marc_type = 'UNIMARCAUTH';
575 } elsif ($marcflavour eq 'UNIMARC') {
576 $marc_type = 'UNIMARC';
578 $marc_type = 'USMARC';
580 my $marc_record = MARC::Record->new_from_usmarc($rowref->{'marc'});
582 if ($record_type eq 'biblio') {
583 # remove any item tags - rely on BatchCommitItems
584 ($item_tag,$item_subfield) = &GetMarcFromKohaField("items.itemnumber",'');
585 foreach my $item_field ($marc_record->field($item_tag)) {
586 $marc_record->delete_field($item_field);
590 my ($record_result, $item_result, $record_match) =
591 _get_commit_action($overlay_action, $nomatch_action, $item_action,
592 $rowref->{'overlay_status'}, $rowref->{'import_record_id'}, $record_type);
596 if ($record_result eq 'create_new') {
598 if ($record_type eq 'biblio') {
599 my $biblioitemnumber;
600 ($recordid, $biblioitemnumber) = AddBiblio($marc_record, $framework);
601 $query = "UPDATE import_biblios SET matched_biblionumber = ? WHERE import_record_id = ?";
602 if ($item_result eq 'create_new' || $item_result eq 'replace') {
603 my ($bib_items_added, $bib_items_replaced, $bib_items_errored) = BatchCommitItems($rowref->{'import_record_id'}, $recordid, $item_result);
604 $num_items_added += $bib_items_added;
605 $num_items_replaced += $bib_items_replaced;
606 $num_items_errored += $bib_items_errored;
609 $recordid = AddAuthority($marc_record, undef, GuessAuthTypeCode($marc_record));
610 $query = "UPDATE import_auths SET matched_authid = ? WHERE import_record_id = ?";
612 my $sth = $dbh->prepare_cached($query);
613 $sth->execute($recordid, $rowref->{'import_record_id'});
615 SetImportRecordStatus($rowref->{'import_record_id'}, 'imported');
616 } elsif ($record_result eq 'replace') {
618 $recordid = $record_match;
620 if ($record_type eq 'biblio') {
621 my ($count, $oldbiblio) = GetBiblio($recordid);
622 $oldxml = GetXmlBiblio($recordid);
624 # remove item fields so that they don't get
625 # added again if record is reverted
626 # FIXME: GetXmlBiblio output should not contain item info any more! So the next foreach should not be needed. Does not hurt either; may remove old 952s that should not have been there anymore.
627 my $old_marc = MARC::Record->new_from_xml(StripNonXmlChars($oldxml), 'UTF-8', $rowref->{'encoding'}, $marc_type);
628 foreach my $item_field ($old_marc->field($item_tag)) {
629 $old_marc->delete_field($item_field);
631 $oldxml = $old_marc->as_xml($marc_type);
633 ModBiblio($marc_record, $recordid, $oldbiblio->{'frameworkcode'});
634 $query = "UPDATE import_biblios SET matched_biblionumber = ? WHERE import_record_id = ?";
636 if ($item_result eq 'create_new' || $item_result eq 'replace') {
637 my ($bib_items_added, $bib_items_replaced, $bib_items_errored) = BatchCommitItems($rowref->{'import_record_id'}, $recordid, $item_result);
638 $num_items_added += $bib_items_added;
639 $num_items_replaced += $bib_items_replaced;
640 $num_items_errored += $bib_items_errored;
643 $oldxml = GetAuthorityXML($recordid);
645 ModAuthority($recordid, $marc_record, GuessAuthTypeCode($marc_record));
646 $query = "UPDATE import_auths SET matched_authid = ? WHERE import_record_id = ?";
648 my $sth = $dbh->prepare_cached("UPDATE import_records SET marcxml_old = ? WHERE import_record_id = ?");
649 $sth->execute($oldxml, $rowref->{'import_record_id'});
651 my $sth2 = $dbh->prepare_cached($query);
652 $sth2->execute($recordid, $rowref->{'import_record_id'});
654 SetImportRecordOverlayStatus($rowref->{'import_record_id'}, 'match_applied');
655 SetImportRecordStatus($rowref->{'import_record_id'}, 'imported');
656 } elsif ($record_result eq 'ignore') {
657 $recordid = $record_match;
659 $recordid = $record_match;
660 if ($record_type eq 'biblio' and defined $recordid and ( $item_result eq 'create_new' || $item_result eq 'replace' ) ) {
661 my ($bib_items_added, $bib_items_replaced, $bib_items_errored) = BatchCommitItems($rowref->{'import_record_id'}, $recordid, $item_result);
662 $num_items_added += $bib_items_added;
663 $num_items_replaced += $bib_items_replaced;
664 $num_items_errored += $bib_items_errored;
665 # still need to record the matched biblionumber so that the
666 # items can be reverted
667 my $sth2 = $dbh->prepare_cached("UPDATE import_biblios SET matched_biblionumber = ? WHERE import_record_id = ?");
668 $sth2->execute($recordid, $rowref->{'import_record_id'});
669 SetImportRecordOverlayStatus($rowref->{'import_record_id'}, 'match_applied');
671 SetImportRecordStatus($rowref->{'import_record_id'}, 'ignored');
675 SetImportBatchStatus($batch_id, 'imported');
676 return ($num_added, $num_updated, $num_items_added, $num_items_replaced, $num_items_errored, $num_ignored);
679 =head2 BatchCommitItems
681 ($num_items_added, $num_items_errored) =
682 BatchCommitItems($import_record_id, $biblionumber);
686 sub BatchCommitItems {
687 my ( $import_record_id, $biblionumber, $action ) = @_;
689 my $dbh = C4::Context->dbh;
691 my $num_items_added = 0;
692 my $num_items_errored = 0;
693 my $num_items_replaced = 0;
695 my $sth = $dbh->prepare( "
696 SELECT import_items_id, import_items.marcxml, encoding
698 JOIN import_records USING (import_record_id)
699 WHERE import_record_id = ?
700 ORDER BY import_items_id
702 $sth->bind_param( 1, $import_record_id );
705 while ( my $row = $sth->fetchrow_hashref() ) {
706 my $item_marc = MARC::Record->new_from_xml( StripNonXmlChars( $row->{'marcxml'} ), 'UTF-8', $row->{'encoding'} );
708 # Delete date_due subfield as to not accidentally delete item checkout due dates
709 my ( $MARCfield, $MARCsubfield ) = GetMarcFromKohaField( 'items.onloan', GetFrameworkCode($biblionumber) );
710 $item_marc->field($MARCfield)->delete_subfield( code => $MARCsubfield );
712 my $item = TransformMarcToKoha( $dbh, $item_marc );
714 my $duplicate_barcode = exists( $item->{'barcode'} ) && GetItemnumberFromBarcode( $item->{'barcode'} );
715 my $duplicate_itemnumber = exists( $item->{'itemnumber'} );
717 my $updsth = $dbh->prepare("UPDATE import_items SET status = ?, itemnumber = ? WHERE import_items_id = ?");
718 if ( $action eq "replace" && $duplicate_itemnumber ) {
719 # Duplicate itemnumbers have precedence, that way we can update barcodes by overlaying
720 ModItemFromMarc( $item_marc, $biblionumber, $item->{itemnumber} );
721 $updsth->bind_param( 1, 'imported' );
722 $updsth->bind_param( 2, $item->{itemnumber} );
723 $updsth->bind_param( 3, $row->{'import_items_id'} );
726 $num_items_replaced++;
727 } elsif ( $action eq "replace" && $duplicate_barcode ) {
728 my $itemnumber = GetItemnumberFromBarcode( $item->{'barcode'} );
729 ModItemFromMarc( $item_marc, $biblionumber, $itemnumber );
730 $updsth->bind_param( 1, 'imported' );
731 $updsth->bind_param( 2, $item->{itemnumber} );
732 $updsth->bind_param( 3, $row->{'import_items_id'} );
735 $num_items_replaced++;
736 } elsif ($duplicate_barcode) {
737 $updsth->bind_param( 1, 'error' );
738 $updsth->bind_param( 2, 'duplicate item barcode' );
739 $updsth->bind_param( 3, $row->{'import_items_id'} );
741 $num_items_errored++;
743 my ( $item_biblionumber, $biblioitemnumber, $itemnumber ) = AddItemFromMarc( $item_marc, $biblionumber );
744 $updsth->bind_param( 1, 'imported' );
745 $updsth->bind_param( 2, $itemnumber );
746 $updsth->bind_param( 3, $row->{'import_items_id'} );
753 return ( $num_items_added, $num_items_replaced, $num_items_errored );
756 =head2 BatchRevertRecords
758 my ($num_deleted, $num_errors, $num_reverted, $num_items_deleted,
759 $num_ignored) = BatchRevertRecords($batch_id);
763 sub BatchRevertRecords {
764 my $batch_id = shift;
769 my $num_reverted = 0;
771 my $num_items_deleted = 0;
772 # commit (i.e., save, all records in the batch)
773 SetImportBatchStatus('reverting');
774 my $overlay_action = GetImportBatchOverlayAction($batch_id);
775 my $nomatch_action = GetImportBatchNoMatchAction($batch_id);
776 my $dbh = C4::Context->dbh;
777 my $sth = $dbh->prepare("SELECT import_records.import_record_id, record_type, status, overlay_status, marcxml_old, encoding, matched_biblionumber, matched_authid
779 LEFT JOIN import_auths ON (import_records.import_record_id=import_auths.import_record_id)
780 LEFT JOIN import_biblios ON (import_records.import_record_id=import_biblios.import_record_id)
781 WHERE import_batch_id = ?");
782 $sth->execute($batch_id);
784 my $marcflavour = C4::Context->preference('marcflavour');
785 while (my $rowref = $sth->fetchrow_hashref) {
786 $record_type = $rowref->{'record_type'};
787 if ($rowref->{'status'} eq 'error' or $rowref->{'status'} eq 'reverted') {
791 if ($marcflavour eq 'UNIMARC' && $record_type eq 'auth') {
792 $marc_type = 'UNIMARCAUTH';
793 } elsif ($marcflavour eq 'UNIMARC') {
794 $marc_type = 'UNIMARC';
796 $marc_type = 'USMARC';
799 my $record_result = _get_revert_action($overlay_action, $rowref->{'overlay_status'}, $rowref->{'status'});
801 if ($record_result eq 'delete') {
803 if ($record_type eq 'biblio') {
804 $num_items_deleted += BatchRevertItems($rowref->{'import_record_id'}, $rowref->{'matched_biblionumber'});
805 $error = DelBiblio($rowref->{'matched_biblionumber'});
807 my $deletedauthid = DelAuthority($rowref->{'matched_authid'});
809 if (defined $error) {
813 SetImportRecordStatus($rowref->{'import_record_id'}, 'reverted');
815 } elsif ($record_result eq 'restore') {
817 my $old_record = MARC::Record->new_from_xml(StripNonXmlChars($rowref->{'marcxml_old'}), 'UTF-8', $rowref->{'encoding'}, $marc_type);
818 if ($record_type eq 'biblio') {
819 my $biblionumber = $rowref->{'matched_biblionumber'};
820 my ($count, $oldbiblio) = GetBiblio($biblionumber);
821 $num_items_deleted += BatchRevertItems($rowref->{'import_record_id'}, $rowref->{'matched_biblionumber'});
822 ModBiblio($old_record, $biblionumber, $oldbiblio->{'frameworkcode'});
824 my $authid = $rowref->{'matched_authid'};
825 ModAuthority($authid, $old_record, GuessAuthTypeCode($old_record));
827 SetImportRecordStatus($rowref->{'import_record_id'}, 'reverted');
828 } elsif ($record_result eq 'ignore') {
829 if ($record_type eq 'biblio') {
830 $num_items_deleted += BatchRevertItems($rowref->{'import_record_id'}, $rowref->{'matched_biblionumber'});
832 SetImportRecordStatus($rowref->{'import_record_id'}, 'reverted');
835 if ($record_type eq 'biblio') {
836 # remove matched_biblionumber only if there is no 'imported' item left
837 $query = "UPDATE import_biblios SET matched_biblionumber = NULL WHERE import_record_id = ?";
838 $query = "UPDATE import_biblios SET matched_biblionumber = NULL WHERE import_record_id = ? AND NOT EXISTS (SELECT * FROM import_items WHERE import_items.import_record_id=import_biblios.import_record_id and status='imported')";
840 $query = "UPDATE import_auths SET matched_authid = NULL WHERE import_record_id = ?";
842 my $sth2 = $dbh->prepare_cached($query);
843 $sth2->execute($rowref->{'import_record_id'});
847 SetImportBatchStatus($batch_id, 'reverted');
848 return ($num_deleted, $num_errors, $num_reverted, $num_items_deleted, $num_ignored);
851 =head2 BatchRevertItems
853 my $num_items_deleted = BatchRevertItems($import_record_id, $biblionumber);
857 sub BatchRevertItems {
858 my ($import_record_id, $biblionumber) = @_;
860 my $dbh = C4::Context->dbh;
861 my $num_items_deleted = 0;
863 my $sth = $dbh->prepare_cached("SELECT import_items_id, itemnumber
865 JOIN items USING (itemnumber)
866 WHERE import_record_id = ?");
867 $sth->bind_param(1, $import_record_id);
869 while (my $row = $sth->fetchrow_hashref()) {
870 my $error = DelItemCheck($dbh, $biblionumber, $row->{'itemnumber'});
872 my $updsth = $dbh->prepare("UPDATE import_items SET status = ? WHERE import_items_id = ?");
873 $updsth->bind_param(1, 'reverted');
874 $updsth->bind_param(2, $row->{'import_items_id'});
877 $num_items_deleted++;
884 return $num_items_deleted;
889 CleanBatch($batch_id)
891 Deletes all staged records from the import batch
892 and sets the status of the batch to 'cleaned'. Note
893 that deleting a stage record does *not* affect
894 any record that has been committed to the database.
899 my $batch_id = shift;
900 return unless defined $batch_id;
902 C4::Context->dbh->do('DELETE FROM import_records WHERE import_batch_id = ?', {}, $batch_id);
903 SetImportBatchStatus($batch_id, 'cleaned');
906 =head2 GetAllImportBatches
908 my $results = GetAllImportBatches();
910 Returns a references to an array of hash references corresponding
911 to all import_batches rows (of batch_type 'batch'), sorted in
912 ascending order by import_batch_id.
916 sub GetAllImportBatches {
917 my $dbh = C4::Context->dbh;
918 my $sth = $dbh->prepare_cached("SELECT * FROM import_batches
919 WHERE batch_type IN ('batch', 'webservice')
920 ORDER BY import_batch_id ASC");
924 while (my $row = $sth->fetchrow_hashref) {
925 push @$results, $row;
931 =head2 GetStagedWebserviceBatches
933 my $batch_ids = GetStagedWebserviceBatches();
935 Returns a references to an array of batch id's
936 of batch_type 'webservice' that are not imported
940 my $PENDING_WEBSERVICE_BATCHES_QRY = <<EOQ;
941 SELECT import_batch_id FROM import_batches
942 WHERE batch_type = 'webservice'
943 AND import_status = 'staged'
945 sub GetStagedWebserviceBatches {
946 my $dbh = C4::Context->dbh;
947 return $dbh->selectcol_arrayref($PENDING_WEBSERVICE_BATCHES_QRY);
950 =head2 GetImportBatchRangeDesc
952 my $results = GetImportBatchRangeDesc($offset, $results_per_group);
954 Returns a reference to an array of hash references corresponding to
955 import_batches rows (sorted in descending order by import_batch_id)
956 start at the given offset.
960 sub GetImportBatchRangeDesc {
961 my ($offset, $results_per_group) = @_;
963 my $dbh = C4::Context->dbh;
964 my $query = "SELECT * FROM import_batches
965 WHERE batch_type IN ('batch', 'webservice')
966 ORDER BY import_batch_id DESC";
968 if ($results_per_group){
969 $query .= " LIMIT ?";
970 push(@params, $results_per_group);
973 $query .= " OFFSET ?";
974 push(@params, $offset);
976 my $sth = $dbh->prepare_cached($query);
977 $sth->execute(@params);
978 my $results = $sth->fetchall_arrayref({});
983 =head2 GetItemNumbersFromImportBatch
985 my @itemsnos = GetItemNumbersFromImportBatch($batch_id);
989 sub GetItemNumbersFromImportBatch {
991 my $dbh = C4::Context->dbh;
992 my $sth = $dbh->prepare("SELECT itemnumber FROM import_batches,import_records,import_items WHERE import_batches.import_batch_id=import_records.import_batch_id AND import_records.import_record_id=import_items.import_record_id AND import_batches.import_batch_id=?");
993 $sth->execute($batch_id);
995 while ( my ($itm) = $sth->fetchrow_array ) {
1001 =head2 GetNumberOfImportBatches
1003 my $count = GetNumberOfImportBatches();
1007 sub GetNumberOfNonZ3950ImportBatches {
1008 my $dbh = C4::Context->dbh;
1009 my $sth = $dbh->prepare("SELECT COUNT(*) FROM import_batches WHERE batch_type != 'z3950'");
1011 my ($count) = $sth->fetchrow_array();
1016 =head2 GetImportRecordsRange
1018 my $results = GetImportRecordsRange($batch_id, $offset, $results_per_group);
1020 Returns a reference to an array of hash references corresponding to
1021 import_biblios/import_auths/import_records rows for a given batch
1022 starting at the given offset.
1026 sub GetImportRecordsRange {
1027 my ($batch_id, $offset, $results_per_group, $status) = @_;
1029 my $dbh = C4::Context->dbh;
1030 my $query = "SELECT title, author, isbn, issn, authorized_heading, import_records.import_record_id,
1031 record_sequence, status, overlay_status,
1032 matched_biblionumber, matched_authid, record_type
1034 LEFT JOIN import_auths ON (import_records.import_record_id=import_auths.import_record_id)
1035 LEFT JOIN import_biblios ON (import_records.import_record_id=import_biblios.import_record_id)
1036 WHERE import_batch_id = ?";
1038 push(@params, $batch_id);
1040 $query .= " AND status=?";
1041 push(@params,$status);
1043 $query.=" ORDER BY import_record_id";
1045 if($results_per_group){
1046 $query .= " LIMIT ?";
1047 push(@params, $results_per_group);
1050 $query .= " OFFSET ?";
1051 push(@params, $offset);
1053 my $sth = $dbh->prepare_cached($query);
1054 $sth->execute(@params);
1055 my $results = $sth->fetchall_arrayref({});
1061 =head2 GetBestRecordMatch
1063 my $record_id = GetBestRecordMatch($import_record_id);
1067 sub GetBestRecordMatch {
1068 my ($import_record_id) = @_;
1070 my $dbh = C4::Context->dbh;
1071 my $sth = $dbh->prepare("SELECT candidate_match_id
1072 FROM import_record_matches
1073 JOIN import_records ON ( import_record_matches.import_record_id = import_records.import_record_id )
1074 LEFT JOIN biblio ON ( candidate_match_id = biblio.biblionumber )
1075 LEFT JOIN auth_header ON ( candidate_match_id = auth_header.authid )
1076 WHERE import_record_matches.import_record_id = ? AND
1077 ( (import_records.record_type = 'biblio' AND biblio.biblionumber IS NOT NULL) OR
1078 (import_records.record_type = 'auth' AND auth_header.authid IS NOT NULL) )
1079 ORDER BY score DESC, candidate_match_id DESC");
1080 $sth->execute($import_record_id);
1081 my ($record_id) = $sth->fetchrow_array();
1086 =head2 GetImportBatchStatus
1088 my $status = GetImportBatchStatus($batch_id);
1092 sub GetImportBatchStatus {
1093 my ($batch_id) = @_;
1095 my $dbh = C4::Context->dbh;
1096 my $sth = $dbh->prepare("SELECT import_status FROM import_batches WHERE import_batch_id = ?");
1097 $sth->execute($batch_id);
1098 my ($status) = $sth->fetchrow_array();
1104 =head2 SetImportBatchStatus
1106 SetImportBatchStatus($batch_id, $new_status);
1110 sub SetImportBatchStatus {
1111 my ($batch_id, $new_status) = @_;
1113 my $dbh = C4::Context->dbh;
1114 my $sth = $dbh->prepare("UPDATE import_batches SET import_status = ? WHERE import_batch_id = ?");
1115 $sth->execute($new_status, $batch_id);
1120 =head2 GetImportBatchOverlayAction
1122 my $overlay_action = GetImportBatchOverlayAction($batch_id);
1126 sub GetImportBatchOverlayAction {
1127 my ($batch_id) = @_;
1129 my $dbh = C4::Context->dbh;
1130 my $sth = $dbh->prepare("SELECT overlay_action FROM import_batches WHERE import_batch_id = ?");
1131 $sth->execute($batch_id);
1132 my ($overlay_action) = $sth->fetchrow_array();
1134 return $overlay_action;
1139 =head2 SetImportBatchOverlayAction
1141 SetImportBatchOverlayAction($batch_id, $new_overlay_action);
1145 sub SetImportBatchOverlayAction {
1146 my ($batch_id, $new_overlay_action) = @_;
1148 my $dbh = C4::Context->dbh;
1149 my $sth = $dbh->prepare("UPDATE import_batches SET overlay_action = ? WHERE import_batch_id = ?");
1150 $sth->execute($new_overlay_action, $batch_id);
1155 =head2 GetImportBatchNoMatchAction
1157 my $nomatch_action = GetImportBatchNoMatchAction($batch_id);
1161 sub GetImportBatchNoMatchAction {
1162 my ($batch_id) = @_;
1164 my $dbh = C4::Context->dbh;
1165 my $sth = $dbh->prepare("SELECT nomatch_action FROM import_batches WHERE import_batch_id = ?");
1166 $sth->execute($batch_id);
1167 my ($nomatch_action) = $sth->fetchrow_array();
1169 return $nomatch_action;
1174 =head2 SetImportBatchNoMatchAction
1176 SetImportBatchNoMatchAction($batch_id, $new_nomatch_action);
1180 sub SetImportBatchNoMatchAction {
1181 my ($batch_id, $new_nomatch_action) = @_;
1183 my $dbh = C4::Context->dbh;
1184 my $sth = $dbh->prepare("UPDATE import_batches SET nomatch_action = ? WHERE import_batch_id = ?");
1185 $sth->execute($new_nomatch_action, $batch_id);
1190 =head2 GetImportBatchItemAction
1192 my $item_action = GetImportBatchItemAction($batch_id);
1196 sub GetImportBatchItemAction {
1197 my ($batch_id) = @_;
1199 my $dbh = C4::Context->dbh;
1200 my $sth = $dbh->prepare("SELECT item_action FROM import_batches WHERE import_batch_id = ?");
1201 $sth->execute($batch_id);
1202 my ($item_action) = $sth->fetchrow_array();
1204 return $item_action;
1209 =head2 SetImportBatchItemAction
1211 SetImportBatchItemAction($batch_id, $new_item_action);
1215 sub SetImportBatchItemAction {
1216 my ($batch_id, $new_item_action) = @_;
1218 my $dbh = C4::Context->dbh;
1219 my $sth = $dbh->prepare("UPDATE import_batches SET item_action = ? WHERE import_batch_id = ?");
1220 $sth->execute($new_item_action, $batch_id);
1225 =head2 GetImportBatchMatcher
1227 my $matcher_id = GetImportBatchMatcher($batch_id);
1231 sub GetImportBatchMatcher {
1232 my ($batch_id) = @_;
1234 my $dbh = C4::Context->dbh;
1235 my $sth = $dbh->prepare("SELECT matcher_id FROM import_batches WHERE import_batch_id = ?");
1236 $sth->execute($batch_id);
1237 my ($matcher_id) = $sth->fetchrow_array();
1244 =head2 SetImportBatchMatcher
1246 SetImportBatchMatcher($batch_id, $new_matcher_id);
1250 sub SetImportBatchMatcher {
1251 my ($batch_id, $new_matcher_id) = @_;
1253 my $dbh = C4::Context->dbh;
1254 my $sth = $dbh->prepare("UPDATE import_batches SET matcher_id = ? WHERE import_batch_id = ?");
1255 $sth->execute($new_matcher_id, $batch_id);
1260 =head2 GetImportRecordOverlayStatus
1262 my $overlay_status = GetImportRecordOverlayStatus($import_record_id);
1266 sub GetImportRecordOverlayStatus {
1267 my ($import_record_id) = @_;
1269 my $dbh = C4::Context->dbh;
1270 my $sth = $dbh->prepare("SELECT overlay_status FROM import_records WHERE import_record_id = ?");
1271 $sth->execute($import_record_id);
1272 my ($overlay_status) = $sth->fetchrow_array();
1274 return $overlay_status;
1279 =head2 SetImportRecordOverlayStatus
1281 SetImportRecordOverlayStatus($import_record_id, $new_overlay_status);
1285 sub SetImportRecordOverlayStatus {
1286 my ($import_record_id, $new_overlay_status) = @_;
1288 my $dbh = C4::Context->dbh;
1289 my $sth = $dbh->prepare("UPDATE import_records SET overlay_status = ? WHERE import_record_id = ?");
1290 $sth->execute($new_overlay_status, $import_record_id);
1295 =head2 GetImportRecordStatus
1297 my $overlay_status = GetImportRecordStatus($import_record_id);
1301 sub GetImportRecordStatus {
1302 my ($import_record_id) = @_;
1304 my $dbh = C4::Context->dbh;
1305 my $sth = $dbh->prepare("SELECT status FROM import_records WHERE import_record_id = ?");
1306 $sth->execute($import_record_id);
1307 my ($overlay_status) = $sth->fetchrow_array();
1309 return $overlay_status;
1314 =head2 SetImportRecordStatus
1316 SetImportRecordStatus($import_record_id, $new_overlay_status);
1320 sub SetImportRecordStatus {
1321 my ($import_record_id, $new_overlay_status) = @_;
1323 my $dbh = C4::Context->dbh;
1324 my $sth = $dbh->prepare("UPDATE import_records SET status = ? WHERE import_record_id = ?");
1325 $sth->execute($new_overlay_status, $import_record_id);
1330 =head2 GetImportRecordMatches
1332 my $results = GetImportRecordMatches($import_record_id, $best_only);
1336 sub GetImportRecordMatches {
1337 my $import_record_id = shift;
1338 my $best_only = @_ ? shift : 0;
1340 my $dbh = C4::Context->dbh;
1341 # FIXME currently biblio only
1342 my $sth = $dbh->prepare_cached("SELECT title, author, biblionumber,
1343 candidate_match_id, score, record_type
1345 JOIN import_record_matches USING (import_record_id)
1346 LEFT JOIN biblio ON (biblionumber = candidate_match_id)
1347 WHERE import_record_id = ?
1348 ORDER BY score DESC, biblionumber DESC");
1349 $sth->bind_param(1, $import_record_id);
1352 while (my $row = $sth->fetchrow_hashref) {
1353 if ($row->{'record_type'} eq 'auth') {
1354 $row->{'authorized_heading'} = C4::AuthoritiesMarc::GetAuthorizedHeading( { authid => $row->{'candidate_match_id'} } );
1356 next if ($row->{'record_type'} eq 'biblio' && not $row->{'biblionumber'});
1357 push @$results, $row;
1367 =head2 SetImportRecordMatches
1369 SetImportRecordMatches($import_record_id, @matches);
1373 sub SetImportRecordMatches {
1374 my $import_record_id = shift;
1377 my $dbh = C4::Context->dbh;
1378 my $delsth = $dbh->prepare("DELETE FROM import_record_matches WHERE import_record_id = ?");
1379 $delsth->execute($import_record_id);
1382 my $sth = $dbh->prepare("INSERT INTO import_record_matches (import_record_id, candidate_match_id, score)
1384 foreach my $match (@matches) {
1385 $sth->execute($import_record_id, $match->{'record_id'}, $match->{'score'});
1390 # internal functions
1392 sub _create_import_record {
1393 my ($batch_id, $record_sequence, $marc_record, $record_type, $encoding, $z3950random, $marc_type) = @_;
1395 my $dbh = C4::Context->dbh;
1396 my $sth = $dbh->prepare("INSERT INTO import_records (import_batch_id, record_sequence, marc, marcxml,
1397 record_type, encoding, z3950random)
1398 VALUES (?, ?, ?, ?, ?, ?, ?)");
1399 $sth->execute($batch_id, $record_sequence, $marc_record->as_usmarc(), $marc_record->as_xml($marc_type),
1400 $record_type, $encoding, $z3950random);
1401 my $import_record_id = $dbh->{'mysql_insertid'};
1403 return $import_record_id;
1406 sub _update_import_record_marc {
1407 my ($import_record_id, $marc_record, $marc_type) = @_;
1409 my $dbh = C4::Context->dbh;
1410 my $sth = $dbh->prepare("UPDATE import_records SET marc = ?, marcxml = ?
1411 WHERE import_record_id = ?");
1412 $sth->execute($marc_record->as_usmarc(), $marc_record->as_xml($marc_type), $import_record_id);
1416 sub _add_auth_fields {
1417 my ($import_record_id, $marc_record) = @_;
1420 if ($marc_record->field('001')) {
1421 $controlnumber = $marc_record->field('001')->data();
1423 my $authorized_heading = C4::AuthoritiesMarc::GetAuthorizedHeading({ record => $marc_record });
1424 my $dbh = C4::Context->dbh;
1425 my $sth = $dbh->prepare("INSERT INTO import_auths (import_record_id, control_number, authorized_heading) VALUES (?, ?, ?)");
1426 $sth->execute($import_record_id, $controlnumber, $authorized_heading);
1430 sub _add_biblio_fields {
1431 my ($import_record_id, $marc_record) = @_;
1433 my ($title, $author, $isbn, $issn) = _parse_biblio_fields($marc_record);
1434 my $dbh = C4::Context->dbh;
1435 # FIXME no controlnumber, originalsource
1436 $isbn = C4::Koha::_isbn_cleanup($isbn); # FIXME C4::Koha::_isbn_cleanup should be made public
1437 my $sth = $dbh->prepare("INSERT INTO import_biblios (import_record_id, title, author, isbn, issn) VALUES (?, ?, ?, ?, ?)");
1438 $sth->execute($import_record_id, $title, $author, $isbn, $issn);
1443 sub _update_biblio_fields {
1444 my ($import_record_id, $marc_record) = @_;
1446 my ($title, $author, $isbn, $issn) = _parse_biblio_fields($marc_record);
1447 my $dbh = C4::Context->dbh;
1448 # FIXME no controlnumber, originalsource
1449 # FIXME 2 - should regularize normalization of ISBN wherever it is done
1453 my $sth = $dbh->prepare("UPDATE import_biblios SET title = ?, author = ?, isbn = ?, issn = ?
1454 WHERE import_record_id = ?");
1455 $sth->execute($title, $author, $isbn, $issn, $import_record_id);
1459 sub _parse_biblio_fields {
1460 my ($marc_record) = @_;
1462 my $dbh = C4::Context->dbh;
1463 my $bibliofields = TransformMarcToKoha($dbh, $marc_record, '');
1464 return ($bibliofields->{'title'}, $bibliofields->{'author'}, $bibliofields->{'isbn'}, $bibliofields->{'issn'});
1468 sub _update_batch_record_counts {
1469 my ($batch_id) = @_;
1471 my $dbh = C4::Context->dbh;
1472 my $sth = $dbh->prepare_cached("UPDATE import_batches SET
1476 WHERE import_batch_id = import_batches.import_batch_id),
1480 JOIN import_items USING (import_record_id)
1481 WHERE import_batch_id = import_batches.import_batch_id
1482 AND record_type = 'biblio')
1483 WHERE import_batch_id = ?");
1484 $sth->bind_param(1, $batch_id);
1489 sub _get_commit_action {
1490 my ($overlay_action, $nomatch_action, $item_action, $overlay_status, $import_record_id, $record_type) = @_;
1492 if ($record_type eq 'biblio') {
1493 my ($bib_result, $bib_match, $item_result);
1495 if ($overlay_status ne 'no_match') {
1496 $bib_match = GetBestRecordMatch($import_record_id);
1497 if ($overlay_action eq 'replace') {
1498 $bib_result = defined($bib_match) ? 'replace' : 'create_new';
1499 } elsif ($overlay_action eq 'create_new') {
1500 $bib_result = 'create_new';
1501 } elsif ($overlay_action eq 'ignore') {
1502 $bib_result = 'ignore';
1504 if($item_action eq 'always_add' or $item_action eq 'add_only_for_matches'){
1505 $item_result = 'create_new';
1507 elsif($item_action eq 'replace'){
1508 $item_result = 'replace';
1511 $item_result = 'ignore';
1514 $bib_result = $nomatch_action;
1515 $item_result = ($item_action eq 'always_add' or $item_action eq 'add_only_for_new') ? 'create_new' : 'ignore';
1517 return ($bib_result, $item_result, $bib_match);
1518 } else { # must be auths
1519 my ($auth_result, $auth_match);
1521 if ($overlay_status ne 'no_match') {
1522 $auth_match = GetBestRecordMatch($import_record_id);
1523 if ($overlay_action eq 'replace') {
1524 $auth_result = defined($auth_match) ? 'replace' : 'create_new';
1525 } elsif ($overlay_action eq 'create_new') {
1526 $auth_result = 'create_new';
1527 } elsif ($overlay_action eq 'ignore') {
1528 $auth_result = 'ignore';
1531 $auth_result = $nomatch_action;
1534 return ($auth_result, undef, $auth_match);
1539 sub _get_revert_action {
1540 my ($overlay_action, $overlay_status, $status) = @_;
1544 if ($status eq 'ignored') {
1545 $bib_result = 'ignore';
1547 if ($overlay_action eq 'create_new') {
1548 $bib_result = 'delete';
1550 $bib_result = ($overlay_status eq 'match_applied') ? 'restore' : 'delete';
1561 Koha Development Team <http://koha-community.org/>
1563 Galen Charlton <galen.charlton@liblime.com>