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
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>.
28 use C4::AuthoritiesMarc;
29 use C4::MarcModificationTemplates;
31 use Koha::Plugins::Handler;
34 use vars qw(@ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);
43 GetImportRecordMarcXML
48 AddItemsToImportBiblio
60 GetStagedWebserviceBatches
61 GetImportBatchRangeDesc
62 GetNumberOfNonZ3950ImportBatches
65 GetItemNumbersFromImportBatch
69 GetImportBatchOverlayAction
70 SetImportBatchOverlayAction
71 GetImportBatchNoMatchAction
72 SetImportBatchNoMatchAction
73 GetImportBatchItemAction
74 SetImportBatchItemAction
77 GetImportRecordOverlayStatus
78 SetImportRecordOverlayStatus
81 SetMatchedBiblionumber
82 GetImportRecordMatches
83 SetImportRecordMatches
89 C4::ImportBatch - manage batches of imported MARC records
97 =head2 GetZ3950BatchId
99 my $batchid = GetZ3950BatchId($z3950server);
101 Retrieves the ID of the import batch for the Z39.50
102 reservoir for the given target. If necessary,
103 creates the import batch.
107 sub GetZ3950BatchId {
108 my ($z3950server) = @_;
110 my $dbh = C4::Context->dbh;
111 my $sth = $dbh->prepare("SELECT import_batch_id FROM import_batches
112 WHERE batch_type = 'z3950'
114 $sth->execute($z3950server);
115 my $rowref = $sth->fetchrow_arrayref();
117 if (defined $rowref) {
120 my $batch_id = AddImportBatch( {
121 overlay_action => 'create_new',
122 import_status => 'staged',
123 batch_type => 'z3950',
124 file_name => $z3950server,
131 =head2 GetWebserviceBatchId
133 my $batchid = GetWebserviceBatchId();
135 Retrieves the ID of the import batch for webservice.
136 If necessary, creates the import batch.
140 my $WEBSERVICE_BASE_QRY = <<EOQ;
141 SELECT import_batch_id FROM import_batches
142 WHERE batch_type = 'webservice'
143 AND import_status = 'staged'
145 sub GetWebserviceBatchId {
148 my $dbh = C4::Context->dbh;
149 my $sql = $WEBSERVICE_BASE_QRY;
151 foreach my $field (qw(matcher_id overlay_action nomatch_action item_action)) {
152 if (my $val = $params->{$field}) {
153 $sql .= " AND $field = ?";
157 my $id = $dbh->selectrow_array($sql, undef, @args);
160 $params->{batch_type} = 'webservice';
161 $params->{import_status} = 'staged';
162 return AddImportBatch($params);
165 =head2 GetImportRecordMarc
167 my ($marcblob, $encoding) = GetImportRecordMarc($import_record_id);
171 sub GetImportRecordMarc {
172 my ($import_record_id) = @_;
174 my $dbh = C4::Context->dbh;
175 my ( $marc, $encoding ) = $dbh->selectrow_array(q|
176 SELECT marc, encoding
178 WHERE import_record_id = ?
179 |, undef, $import_record_id );
181 return $marc, $encoding;
184 sub GetRecordFromImportBiblio {
185 my ( $import_record_id, $embed_items ) = @_;
187 my ($marc) = GetImportRecordMarc($import_record_id);
188 my $record = MARC::Record->new_from_usmarc($marc);
190 EmbedItemsInImportBiblio( $record, $import_record_id ) if $embed_items;
195 sub EmbedItemsInImportBiblio {
196 my ( $record, $import_record_id ) = @_;
197 my ( $itemtag, $itemsubfield ) = GetMarcFromKohaField( "items.itemnumber" );
198 my $dbh = C4::Context->dbh;
199 my $import_items = $dbh->selectall_arrayref(q|
200 SELECT import_items.marcxml
202 WHERE import_record_id = ?
203 |, { Slice => {} }, $import_record_id );
205 for my $import_item ( @$import_items ) {
206 my $item_marc = MARC::Record::new_from_xml($import_item->{marcxml}, 'UTF-8');
207 push @item_fields, $item_marc->field($itemtag);
209 $record->append_fields(@item_fields);
213 =head2 GetImportRecordMarcXML
215 my $marcxml = GetImportRecordMarcXML($import_record_id);
219 sub GetImportRecordMarcXML {
220 my ($import_record_id) = @_;
222 my $dbh = C4::Context->dbh;
223 my $sth = $dbh->prepare("SELECT marcxml FROM import_records WHERE import_record_id = ?");
224 $sth->execute($import_record_id);
225 my ($marcxml) = $sth->fetchrow();
231 =head2 AddImportBatch
233 my $batch_id = AddImportBatch($params_hash);
241 foreach (qw( matcher_id template_id branchcode
242 overlay_action nomatch_action item_action
243 import_status batch_type file_name comments record_type )) {
244 if (exists $params->{$_}) {
246 push @vals, $params->{$_};
249 my $dbh = C4::Context->dbh;
250 $dbh->do("INSERT INTO import_batches (".join( ',', @fields).")
251 VALUES (".join( ',', map '?', @fields).")",
254 return $dbh->{'mysql_insertid'};
257 =head2 GetImportBatch
259 my $row = GetImportBatch($batch_id);
261 Retrieve a hashref of an import_batches row.
268 my $dbh = C4::Context->dbh;
269 my $sth = $dbh->prepare_cached("SELECT b.*, p.name as profile FROM import_batches b LEFT JOIN import_batch_profiles p ON p.id = b.profile_id WHERE import_batch_id = ?");
270 $sth->bind_param(1, $batch_id);
272 my $result = $sth->fetchrow_hashref;
278 =head2 AddBiblioToBatch
280 my $import_record_id = AddBiblioToBatch($batch_id, $record_sequence,
281 $marc_record, $encoding, $update_counts);
285 sub AddBiblioToBatch {
286 my $batch_id = shift;
287 my $record_sequence = shift;
288 my $marc_record = shift;
289 my $encoding = shift;
290 my $update_counts = @_ ? shift : 1;
292 my $import_record_id = _create_import_record($batch_id, $record_sequence, $marc_record, 'biblio', $encoding, C4::Context->preference('marcflavour'));
293 _add_biblio_fields($import_record_id, $marc_record);
294 _update_batch_record_counts($batch_id) if $update_counts;
295 return $import_record_id;
298 =head2 ModBiblioInBatch
300 ModBiblioInBatch($import_record_id, $marc_record);
304 sub ModBiblioInBatch {
305 my ($import_record_id, $marc_record) = @_;
307 _update_import_record_marc($import_record_id, $marc_record, C4::Context->preference('marcflavour'));
308 _update_biblio_fields($import_record_id, $marc_record);
312 =head2 AddAuthToBatch
314 my $import_record_id = AddAuthToBatch($batch_id, $record_sequence,
315 $marc_record, $encoding, $update_counts, [$marc_type]);
320 my $batch_id = shift;
321 my $record_sequence = shift;
322 my $marc_record = shift;
323 my $encoding = shift;
324 my $update_counts = @_ ? shift : 1;
325 my $marc_type = shift || C4::Context->preference('marcflavour');
327 $marc_type = 'UNIMARCAUTH' if $marc_type eq 'UNIMARC';
329 my $import_record_id = _create_import_record($batch_id, $record_sequence, $marc_record, 'auth', $encoding, $marc_type);
330 _add_auth_fields($import_record_id, $marc_record);
331 _update_batch_record_counts($batch_id) if $update_counts;
332 return $import_record_id;
335 =head2 ModAuthInBatch
337 ModAuthInBatch($import_record_id, $marc_record);
342 my ($import_record_id, $marc_record) = @_;
344 my $marcflavour = C4::Context->preference('marcflavour');
345 _update_import_record_marc($import_record_id, $marc_record, $marcflavour eq 'UNIMARC' ? 'UNIMARCAUTH' : 'USMARC');
349 =head2 BatchStageMarcRecords
351 ( $batch_id, $num_records, $num_items, @invalid_records ) =
352 BatchStageMarcRecords(
353 $record_type, $encoding,
354 $marc_records, $file_name,
355 $marc_modification_template, $comments,
356 $branch_code, $parse_items,
357 $leave_as_staging, $progress_interval,
363 sub BatchStageMarcRecords {
364 my $record_type = shift;
365 my $encoding = shift;
366 my $marc_records = shift;
367 my $file_name = shift;
368 my $marc_modification_template = shift;
369 my $comments = shift;
370 my $branch_code = shift;
371 my $parse_items = shift;
372 my $leave_as_staging = shift;
374 # optional callback to monitor status
376 my $progress_interval = 0;
377 my $progress_callback = undef;
379 $progress_interval = shift;
380 $progress_callback = shift;
381 $progress_interval = 0 unless $progress_interval =~ /^\d+$/ and $progress_interval > 0;
382 $progress_interval = 0 unless 'CODE' eq ref $progress_callback;
385 my $batch_id = AddImportBatch( {
386 overlay_action => 'create_new',
387 import_status => 'staging',
388 batch_type => 'batch',
389 file_name => $file_name,
390 comments => $comments,
391 record_type => $record_type,
394 SetImportBatchItemAction($batch_id, 'always_add');
396 SetImportBatchItemAction($batch_id, 'ignore');
400 my $marc_type = C4::Context->preference('marcflavour');
401 $marc_type .= 'AUTH' if ($marc_type eq 'UNIMARC' && $record_type eq 'auth');
402 my @invalid_records = ();
405 # FIXME - for now, we're dealing only with bibs
407 foreach my $marc_record (@$marc_records) {
409 if ($progress_interval and (0 == ($rec_num % $progress_interval))) {
410 &$progress_callback($rec_num);
413 ModifyRecordWithTemplate( $marc_modification_template, $marc_record ) if ( $marc_modification_template );
415 my $import_record_id;
416 if (scalar($marc_record->fields()) == 0) {
417 push @invalid_records, $marc_record;
420 # Normalize the record so it doesn't have separated diacritics
421 SetUTF8Flag($marc_record);
424 if ($record_type eq 'biblio') {
425 $import_record_id = AddBiblioToBatch($batch_id, $rec_num, $marc_record, $encoding, int(rand(99999)), 0);
427 my @import_items_ids = AddItemsToImportBiblio($batch_id, $import_record_id, $marc_record, 0);
428 $num_items += scalar(@import_items_ids);
430 } elsif ($record_type eq 'auth') {
431 $import_record_id = AddAuthToBatch($batch_id, $rec_num, $marc_record, $encoding, int(rand(99999)), 0, $marc_type);
435 unless ($leave_as_staging) {
436 SetImportBatchStatus($batch_id, 'staged');
438 # FIXME branch_code, number of bibs, number of items
439 _update_batch_record_counts($batch_id);
440 return ($batch_id, $num_valid, $num_items, @invalid_records);
443 =head2 AddItemsToImportBiblio
445 my @import_items_ids = AddItemsToImportBiblio($batch_id,
446 $import_record_id, $marc_record, $update_counts);
450 sub AddItemsToImportBiblio {
451 my $batch_id = shift;
452 my $import_record_id = shift;
453 my $marc_record = shift;
454 my $update_counts = @_ ? shift : 0;
456 my @import_items_ids = ();
458 my $dbh = C4::Context->dbh;
459 my ($item_tag,$item_subfield) = &GetMarcFromKohaField( "items.itemnumber" );
460 foreach my $item_field ($marc_record->field($item_tag)) {
461 my $item_marc = MARC::Record->new();
462 $item_marc->leader("00000 a "); # must set Leader/09 to 'a'
463 $item_marc->append_fields($item_field);
464 $marc_record->delete_field($item_field);
465 my $sth = $dbh->prepare_cached("INSERT INTO import_items (import_record_id, status, marcxml)
467 $sth->bind_param(1, $import_record_id);
468 $sth->bind_param(2, 'staged');
469 $sth->bind_param(3, $item_marc->as_xml("USMARC"));
471 push @import_items_ids, $dbh->{'mysql_insertid'};
475 if ($#import_items_ids > -1) {
476 _update_batch_record_counts($batch_id) if $update_counts;
477 _update_import_record_marc($import_record_id, $marc_record, C4::Context->preference('marcflavour'));
479 return @import_items_ids;
482 =head2 BatchFindDuplicates
484 my $num_with_matches = BatchFindDuplicates($batch_id, $matcher,
485 $max_matches, $progress_interval, $progress_callback);
487 Goes through the records loaded in the batch and attempts to
488 find duplicates for each one. Sets the matching status
489 of each record to "no_match" or "auto_match" as appropriate.
491 The $max_matches parameter is optional; if it is not supplied,
494 The $progress_interval and $progress_callback parameters are
495 optional; if both are supplied, the sub referred to by
496 $progress_callback will be invoked every $progress_interval
497 records using the number of records processed as the
502 sub BatchFindDuplicates {
503 my $batch_id = shift;
505 my $max_matches = @_ ? shift : 10;
507 # optional callback to monitor status
509 my $progress_interval = 0;
510 my $progress_callback = undef;
512 $progress_interval = shift;
513 $progress_callback = shift;
514 $progress_interval = 0 unless $progress_interval =~ /^\d+$/ and $progress_interval > 0;
515 $progress_interval = 0 unless 'CODE' eq ref $progress_callback;
518 my $dbh = C4::Context->dbh;
520 my $sth = $dbh->prepare("SELECT import_record_id, record_type, marc
522 WHERE import_batch_id = ?");
523 $sth->execute($batch_id);
524 my $num_with_matches = 0;
526 while (my $rowref = $sth->fetchrow_hashref) {
528 if ($progress_interval and (0 == ($rec_num % $progress_interval))) {
529 &$progress_callback($rec_num);
531 my $marc_record = MARC::Record->new_from_usmarc($rowref->{'marc'});
533 if (defined $matcher) {
534 @matches = $matcher->get_matches($marc_record, $max_matches);
536 if (scalar(@matches) > 0) {
538 SetImportRecordMatches($rowref->{'import_record_id'}, @matches);
539 SetImportRecordOverlayStatus($rowref->{'import_record_id'}, 'auto_match');
541 SetImportRecordMatches($rowref->{'import_record_id'}, ());
542 SetImportRecordOverlayStatus($rowref->{'import_record_id'}, 'no_match');
546 return $num_with_matches;
549 =head2 BatchCommitRecords
551 my ($num_added, $num_updated, $num_items_added, $num_items_replaced, $num_items_errored, $num_ignored) =
552 BatchCommitRecords($batch_id, $framework,
553 $progress_interval, $progress_callback);
557 sub BatchCommitRecords {
558 my $batch_id = shift;
559 my $framework = shift;
561 # optional callback to monitor status
563 my $progress_interval = 0;
564 my $progress_callback = undef;
566 $progress_interval = shift;
567 $progress_callback = shift;
568 $progress_interval = 0 unless $progress_interval =~ /^\d+$/ and $progress_interval > 0;
569 $progress_interval = 0 unless 'CODE' eq ref $progress_callback;
575 my $num_items_added = 0;
576 my $num_items_replaced = 0;
577 my $num_items_errored = 0;
579 # commit (i.e., save, all records in the batch)
580 SetImportBatchStatus($batch_id, 'importing');
581 my $overlay_action = GetImportBatchOverlayAction($batch_id);
582 my $nomatch_action = GetImportBatchNoMatchAction($batch_id);
583 my $item_action = GetImportBatchItemAction($batch_id);
586 my $dbh = C4::Context->dbh;
587 my $sth = $dbh->prepare("SELECT import_records.import_record_id, record_type, status, overlay_status, marc, encoding
589 LEFT JOIN import_auths ON (import_records.import_record_id=import_auths.import_record_id)
590 LEFT JOIN import_biblios ON (import_records.import_record_id=import_biblios.import_record_id)
591 WHERE import_batch_id = ?");
592 $sth->execute($batch_id);
593 my $marcflavour = C4::Context->preference('marcflavour');
595 while (my $rowref = $sth->fetchrow_hashref) {
596 $record_type = $rowref->{'record_type'};
598 if ($progress_interval and (0 == ($rec_num % $progress_interval))) {
599 &$progress_callback($rec_num);
601 if ($rowref->{'status'} eq 'error' or $rowref->{'status'} eq 'imported') {
607 if ($marcflavour eq 'UNIMARC' && $record_type eq 'auth') {
608 $marc_type = 'UNIMARCAUTH';
609 } elsif ($marcflavour eq 'UNIMARC') {
610 $marc_type = 'UNIMARC';
612 $marc_type = 'USMARC';
614 my $marc_record = MARC::Record->new_from_usmarc($rowref->{'marc'});
616 if ($record_type eq 'biblio') {
617 # remove any item tags - rely on BatchCommitItems
618 ($item_tag,$item_subfield) = &GetMarcFromKohaField( "items.itemnumber" );
619 foreach my $item_field ($marc_record->field($item_tag)) {
620 $marc_record->delete_field($item_field);
624 my ($record_result, $item_result, $record_match) =
625 _get_commit_action($overlay_action, $nomatch_action, $item_action,
626 $rowref->{'overlay_status'}, $rowref->{'import_record_id'}, $record_type);
630 if ($record_result eq 'create_new') {
632 if ($record_type eq 'biblio') {
633 my $biblioitemnumber;
634 ($recordid, $biblioitemnumber) = AddBiblio($marc_record, $framework);
635 $query = "UPDATE import_biblios SET matched_biblionumber = ? WHERE import_record_id = ?"; # FIXME call SetMatchedBiblionumber instead
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 $recordid = AddAuthority($marc_record, undef, GuessAuthTypeCode($marc_record));
644 $query = "UPDATE import_auths SET matched_authid = ? WHERE import_record_id = ?";
646 my $sth = $dbh->prepare_cached($query);
647 $sth->execute($recordid, $rowref->{'import_record_id'});
649 SetImportRecordStatus($rowref->{'import_record_id'}, 'imported');
650 } elsif ($record_result eq 'replace') {
652 $recordid = $record_match;
654 if ($record_type eq 'biblio') {
655 my $oldbiblio = Koha::Biblios->find( $recordid );
656 $oldxml = GetXmlBiblio($recordid);
658 # remove item fields so that they don't get
659 # added again if record is reverted
660 # 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.
661 my $old_marc = MARC::Record->new_from_xml(StripNonXmlChars($oldxml), 'UTF-8', $rowref->{'encoding'}, $marc_type);
662 foreach my $item_field ($old_marc->field($item_tag)) {
663 $old_marc->delete_field($item_field);
665 $oldxml = $old_marc->as_xml($marc_type);
667 ModBiblio($marc_record, $recordid, $oldbiblio->frameworkcode);
668 $query = "UPDATE import_biblios SET matched_biblionumber = ? WHERE import_record_id = ?"; # FIXME call SetMatchedBiblionumber instead
670 if ($item_result eq 'create_new' || $item_result eq 'replace') {
671 my ($bib_items_added, $bib_items_replaced, $bib_items_errored) = BatchCommitItems($rowref->{'import_record_id'}, $recordid, $item_result);
672 $num_items_added += $bib_items_added;
673 $num_items_replaced += $bib_items_replaced;
674 $num_items_errored += $bib_items_errored;
677 $oldxml = GetAuthorityXML($recordid);
679 ModAuthority($recordid, $marc_record, GuessAuthTypeCode($marc_record));
680 $query = "UPDATE import_auths SET matched_authid = ? WHERE import_record_id = ?";
682 my $sth = $dbh->prepare_cached("UPDATE import_records SET marcxml_old = ? WHERE import_record_id = ?");
683 $sth->execute($oldxml, $rowref->{'import_record_id'});
685 my $sth2 = $dbh->prepare_cached($query);
686 $sth2->execute($recordid, $rowref->{'import_record_id'});
688 SetImportRecordOverlayStatus($rowref->{'import_record_id'}, 'match_applied');
689 SetImportRecordStatus($rowref->{'import_record_id'}, 'imported');
690 } elsif ($record_result eq 'ignore') {
691 $recordid = $record_match;
693 $recordid = $record_match;
694 if ($record_type eq 'biblio' and defined $recordid and ( $item_result eq 'create_new' || $item_result eq 'replace' ) ) {
695 my ($bib_items_added, $bib_items_replaced, $bib_items_errored) = BatchCommitItems($rowref->{'import_record_id'}, $recordid, $item_result);
696 $num_items_added += $bib_items_added;
697 $num_items_replaced += $bib_items_replaced;
698 $num_items_errored += $bib_items_errored;
699 # still need to record the matched biblionumber so that the
700 # items can be reverted
701 my $sth2 = $dbh->prepare_cached("UPDATE import_biblios SET matched_biblionumber = ? WHERE import_record_id = ?"); # FIXME call SetMatchedBiblionumber instead
702 $sth2->execute($recordid, $rowref->{'import_record_id'});
703 SetImportRecordOverlayStatus($rowref->{'import_record_id'}, 'match_applied');
705 SetImportRecordStatus($rowref->{'import_record_id'}, 'ignored');
709 SetImportBatchStatus($batch_id, 'imported');
710 return ($num_added, $num_updated, $num_items_added, $num_items_replaced, $num_items_errored, $num_ignored);
713 =head2 BatchCommitItems
715 ($num_items_added, $num_items_errored) =
716 BatchCommitItems($import_record_id, $biblionumber);
720 sub BatchCommitItems {
721 my ( $import_record_id, $biblionumber, $action ) = @_;
723 my $dbh = C4::Context->dbh;
725 my $num_items_added = 0;
726 my $num_items_errored = 0;
727 my $num_items_replaced = 0;
729 my $sth = $dbh->prepare( "
730 SELECT import_items_id, import_items.marcxml, encoding
732 JOIN import_records USING (import_record_id)
733 WHERE import_record_id = ?
734 ORDER BY import_items_id
736 $sth->bind_param( 1, $import_record_id );
739 while ( my $row = $sth->fetchrow_hashref() ) {
740 my $item_marc = MARC::Record->new_from_xml( StripNonXmlChars( $row->{'marcxml'} ), 'UTF-8', $row->{'encoding'} );
742 # Delete date_due subfield as to not accidentally delete item checkout due dates
743 my ( $MARCfield, $MARCsubfield ) = GetMarcFromKohaField( 'items.onloan' );
744 $item_marc->field($MARCfield)->delete_subfield( code => $MARCsubfield );
746 my $item = TransformMarcToKoha( $item_marc );
748 my $duplicate_barcode = exists( $item->{'barcode'} ) && Koha::Items->find({ barcode => $item->{'barcode'} });
749 my $duplicate_itemnumber = exists( $item->{'itemnumber'} );
751 my $updsth = $dbh->prepare("UPDATE import_items SET status = ?, itemnumber = ? WHERE import_items_id = ?");
752 if ( $action eq "replace" && $duplicate_itemnumber ) {
753 # Duplicate itemnumbers have precedence, that way we can update barcodes by overlaying
754 ModItemFromMarc( $item_marc, $biblionumber, $item->{itemnumber} );
755 $updsth->bind_param( 1, 'imported' );
756 $updsth->bind_param( 2, $item->{itemnumber} );
757 $updsth->bind_param( 3, $row->{'import_items_id'} );
760 $num_items_replaced++;
761 } elsif ( $action eq "replace" && $duplicate_barcode ) {
762 my $itemnumber = $duplicate_barcode->itemnumber;
763 ModItemFromMarc( $item_marc, $biblionumber, $itemnumber );
764 $updsth->bind_param( 1, 'imported' );
765 $updsth->bind_param( 2, $item->{itemnumber} );
766 $updsth->bind_param( 3, $row->{'import_items_id'} );
769 $num_items_replaced++;
770 } elsif ($duplicate_barcode) {
771 $updsth->bind_param( 1, 'error' );
772 $updsth->bind_param( 2, 'duplicate item barcode' );
773 $updsth->bind_param( 3, $row->{'import_items_id'} );
775 $num_items_errored++;
777 # Remove the itemnumber if it exists, we want to create a new item
778 my ( $itemtag, $itemsubfield ) = GetMarcFromKohaField( "items.itemnumber" );
779 $item_marc->field($itemtag)->delete_subfield( code => $itemsubfield );
781 my ( $item_biblionumber, $biblioitemnumber, $itemnumber ) = AddItemFromMarc( $item_marc, $biblionumber );
783 $updsth->bind_param( 1, 'imported' );
784 $updsth->bind_param( 2, $itemnumber );
785 $updsth->bind_param( 3, $row->{'import_items_id'} );
793 return ( $num_items_added, $num_items_replaced, $num_items_errored );
796 =head2 BatchRevertRecords
798 my ($num_deleted, $num_errors, $num_reverted, $num_items_deleted,
799 $num_ignored) = BatchRevertRecords($batch_id);
803 sub BatchRevertRecords {
804 my $batch_id = shift;
806 my $logger = Koha::Logger->get( { category => 'C4.ImportBatch' } );
808 $logger->trace("C4::ImportBatch::BatchRevertRecords( $batch_id )");
813 my $num_reverted = 0;
815 my $num_items_deleted = 0;
816 # commit (i.e., save, all records in the batch)
817 SetImportBatchStatus($batch_id, 'reverting');
818 my $overlay_action = GetImportBatchOverlayAction($batch_id);
819 my $nomatch_action = GetImportBatchNoMatchAction($batch_id);
820 my $dbh = C4::Context->dbh;
821 my $sth = $dbh->prepare("SELECT import_records.import_record_id, record_type, status, overlay_status, marcxml_old, encoding, matched_biblionumber, matched_authid
823 LEFT JOIN import_auths ON (import_records.import_record_id=import_auths.import_record_id)
824 LEFT JOIN import_biblios ON (import_records.import_record_id=import_biblios.import_record_id)
825 WHERE import_batch_id = ?");
826 $sth->execute($batch_id);
828 my $marcflavour = C4::Context->preference('marcflavour');
829 while (my $rowref = $sth->fetchrow_hashref) {
830 $record_type = $rowref->{'record_type'};
831 if ($rowref->{'status'} eq 'error' or $rowref->{'status'} eq 'reverted') {
835 if ($marcflavour eq 'UNIMARC' && $record_type eq 'auth') {
836 $marc_type = 'UNIMARCAUTH';
837 } elsif ($marcflavour eq 'UNIMARC') {
838 $marc_type = 'UNIMARC';
840 $marc_type = 'USMARC';
843 my $record_result = _get_revert_action($overlay_action, $rowref->{'overlay_status'}, $rowref->{'status'});
845 if ($record_result eq 'delete') {
847 if ($record_type eq 'biblio') {
848 $num_items_deleted += BatchRevertItems($rowref->{'import_record_id'}, $rowref->{'matched_biblionumber'});
849 $error = DelBiblio($rowref->{'matched_biblionumber'});
851 DelAuthority({ authid => $rowref->{'matched_authid'} });
853 if (defined $error) {
857 SetImportRecordStatus($rowref->{'import_record_id'}, 'reverted');
859 } elsif ($record_result eq 'restore') {
861 my $old_record = MARC::Record->new_from_xml(StripNonXmlChars($rowref->{'marcxml_old'}), 'UTF-8', $rowref->{'encoding'}, $marc_type);
862 if ($record_type eq 'biblio') {
863 my $biblionumber = $rowref->{'matched_biblionumber'};
864 my $oldbiblio = Koha::Biblios->find( $biblionumber );
866 $logger->info("C4::ImportBatch::BatchRevertRecords: Biblio record $biblionumber does not exist, restoration of this record was skipped") unless $oldbiblio;
867 next unless $oldbiblio; # Record has since been deleted. Deleted records should stay deleted.
869 $num_items_deleted += BatchRevertItems($rowref->{'import_record_id'}, $rowref->{'matched_biblionumber'});
870 ModBiblio($old_record, $biblionumber, $oldbiblio->frameworkcode);
872 my $authid = $rowref->{'matched_authid'};
873 ModAuthority($authid, $old_record, GuessAuthTypeCode($old_record));
875 SetImportRecordStatus($rowref->{'import_record_id'}, 'reverted');
876 } elsif ($record_result eq 'ignore') {
877 if ($record_type eq 'biblio') {
878 $num_items_deleted += BatchRevertItems($rowref->{'import_record_id'}, $rowref->{'matched_biblionumber'});
880 SetImportRecordStatus($rowref->{'import_record_id'}, 'reverted');
883 if ($record_type eq 'biblio') {
884 # remove matched_biblionumber only if there is no 'imported' item left
885 $query = "UPDATE import_biblios SET matched_biblionumber = NULL WHERE import_record_id = ?"; # FIXME Remove me
886 $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')";
888 $query = "UPDATE import_auths SET matched_authid = NULL WHERE import_record_id = ?";
890 my $sth2 = $dbh->prepare_cached($query);
891 $sth2->execute($rowref->{'import_record_id'});
895 SetImportBatchStatus($batch_id, 'reverted');
896 return ($num_deleted, $num_errors, $num_reverted, $num_items_deleted, $num_ignored);
899 =head2 BatchRevertItems
901 my $num_items_deleted = BatchRevertItems($import_record_id, $biblionumber);
905 sub BatchRevertItems {
906 my ($import_record_id, $biblionumber) = @_;
908 my $dbh = C4::Context->dbh;
909 my $num_items_deleted = 0;
911 my $sth = $dbh->prepare_cached("SELECT import_items_id, itemnumber
913 JOIN items USING (itemnumber)
914 WHERE import_record_id = ?");
915 $sth->bind_param(1, $import_record_id);
917 while (my $row = $sth->fetchrow_hashref()) {
918 my $item = Koha::Items->find($row->{itemnumber});
919 my $error = $item->safe_delete;
921 my $updsth = $dbh->prepare("UPDATE import_items SET status = ? WHERE import_items_id = ?");
922 $updsth->bind_param(1, 'reverted');
923 $updsth->bind_param(2, $row->{'import_items_id'});
926 $num_items_deleted++;
933 return $num_items_deleted;
938 CleanBatch($batch_id)
940 Deletes all staged records from the import batch
941 and sets the status of the batch to 'cleaned'. Note
942 that deleting a stage record does *not* affect
943 any record that has been committed to the database.
948 my $batch_id = shift;
949 return unless defined $batch_id;
951 C4::Context->dbh->do('DELETE FROM import_records WHERE import_batch_id = ?', {}, $batch_id);
952 SetImportBatchStatus($batch_id, 'cleaned');
957 DeleteBatch($batch_id)
959 Deletes the record from the database. This can only be done
960 once the batch has been cleaned.
965 my $batch_id = shift;
966 return unless defined $batch_id;
968 my $dbh = C4::Context->dbh;
969 my $sth = $dbh->prepare('DELETE FROM import_batches WHERE import_batch_id = ?');
970 $sth->execute( $batch_id );
973 =head2 GetAllImportBatches
975 my $results = GetAllImportBatches();
977 Returns a references to an array of hash references corresponding
978 to all import_batches rows (of batch_type 'batch'), sorted in
979 ascending order by import_batch_id.
983 sub GetAllImportBatches {
984 my $dbh = C4::Context->dbh;
985 my $sth = $dbh->prepare_cached("SELECT * FROM import_batches
986 WHERE batch_type IN ('batch', 'webservice')
987 ORDER BY import_batch_id ASC");
991 while (my $row = $sth->fetchrow_hashref) {
992 push @$results, $row;
998 =head2 GetStagedWebserviceBatches
1000 my $batch_ids = GetStagedWebserviceBatches();
1002 Returns a references to an array of batch id's
1003 of batch_type 'webservice' that are not imported
1007 my $PENDING_WEBSERVICE_BATCHES_QRY = <<EOQ;
1008 SELECT import_batch_id FROM import_batches
1009 WHERE batch_type = 'webservice'
1010 AND import_status = 'staged'
1012 sub GetStagedWebserviceBatches {
1013 my $dbh = C4::Context->dbh;
1014 return $dbh->selectcol_arrayref($PENDING_WEBSERVICE_BATCHES_QRY);
1017 =head2 GetImportBatchRangeDesc
1019 my $results = GetImportBatchRangeDesc($offset, $results_per_group);
1021 Returns a reference to an array of hash references corresponding to
1022 import_batches rows (sorted in descending order by import_batch_id)
1023 start at the given offset.
1027 sub GetImportBatchRangeDesc {
1028 my ($offset, $results_per_group) = @_;
1030 my $dbh = C4::Context->dbh;
1031 my $query = "SELECT b.*, p.name as profile FROM import_batches b
1032 LEFT JOIN import_batch_profiles p
1033 ON b.profile_id = p.id
1034 WHERE b.batch_type IN ('batch', 'webservice')
1035 ORDER BY b.import_batch_id DESC";
1037 if ($results_per_group){
1038 $query .= " LIMIT ?";
1039 push(@params, $results_per_group);
1042 $query .= " OFFSET ?";
1043 push(@params, $offset);
1045 my $sth = $dbh->prepare_cached($query);
1046 $sth->execute(@params);
1047 my $results = $sth->fetchall_arrayref({});
1052 =head2 GetItemNumbersFromImportBatch
1054 my @itemsnos = GetItemNumbersFromImportBatch($batch_id);
1058 sub GetItemNumbersFromImportBatch {
1059 my ($batch_id) = @_;
1060 my $dbh = C4::Context->dbh;
1062 SELECT itemnumber FROM import_items
1063 INNER JOIN items USING (itemnumber)
1064 INNER JOIN import_records USING (import_record_id)
1065 WHERE import_batch_id = ?|;
1066 my $sth = $dbh->prepare( $sql );
1067 $sth->execute($batch_id);
1069 while ( my ($itm) = $sth->fetchrow_array ) {
1075 =head2 GetNumberOfImportBatches
1077 my $count = GetNumberOfImportBatches();
1081 sub GetNumberOfNonZ3950ImportBatches {
1082 my $dbh = C4::Context->dbh;
1083 my $sth = $dbh->prepare("SELECT COUNT(*) FROM import_batches WHERE batch_type != 'z3950'");
1085 my ($count) = $sth->fetchrow_array();
1090 =head2 GetImportBiblios
1092 my $results = GetImportBiblios($importid);
1096 sub GetImportBiblios {
1097 my ($import_record_id) = @_;
1099 my $dbh = C4::Context->dbh;
1100 my $query = "SELECT * FROM import_biblios WHERE import_record_id = ?";
1101 return $dbh->selectall_arrayref(
1109 =head2 GetImportRecordsRange
1111 my $results = GetImportRecordsRange($batch_id, $offset, $results_per_group);
1113 Returns a reference to an array of hash references corresponding to
1114 import_biblios/import_auths/import_records rows for a given batch
1115 starting at the given offset.
1119 sub GetImportRecordsRange {
1120 my ( $batch_id, $offset, $results_per_group, $status, $parameters ) = @_;
1122 my $dbh = C4::Context->dbh;
1124 my $order_by = $parameters->{order_by} || 'import_record_id';
1125 ( $order_by ) = grep( { $_ eq $order_by } qw( import_record_id title status overlay_status ) ) ? $order_by : 'import_record_id';
1127 my $order_by_direction =
1128 uc( $parameters->{order_by_direction} // 'ASC' ) eq 'DESC' ? 'DESC' : 'ASC';
1130 $order_by .= " $order_by_direction, authorized_heading" if $order_by eq 'title';
1132 my $query = "SELECT title, author, isbn, issn, authorized_heading, import_records.import_record_id,
1133 record_sequence, status, overlay_status,
1134 matched_biblionumber, matched_authid, record_type
1136 LEFT JOIN import_auths ON (import_records.import_record_id=import_auths.import_record_id)
1137 LEFT JOIN import_biblios ON (import_records.import_record_id=import_biblios.import_record_id)
1138 WHERE import_batch_id = ?";
1140 push(@params, $batch_id);
1142 $query .= " AND status=?";
1143 push(@params,$status);
1146 $query.=" ORDER BY $order_by $order_by_direction";
1148 if($results_per_group){
1149 $query .= " LIMIT ?";
1150 push(@params, $results_per_group);
1153 $query .= " OFFSET ?";
1154 push(@params, $offset);
1156 my $sth = $dbh->prepare_cached($query);
1157 $sth->execute(@params);
1158 my $results = $sth->fetchall_arrayref({});
1164 =head2 GetBestRecordMatch
1166 my $record_id = GetBestRecordMatch($import_record_id);
1170 sub GetBestRecordMatch {
1171 my ($import_record_id) = @_;
1173 my $dbh = C4::Context->dbh;
1174 my $sth = $dbh->prepare("SELECT candidate_match_id
1175 FROM import_record_matches
1176 JOIN import_records ON ( import_record_matches.import_record_id = import_records.import_record_id )
1177 LEFT JOIN biblio ON ( candidate_match_id = biblio.biblionumber )
1178 LEFT JOIN auth_header ON ( candidate_match_id = auth_header.authid )
1179 WHERE import_record_matches.import_record_id = ? AND
1180 ( (import_records.record_type = 'biblio' AND biblio.biblionumber IS NOT NULL) OR
1181 (import_records.record_type = 'auth' AND auth_header.authid IS NOT NULL) )
1182 ORDER BY score DESC, candidate_match_id DESC");
1183 $sth->execute($import_record_id);
1184 my ($record_id) = $sth->fetchrow_array();
1189 =head2 GetImportBatchStatus
1191 my $status = GetImportBatchStatus($batch_id);
1195 sub GetImportBatchStatus {
1196 my ($batch_id) = @_;
1198 my $dbh = C4::Context->dbh;
1199 my $sth = $dbh->prepare("SELECT import_status FROM import_batches WHERE import_batch_id = ?");
1200 $sth->execute($batch_id);
1201 my ($status) = $sth->fetchrow_array();
1207 =head2 SetImportBatchStatus
1209 SetImportBatchStatus($batch_id, $new_status);
1213 sub SetImportBatchStatus {
1214 my ($batch_id, $new_status) = @_;
1216 my $dbh = C4::Context->dbh;
1217 my $sth = $dbh->prepare("UPDATE import_batches SET import_status = ? WHERE import_batch_id = ?");
1218 $sth->execute($new_status, $batch_id);
1223 =head2 SetMatchedBiblionumber
1225 SetMatchedBiblionumber($import_record_id, $biblionumber);
1229 sub SetMatchedBiblionumber {
1230 my ($import_record_id, $biblionumber) = @_;
1232 my $dbh = C4::Context->dbh;
1234 q|UPDATE import_biblios SET matched_biblionumber = ? WHERE import_record_id = ?|,
1235 undef, $biblionumber, $import_record_id
1239 =head2 GetImportBatchOverlayAction
1241 my $overlay_action = GetImportBatchOverlayAction($batch_id);
1245 sub GetImportBatchOverlayAction {
1246 my ($batch_id) = @_;
1248 my $dbh = C4::Context->dbh;
1249 my $sth = $dbh->prepare("SELECT overlay_action FROM import_batches WHERE import_batch_id = ?");
1250 $sth->execute($batch_id);
1251 my ($overlay_action) = $sth->fetchrow_array();
1253 return $overlay_action;
1258 =head2 SetImportBatchOverlayAction
1260 SetImportBatchOverlayAction($batch_id, $new_overlay_action);
1264 sub SetImportBatchOverlayAction {
1265 my ($batch_id, $new_overlay_action) = @_;
1267 my $dbh = C4::Context->dbh;
1268 my $sth = $dbh->prepare("UPDATE import_batches SET overlay_action = ? WHERE import_batch_id = ?");
1269 $sth->execute($new_overlay_action, $batch_id);
1274 =head2 GetImportBatchNoMatchAction
1276 my $nomatch_action = GetImportBatchNoMatchAction($batch_id);
1280 sub GetImportBatchNoMatchAction {
1281 my ($batch_id) = @_;
1283 my $dbh = C4::Context->dbh;
1284 my $sth = $dbh->prepare("SELECT nomatch_action FROM import_batches WHERE import_batch_id = ?");
1285 $sth->execute($batch_id);
1286 my ($nomatch_action) = $sth->fetchrow_array();
1288 return $nomatch_action;
1293 =head2 SetImportBatchNoMatchAction
1295 SetImportBatchNoMatchAction($batch_id, $new_nomatch_action);
1299 sub SetImportBatchNoMatchAction {
1300 my ($batch_id, $new_nomatch_action) = @_;
1302 my $dbh = C4::Context->dbh;
1303 my $sth = $dbh->prepare("UPDATE import_batches SET nomatch_action = ? WHERE import_batch_id = ?");
1304 $sth->execute($new_nomatch_action, $batch_id);
1309 =head2 GetImportBatchItemAction
1311 my $item_action = GetImportBatchItemAction($batch_id);
1315 sub GetImportBatchItemAction {
1316 my ($batch_id) = @_;
1318 my $dbh = C4::Context->dbh;
1319 my $sth = $dbh->prepare("SELECT item_action FROM import_batches WHERE import_batch_id = ?");
1320 $sth->execute($batch_id);
1321 my ($item_action) = $sth->fetchrow_array();
1323 return $item_action;
1328 =head2 SetImportBatchItemAction
1330 SetImportBatchItemAction($batch_id, $new_item_action);
1334 sub SetImportBatchItemAction {
1335 my ($batch_id, $new_item_action) = @_;
1337 my $dbh = C4::Context->dbh;
1338 my $sth = $dbh->prepare("UPDATE import_batches SET item_action = ? WHERE import_batch_id = ?");
1339 $sth->execute($new_item_action, $batch_id);
1344 =head2 GetImportBatchMatcher
1346 my $matcher_id = GetImportBatchMatcher($batch_id);
1350 sub GetImportBatchMatcher {
1351 my ($batch_id) = @_;
1353 my $dbh = C4::Context->dbh;
1354 my $sth = $dbh->prepare("SELECT matcher_id FROM import_batches WHERE import_batch_id = ?");
1355 $sth->execute($batch_id);
1356 my ($matcher_id) = $sth->fetchrow_array();
1363 =head2 SetImportBatchMatcher
1365 SetImportBatchMatcher($batch_id, $new_matcher_id);
1369 sub SetImportBatchMatcher {
1370 my ($batch_id, $new_matcher_id) = @_;
1372 my $dbh = C4::Context->dbh;
1373 my $sth = $dbh->prepare("UPDATE import_batches SET matcher_id = ? WHERE import_batch_id = ?");
1374 $sth->execute($new_matcher_id, $batch_id);
1379 =head2 GetImportRecordOverlayStatus
1381 my $overlay_status = GetImportRecordOverlayStatus($import_record_id);
1385 sub GetImportRecordOverlayStatus {
1386 my ($import_record_id) = @_;
1388 my $dbh = C4::Context->dbh;
1389 my $sth = $dbh->prepare("SELECT overlay_status FROM import_records WHERE import_record_id = ?");
1390 $sth->execute($import_record_id);
1391 my ($overlay_status) = $sth->fetchrow_array();
1393 return $overlay_status;
1398 =head2 SetImportRecordOverlayStatus
1400 SetImportRecordOverlayStatus($import_record_id, $new_overlay_status);
1404 sub SetImportRecordOverlayStatus {
1405 my ($import_record_id, $new_overlay_status) = @_;
1407 my $dbh = C4::Context->dbh;
1408 my $sth = $dbh->prepare("UPDATE import_records SET overlay_status = ? WHERE import_record_id = ?");
1409 $sth->execute($new_overlay_status, $import_record_id);
1414 =head2 GetImportRecordStatus
1416 my $status = GetImportRecordStatus($import_record_id);
1420 sub GetImportRecordStatus {
1421 my ($import_record_id) = @_;
1423 my $dbh = C4::Context->dbh;
1424 my $sth = $dbh->prepare("SELECT status FROM import_records WHERE import_record_id = ?");
1425 $sth->execute($import_record_id);
1426 my ($status) = $sth->fetchrow_array();
1433 =head2 SetImportRecordStatus
1435 SetImportRecordStatus($import_record_id, $new_status);
1439 sub SetImportRecordStatus {
1440 my ($import_record_id, $new_status) = @_;
1442 my $dbh = C4::Context->dbh;
1443 my $sth = $dbh->prepare("UPDATE import_records SET status = ? WHERE import_record_id = ?");
1444 $sth->execute($new_status, $import_record_id);
1449 =head2 GetImportRecordMatches
1451 my $results = GetImportRecordMatches($import_record_id, $best_only);
1455 sub GetImportRecordMatches {
1456 my $import_record_id = shift;
1457 my $best_only = @_ ? shift : 0;
1459 my $dbh = C4::Context->dbh;
1460 # FIXME currently biblio only
1461 my $sth = $dbh->prepare_cached("SELECT title, author, biblionumber,
1462 candidate_match_id, score, record_type
1464 JOIN import_record_matches USING (import_record_id)
1465 LEFT JOIN biblio ON (biblionumber = candidate_match_id)
1466 WHERE import_record_id = ?
1467 ORDER BY score DESC, biblionumber DESC");
1468 $sth->bind_param(1, $import_record_id);
1471 while (my $row = $sth->fetchrow_hashref) {
1472 if ($row->{'record_type'} eq 'auth') {
1473 $row->{'authorized_heading'} = C4::AuthoritiesMarc::GetAuthorizedHeading( { authid => $row->{'candidate_match_id'} } );
1475 next if ($row->{'record_type'} eq 'biblio' && not $row->{'biblionumber'});
1476 push @$results, $row;
1485 =head2 SetImportRecordMatches
1487 SetImportRecordMatches($import_record_id, @matches);
1491 sub SetImportRecordMatches {
1492 my $import_record_id = shift;
1495 my $dbh = C4::Context->dbh;
1496 my $delsth = $dbh->prepare("DELETE FROM import_record_matches WHERE import_record_id = ?");
1497 $delsth->execute($import_record_id);
1500 my $sth = $dbh->prepare("INSERT INTO import_record_matches (import_record_id, candidate_match_id, score)
1502 foreach my $match (@matches) {
1503 $sth->execute($import_record_id, $match->{'record_id'}, $match->{'score'});
1507 =head2 RecordsFromISO2709File
1509 my ($errors, $records) = C4::ImportBatch::RecordsFromISO2709File($input_file, $record_type, $encoding);
1511 Reads ISO2709 binary porridge from the given file and creates MARC::Record-objects out of it.
1513 @PARAM1, String, absolute path to the ISO2709 file.
1514 @PARAM2, String, see stage_file.pl
1515 @PARAM3, String, should be utf8
1517 Returns two array refs.
1521 sub RecordsFromISO2709File {
1522 my ($input_file, $record_type, $encoding) = @_;
1525 my $marc_type = C4::Context->preference('marcflavour');
1526 $marc_type .= 'AUTH' if ($marc_type eq 'UNIMARC' && $record_type eq 'auth');
1528 open my $fh, '<', $input_file or die "$0: cannot open input file $input_file: $!\n";
1534 next unless $_; # skip if record has only whitespace, as might occur
1535 # if file includes newlines between each MARC record
1536 my ($marc_record, $charset_guessed, $char_errors) = MarcToUTF8Record($_, $marc_type, $encoding);
1537 push @marc_records, $marc_record;
1538 if ($charset_guessed ne $encoding) {
1540 "Unexpected charset $charset_guessed, expecting $encoding";
1544 return ( \@errors, \@marc_records );
1547 =head2 RecordsFromMARCXMLFile
1549 my ($errors, $records) = C4::ImportBatch::RecordsFromMARCXMLFile($input_file, $encoding);
1551 Creates MARC::Record-objects out of the given MARCXML-file.
1553 @PARAM1, String, absolute path to the ISO2709 file.
1554 @PARAM2, String, should be utf8
1556 Returns two array refs.
1560 sub RecordsFromMARCXMLFile {
1561 my ( $filename, $encoding ) = @_;
1562 my $batch = MARC::File::XML->in( $filename );
1563 my ( @marcRecords, @errors, $record );
1565 eval { $record = $batch->next( $encoding ); };
1569 push @marcRecords, $record if $record;
1571 return (\@errors, \@marcRecords);
1574 =head2 RecordsFromMarcPlugin
1576 Converts text of input_file into array of MARC records with to_marc plugin
1580 sub RecordsFromMarcPlugin {
1581 my ($input_file, $plugin_class, $encoding) = @_;
1582 my ( $text, @return );
1583 return \@return if !$input_file || !$plugin_class;
1586 open my $fh, '<', $input_file or die "$0: cannot open input file $input_file: $!\n";
1596 # Convert to large MARC blob with plugin
1597 $text = Koha::Plugins::Handler->run({
1598 class => $plugin_class,
1599 method => 'to_marc',
1600 params => { data => $text },
1603 # Convert to array of MARC records
1605 my $marc_type = C4::Context->preference('marcflavour');
1606 foreach my $blob ( split(/\x1D/, $text) ) {
1607 next if $blob =~ /^\s*$/;
1608 my ($marcrecord) = MarcToUTF8Record($blob, $marc_type, $encoding);
1609 push @return, $marcrecord;
1615 # internal functions
1617 sub _create_import_record {
1618 my ($batch_id, $record_sequence, $marc_record, $record_type, $encoding, $marc_type) = @_;
1620 my $dbh = C4::Context->dbh;
1621 my $sth = $dbh->prepare("INSERT INTO import_records (import_batch_id, record_sequence, marc, marcxml, marcxml_old,
1622 record_type, encoding)
1623 VALUES (?, ?, ?, ?, ?, ?, ?)");
1624 $sth->execute($batch_id, $record_sequence, $marc_record->as_usmarc(), $marc_record->as_xml($marc_type), '',
1625 $record_type, $encoding);
1626 my $import_record_id = $dbh->{'mysql_insertid'};
1628 return $import_record_id;
1631 sub _update_import_record_marc {
1632 my ($import_record_id, $marc_record, $marc_type) = @_;
1634 my $dbh = C4::Context->dbh;
1635 my $sth = $dbh->prepare("UPDATE import_records SET marc = ?, marcxml = ?
1636 WHERE import_record_id = ?");
1637 $sth->execute($marc_record->as_usmarc(), $marc_record->as_xml($marc_type), $import_record_id);
1641 sub _add_auth_fields {
1642 my ($import_record_id, $marc_record) = @_;
1645 if ($marc_record->field('001')) {
1646 $controlnumber = $marc_record->field('001')->data();
1648 my $authorized_heading = C4::AuthoritiesMarc::GetAuthorizedHeading({ record => $marc_record });
1649 my $dbh = C4::Context->dbh;
1650 my $sth = $dbh->prepare("INSERT INTO import_auths (import_record_id, control_number, authorized_heading) VALUES (?, ?, ?)");
1651 $sth->execute($import_record_id, $controlnumber, $authorized_heading);
1655 sub _add_biblio_fields {
1656 my ($import_record_id, $marc_record) = @_;
1658 my ($title, $author, $isbn, $issn) = _parse_biblio_fields($marc_record);
1659 my $dbh = C4::Context->dbh;
1660 # FIXME no controlnumber, originalsource
1661 $isbn = C4::Koha::GetNormalizedISBN($isbn);
1662 my $sth = $dbh->prepare("INSERT INTO import_biblios (import_record_id, title, author, isbn, issn) VALUES (?, ?, ?, ?, ?)");
1663 $sth->execute($import_record_id, $title, $author, $isbn, $issn) or die $sth->errstr;
1668 sub _update_biblio_fields {
1669 my ($import_record_id, $marc_record) = @_;
1671 my ($title, $author, $isbn, $issn) = _parse_biblio_fields($marc_record);
1672 my $dbh = C4::Context->dbh;
1673 # FIXME no controlnumber, originalsource
1674 # FIXME 2 - should regularize normalization of ISBN wherever it is done
1678 my $sth = $dbh->prepare("UPDATE import_biblios SET title = ?, author = ?, isbn = ?, issn = ?
1679 WHERE import_record_id = ?");
1680 $sth->execute($title, $author, $isbn, $issn, $import_record_id);
1684 sub _parse_biblio_fields {
1685 my ($marc_record) = @_;
1687 my $dbh = C4::Context->dbh;
1688 my $bibliofields = TransformMarcToKoha($marc_record, '');
1689 return ($bibliofields->{'title'}, $bibliofields->{'author'}, $bibliofields->{'isbn'}, $bibliofields->{'issn'});
1693 sub _update_batch_record_counts {
1694 my ($batch_id) = @_;
1696 my $dbh = C4::Context->dbh;
1697 my $sth = $dbh->prepare_cached("UPDATE import_batches SET
1701 WHERE import_batch_id = import_batches.import_batch_id),
1705 JOIN import_items USING (import_record_id)
1706 WHERE import_batch_id = import_batches.import_batch_id
1707 AND record_type = 'biblio')
1708 WHERE import_batch_id = ?");
1709 $sth->bind_param(1, $batch_id);
1714 sub _get_commit_action {
1715 my ($overlay_action, $nomatch_action, $item_action, $overlay_status, $import_record_id, $record_type) = @_;
1717 if ($record_type eq 'biblio') {
1718 my ($bib_result, $bib_match, $item_result);
1720 if ($overlay_status ne 'no_match') {
1721 $bib_match = GetBestRecordMatch($import_record_id);
1722 if ($overlay_action eq 'replace') {
1723 $bib_result = defined($bib_match) ? 'replace' : 'create_new';
1724 } elsif ($overlay_action eq 'create_new') {
1725 $bib_result = 'create_new';
1726 } elsif ($overlay_action eq 'ignore') {
1727 $bib_result = 'ignore';
1729 if($item_action eq 'always_add' or $item_action eq 'add_only_for_matches'){
1730 $item_result = 'create_new';
1732 elsif($item_action eq 'replace'){
1733 $item_result = 'replace';
1736 $item_result = 'ignore';
1739 $bib_result = $nomatch_action;
1740 $item_result = ($item_action eq 'always_add' or $item_action eq 'add_only_for_new') ? 'create_new' : 'ignore';
1742 return ($bib_result, $item_result, $bib_match);
1743 } else { # must be auths
1744 my ($auth_result, $auth_match);
1746 if ($overlay_status ne 'no_match') {
1747 $auth_match = GetBestRecordMatch($import_record_id);
1748 if ($overlay_action eq 'replace') {
1749 $auth_result = defined($auth_match) ? 'replace' : 'create_new';
1750 } elsif ($overlay_action eq 'create_new') {
1751 $auth_result = 'create_new';
1752 } elsif ($overlay_action eq 'ignore') {
1753 $auth_result = 'ignore';
1756 $auth_result = $nomatch_action;
1759 return ($auth_result, undef, $auth_match);
1764 sub _get_revert_action {
1765 my ($overlay_action, $overlay_status, $status) = @_;
1769 if ($status eq 'ignored') {
1770 $bib_result = 'ignore';
1772 if ($overlay_action eq 'create_new') {
1773 $bib_result = 'delete';
1775 $bib_result = ($overlay_status eq 'match_applied') ? 'restore' : 'delete';
1786 Koha Development Team <http://koha-community.org/>
1788 Galen Charlton <galen.charlton@liblime.com>