Bug 14649: Fix regression - display fund name on budget planning
[koha.git] / C4 / ImportBatch.pm
blob29a1f94141634b46d4d2cb1bed584d6eb06034fa
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>.
20 use strict;
21 use warnings;
23 use C4::Context;
24 use C4::Koha;
25 use C4::Biblio;
26 use C4::Items;
27 use C4::Charset;
28 use C4::AuthoritiesMarc;
29 use C4::MarcModificationTemplates;
30 use Koha::Plugins::Handler;
32 use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);
34 BEGIN {
35 # set the version for version checking
36 $VERSION = 3.07.00.049;
37 require Exporter;
38 @ISA = qw(Exporter);
39 @EXPORT = qw(
40 GetZ3950BatchId
41 GetWebserviceBatchId
42 GetImportRecordMarc
43 GetImportRecordMarcXML
44 AddImportBatch
45 GetImportBatch
46 AddAuthToBatch
47 AddBiblioToBatch
48 AddItemsToImportBiblio
49 ModAuthorityInBatch
50 ModBiblioInBatch
52 BatchStageMarcRecords
53 BatchFindDuplicates
54 BatchCommitRecords
55 BatchRevertRecords
56 CleanBatch
58 GetAllImportBatches
59 GetStagedWebserviceBatches
60 GetImportBatchRangeDesc
61 GetNumberOfNonZ3950ImportBatches
62 GetImportBiblios
63 GetImportRecordsRange
64 GetItemNumbersFromImportBatch
66 GetImportBatchStatus
67 SetImportBatchStatus
68 GetImportBatchOverlayAction
69 SetImportBatchOverlayAction
70 GetImportBatchNoMatchAction
71 SetImportBatchNoMatchAction
72 GetImportBatchItemAction
73 SetImportBatchItemAction
74 GetImportBatchMatcher
75 SetImportBatchMatcher
76 GetImportRecordOverlayStatus
77 SetImportRecordOverlayStatus
78 GetImportRecordStatus
79 SetImportRecordStatus
80 GetImportRecordMatches
81 SetImportRecordMatches
85 =head1 NAME
87 C4::ImportBatch - manage batches of imported MARC records
89 =head1 SYNOPSIS
91 use C4::ImportBatch;
93 =head1 FUNCTIONS
95 =head2 GetZ3950BatchId
97 my $batchid = GetZ3950BatchId($z3950server);
99 Retrieves the ID of the import batch for the Z39.50
100 reservoir for the given target. If necessary,
101 creates the import batch.
103 =cut
105 sub GetZ3950BatchId {
106 my ($z3950server) = @_;
108 my $dbh = C4::Context->dbh;
109 my $sth = $dbh->prepare("SELECT import_batch_id FROM import_batches
110 WHERE batch_type = 'z3950'
111 AND file_name = ?");
112 $sth->execute($z3950server);
113 my $rowref = $sth->fetchrow_arrayref();
114 $sth->finish();
115 if (defined $rowref) {
116 return $rowref->[0];
117 } else {
118 my $batch_id = AddImportBatch( {
119 overlay_action => 'create_new',
120 import_status => 'staged',
121 batch_type => 'z3950',
122 file_name => $z3950server,
123 } );
124 return $batch_id;
129 =head2 GetWebserviceBatchId
131 my $batchid = GetWebserviceBatchId();
133 Retrieves the ID of the import batch for webservice.
134 If necessary, creates the import batch.
136 =cut
138 my $WEBSERVICE_BASE_QRY = <<EOQ;
139 SELECT import_batch_id FROM import_batches
140 WHERE batch_type = 'webservice'
141 AND import_status = 'staged'
143 sub GetWebserviceBatchId {
144 my ($params) = @_;
146 my $dbh = C4::Context->dbh;
147 my $sql = $WEBSERVICE_BASE_QRY;
148 my @args;
149 foreach my $field (qw(matcher_id overlay_action nomatch_action item_action)) {
150 if (my $val = $params->{$field}) {
151 $sql .= " AND $field = ?";
152 push @args, $val;
155 my $id = $dbh->selectrow_array($sql, undef, @args);
156 return $id if $id;
158 $params->{batch_type} = 'webservice';
159 $params->{import_status} = 'staged';
160 return AddImportBatch($params);
163 =head2 GetImportRecordMarc
165 my ($marcblob, $encoding) = GetImportRecordMarc($import_record_id);
167 =cut
169 sub GetImportRecordMarc {
170 my ($import_record_id) = @_;
172 my $dbh = C4::Context->dbh;
173 my ( $marc, $encoding ) = $dbh->selectrow_array(q|
174 SELECT marc, encoding
175 FROM import_records
176 WHERE import_record_id = ?
177 |, undef, $import_record_id );
179 return $marc, $encoding;
182 sub GetRecordFromImportBiblio {
183 my ( $import_record_id, $embed_items ) = @_;
185 my ($marc) = GetImportRecordMarc($import_record_id);
186 my $record = MARC::Record->new_from_usmarc($marc);
188 EmbedItemsInImportBiblio( $record, $import_record_id ) if $embed_items;
190 return $record;
193 sub EmbedItemsInImportBiblio {
194 my ( $record, $import_record_id ) = @_;
195 my ( $itemtag, $itemsubfield ) = GetMarcFromKohaField("items.itemnumber", '');
196 my $dbh = C4::Context->dbh;
197 my $import_items = $dbh->selectall_arrayref(q|
198 SELECT import_items.marcxml
199 FROM import_items
200 WHERE import_record_id = ?
201 |, { Slice => {} }, $import_record_id );
202 my @item_fields;
203 for my $import_item ( @$import_items ) {
204 my $item_marc = MARC::Record::new_from_xml($import_item->{marcxml});
205 push @item_fields, $item_marc->field($itemtag);
207 $record->append_fields(@item_fields);
208 return $record;
211 =head2 GetImportRecordMarcXML
213 my $marcxml = GetImportRecordMarcXML($import_record_id);
215 =cut
217 sub GetImportRecordMarcXML {
218 my ($import_record_id) = @_;
220 my $dbh = C4::Context->dbh;
221 my $sth = $dbh->prepare("SELECT marcxml FROM import_records WHERE import_record_id = ?");
222 $sth->execute($import_record_id);
223 my ($marcxml) = $sth->fetchrow();
224 $sth->finish();
225 return $marcxml;
229 =head2 AddImportBatch
231 my $batch_id = AddImportBatch($params_hash);
233 =cut
235 sub AddImportBatch {
236 my ($params) = @_;
238 my (@fields, @vals);
239 foreach (qw( matcher_id template_id branchcode
240 overlay_action nomatch_action item_action
241 import_status batch_type file_name comments record_type )) {
242 if (exists $params->{$_}) {
243 push @fields, $_;
244 push @vals, $params->{$_};
247 my $dbh = C4::Context->dbh;
248 $dbh->do("INSERT INTO import_batches (".join( ',', @fields).")
249 VALUES (".join( ',', map '?', @fields).")",
250 undef,
251 @vals);
252 return $dbh->{'mysql_insertid'};
255 =head2 GetImportBatch
257 my $row = GetImportBatch($batch_id);
259 Retrieve a hashref of an import_batches row.
261 =cut
263 sub GetImportBatch {
264 my ($batch_id) = @_;
266 my $dbh = C4::Context->dbh;
267 my $sth = $dbh->prepare_cached("SELECT * FROM import_batches WHERE import_batch_id = ?");
268 $sth->bind_param(1, $batch_id);
269 $sth->execute();
270 my $result = $sth->fetchrow_hashref;
271 $sth->finish();
272 return $result;
276 =head2 AddBiblioToBatch
278 my $import_record_id = AddBiblioToBatch($batch_id, $record_sequence,
279 $marc_record, $encoding, $z3950random, $update_counts);
281 =cut
283 sub AddBiblioToBatch {
284 my $batch_id = shift;
285 my $record_sequence = shift;
286 my $marc_record = shift;
287 my $encoding = shift;
288 my $z3950random = shift;
289 my $update_counts = @_ ? shift : 1;
291 my $import_record_id = _create_import_record($batch_id, $record_sequence, $marc_record, 'biblio', $encoding, $z3950random, C4::Context->preference('marcflavour'));
292 _add_biblio_fields($import_record_id, $marc_record);
293 _update_batch_record_counts($batch_id) if $update_counts;
294 return $import_record_id;
297 =head2 ModBiblioInBatch
299 ModBiblioInBatch($import_record_id, $marc_record);
301 =cut
303 sub ModBiblioInBatch {
304 my ($import_record_id, $marc_record) = @_;
306 _update_import_record_marc($import_record_id, $marc_record, C4::Context->preference('marcflavour'));
307 _update_biblio_fields($import_record_id, $marc_record);
311 =head2 AddAuthToBatch
313 my $import_record_id = AddAuthToBatch($batch_id, $record_sequence,
314 $marc_record, $encoding, $z3950random, $update_counts, [$marc_type]);
316 =cut
318 sub AddAuthToBatch {
319 my $batch_id = shift;
320 my $record_sequence = shift;
321 my $marc_record = shift;
322 my $encoding = shift;
323 my $z3950random = 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, $z3950random, $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);
339 =cut
341 sub ModAuthInBatch {
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 $encoding, $marc_records,
354 $file_name, $to_marc_plugin,
355 $marc_modification_template, $comments,
356 $branch_code, $parse_items,
357 $leave_as_staging, $progress_interval,
358 $progress_callback
361 =cut
363 sub BatchStageMarcRecords {
364 my $record_type = shift;
365 my $encoding = shift;
366 my $marc_records = shift;
367 my $file_name = shift;
368 my $to_marc_plugin = shift;
369 my $marc_modification_template = shift;
370 my $comments = shift;
371 my $branch_code = shift;
372 my $parse_items = shift;
373 my $leave_as_staging = shift;
375 # optional callback to monitor status
376 # of job
377 my $progress_interval = 0;
378 my $progress_callback = undef;
379 if ($#_ == 1) {
380 $progress_interval = shift;
381 $progress_callback = shift;
382 $progress_interval = 0 unless $progress_interval =~ /^\d+$/ and $progress_interval > 0;
383 $progress_interval = 0 unless 'CODE' eq ref $progress_callback;
386 my $batch_id = AddImportBatch( {
387 overlay_action => 'create_new',
388 import_status => 'staging',
389 batch_type => 'batch',
390 file_name => $file_name,
391 comments => $comments,
392 record_type => $record_type,
393 } );
394 if ($parse_items) {
395 SetImportBatchItemAction($batch_id, 'always_add');
396 } else {
397 SetImportBatchItemAction($batch_id, 'ignore');
400 $marc_records = Koha::Plugins::Handler->run(
402 class => $to_marc_plugin,
403 method => 'to_marc',
404 params => { data => $marc_records }
406 ) if $to_marc_plugin;
408 my $marc_type = C4::Context->preference('marcflavour');
409 $marc_type .= 'AUTH' if ($marc_type eq 'UNIMARC' && $record_type eq 'auth');
410 my @invalid_records = ();
411 my $num_valid = 0;
412 my $num_items = 0;
413 # FIXME - for now, we're dealing only with bibs
414 my $rec_num = 0;
415 foreach my $marc_blob (split(/\x1D/, $marc_records)) {
416 $marc_blob =~ s/^\s+//g;
417 $marc_blob =~ s/\s+$//g;
418 next unless $marc_blob;
419 $rec_num++;
420 if ($progress_interval and (0 == ($rec_num % $progress_interval))) {
421 &$progress_callback($rec_num);
423 my ($marc_record, $charset_guessed, $char_errors) =
424 MarcToUTF8Record($marc_blob, $marc_type, $encoding);
426 $encoding = $charset_guessed unless $encoding;
428 ModifyRecordWithTemplate( $marc_modification_template, $marc_record ) if ( $marc_modification_template );
430 my $import_record_id;
431 if (scalar($marc_record->fields()) == 0) {
432 push @invalid_records, $marc_blob;
433 } else {
435 # Normalize the record so it doesn't have separated diacritics
436 SetUTF8Flag($marc_record);
438 $num_valid++;
439 if ($record_type eq 'biblio') {
440 $import_record_id = AddBiblioToBatch($batch_id, $rec_num, $marc_record, $encoding, int(rand(99999)), 0);
441 if ($parse_items) {
442 my @import_items_ids = AddItemsToImportBiblio($batch_id, $import_record_id, $marc_record, 0);
443 $num_items += scalar(@import_items_ids);
445 } elsif ($record_type eq 'auth') {
446 $import_record_id = AddAuthToBatch($batch_id, $rec_num, $marc_record, $encoding, int(rand(99999)), 0, $marc_type);
450 unless ($leave_as_staging) {
451 SetImportBatchStatus($batch_id, 'staged');
453 # FIXME branch_code, number of bibs, number of items
454 _update_batch_record_counts($batch_id);
455 return ($batch_id, $num_valid, $num_items, @invalid_records);
458 =head2 AddItemsToImportBiblio
460 my @import_items_ids = AddItemsToImportBiblio($batch_id,
461 $import_record_id, $marc_record, $update_counts);
463 =cut
465 sub AddItemsToImportBiblio {
466 my $batch_id = shift;
467 my $import_record_id = shift;
468 my $marc_record = shift;
469 my $update_counts = @_ ? shift : 0;
471 my @import_items_ids = ();
473 my $dbh = C4::Context->dbh;
474 my ($item_tag,$item_subfield) = &GetMarcFromKohaField("items.itemnumber",'');
475 foreach my $item_field ($marc_record->field($item_tag)) {
476 my $item_marc = MARC::Record->new();
477 $item_marc->leader("00000 a "); # must set Leader/09 to 'a'
478 $item_marc->append_fields($item_field);
479 $marc_record->delete_field($item_field);
480 my $sth = $dbh->prepare_cached("INSERT INTO import_items (import_record_id, status, marcxml)
481 VALUES (?, ?, ?)");
482 $sth->bind_param(1, $import_record_id);
483 $sth->bind_param(2, 'staged');
484 $sth->bind_param(3, $item_marc->as_xml());
485 $sth->execute();
486 push @import_items_ids, $dbh->{'mysql_insertid'};
487 $sth->finish();
490 if ($#import_items_ids > -1) {
491 _update_batch_record_counts($batch_id) if $update_counts;
492 _update_import_record_marc($import_record_id, $marc_record, C4::Context->preference('marcflavour'));
494 return @import_items_ids;
497 =head2 BatchFindDuplicates
499 my $num_with_matches = BatchFindDuplicates($batch_id, $matcher,
500 $max_matches, $progress_interval, $progress_callback);
502 Goes through the records loaded in the batch and attempts to
503 find duplicates for each one. Sets the matching status
504 of each record to "no_match" or "auto_match" as appropriate.
506 The $max_matches parameter is optional; if it is not supplied,
507 it defaults to 10.
509 The $progress_interval and $progress_callback parameters are
510 optional; if both are supplied, the sub referred to by
511 $progress_callback will be invoked every $progress_interval
512 records using the number of records processed as the
513 singular argument.
515 =cut
517 sub BatchFindDuplicates {
518 my $batch_id = shift;
519 my $matcher = shift;
520 my $max_matches = @_ ? shift : 10;
522 # optional callback to monitor status
523 # of job
524 my $progress_interval = 0;
525 my $progress_callback = undef;
526 if ($#_ == 1) {
527 $progress_interval = shift;
528 $progress_callback = shift;
529 $progress_interval = 0 unless $progress_interval =~ /^\d+$/ and $progress_interval > 0;
530 $progress_interval = 0 unless 'CODE' eq ref $progress_callback;
533 my $dbh = C4::Context->dbh;
535 my $sth = $dbh->prepare("SELECT import_record_id, record_type, marc
536 FROM import_records
537 WHERE import_batch_id = ?");
538 $sth->execute($batch_id);
539 my $num_with_matches = 0;
540 my $rec_num = 0;
541 while (my $rowref = $sth->fetchrow_hashref) {
542 $rec_num++;
543 if ($progress_interval and (0 == ($rec_num % $progress_interval))) {
544 &$progress_callback($rec_num);
546 my $marc_record = MARC::Record->new_from_usmarc($rowref->{'marc'});
547 my @matches = ();
548 if (defined $matcher) {
549 @matches = $matcher->get_matches($marc_record, $max_matches);
551 if (scalar(@matches) > 0) {
552 $num_with_matches++;
553 SetImportRecordMatches($rowref->{'import_record_id'}, @matches);
554 SetImportRecordOverlayStatus($rowref->{'import_record_id'}, 'auto_match');
555 } else {
556 SetImportRecordMatches($rowref->{'import_record_id'}, ());
557 SetImportRecordOverlayStatus($rowref->{'import_record_id'}, 'no_match');
560 $sth->finish();
561 return $num_with_matches;
564 =head2 BatchCommitRecords
566 my ($num_added, $num_updated, $num_items_added, $num_items_replaced, $num_items_errored, $num_ignored) =
567 BatchCommitRecords($batch_id, $framework,
568 $progress_interval, $progress_callback);
570 =cut
572 sub BatchCommitRecords {
573 my $batch_id = shift;
574 my $framework = shift;
576 # optional callback to monitor status
577 # of job
578 my $progress_interval = 0;
579 my $progress_callback = undef;
580 if ($#_ == 1) {
581 $progress_interval = shift;
582 $progress_callback = shift;
583 $progress_interval = 0 unless $progress_interval =~ /^\d+$/ and $progress_interval > 0;
584 $progress_interval = 0 unless 'CODE' eq ref $progress_callback;
587 my $record_type;
588 my $num_added = 0;
589 my $num_updated = 0;
590 my $num_items_added = 0;
591 my $num_items_replaced = 0;
592 my $num_items_errored = 0;
593 my $num_ignored = 0;
594 # commit (i.e., save, all records in the batch)
595 SetImportBatchStatus('importing');
596 my $overlay_action = GetImportBatchOverlayAction($batch_id);
597 my $nomatch_action = GetImportBatchNoMatchAction($batch_id);
598 my $item_action = GetImportBatchItemAction($batch_id);
599 my $item_tag;
600 my $item_subfield;
601 my $dbh = C4::Context->dbh;
602 my $sth = $dbh->prepare("SELECT import_records.import_record_id, record_type, status, overlay_status, marc, encoding
603 FROM import_records
604 LEFT JOIN import_auths ON (import_records.import_record_id=import_auths.import_record_id)
605 LEFT JOIN import_biblios ON (import_records.import_record_id=import_biblios.import_record_id)
606 WHERE import_batch_id = ?");
607 $sth->execute($batch_id);
608 my $marcflavour = C4::Context->preference('marcflavour');
609 my $rec_num = 0;
610 while (my $rowref = $sth->fetchrow_hashref) {
611 $record_type = $rowref->{'record_type'};
612 $rec_num++;
613 if ($progress_interval and (0 == ($rec_num % $progress_interval))) {
614 &$progress_callback($rec_num);
616 if ($rowref->{'status'} eq 'error' or $rowref->{'status'} eq 'imported') {
617 $num_ignored++;
618 next;
621 my $marc_type;
622 if ($marcflavour eq 'UNIMARC' && $record_type eq 'auth') {
623 $marc_type = 'UNIMARCAUTH';
624 } elsif ($marcflavour eq 'UNIMARC') {
625 $marc_type = 'UNIMARC';
626 } else {
627 $marc_type = 'USMARC';
629 my $marc_record = MARC::Record->new_from_usmarc($rowref->{'marc'});
631 if ($record_type eq 'biblio') {
632 # remove any item tags - rely on BatchCommitItems
633 ($item_tag,$item_subfield) = &GetMarcFromKohaField("items.itemnumber",'');
634 foreach my $item_field ($marc_record->field($item_tag)) {
635 $marc_record->delete_field($item_field);
639 my ($record_result, $item_result, $record_match) =
640 _get_commit_action($overlay_action, $nomatch_action, $item_action,
641 $rowref->{'overlay_status'}, $rowref->{'import_record_id'}, $record_type);
643 my $recordid;
644 my $query;
645 if ($record_result eq 'create_new') {
646 $num_added++;
647 if ($record_type eq 'biblio') {
648 my $biblioitemnumber;
649 ($recordid, $biblioitemnumber) = AddBiblio($marc_record, $framework);
650 $query = "UPDATE import_biblios SET matched_biblionumber = ? WHERE import_record_id = ?";
651 if ($item_result eq 'create_new' || $item_result eq 'replace') {
652 my ($bib_items_added, $bib_items_replaced, $bib_items_errored) = BatchCommitItems($rowref->{'import_record_id'}, $recordid, $item_result);
653 $num_items_added += $bib_items_added;
654 $num_items_replaced += $bib_items_replaced;
655 $num_items_errored += $bib_items_errored;
657 } else {
658 $recordid = AddAuthority($marc_record, undef, GuessAuthTypeCode($marc_record));
659 $query = "UPDATE import_auths SET matched_authid = ? WHERE import_record_id = ?";
661 my $sth = $dbh->prepare_cached($query);
662 $sth->execute($recordid, $rowref->{'import_record_id'});
663 $sth->finish();
664 SetImportRecordStatus($rowref->{'import_record_id'}, 'imported');
665 } elsif ($record_result eq 'replace') {
666 $num_updated++;
667 $recordid = $record_match;
668 my $oldxml;
669 if ($record_type eq 'biblio') {
670 my $oldbiblio = GetBiblio($recordid);
671 $oldxml = GetXmlBiblio($recordid);
673 # remove item fields so that they don't get
674 # added again if record is reverted
675 # 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.
676 my $old_marc = MARC::Record->new_from_xml(StripNonXmlChars($oldxml), 'UTF-8', $rowref->{'encoding'}, $marc_type);
677 foreach my $item_field ($old_marc->field($item_tag)) {
678 $old_marc->delete_field($item_field);
680 $oldxml = $old_marc->as_xml($marc_type);
682 ModBiblio($marc_record, $recordid, $oldbiblio->{'frameworkcode'});
683 $query = "UPDATE import_biblios SET matched_biblionumber = ? WHERE import_record_id = ?";
685 if ($item_result eq 'create_new' || $item_result eq 'replace') {
686 my ($bib_items_added, $bib_items_replaced, $bib_items_errored) = BatchCommitItems($rowref->{'import_record_id'}, $recordid, $item_result);
687 $num_items_added += $bib_items_added;
688 $num_items_replaced += $bib_items_replaced;
689 $num_items_errored += $bib_items_errored;
691 } else {
692 $oldxml = GetAuthorityXML($recordid);
694 ModAuthority($recordid, $marc_record, GuessAuthTypeCode($marc_record));
695 $query = "UPDATE import_auths SET matched_authid = ? WHERE import_record_id = ?";
697 my $sth = $dbh->prepare_cached("UPDATE import_records SET marcxml_old = ? WHERE import_record_id = ?");
698 $sth->execute($oldxml, $rowref->{'import_record_id'});
699 $sth->finish();
700 my $sth2 = $dbh->prepare_cached($query);
701 $sth2->execute($recordid, $rowref->{'import_record_id'});
702 $sth2->finish();
703 SetImportRecordOverlayStatus($rowref->{'import_record_id'}, 'match_applied');
704 SetImportRecordStatus($rowref->{'import_record_id'}, 'imported');
705 } elsif ($record_result eq 'ignore') {
706 $recordid = $record_match;
707 $num_ignored++;
708 $recordid = $record_match;
709 if ($record_type eq 'biblio' and defined $recordid and ( $item_result eq 'create_new' || $item_result eq 'replace' ) ) {
710 my ($bib_items_added, $bib_items_replaced, $bib_items_errored) = BatchCommitItems($rowref->{'import_record_id'}, $recordid, $item_result);
711 $num_items_added += $bib_items_added;
712 $num_items_replaced += $bib_items_replaced;
713 $num_items_errored += $bib_items_errored;
714 # still need to record the matched biblionumber so that the
715 # items can be reverted
716 my $sth2 = $dbh->prepare_cached("UPDATE import_biblios SET matched_biblionumber = ? WHERE import_record_id = ?");
717 $sth2->execute($recordid, $rowref->{'import_record_id'});
718 SetImportRecordOverlayStatus($rowref->{'import_record_id'}, 'match_applied');
720 SetImportRecordStatus($rowref->{'import_record_id'}, 'ignored');
723 $sth->finish();
724 SetImportBatchStatus($batch_id, 'imported');
725 return ($num_added, $num_updated, $num_items_added, $num_items_replaced, $num_items_errored, $num_ignored);
728 =head2 BatchCommitItems
730 ($num_items_added, $num_items_errored) =
731 BatchCommitItems($import_record_id, $biblionumber);
733 =cut
735 sub BatchCommitItems {
736 my ( $import_record_id, $biblionumber, $action ) = @_;
738 my $dbh = C4::Context->dbh;
740 my $num_items_added = 0;
741 my $num_items_errored = 0;
742 my $num_items_replaced = 0;
744 my $sth = $dbh->prepare( "
745 SELECT import_items_id, import_items.marcxml, encoding
746 FROM import_items
747 JOIN import_records USING (import_record_id)
748 WHERE import_record_id = ?
749 ORDER BY import_items_id
750 " );
751 $sth->bind_param( 1, $import_record_id );
752 $sth->execute();
754 while ( my $row = $sth->fetchrow_hashref() ) {
755 my $item_marc = MARC::Record->new_from_xml( StripNonXmlChars( $row->{'marcxml'} ), 'UTF-8', $row->{'encoding'} );
757 # Delete date_due subfield as to not accidentally delete item checkout due dates
758 my ( $MARCfield, $MARCsubfield ) = GetMarcFromKohaField( 'items.onloan', GetFrameworkCode($biblionumber) );
759 $item_marc->field($MARCfield)->delete_subfield( code => $MARCsubfield );
761 my $item = TransformMarcToKoha( $dbh, $item_marc );
763 my $duplicate_barcode = exists( $item->{'barcode'} ) && GetItemnumberFromBarcode( $item->{'barcode'} );
764 my $duplicate_itemnumber = exists( $item->{'itemnumber'} );
766 my $updsth = $dbh->prepare("UPDATE import_items SET status = ?, itemnumber = ? WHERE import_items_id = ?");
767 if ( $action eq "replace" && $duplicate_itemnumber ) {
768 # Duplicate itemnumbers have precedence, that way we can update barcodes by overlaying
769 ModItemFromMarc( $item_marc, $biblionumber, $item->{itemnumber} );
770 $updsth->bind_param( 1, 'imported' );
771 $updsth->bind_param( 2, $item->{itemnumber} );
772 $updsth->bind_param( 3, $row->{'import_items_id'} );
773 $updsth->execute();
774 $updsth->finish();
775 $num_items_replaced++;
776 } elsif ( $action eq "replace" && $duplicate_barcode ) {
777 my $itemnumber = GetItemnumberFromBarcode( $item->{'barcode'} );
778 ModItemFromMarc( $item_marc, $biblionumber, $itemnumber );
779 $updsth->bind_param( 1, 'imported' );
780 $updsth->bind_param( 2, $item->{itemnumber} );
781 $updsth->bind_param( 3, $row->{'import_items_id'} );
782 $updsth->execute();
783 $updsth->finish();
784 $num_items_replaced++;
785 } elsif ($duplicate_barcode) {
786 $updsth->bind_param( 1, 'error' );
787 $updsth->bind_param( 2, 'duplicate item barcode' );
788 $updsth->bind_param( 3, $row->{'import_items_id'} );
789 $updsth->execute();
790 $num_items_errored++;
791 } else {
792 my ( $item_biblionumber, $biblioitemnumber, $itemnumber ) = AddItemFromMarc( $item_marc, $biblionumber );
793 $updsth->bind_param( 1, 'imported' );
794 $updsth->bind_param( 2, $itemnumber );
795 $updsth->bind_param( 3, $row->{'import_items_id'} );
796 $updsth->execute();
797 $updsth->finish();
798 $num_items_added++;
802 return ( $num_items_added, $num_items_replaced, $num_items_errored );
805 =head2 BatchRevertRecords
807 my ($num_deleted, $num_errors, $num_reverted, $num_items_deleted,
808 $num_ignored) = BatchRevertRecords($batch_id);
810 =cut
812 sub BatchRevertRecords {
813 my $batch_id = shift;
815 my $record_type;
816 my $num_deleted = 0;
817 my $num_errors = 0;
818 my $num_reverted = 0;
819 my $num_ignored = 0;
820 my $num_items_deleted = 0;
821 # commit (i.e., save, all records in the batch)
822 SetImportBatchStatus('reverting');
823 my $overlay_action = GetImportBatchOverlayAction($batch_id);
824 my $nomatch_action = GetImportBatchNoMatchAction($batch_id);
825 my $dbh = C4::Context->dbh;
826 my $sth = $dbh->prepare("SELECT import_records.import_record_id, record_type, status, overlay_status, marcxml_old, encoding, matched_biblionumber, matched_authid
827 FROM import_records
828 LEFT JOIN import_auths ON (import_records.import_record_id=import_auths.import_record_id)
829 LEFT JOIN import_biblios ON (import_records.import_record_id=import_biblios.import_record_id)
830 WHERE import_batch_id = ?");
831 $sth->execute($batch_id);
832 my $marc_type;
833 my $marcflavour = C4::Context->preference('marcflavour');
834 while (my $rowref = $sth->fetchrow_hashref) {
835 $record_type = $rowref->{'record_type'};
836 if ($rowref->{'status'} eq 'error' or $rowref->{'status'} eq 'reverted') {
837 $num_ignored++;
838 next;
840 if ($marcflavour eq 'UNIMARC' && $record_type eq 'auth') {
841 $marc_type = 'UNIMARCAUTH';
842 } elsif ($marcflavour eq 'UNIMARC') {
843 $marc_type = 'UNIMARC';
844 } else {
845 $marc_type = 'USMARC';
848 my $record_result = _get_revert_action($overlay_action, $rowref->{'overlay_status'}, $rowref->{'status'});
850 if ($record_result eq 'delete') {
851 my $error = undef;
852 if ($record_type eq 'biblio') {
853 $num_items_deleted += BatchRevertItems($rowref->{'import_record_id'}, $rowref->{'matched_biblionumber'});
854 $error = DelBiblio($rowref->{'matched_biblionumber'});
855 } else {
856 my $deletedauthid = DelAuthority($rowref->{'matched_authid'});
858 if (defined $error) {
859 $num_errors++;
860 } else {
861 $num_deleted++;
862 SetImportRecordStatus($rowref->{'import_record_id'}, 'reverted');
864 } elsif ($record_result eq 'restore') {
865 $num_reverted++;
866 my $old_record = MARC::Record->new_from_xml(StripNonXmlChars($rowref->{'marcxml_old'}), 'UTF-8', $rowref->{'encoding'}, $marc_type);
867 if ($record_type eq 'biblio') {
868 my $biblionumber = $rowref->{'matched_biblionumber'};
869 my $oldbiblio = GetBiblio($biblionumber);
870 $num_items_deleted += BatchRevertItems($rowref->{'import_record_id'}, $rowref->{'matched_biblionumber'});
871 ModBiblio($old_record, $biblionumber, $oldbiblio->{'frameworkcode'});
872 } else {
873 my $authid = $rowref->{'matched_authid'};
874 ModAuthority($authid, $old_record, GuessAuthTypeCode($old_record));
876 SetImportRecordStatus($rowref->{'import_record_id'}, 'reverted');
877 } elsif ($record_result eq 'ignore') {
878 if ($record_type eq 'biblio') {
879 $num_items_deleted += BatchRevertItems($rowref->{'import_record_id'}, $rowref->{'matched_biblionumber'});
881 SetImportRecordStatus($rowref->{'import_record_id'}, 'reverted');
883 my $query;
884 if ($record_type eq 'biblio') {
885 # remove matched_biblionumber only if there is no 'imported' item left
886 $query = "UPDATE import_biblios SET matched_biblionumber = NULL WHERE import_record_id = ?";
887 $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 } else {
889 $query = "UPDATE import_auths SET matched_authid = NULL WHERE import_record_id = ?";
891 my $sth2 = $dbh->prepare_cached($query);
892 $sth2->execute($rowref->{'import_record_id'});
895 $sth->finish();
896 SetImportBatchStatus($batch_id, 'reverted');
897 return ($num_deleted, $num_errors, $num_reverted, $num_items_deleted, $num_ignored);
900 =head2 BatchRevertItems
902 my $num_items_deleted = BatchRevertItems($import_record_id, $biblionumber);
904 =cut
906 sub BatchRevertItems {
907 my ($import_record_id, $biblionumber) = @_;
909 my $dbh = C4::Context->dbh;
910 my $num_items_deleted = 0;
912 my $sth = $dbh->prepare_cached("SELECT import_items_id, itemnumber
913 FROM import_items
914 JOIN items USING (itemnumber)
915 WHERE import_record_id = ?");
916 $sth->bind_param(1, $import_record_id);
917 $sth->execute();
918 while (my $row = $sth->fetchrow_hashref()) {
919 my $error = DelItemCheck($dbh, $biblionumber, $row->{'itemnumber'});
920 if ($error == 1){
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'});
924 $updsth->execute();
925 $updsth->finish();
926 $num_items_deleted++;
928 else {
929 next;
932 $sth->finish();
933 return $num_items_deleted;
936 =head2 CleanBatch
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.
945 =cut
947 sub CleanBatch {
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');
955 =head2 GetAllImportBatches
957 my $results = GetAllImportBatches();
959 Returns a references to an array of hash references corresponding
960 to all import_batches rows (of batch_type 'batch'), sorted in
961 ascending order by import_batch_id.
963 =cut
965 sub GetAllImportBatches {
966 my $dbh = C4::Context->dbh;
967 my $sth = $dbh->prepare_cached("SELECT * FROM import_batches
968 WHERE batch_type IN ('batch', 'webservice')
969 ORDER BY import_batch_id ASC");
971 my $results = [];
972 $sth->execute();
973 while (my $row = $sth->fetchrow_hashref) {
974 push @$results, $row;
976 $sth->finish();
977 return $results;
980 =head2 GetStagedWebserviceBatches
982 my $batch_ids = GetStagedWebserviceBatches();
984 Returns a references to an array of batch id's
985 of batch_type 'webservice' that are not imported
987 =cut
989 my $PENDING_WEBSERVICE_BATCHES_QRY = <<EOQ;
990 SELECT import_batch_id FROM import_batches
991 WHERE batch_type = 'webservice'
992 AND import_status = 'staged'
994 sub GetStagedWebserviceBatches {
995 my $dbh = C4::Context->dbh;
996 return $dbh->selectcol_arrayref($PENDING_WEBSERVICE_BATCHES_QRY);
999 =head2 GetImportBatchRangeDesc
1001 my $results = GetImportBatchRangeDesc($offset, $results_per_group);
1003 Returns a reference to an array of hash references corresponding to
1004 import_batches rows (sorted in descending order by import_batch_id)
1005 start at the given offset.
1007 =cut
1009 sub GetImportBatchRangeDesc {
1010 my ($offset, $results_per_group) = @_;
1012 my $dbh = C4::Context->dbh;
1013 my $query = "SELECT * FROM import_batches
1014 WHERE batch_type IN ('batch', 'webservice')
1015 ORDER BY import_batch_id DESC";
1016 my @params;
1017 if ($results_per_group){
1018 $query .= " LIMIT ?";
1019 push(@params, $results_per_group);
1021 if ($offset){
1022 $query .= " OFFSET ?";
1023 push(@params, $offset);
1025 my $sth = $dbh->prepare_cached($query);
1026 $sth->execute(@params);
1027 my $results = $sth->fetchall_arrayref({});
1028 $sth->finish();
1029 return $results;
1032 =head2 GetItemNumbersFromImportBatch
1034 my @itemsnos = GetItemNumbersFromImportBatch($batch_id);
1036 =cut
1038 sub GetItemNumbersFromImportBatch {
1039 my ($batch_id) = @_;
1040 my $dbh = C4::Context->dbh;
1041 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=?");
1042 $sth->execute($batch_id);
1043 my @items ;
1044 while ( my ($itm) = $sth->fetchrow_array ) {
1045 push @items, $itm;
1047 return @items;
1050 =head2 GetNumberOfImportBatches
1052 my $count = GetNumberOfImportBatches();
1054 =cut
1056 sub GetNumberOfNonZ3950ImportBatches {
1057 my $dbh = C4::Context->dbh;
1058 my $sth = $dbh->prepare("SELECT COUNT(*) FROM import_batches WHERE batch_type != 'z3950'");
1059 $sth->execute();
1060 my ($count) = $sth->fetchrow_array();
1061 $sth->finish();
1062 return $count;
1065 =head2 GetImportBiblios
1067 my $results = GetImportBiblios($importid);
1069 =cut
1071 sub GetImportBiblios {
1072 my ($import_record_id) = @_;
1074 my $dbh = C4::Context->dbh;
1075 my $query = "SELECT * FROM import_biblios WHERE import_record_id = ?";
1076 return $dbh->selectall_arrayref(
1077 $query,
1078 { Slice => {} },
1079 $import_record_id
1084 =head2 GetImportRecordsRange
1086 my $results = GetImportRecordsRange($batch_id, $offset, $results_per_group);
1088 Returns a reference to an array of hash references corresponding to
1089 import_biblios/import_auths/import_records rows for a given batch
1090 starting at the given offset.
1092 =cut
1094 sub GetImportRecordsRange {
1095 my ( $batch_id, $offset, $results_per_group, $status, $parameters ) = @_;
1097 my $dbh = C4::Context->dbh;
1099 my $order_by = $parameters->{order_by} || 'import_record_id';
1100 ( $order_by ) = grep( /^$order_by$/, qw( import_record_id title status overlay_status ) ) ? $order_by : 'import_record_id';
1102 my $order_by_direction =
1103 uc( $parameters->{order_by_direction} ) eq 'DESC' ? 'DESC' : 'ASC';
1105 $order_by .= " $order_by_direction, authorized_heading" if $order_by eq 'title';
1107 my $query = "SELECT title, author, isbn, issn, authorized_heading, import_records.import_record_id,
1108 record_sequence, status, overlay_status,
1109 matched_biblionumber, matched_authid, record_type
1110 FROM import_records
1111 LEFT JOIN import_auths ON (import_records.import_record_id=import_auths.import_record_id)
1112 LEFT JOIN import_biblios ON (import_records.import_record_id=import_biblios.import_record_id)
1113 WHERE import_batch_id = ?";
1114 my @params;
1115 push(@params, $batch_id);
1116 if ($status) {
1117 $query .= " AND status=?";
1118 push(@params,$status);
1121 $query.=" ORDER BY $order_by $order_by_direction";
1123 if($results_per_group){
1124 $query .= " LIMIT ?";
1125 push(@params, $results_per_group);
1127 if($offset){
1128 $query .= " OFFSET ?";
1129 push(@params, $offset);
1131 my $sth = $dbh->prepare_cached($query);
1132 $sth->execute(@params);
1133 my $results = $sth->fetchall_arrayref({});
1134 $sth->finish();
1135 return $results;
1139 =head2 GetBestRecordMatch
1141 my $record_id = GetBestRecordMatch($import_record_id);
1143 =cut
1145 sub GetBestRecordMatch {
1146 my ($import_record_id) = @_;
1148 my $dbh = C4::Context->dbh;
1149 my $sth = $dbh->prepare("SELECT candidate_match_id
1150 FROM import_record_matches
1151 JOIN import_records ON ( import_record_matches.import_record_id = import_records.import_record_id )
1152 LEFT JOIN biblio ON ( candidate_match_id = biblio.biblionumber )
1153 LEFT JOIN auth_header ON ( candidate_match_id = auth_header.authid )
1154 WHERE import_record_matches.import_record_id = ? AND
1155 ( (import_records.record_type = 'biblio' AND biblio.biblionumber IS NOT NULL) OR
1156 (import_records.record_type = 'auth' AND auth_header.authid IS NOT NULL) )
1157 ORDER BY score DESC, candidate_match_id DESC");
1158 $sth->execute($import_record_id);
1159 my ($record_id) = $sth->fetchrow_array();
1160 $sth->finish();
1161 return $record_id;
1164 =head2 GetImportBatchStatus
1166 my $status = GetImportBatchStatus($batch_id);
1168 =cut
1170 sub GetImportBatchStatus {
1171 my ($batch_id) = @_;
1173 my $dbh = C4::Context->dbh;
1174 my $sth = $dbh->prepare("SELECT import_status FROM import_batches WHERE import_batch_id = ?");
1175 $sth->execute($batch_id);
1176 my ($status) = $sth->fetchrow_array();
1177 $sth->finish();
1178 return $status;
1182 =head2 SetImportBatchStatus
1184 SetImportBatchStatus($batch_id, $new_status);
1186 =cut
1188 sub SetImportBatchStatus {
1189 my ($batch_id, $new_status) = @_;
1191 my $dbh = C4::Context->dbh;
1192 my $sth = $dbh->prepare("UPDATE import_batches SET import_status = ? WHERE import_batch_id = ?");
1193 $sth->execute($new_status, $batch_id);
1194 $sth->finish();
1198 =head2 GetImportBatchOverlayAction
1200 my $overlay_action = GetImportBatchOverlayAction($batch_id);
1202 =cut
1204 sub GetImportBatchOverlayAction {
1205 my ($batch_id) = @_;
1207 my $dbh = C4::Context->dbh;
1208 my $sth = $dbh->prepare("SELECT overlay_action FROM import_batches WHERE import_batch_id = ?");
1209 $sth->execute($batch_id);
1210 my ($overlay_action) = $sth->fetchrow_array();
1211 $sth->finish();
1212 return $overlay_action;
1217 =head2 SetImportBatchOverlayAction
1219 SetImportBatchOverlayAction($batch_id, $new_overlay_action);
1221 =cut
1223 sub SetImportBatchOverlayAction {
1224 my ($batch_id, $new_overlay_action) = @_;
1226 my $dbh = C4::Context->dbh;
1227 my $sth = $dbh->prepare("UPDATE import_batches SET overlay_action = ? WHERE import_batch_id = ?");
1228 $sth->execute($new_overlay_action, $batch_id);
1229 $sth->finish();
1233 =head2 GetImportBatchNoMatchAction
1235 my $nomatch_action = GetImportBatchNoMatchAction($batch_id);
1237 =cut
1239 sub GetImportBatchNoMatchAction {
1240 my ($batch_id) = @_;
1242 my $dbh = C4::Context->dbh;
1243 my $sth = $dbh->prepare("SELECT nomatch_action FROM import_batches WHERE import_batch_id = ?");
1244 $sth->execute($batch_id);
1245 my ($nomatch_action) = $sth->fetchrow_array();
1246 $sth->finish();
1247 return $nomatch_action;
1252 =head2 SetImportBatchNoMatchAction
1254 SetImportBatchNoMatchAction($batch_id, $new_nomatch_action);
1256 =cut
1258 sub SetImportBatchNoMatchAction {
1259 my ($batch_id, $new_nomatch_action) = @_;
1261 my $dbh = C4::Context->dbh;
1262 my $sth = $dbh->prepare("UPDATE import_batches SET nomatch_action = ? WHERE import_batch_id = ?");
1263 $sth->execute($new_nomatch_action, $batch_id);
1264 $sth->finish();
1268 =head2 GetImportBatchItemAction
1270 my $item_action = GetImportBatchItemAction($batch_id);
1272 =cut
1274 sub GetImportBatchItemAction {
1275 my ($batch_id) = @_;
1277 my $dbh = C4::Context->dbh;
1278 my $sth = $dbh->prepare("SELECT item_action FROM import_batches WHERE import_batch_id = ?");
1279 $sth->execute($batch_id);
1280 my ($item_action) = $sth->fetchrow_array();
1281 $sth->finish();
1282 return $item_action;
1287 =head2 SetImportBatchItemAction
1289 SetImportBatchItemAction($batch_id, $new_item_action);
1291 =cut
1293 sub SetImportBatchItemAction {
1294 my ($batch_id, $new_item_action) = @_;
1296 my $dbh = C4::Context->dbh;
1297 my $sth = $dbh->prepare("UPDATE import_batches SET item_action = ? WHERE import_batch_id = ?");
1298 $sth->execute($new_item_action, $batch_id);
1299 $sth->finish();
1303 =head2 GetImportBatchMatcher
1305 my $matcher_id = GetImportBatchMatcher($batch_id);
1307 =cut
1309 sub GetImportBatchMatcher {
1310 my ($batch_id) = @_;
1312 my $dbh = C4::Context->dbh;
1313 my $sth = $dbh->prepare("SELECT matcher_id FROM import_batches WHERE import_batch_id = ?");
1314 $sth->execute($batch_id);
1315 my ($matcher_id) = $sth->fetchrow_array();
1316 $sth->finish();
1317 return $matcher_id;
1322 =head2 SetImportBatchMatcher
1324 SetImportBatchMatcher($batch_id, $new_matcher_id);
1326 =cut
1328 sub SetImportBatchMatcher {
1329 my ($batch_id, $new_matcher_id) = @_;
1331 my $dbh = C4::Context->dbh;
1332 my $sth = $dbh->prepare("UPDATE import_batches SET matcher_id = ? WHERE import_batch_id = ?");
1333 $sth->execute($new_matcher_id, $batch_id);
1334 $sth->finish();
1338 =head2 GetImportRecordOverlayStatus
1340 my $overlay_status = GetImportRecordOverlayStatus($import_record_id);
1342 =cut
1344 sub GetImportRecordOverlayStatus {
1345 my ($import_record_id) = @_;
1347 my $dbh = C4::Context->dbh;
1348 my $sth = $dbh->prepare("SELECT overlay_status FROM import_records WHERE import_record_id = ?");
1349 $sth->execute($import_record_id);
1350 my ($overlay_status) = $sth->fetchrow_array();
1351 $sth->finish();
1352 return $overlay_status;
1357 =head2 SetImportRecordOverlayStatus
1359 SetImportRecordOverlayStatus($import_record_id, $new_overlay_status);
1361 =cut
1363 sub SetImportRecordOverlayStatus {
1364 my ($import_record_id, $new_overlay_status) = @_;
1366 my $dbh = C4::Context->dbh;
1367 my $sth = $dbh->prepare("UPDATE import_records SET overlay_status = ? WHERE import_record_id = ?");
1368 $sth->execute($new_overlay_status, $import_record_id);
1369 $sth->finish();
1373 =head2 GetImportRecordStatus
1375 my $status = GetImportRecordStatus($import_record_id);
1377 =cut
1379 sub GetImportRecordStatus {
1380 my ($import_record_id) = @_;
1382 my $dbh = C4::Context->dbh;
1383 my $sth = $dbh->prepare("SELECT status FROM import_records WHERE import_record_id = ?");
1384 $sth->execute($import_record_id);
1385 my ($status) = $sth->fetchrow_array();
1386 $sth->finish();
1387 return $status;
1392 =head2 SetImportRecordStatus
1394 SetImportRecordStatus($import_record_id, $new_status);
1396 =cut
1398 sub SetImportRecordStatus {
1399 my ($import_record_id, $new_status) = @_;
1401 my $dbh = C4::Context->dbh;
1402 my $sth = $dbh->prepare("UPDATE import_records SET status = ? WHERE import_record_id = ?");
1403 $sth->execute($new_status, $import_record_id);
1404 $sth->finish();
1408 =head2 GetImportRecordMatches
1410 my $results = GetImportRecordMatches($import_record_id, $best_only);
1412 =cut
1414 sub GetImportRecordMatches {
1415 my $import_record_id = shift;
1416 my $best_only = @_ ? shift : 0;
1418 my $dbh = C4::Context->dbh;
1419 # FIXME currently biblio only
1420 my $sth = $dbh->prepare_cached("SELECT title, author, biblionumber,
1421 candidate_match_id, score, record_type
1422 FROM import_records
1423 JOIN import_record_matches USING (import_record_id)
1424 LEFT JOIN biblio ON (biblionumber = candidate_match_id)
1425 WHERE import_record_id = ?
1426 ORDER BY score DESC, biblionumber DESC");
1427 $sth->bind_param(1, $import_record_id);
1428 my $results = [];
1429 $sth->execute();
1430 while (my $row = $sth->fetchrow_hashref) {
1431 if ($row->{'record_type'} eq 'auth') {
1432 $row->{'authorized_heading'} = C4::AuthoritiesMarc::GetAuthorizedHeading( { authid => $row->{'candidate_match_id'} } );
1434 next if ($row->{'record_type'} eq 'biblio' && not $row->{'biblionumber'});
1435 push @$results, $row;
1436 last if $best_only;
1438 $sth->finish();
1440 return $results;
1445 =head2 SetImportRecordMatches
1447 SetImportRecordMatches($import_record_id, @matches);
1449 =cut
1451 sub SetImportRecordMatches {
1452 my $import_record_id = shift;
1453 my @matches = @_;
1455 my $dbh = C4::Context->dbh;
1456 my $delsth = $dbh->prepare("DELETE FROM import_record_matches WHERE import_record_id = ?");
1457 $delsth->execute($import_record_id);
1458 $delsth->finish();
1460 my $sth = $dbh->prepare("INSERT INTO import_record_matches (import_record_id, candidate_match_id, score)
1461 VALUES (?, ?, ?)");
1462 foreach my $match (@matches) {
1463 $sth->execute($import_record_id, $match->{'record_id'}, $match->{'score'});
1468 # internal functions
1470 sub _create_import_record {
1471 my ($batch_id, $record_sequence, $marc_record, $record_type, $encoding, $z3950random, $marc_type) = @_;
1473 my $dbh = C4::Context->dbh;
1474 my $sth = $dbh->prepare("INSERT INTO import_records (import_batch_id, record_sequence, marc, marcxml,
1475 record_type, encoding, z3950random)
1476 VALUES (?, ?, ?, ?, ?, ?, ?)");
1477 $sth->execute($batch_id, $record_sequence, $marc_record->as_usmarc(), $marc_record->as_xml($marc_type),
1478 $record_type, $encoding, $z3950random);
1479 my $import_record_id = $dbh->{'mysql_insertid'};
1480 $sth->finish();
1481 return $import_record_id;
1484 sub _update_import_record_marc {
1485 my ($import_record_id, $marc_record, $marc_type) = @_;
1487 my $dbh = C4::Context->dbh;
1488 my $sth = $dbh->prepare("UPDATE import_records SET marc = ?, marcxml = ?
1489 WHERE import_record_id = ?");
1490 $sth->execute($marc_record->as_usmarc(), $marc_record->as_xml($marc_type), $import_record_id);
1491 $sth->finish();
1494 sub _add_auth_fields {
1495 my ($import_record_id, $marc_record) = @_;
1497 my $controlnumber;
1498 if ($marc_record->field('001')) {
1499 $controlnumber = $marc_record->field('001')->data();
1501 my $authorized_heading = C4::AuthoritiesMarc::GetAuthorizedHeading({ record => $marc_record });
1502 my $dbh = C4::Context->dbh;
1503 my $sth = $dbh->prepare("INSERT INTO import_auths (import_record_id, control_number, authorized_heading) VALUES (?, ?, ?)");
1504 $sth->execute($import_record_id, $controlnumber, $authorized_heading);
1505 $sth->finish();
1508 sub _add_biblio_fields {
1509 my ($import_record_id, $marc_record) = @_;
1511 my ($title, $author, $isbn, $issn) = _parse_biblio_fields($marc_record);
1512 my $dbh = C4::Context->dbh;
1513 # FIXME no controlnumber, originalsource
1514 $isbn = C4::Koha::GetNormalizedISBN($isbn);
1515 my $sth = $dbh->prepare("INSERT INTO import_biblios (import_record_id, title, author, isbn, issn) VALUES (?, ?, ?, ?, ?)");
1516 $sth->execute($import_record_id, $title, $author, $isbn, $issn);
1517 $sth->finish();
1521 sub _update_biblio_fields {
1522 my ($import_record_id, $marc_record) = @_;
1524 my ($title, $author, $isbn, $issn) = _parse_biblio_fields($marc_record);
1525 my $dbh = C4::Context->dbh;
1526 # FIXME no controlnumber, originalsource
1527 # FIXME 2 - should regularize normalization of ISBN wherever it is done
1528 $isbn =~ s/\(.*$//;
1529 $isbn =~ tr/ -_//;
1530 $isbn = uc $isbn;
1531 my $sth = $dbh->prepare("UPDATE import_biblios SET title = ?, author = ?, isbn = ?, issn = ?
1532 WHERE import_record_id = ?");
1533 $sth->execute($title, $author, $isbn, $issn, $import_record_id);
1534 $sth->finish();
1537 sub _parse_biblio_fields {
1538 my ($marc_record) = @_;
1540 my $dbh = C4::Context->dbh;
1541 my $bibliofields = TransformMarcToKoha($dbh, $marc_record, '');
1542 return ($bibliofields->{'title'}, $bibliofields->{'author'}, $bibliofields->{'isbn'}, $bibliofields->{'issn'});
1546 sub _update_batch_record_counts {
1547 my ($batch_id) = @_;
1549 my $dbh = C4::Context->dbh;
1550 my $sth = $dbh->prepare_cached("UPDATE import_batches SET
1551 num_records = (
1552 SELECT COUNT(*)
1553 FROM import_records
1554 WHERE import_batch_id = import_batches.import_batch_id),
1555 num_items = (
1556 SELECT COUNT(*)
1557 FROM import_records
1558 JOIN import_items USING (import_record_id)
1559 WHERE import_batch_id = import_batches.import_batch_id
1560 AND record_type = 'biblio')
1561 WHERE import_batch_id = ?");
1562 $sth->bind_param(1, $batch_id);
1563 $sth->execute();
1564 $sth->finish();
1567 sub _get_commit_action {
1568 my ($overlay_action, $nomatch_action, $item_action, $overlay_status, $import_record_id, $record_type) = @_;
1570 if ($record_type eq 'biblio') {
1571 my ($bib_result, $bib_match, $item_result);
1573 if ($overlay_status ne 'no_match') {
1574 $bib_match = GetBestRecordMatch($import_record_id);
1575 if ($overlay_action eq 'replace') {
1576 $bib_result = defined($bib_match) ? 'replace' : 'create_new';
1577 } elsif ($overlay_action eq 'create_new') {
1578 $bib_result = 'create_new';
1579 } elsif ($overlay_action eq 'ignore') {
1580 $bib_result = 'ignore';
1582 if($item_action eq 'always_add' or $item_action eq 'add_only_for_matches'){
1583 $item_result = 'create_new';
1585 elsif($item_action eq 'replace'){
1586 $item_result = 'replace';
1588 else {
1589 $item_result = 'ignore';
1591 } else {
1592 $bib_result = $nomatch_action;
1593 $item_result = ($item_action eq 'always_add' or $item_action eq 'add_only_for_new') ? 'create_new' : 'ignore';
1595 return ($bib_result, $item_result, $bib_match);
1596 } else { # must be auths
1597 my ($auth_result, $auth_match);
1599 if ($overlay_status ne 'no_match') {
1600 $auth_match = GetBestRecordMatch($import_record_id);
1601 if ($overlay_action eq 'replace') {
1602 $auth_result = defined($auth_match) ? 'replace' : 'create_new';
1603 } elsif ($overlay_action eq 'create_new') {
1604 $auth_result = 'create_new';
1605 } elsif ($overlay_action eq 'ignore') {
1606 $auth_result = 'ignore';
1608 } else {
1609 $auth_result = $nomatch_action;
1612 return ($auth_result, undef, $auth_match);
1617 sub _get_revert_action {
1618 my ($overlay_action, $overlay_status, $status) = @_;
1620 my $bib_result;
1622 if ($status eq 'ignored') {
1623 $bib_result = 'ignore';
1624 } else {
1625 if ($overlay_action eq 'create_new') {
1626 $bib_result = 'delete';
1627 } else {
1628 $bib_result = ($overlay_status eq 'match_applied') ? 'restore' : 'delete';
1631 return $bib_result;
1635 __END__
1637 =head1 AUTHOR
1639 Koha Development Team <http://koha-community.org/>
1641 Galen Charlton <galen.charlton@liblime.com>
1643 =cut