Merge branch 'bug_9839' into 3.14-master
[koha.git] / C4 / ImportBatch.pm
1 package C4::ImportBatch;
2
3 # Copyright (C) 2007 LibLime, 2012 C & P Bibliography Services
4 #
5 # This file is part of Koha.
6 #
7 # Koha is free software; you can redistribute it and/or modify it under the
8 # terms of the GNU General Public License as published by the Free Software
9 # Foundation; either version 2 of the License, or (at your option) any later
10 # version.
11 #
12 # Koha is distributed in the hope that it will be useful, but WITHOUT ANY
13 # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
14 # A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
15 #
16 # You should have received a copy of the GNU General Public License along
17 # with Koha; if not, write to the Free Software Foundation, Inc.,
18 # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19
20 use strict;
21 use warnings;
22
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
30 use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);
31
32 BEGIN {
33         # set the version for version checking
34     $VERSION = 3.07.00.049;
35         require Exporter;
36         @ISA    = qw(Exporter);
37         @EXPORT = qw(
38     GetZ3950BatchId
39     GetWebserviceBatchId
40     GetImportRecordMarc
41     GetImportRecordMarcXML
42     AddImportBatch
43     GetImportBatch
44     AddAuthToBatch
45     AddBiblioToBatch
46     AddItemsToImportBiblio
47     ModAuthorityInBatch
48     ModBiblioInBatch
49
50     BatchStageMarcRecords
51     BatchFindDuplicates
52     BatchCommitRecords
53     BatchRevertRecords
54     CleanBatch
55
56     GetAllImportBatches
57     GetStagedWebserviceBatches
58     GetImportBatchRangeDesc
59     GetNumberOfNonZ3950ImportBatches
60     GetImportRecordsRange
61         GetItemNumbersFromImportBatch
62     
63     GetImportBatchStatus
64     SetImportBatchStatus
65     GetImportBatchOverlayAction
66     SetImportBatchOverlayAction
67     GetImportBatchNoMatchAction
68     SetImportBatchNoMatchAction
69     GetImportBatchItemAction
70     SetImportBatchItemAction
71     GetImportBatchMatcher
72     SetImportBatchMatcher
73     GetImportRecordOverlayStatus
74     SetImportRecordOverlayStatus
75     GetImportRecordStatus
76     SetImportRecordStatus
77     GetImportRecordMatches
78     SetImportRecordMatches
79         );
80 }
81
82 =head1 NAME
83
84 C4::ImportBatch - manage batches of imported MARC records
85
86 =head1 SYNOPSIS
87
88 use C4::ImportBatch;
89
90 =head1 FUNCTIONS
91
92 =head2 GetZ3950BatchId
93
94   my $batchid = GetZ3950BatchId($z3950server);
95
96 Retrieves the ID of the import batch for the Z39.50
97 reservoir for the given target.  If necessary,
98 creates the import batch.
99
100 =cut
101
102 sub GetZ3950BatchId {
103     my ($z3950server) = @_;
104
105     my $dbh = C4::Context->dbh;
106     my $sth = $dbh->prepare("SELECT import_batch_id FROM import_batches
107                              WHERE  batch_type = 'z3950'
108                              AND    file_name = ?");
109     $sth->execute($z3950server);
110     my $rowref = $sth->fetchrow_arrayref();
111     $sth->finish();
112     if (defined $rowref) {
113         return $rowref->[0];
114     } else {
115         my $batch_id = AddImportBatch( {
116                 overlay_action => 'create_new',
117                 import_status => 'staged',
118                 batch_type => 'z3950',
119                 file_name => $z3950server,
120             } );
121         return $batch_id;
122     }
123     
124 }
125
126 =head2 GetWebserviceBatchId
127
128   my $batchid = GetWebserviceBatchId();
129
130 Retrieves the ID of the import batch for webservice.
131 If necessary, creates the import batch.
132
133 =cut
134
135 my $WEBSERVICE_BASE_QRY = <<EOQ;
136 SELECT import_batch_id FROM import_batches
137 WHERE  batch_type = 'webservice'
138 AND    import_status = 'staged'
139 EOQ
140 sub GetWebserviceBatchId {
141     my ($params) = @_;
142
143     my $dbh = C4::Context->dbh;
144     my $sql = $WEBSERVICE_BASE_QRY;
145     my @args;
146     foreach my $field (qw(matcher_id overlay_action nomatch_action item_action)) {
147         if (my $val = $params->{$field}) {
148             $sql .= " AND $field = ?";
149             push @args, $val;
150         }
151     }
152     my $id = $dbh->selectrow_array($sql, undef, @args);
153     return $id if $id;
154
155     $params->{batch_type} = 'webservice';
156     $params->{import_status} = 'staged';
157     return AddImportBatch($params);
158 }
159
160 =head2 GetImportRecordMarc
161
162   my ($marcblob, $encoding) = GetImportRecordMarc($import_record_id);
163
164 =cut
165
166 sub GetImportRecordMarc {
167     my ($import_record_id) = @_;
168
169     my $dbh = C4::Context->dbh;
170     my $sth = $dbh->prepare("SELECT marc, encoding FROM import_records WHERE import_record_id = ?");
171     $sth->execute($import_record_id);
172     my ($marc, $encoding) = $sth->fetchrow();
173     $sth->finish();
174     return $marc, $encoding;
175
176 }
177
178 =head2 GetImportRecordMarcXML
179
180   my $marcxml = GetImportRecordMarcXML($import_record_id);
181
182 =cut
183
184 sub GetImportRecordMarcXML {
185     my ($import_record_id) = @_;
186
187     my $dbh = C4::Context->dbh;
188     my $sth = $dbh->prepare("SELECT marcxml FROM import_records WHERE import_record_id = ?");
189     $sth->execute($import_record_id);
190     my ($marcxml) = $sth->fetchrow();
191     $sth->finish();
192     return $marcxml;
193
194 }
195
196 =head2 AddImportBatch
197
198   my $batch_id = AddImportBatch($params_hash);
199
200 =cut
201
202 sub AddImportBatch {
203     my ($params) = @_;
204
205     my (@fields, @vals);
206     foreach (qw( matcher_id template_id branchcode
207                  overlay_action nomatch_action item_action
208                  import_status batch_type file_name comments record_type )) {
209         if (exists $params->{$_}) {
210             push @fields, $_;
211             push @vals, $params->{$_};
212         }
213     }
214     my $dbh = C4::Context->dbh;
215     $dbh->do("INSERT INTO import_batches (".join( ',', @fields).")
216                                   VALUES (".join( ',', map '?', @fields).")",
217              undef,
218              @vals);
219     return $dbh->{'mysql_insertid'};
220 }
221
222 =head2 GetImportBatch 
223
224   my $row = GetImportBatch($batch_id);
225
226 Retrieve a hashref of an import_batches row.
227
228 =cut
229
230 sub GetImportBatch {
231     my ($batch_id) = @_;
232
233     my $dbh = C4::Context->dbh;
234     my $sth = $dbh->prepare_cached("SELECT * FROM import_batches WHERE import_batch_id = ?");
235     $sth->bind_param(1, $batch_id);
236     $sth->execute();
237     my $result = $sth->fetchrow_hashref;
238     $sth->finish();
239     return $result;
240
241 }
242
243 =head2 AddBiblioToBatch 
244
245   my $import_record_id = AddBiblioToBatch($batch_id, $record_sequence, 
246                 $marc_record, $encoding, $z3950random, $update_counts);
247
248 =cut
249
250 sub AddBiblioToBatch {
251     my $batch_id = shift;
252     my $record_sequence = shift;
253     my $marc_record = shift;
254     my $encoding = shift;
255     my $z3950random = shift;
256     my $update_counts = @_ ? shift : 1;
257
258     my $import_record_id = _create_import_record($batch_id, $record_sequence, $marc_record, 'biblio', $encoding, $z3950random, C4::Context->preference('marcflavour'));
259     _add_biblio_fields($import_record_id, $marc_record);
260     _update_batch_record_counts($batch_id) if $update_counts;
261     return $import_record_id;
262 }
263
264 =head2 ModBiblioInBatch
265
266   ModBiblioInBatch($import_record_id, $marc_record);
267
268 =cut
269
270 sub ModBiblioInBatch {
271     my ($import_record_id, $marc_record) = @_;
272
273     _update_import_record_marc($import_record_id, $marc_record, C4::Context->preference('marcflavour'));
274     _update_biblio_fields($import_record_id, $marc_record);
275
276 }
277
278 =head2 AddAuthToBatch
279
280   my $import_record_id = AddAuthToBatch($batch_id, $record_sequence,
281                 $marc_record, $encoding, $z3950random, $update_counts, [$marc_type]);
282
283 =cut
284
285 sub AddAuthToBatch {
286     my $batch_id = shift;
287     my $record_sequence = shift;
288     my $marc_record = shift;
289     my $encoding = shift;
290     my $z3950random = shift;
291     my $update_counts = @_ ? shift : 1;
292     my $marc_type = shift || C4::Context->preference('marcflavour');
293
294     $marc_type = 'UNIMARCAUTH' if $marc_type eq 'UNIMARC';
295
296     my $import_record_id = _create_import_record($batch_id, $record_sequence, $marc_record, 'auth', $encoding, $z3950random, $marc_type);
297     _add_auth_fields($import_record_id, $marc_record);
298     _update_batch_record_counts($batch_id) if $update_counts;
299     return $import_record_id;
300 }
301
302 =head2 ModAuthInBatch
303
304   ModAuthInBatch($import_record_id, $marc_record);
305
306 =cut
307
308 sub ModAuthInBatch {
309     my ($import_record_id, $marc_record) = @_;
310
311     my $marcflavour = C4::Context->preference('marcflavour');
312     _update_import_record_marc($import_record_id, $marc_record, $marcflavour eq 'UNIMARC' ? 'UNIMARCAUTH' : 'USMARC');
313
314 }
315
316 =head2 BatchStageMarcRecords
317
318   ($batch_id, $num_records, $num_items, @invalid_records) = 
319     BatchStageMarcRecords($record_type, $encoding, $marc_records, $file_name,
320                           $comments, $branch_code, $parse_items,
321                           $leave_as_staging, 
322                           $progress_interval, $progress_callback);
323
324 =cut
325
326 sub  BatchStageMarcRecords {
327     my $record_type = shift;
328     my $encoding = shift;
329     my $marc_records = shift;
330     my $file_name = shift;
331     my $comments = shift;
332     my $branch_code = shift;
333     my $parse_items = shift;
334     my $leave_as_staging = shift;
335
336     # optional callback to monitor status 
337     # of job
338     my $progress_interval = 0;
339     my $progress_callback = undef;
340     if ($#_ == 1) {
341         $progress_interval = shift;
342         $progress_callback = shift;
343         $progress_interval = 0 unless $progress_interval =~ /^\d+$/ and $progress_interval > 0;
344         $progress_interval = 0 unless 'CODE' eq ref $progress_callback;
345     } 
346     
347     my $batch_id = AddImportBatch( {
348             overlay_action => 'create_new',
349             import_status => 'staging',
350             batch_type => 'batch',
351             file_name => $file_name,
352             comments => $comments,
353             record_type => $record_type,
354         } );
355     if ($parse_items) {
356         SetImportBatchItemAction($batch_id, 'always_add');
357     } else {
358         SetImportBatchItemAction($batch_id, 'ignore');
359     }
360
361     my $marc_type = C4::Context->preference('marcflavour');
362     $marc_type .= 'AUTH' if ($marc_type eq 'UNIMARC' && $record_type eq 'auth');
363     my @invalid_records = ();
364     my $num_valid = 0;
365     my $num_items = 0;
366     # FIXME - for now, we're dealing only with bibs
367     my $rec_num = 0;
368     foreach my $marc_blob (split(/\x1D/, $marc_records)) {
369         $marc_blob =~ s/^\s+//g;
370         $marc_blob =~ s/\s+$//g;
371         next unless $marc_blob;
372         $rec_num++;
373         if ($progress_interval and (0 == ($rec_num % $progress_interval))) {
374             &$progress_callback($rec_num);
375         }
376         my ($marc_record, $charset_guessed, $char_errors) =
377             MarcToUTF8Record($marc_blob, $marc_type, $encoding);
378
379         $encoding = $charset_guessed unless $encoding;
380
381         my $import_record_id;
382         if (scalar($marc_record->fields()) == 0) {
383             push @invalid_records, $marc_blob;
384         } else {
385
386             # Normalize the record so it doesn't have separated diacritics
387             SetUTF8Flag($marc_record);
388
389             $num_valid++;
390             if ($record_type eq 'biblio') {
391                 $import_record_id = AddBiblioToBatch($batch_id, $rec_num, $marc_record, $encoding, int(rand(99999)), 0);
392                 if ($parse_items) {
393                     my @import_items_ids = AddItemsToImportBiblio($batch_id, $import_record_id, $marc_record, 0);
394                     $num_items += scalar(@import_items_ids);
395                 }
396             } elsif ($record_type eq 'auth') {
397                 $import_record_id = AddAuthToBatch($batch_id, $rec_num, $marc_record, $encoding, int(rand(99999)), 0, $marc_type);
398             }
399         }
400     }
401     unless ($leave_as_staging) {
402         SetImportBatchStatus($batch_id, 'staged');
403     }
404     # FIXME branch_code, number of bibs, number of items
405     _update_batch_record_counts($batch_id);
406     return ($batch_id, $num_valid, $num_items, @invalid_records);
407 }
408
409 =head2 AddItemsToImportBiblio
410
411   my @import_items_ids = AddItemsToImportBiblio($batch_id, 
412                 $import_record_id, $marc_record, $update_counts);
413
414 =cut
415
416 sub AddItemsToImportBiblio {
417     my $batch_id = shift;
418     my $import_record_id = shift;
419     my $marc_record = shift;
420     my $update_counts = @_ ? shift : 0;
421
422     my @import_items_ids = ();
423    
424     my $dbh = C4::Context->dbh; 
425     my ($item_tag,$item_subfield) = &GetMarcFromKohaField("items.itemnumber",'');
426     foreach my $item_field ($marc_record->field($item_tag)) {
427         my $item_marc = MARC::Record->new();
428         $item_marc->leader("00000    a              "); # must set Leader/09 to 'a'
429         $item_marc->append_fields($item_field);
430         $marc_record->delete_field($item_field);
431         my $sth = $dbh->prepare_cached("INSERT INTO import_items (import_record_id, status, marcxml)
432                                         VALUES (?, ?, ?)");
433         $sth->bind_param(1, $import_record_id);
434         $sth->bind_param(2, 'staged');
435         $sth->bind_param(3, $item_marc->as_xml());
436         $sth->execute();
437         push @import_items_ids, $dbh->{'mysql_insertid'};
438         $sth->finish();
439     }
440
441     if ($#import_items_ids > -1) {
442         _update_batch_record_counts($batch_id) if $update_counts;
443         _update_import_record_marc($import_record_id, $marc_record, C4::Context->preference('marcflavour'));
444     }
445     return @import_items_ids;
446 }
447
448 =head2 BatchFindDuplicates
449
450   my $num_with_matches = BatchFindDuplicates($batch_id, $matcher,
451              $max_matches, $progress_interval, $progress_callback);
452
453 Goes through the records loaded in the batch and attempts to 
454 find duplicates for each one.  Sets the matching status 
455 of each record to "no_match" or "auto_match" as appropriate.
456
457 The $max_matches parameter is optional; if it is not supplied,
458 it defaults to 10.
459
460 The $progress_interval and $progress_callback parameters are 
461 optional; if both are supplied, the sub referred to by
462 $progress_callback will be invoked every $progress_interval
463 records using the number of records processed as the 
464 singular argument.
465
466 =cut
467
468 sub BatchFindDuplicates {
469     my $batch_id = shift;
470     my $matcher = shift;
471     my $max_matches = @_ ? shift : 10;
472
473     # optional callback to monitor status 
474     # of job
475     my $progress_interval = 0;
476     my $progress_callback = undef;
477     if ($#_ == 1) {
478         $progress_interval = shift;
479         $progress_callback = shift;
480         $progress_interval = 0 unless $progress_interval =~ /^\d+$/ and $progress_interval > 0;
481         $progress_interval = 0 unless 'CODE' eq ref $progress_callback;
482     }
483
484     my $dbh = C4::Context->dbh;
485
486     my $sth = $dbh->prepare("SELECT import_record_id, record_type, marc
487                              FROM import_records
488                              WHERE import_batch_id = ?");
489     $sth->execute($batch_id);
490     my $num_with_matches = 0;
491     my $rec_num = 0;
492     while (my $rowref = $sth->fetchrow_hashref) {
493         $rec_num++;
494         if ($progress_interval and (0 == ($rec_num % $progress_interval))) {
495             &$progress_callback($rec_num);
496         }
497         my $marc_record = MARC::Record->new_from_usmarc($rowref->{'marc'});
498         my @matches = ();
499         if (defined $matcher) {
500             @matches = $matcher->get_matches($marc_record, $max_matches);
501         }
502         if (scalar(@matches) > 0) {
503             $num_with_matches++;
504             SetImportRecordMatches($rowref->{'import_record_id'}, @matches);
505             SetImportRecordOverlayStatus($rowref->{'import_record_id'}, 'auto_match');
506         } else {
507             SetImportRecordMatches($rowref->{'import_record_id'}, ());
508             SetImportRecordOverlayStatus($rowref->{'import_record_id'}, 'no_match');
509         }
510     }
511     $sth->finish();
512     return $num_with_matches;
513 }
514
515 =head2 BatchCommitRecords
516
517   my ($num_added, $num_updated, $num_items_added, $num_items_errored, $num_ignored) =
518         BatchCommitRecords($batch_id, $framework,
519         $progress_interval, $progress_callback);
520
521 =cut
522
523 sub BatchCommitRecords {
524     my $batch_id = shift;
525     my $framework = shift;
526
527     # optional callback to monitor status 
528     # of job
529     my $progress_interval = 0;
530     my $progress_callback = undef;
531     if ($#_ == 1) {
532         $progress_interval = shift;
533         $progress_callback = shift;
534         $progress_interval = 0 unless $progress_interval =~ /^\d+$/ and $progress_interval > 0;
535         $progress_interval = 0 unless 'CODE' eq ref $progress_callback;
536     }
537
538     my $record_type;
539     my $num_added = 0;
540     my $num_updated = 0;
541     my $num_items_added = 0;
542     my $num_items_errored = 0;
543     my $num_ignored = 0;
544     # commit (i.e., save, all records in the batch)
545     SetImportBatchStatus('importing');
546     my $overlay_action = GetImportBatchOverlayAction($batch_id);
547     my $nomatch_action = GetImportBatchNoMatchAction($batch_id);
548     my $item_action = GetImportBatchItemAction($batch_id);
549     my $item_tag;
550     my $item_subfield;
551     my $dbh = C4::Context->dbh;
552     my $sth = $dbh->prepare("SELECT import_records.import_record_id, record_type, status, overlay_status, marc, encoding
553                              FROM import_records
554                              LEFT JOIN import_auths ON (import_records.import_record_id=import_auths.import_record_id)
555                              LEFT JOIN import_biblios ON (import_records.import_record_id=import_biblios.import_record_id)
556                              WHERE import_batch_id = ?");
557     $sth->execute($batch_id);
558     my $marcflavour = C4::Context->preference('marcflavour');
559     my $rec_num = 0;
560     while (my $rowref = $sth->fetchrow_hashref) {
561         $record_type = $rowref->{'record_type'};
562         $rec_num++;
563         if ($progress_interval and (0 == ($rec_num % $progress_interval))) {
564             &$progress_callback($rec_num);
565         }
566         if ($rowref->{'status'} eq 'error' or $rowref->{'status'} eq 'imported') {
567             $num_ignored++;
568             next;
569         }
570
571         my $marc_type;
572         if ($marcflavour eq 'UNIMARC' && $record_type eq 'auth') {
573             $marc_type = 'UNIMARCAUTH';
574         } elsif ($marcflavour eq 'UNIMARC') {
575             $marc_type = 'UNIMARC';
576         } else {
577             $marc_type = 'USMARC';
578         }
579         my $marc_record = MARC::Record->new_from_usmarc($rowref->{'marc'});
580
581         if ($record_type eq 'biblio') {
582             # remove any item tags - rely on BatchCommitItems
583             ($item_tag,$item_subfield) = &GetMarcFromKohaField("items.itemnumber",'');
584             foreach my $item_field ($marc_record->field($item_tag)) {
585                 $marc_record->delete_field($item_field);
586             }
587         }
588
589         my ($record_result, $item_result, $record_match) =
590             _get_commit_action($overlay_action, $nomatch_action, $item_action, 
591                                $rowref->{'overlay_status'}, $rowref->{'import_record_id'}, $record_type);
592
593         my $recordid;
594         my $query;
595         if ($record_result eq 'create_new') {
596             $num_added++;
597             if ($record_type eq 'biblio') {
598                 my $biblioitemnumber;
599                 ($recordid, $biblioitemnumber) = AddBiblio($marc_record, $framework);
600                 $query = "UPDATE import_biblios SET matched_biblionumber = ? WHERE import_record_id = ?";
601                 if ($item_result eq 'create_new') {
602                     my ($bib_items_added, $bib_items_errored) = BatchCommitItems($rowref->{'import_record_id'}, $recordid);
603                     $num_items_added += $bib_items_added;
604                     $num_items_errored += $bib_items_errored;
605                 }
606             } else {
607                 $recordid = AddAuthority($marc_record, undef, GuessAuthTypeCode($marc_record));
608                 $query = "UPDATE import_auths SET matched_authid = ? WHERE import_record_id = ?";
609             }
610             my $sth = $dbh->prepare_cached($query);
611             $sth->execute($recordid, $rowref->{'import_record_id'});
612             $sth->finish();
613             SetImportRecordStatus($rowref->{'import_record_id'}, 'imported');
614         } elsif ($record_result eq 'replace') {
615             $num_updated++;
616             $recordid = $record_match;
617             my $oldxml;
618             if ($record_type eq 'biblio') {
619                 my ($count, $oldbiblio) = GetBiblio($recordid);
620                 $oldxml = GetXmlBiblio($recordid);
621
622                 # remove item fields so that they don't get
623                 # added again if record is reverted
624                 # 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.
625                 my $old_marc = MARC::Record->new_from_xml(StripNonXmlChars($oldxml), 'UTF-8', $rowref->{'encoding'}, $marc_type);
626                 foreach my $item_field ($old_marc->field($item_tag)) {
627                     $old_marc->delete_field($item_field);
628                 }
629                 $oldxml = $old_marc->as_xml($marc_type);
630
631                 ModBiblio($marc_record, $recordid, $oldbiblio->{'frameworkcode'});
632                 $query = "UPDATE import_biblios SET matched_biblionumber = ? WHERE import_record_id = ?";
633
634                 if ($item_result eq 'create_new') {
635                     my ($bib_items_added, $bib_items_errored) = BatchCommitItems($rowref->{'import_record_id'}, $recordid);
636                     $num_items_added += $bib_items_added;
637                     $num_items_errored += $bib_items_errored;
638                 }
639             } else {
640                 $oldxml = GetAuthorityXML($recordid);
641
642                 ModAuthority($recordid, $marc_record, GuessAuthTypeCode($marc_record));
643                 $query = "UPDATE import_auths SET matched_authid = ? WHERE import_record_id = ?";
644             }
645             my $sth = $dbh->prepare_cached("UPDATE import_records SET marcxml_old = ? WHERE import_record_id = ?");
646             $sth->execute($oldxml, $rowref->{'import_record_id'});
647             $sth->finish();
648             my $sth2 = $dbh->prepare_cached($query);
649             $sth2->execute($recordid, $rowref->{'import_record_id'});
650             $sth2->finish();
651             SetImportRecordOverlayStatus($rowref->{'import_record_id'}, 'match_applied');
652             SetImportRecordStatus($rowref->{'import_record_id'}, 'imported');
653         } elsif ($record_result eq 'ignore') {
654             $num_ignored++;
655             $recordid = $record_match;
656             if ($record_type eq 'biblio' and defined $recordid and $item_result eq 'create_new') {
657                 my ($bib_items_added, $bib_items_errored) = BatchCommitItems($rowref->{'import_record_id'}, $recordid);
658                 $num_items_added += $bib_items_added;
659                 $num_items_errored += $bib_items_errored;
660                 # still need to record the matched biblionumber so that the
661                 # items can be reverted
662                 my $sth2 = $dbh->prepare_cached("UPDATE import_biblios SET matched_biblionumber = ? WHERE import_record_id = ?");
663                 $sth2->execute($recordid, $rowref->{'import_record_id'});
664                 SetImportRecordOverlayStatus($rowref->{'import_record_id'}, 'match_applied');
665             }
666             SetImportRecordStatus($rowref->{'import_record_id'}, 'ignored');
667         }
668     }
669     $sth->finish();
670     SetImportBatchStatus($batch_id, 'imported');
671     return ($num_added, $num_updated, $num_items_added, $num_items_errored, $num_ignored);
672 }
673
674 =head2 BatchCommitItems
675
676   ($num_items_added, $num_items_errored) = 
677          BatchCommitItems($import_record_id, $biblionumber);
678
679 =cut
680
681 sub BatchCommitItems {
682     my ($import_record_id, $biblionumber) = @_;
683
684     my $dbh = C4::Context->dbh;
685
686     my $num_items_added = 0;
687     my $num_items_errored = 0;
688     my $sth = $dbh->prepare("SELECT import_items_id, import_items.marcxml, encoding
689                              FROM import_items
690                              JOIN import_records USING (import_record_id)
691                              WHERE import_record_id = ?
692                              ORDER BY import_items_id");
693     $sth->bind_param(1, $import_record_id);
694     $sth->execute();
695     while (my $row = $sth->fetchrow_hashref()) {
696         my $item_marc = MARC::Record->new_from_xml(StripNonXmlChars($row->{'marcxml'}), 'UTF-8', $row->{'encoding'});
697         # FIXME - duplicate barcode check needs to become part of AddItemFromMarc()
698         my $item = TransformMarcToKoha($dbh, $item_marc);
699         my $duplicate_barcode = exists($item->{'barcode'}) && GetItemnumberFromBarcode($item->{'barcode'});
700         if ($duplicate_barcode) {
701             my $updsth = $dbh->prepare("UPDATE import_items SET status = ?, import_error = ? WHERE import_items_id = ?");
702             $updsth->bind_param(1, 'error');
703             $updsth->bind_param(2, 'duplicate item barcode');
704             $updsth->bind_param(3, $row->{'import_items_id'});
705             $updsth->execute();
706             $num_items_errored++;
707         } else {
708             my ($item_biblionumber, $biblioitemnumber, $itemnumber) = AddItemFromMarc($item_marc, $biblionumber);
709             my $updsth = $dbh->prepare("UPDATE import_items SET status = ?, itemnumber = ? WHERE import_items_id = ?");
710             $updsth->bind_param(1, 'imported');
711             $updsth->bind_param(2, $itemnumber);
712             $updsth->bind_param(3, $row->{'import_items_id'});
713             $updsth->execute();
714             $updsth->finish();
715             $num_items_added++;
716         }
717     }
718     $sth->finish();
719     return ($num_items_added, $num_items_errored);
720 }
721
722 =head2 BatchRevertRecords
723
724   my ($num_deleted, $num_errors, $num_reverted, $num_items_deleted, 
725       $num_ignored) = BatchRevertRecords($batch_id);
726
727 =cut
728
729 sub BatchRevertRecords {
730     my $batch_id = shift;
731
732     my $record_type;
733     my $num_deleted = 0;
734     my $num_errors = 0;
735     my $num_reverted = 0;
736     my $num_ignored = 0;
737     my $num_items_deleted = 0;
738     # commit (i.e., save, all records in the batch)
739     SetImportBatchStatus('reverting');
740     my $overlay_action = GetImportBatchOverlayAction($batch_id);
741     my $nomatch_action = GetImportBatchNoMatchAction($batch_id);
742     my $dbh = C4::Context->dbh;
743     my $sth = $dbh->prepare("SELECT import_records.import_record_id, record_type, status, overlay_status, marcxml_old, encoding, matched_biblionumber, matched_authid
744                              FROM import_records
745                              LEFT JOIN import_auths ON (import_records.import_record_id=import_auths.import_record_id)
746                              LEFT JOIN import_biblios ON (import_records.import_record_id=import_biblios.import_record_id)
747                              WHERE import_batch_id = ?");
748     $sth->execute($batch_id);
749     my $marc_type;
750     my $marcflavour = C4::Context->preference('marcflavour');
751     while (my $rowref = $sth->fetchrow_hashref) {
752         $record_type = $rowref->{'record_type'};
753         if ($rowref->{'status'} eq 'error' or $rowref->{'status'} eq 'reverted') {
754             $num_ignored++;
755             next;
756         }
757         if ($marcflavour eq 'UNIMARC' && $record_type eq 'auth') {
758             $marc_type = 'UNIMARCAUTH';
759         } elsif ($marcflavour eq 'UNIMARC') {
760             $marc_type = 'UNIMARC';
761         } else {
762             $marc_type = 'USMARC';
763         }
764
765         my $record_result = _get_revert_action($overlay_action, $rowref->{'overlay_status'}, $rowref->{'status'});
766
767         if ($record_result eq 'delete') {
768             my $error = undef;
769             if  ($record_type eq 'biblio') {
770                 $num_items_deleted += BatchRevertItems($rowref->{'import_record_id'}, $rowref->{'matched_biblionumber'});
771                 $error = DelBiblio($rowref->{'matched_biblionumber'});
772             } else {
773                 my $deletedauthid = DelAuthority($rowref->{'matched_authid'});
774             }
775             if (defined $error) {
776                 $num_errors++;
777             } else {
778                 $num_deleted++;
779                 SetImportRecordStatus($rowref->{'import_record_id'}, 'reverted');
780             }
781         } elsif ($record_result eq 'restore') {
782             $num_reverted++;
783             my $old_record = MARC::Record->new_from_xml(StripNonXmlChars($rowref->{'marcxml_old'}), 'UTF-8', $rowref->{'encoding'}, $marc_type);
784             if ($record_type eq 'biblio') {
785                 my $biblionumber = $rowref->{'matched_biblionumber'};
786                 my ($count, $oldbiblio) = GetBiblio($biblionumber);
787                 $num_items_deleted += BatchRevertItems($rowref->{'import_record_id'}, $rowref->{'matched_biblionumber'});
788                 ModBiblio($old_record, $biblionumber, $oldbiblio->{'frameworkcode'});
789             } else {
790                 my $authid = $rowref->{'matched_authid'};
791                 ModAuthority($authid, $old_record, GuessAuthTypeCode($old_record));
792             }
793             SetImportRecordStatus($rowref->{'import_record_id'}, 'reverted');
794         } elsif ($record_result eq 'ignore') {
795             if ($record_type eq 'biblio') {
796                 $num_items_deleted += BatchRevertItems($rowref->{'import_record_id'}, $rowref->{'matched_biblionumber'});
797             }
798             SetImportRecordStatus($rowref->{'import_record_id'}, 'reverted');
799         }
800         my $query;
801         if ($record_type eq 'biblio') {
802             # remove matched_biblionumber only if there is no 'imported' item left
803             $query = "UPDATE import_biblios SET matched_biblionumber = NULL WHERE import_record_id = ?";
804             $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')";
805         } else {
806             $query = "UPDATE import_auths SET matched_authid = NULL WHERE import_record_id = ?";
807         }
808         my $sth2 = $dbh->prepare_cached($query);
809         $sth2->execute($rowref->{'import_record_id'});
810     }
811
812     $sth->finish();
813     SetImportBatchStatus($batch_id, 'reverted');
814     return ($num_deleted, $num_errors, $num_reverted, $num_items_deleted, $num_ignored);
815 }
816
817 =head2 BatchRevertItems
818
819   my $num_items_deleted = BatchRevertItems($import_record_id, $biblionumber);
820
821 =cut
822
823 sub BatchRevertItems {
824     my ($import_record_id, $biblionumber) = @_;
825
826     my $dbh = C4::Context->dbh;
827     my $num_items_deleted = 0;
828
829     my $sth = $dbh->prepare_cached("SELECT import_items_id, itemnumber
830                                    FROM import_items
831                                    JOIN items USING (itemnumber)
832                                    WHERE import_record_id = ?");
833     $sth->bind_param(1, $import_record_id);
834     $sth->execute();
835     while (my $row = $sth->fetchrow_hashref()) {
836         my $error = DelItemCheck($dbh, $biblionumber, $row->{'itemnumber'});
837         if ($error == 1){
838             my $updsth = $dbh->prepare("UPDATE import_items SET status = ? WHERE import_items_id = ?");
839             $updsth->bind_param(1, 'reverted');
840             $updsth->bind_param(2, $row->{'import_items_id'});
841             $updsth->execute();
842             $updsth->finish();
843             $num_items_deleted++;
844         }
845         else {
846             next;
847         }
848     }
849     $sth->finish();
850     return $num_items_deleted;
851 }
852
853 =head2 CleanBatch
854
855   CleanBatch($batch_id)
856
857 Deletes all staged records from the import batch
858 and sets the status of the batch to 'cleaned'.  Note
859 that deleting a stage record does *not* affect
860 any record that has been committed to the database.
861
862 =cut
863
864 sub CleanBatch {
865     my $batch_id = shift;
866     return unless defined $batch_id;
867
868     C4::Context->dbh->do('DELETE FROM import_records WHERE import_batch_id = ?', {}, $batch_id);
869     SetImportBatchStatus($batch_id, 'cleaned');
870 }
871
872 =head2 GetAllImportBatches
873
874   my $results = GetAllImportBatches();
875
876 Returns a references to an array of hash references corresponding
877 to all import_batches rows (of batch_type 'batch'), sorted in 
878 ascending order by import_batch_id.
879
880 =cut
881
882 sub  GetAllImportBatches {
883     my $dbh = C4::Context->dbh;
884     my $sth = $dbh->prepare_cached("SELECT * FROM import_batches
885                                     WHERE batch_type IN ('batch', 'webservice')
886                                     ORDER BY import_batch_id ASC");
887
888     my $results = [];
889     $sth->execute();
890     while (my $row = $sth->fetchrow_hashref) {
891         push @$results, $row;
892     }
893     $sth->finish();
894     return $results;
895 }
896
897 =head2 GetStagedWebserviceBatches
898
899   my $batch_ids = GetStagedWebserviceBatches();
900
901 Returns a references to an array of batch id's
902 of batch_type 'webservice' that are not imported
903
904 =cut
905
906 my $PENDING_WEBSERVICE_BATCHES_QRY = <<EOQ;
907 SELECT import_batch_id FROM import_batches
908 WHERE batch_type = 'webservice'
909 AND import_status = 'staged'
910 EOQ
911 sub  GetStagedWebserviceBatches {
912     my $dbh = C4::Context->dbh;
913     return $dbh->selectcol_arrayref($PENDING_WEBSERVICE_BATCHES_QRY);
914 }
915
916 =head2 GetImportBatchRangeDesc
917
918   my $results = GetImportBatchRangeDesc($offset, $results_per_group);
919
920 Returns a reference to an array of hash references corresponding to
921 import_batches rows (sorted in descending order by import_batch_id)
922 start at the given offset.
923
924 =cut
925
926 sub GetImportBatchRangeDesc {
927     my ($offset, $results_per_group) = @_;
928
929     my $dbh = C4::Context->dbh;
930     my $query = "SELECT * FROM import_batches
931                                     WHERE batch_type IN ('batch', 'webservice')
932                                     ORDER BY import_batch_id DESC";
933     my @params;
934     if ($results_per_group){
935         $query .= " LIMIT ?";
936         push(@params, $results_per_group);
937     }
938     if ($offset){
939         $query .= " OFFSET ?";
940         push(@params, $offset);
941     }
942     my $sth = $dbh->prepare_cached($query);
943     $sth->execute(@params);
944     my $results = $sth->fetchall_arrayref({});
945     $sth->finish();
946     return $results;
947 }
948
949 =head2 GetItemNumbersFromImportBatch
950
951   my @itemsnos = GetItemNumbersFromImportBatch($batch_id);
952
953 =cut
954
955 sub GetItemNumbersFromImportBatch {
956         my ($batch_id) = @_;
957         my $dbh = C4::Context->dbh;
958         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=?");
959         $sth->execute($batch_id);
960         my @items ;
961         while ( my ($itm) = $sth->fetchrow_array ) {
962                 push @items, $itm;
963         }
964         return @items;
965 }
966
967 =head2 GetNumberOfImportBatches 
968
969   my $count = GetNumberOfImportBatches();
970
971 =cut
972
973 sub GetNumberOfNonZ3950ImportBatches {
974     my $dbh = C4::Context->dbh;
975     my $sth = $dbh->prepare("SELECT COUNT(*) FROM import_batches WHERE batch_type != 'z3950'");
976     $sth->execute();
977     my ($count) = $sth->fetchrow_array();
978     $sth->finish();
979     return $count;
980 }
981
982 =head2 GetImportRecordsRange
983
984   my $results = GetImportRecordsRange($batch_id, $offset, $results_per_group);
985
986 Returns a reference to an array of hash references corresponding to
987 import_biblios/import_auths/import_records rows for a given batch
988 starting at the given offset.
989
990 =cut
991
992 sub GetImportRecordsRange {
993     my ($batch_id, $offset, $results_per_group, $status) = @_;
994
995     my $dbh = C4::Context->dbh;
996     my $query = "SELECT title, author, isbn, issn, authorized_heading, import_records.import_record_id,
997                                            record_sequence, status, overlay_status,
998                                            matched_biblionumber, matched_authid, record_type
999                                     FROM   import_records
1000                                     LEFT JOIN import_auths ON (import_records.import_record_id=import_auths.import_record_id)
1001                                     LEFT JOIN import_biblios ON (import_records.import_record_id=import_biblios.import_record_id)
1002                                     WHERE  import_batch_id = ?";
1003     my @params;
1004     push(@params, $batch_id);
1005     if ($status) {
1006         $query .= " AND status=?";
1007         push(@params,$status);
1008     }
1009     $query.=" ORDER BY import_record_id";
1010
1011     if($results_per_group){
1012         $query .= " LIMIT ?";
1013         push(@params, $results_per_group);
1014     }
1015     if($offset){
1016         $query .= " OFFSET ?";
1017         push(@params, $offset);
1018     }
1019     my $sth = $dbh->prepare_cached($query);
1020     $sth->execute(@params);
1021     my $results = $sth->fetchall_arrayref({});
1022     $sth->finish();
1023     return $results;
1024
1025 }
1026
1027 =head2 GetBestRecordMatch
1028
1029   my $record_id = GetBestRecordMatch($import_record_id);
1030
1031 =cut
1032
1033 sub GetBestRecordMatch {
1034     my ($import_record_id) = @_;
1035
1036     my $dbh = C4::Context->dbh;
1037     my $sth = $dbh->prepare("SELECT candidate_match_id
1038                              FROM   import_record_matches
1039                              WHERE  import_record_id = ?
1040                              ORDER BY score DESC, candidate_match_id DESC");
1041     $sth->execute($import_record_id);
1042     my ($record_id) = $sth->fetchrow_array();
1043     $sth->finish();
1044     return $record_id;
1045 }
1046
1047 =head2 GetImportBatchStatus
1048
1049   my $status = GetImportBatchStatus($batch_id);
1050
1051 =cut
1052
1053 sub GetImportBatchStatus {
1054     my ($batch_id) = @_;
1055
1056     my $dbh = C4::Context->dbh;
1057     my $sth = $dbh->prepare("SELECT import_status FROM import_batches WHERE import_batch_id = ?");
1058     $sth->execute($batch_id);
1059     my ($status) = $sth->fetchrow_array();
1060     $sth->finish();
1061     return $status;
1062
1063 }
1064
1065 =head2 SetImportBatchStatus
1066
1067   SetImportBatchStatus($batch_id, $new_status);
1068
1069 =cut
1070
1071 sub SetImportBatchStatus {
1072     my ($batch_id, $new_status) = @_;
1073
1074     my $dbh = C4::Context->dbh;
1075     my $sth = $dbh->prepare("UPDATE import_batches SET import_status = ? WHERE import_batch_id = ?");
1076     $sth->execute($new_status, $batch_id);
1077     $sth->finish();
1078
1079 }
1080
1081 =head2 GetImportBatchOverlayAction
1082
1083   my $overlay_action = GetImportBatchOverlayAction($batch_id);
1084
1085 =cut
1086
1087 sub GetImportBatchOverlayAction {
1088     my ($batch_id) = @_;
1089
1090     my $dbh = C4::Context->dbh;
1091     my $sth = $dbh->prepare("SELECT overlay_action FROM import_batches WHERE import_batch_id = ?");
1092     $sth->execute($batch_id);
1093     my ($overlay_action) = $sth->fetchrow_array();
1094     $sth->finish();
1095     return $overlay_action;
1096
1097 }
1098
1099
1100 =head2 SetImportBatchOverlayAction
1101
1102   SetImportBatchOverlayAction($batch_id, $new_overlay_action);
1103
1104 =cut
1105
1106 sub SetImportBatchOverlayAction {
1107     my ($batch_id, $new_overlay_action) = @_;
1108
1109     my $dbh = C4::Context->dbh;
1110     my $sth = $dbh->prepare("UPDATE import_batches SET overlay_action = ? WHERE import_batch_id = ?");
1111     $sth->execute($new_overlay_action, $batch_id);
1112     $sth->finish();
1113
1114 }
1115
1116 =head2 GetImportBatchNoMatchAction
1117
1118   my $nomatch_action = GetImportBatchNoMatchAction($batch_id);
1119
1120 =cut
1121
1122 sub GetImportBatchNoMatchAction {
1123     my ($batch_id) = @_;
1124
1125     my $dbh = C4::Context->dbh;
1126     my $sth = $dbh->prepare("SELECT nomatch_action FROM import_batches WHERE import_batch_id = ?");
1127     $sth->execute($batch_id);
1128     my ($nomatch_action) = $sth->fetchrow_array();
1129     $sth->finish();
1130     return $nomatch_action;
1131
1132 }
1133
1134
1135 =head2 SetImportBatchNoMatchAction
1136
1137   SetImportBatchNoMatchAction($batch_id, $new_nomatch_action);
1138
1139 =cut
1140
1141 sub SetImportBatchNoMatchAction {
1142     my ($batch_id, $new_nomatch_action) = @_;
1143
1144     my $dbh = C4::Context->dbh;
1145     my $sth = $dbh->prepare("UPDATE import_batches SET nomatch_action = ? WHERE import_batch_id = ?");
1146     $sth->execute($new_nomatch_action, $batch_id);
1147     $sth->finish();
1148
1149 }
1150
1151 =head2 GetImportBatchItemAction
1152
1153   my $item_action = GetImportBatchItemAction($batch_id);
1154
1155 =cut
1156
1157 sub GetImportBatchItemAction {
1158     my ($batch_id) = @_;
1159
1160     my $dbh = C4::Context->dbh;
1161     my $sth = $dbh->prepare("SELECT item_action FROM import_batches WHERE import_batch_id = ?");
1162     $sth->execute($batch_id);
1163     my ($item_action) = $sth->fetchrow_array();
1164     $sth->finish();
1165     return $item_action;
1166
1167 }
1168
1169
1170 =head2 SetImportBatchItemAction
1171
1172   SetImportBatchItemAction($batch_id, $new_item_action);
1173
1174 =cut
1175
1176 sub SetImportBatchItemAction {
1177     my ($batch_id, $new_item_action) = @_;
1178
1179     my $dbh = C4::Context->dbh;
1180     my $sth = $dbh->prepare("UPDATE import_batches SET item_action = ? WHERE import_batch_id = ?");
1181     $sth->execute($new_item_action, $batch_id);
1182     $sth->finish();
1183
1184 }
1185
1186 =head2 GetImportBatchMatcher
1187
1188   my $matcher_id = GetImportBatchMatcher($batch_id);
1189
1190 =cut
1191
1192 sub GetImportBatchMatcher {
1193     my ($batch_id) = @_;
1194
1195     my $dbh = C4::Context->dbh;
1196     my $sth = $dbh->prepare("SELECT matcher_id FROM import_batches WHERE import_batch_id = ?");
1197     $sth->execute($batch_id);
1198     my ($matcher_id) = $sth->fetchrow_array();
1199     $sth->finish();
1200     return $matcher_id;
1201
1202 }
1203
1204
1205 =head2 SetImportBatchMatcher
1206
1207   SetImportBatchMatcher($batch_id, $new_matcher_id);
1208
1209 =cut
1210
1211 sub SetImportBatchMatcher {
1212     my ($batch_id, $new_matcher_id) = @_;
1213
1214     my $dbh = C4::Context->dbh;
1215     my $sth = $dbh->prepare("UPDATE import_batches SET matcher_id = ? WHERE import_batch_id = ?");
1216     $sth->execute($new_matcher_id, $batch_id);
1217     $sth->finish();
1218
1219 }
1220
1221 =head2 GetImportRecordOverlayStatus
1222
1223   my $overlay_status = GetImportRecordOverlayStatus($import_record_id);
1224
1225 =cut
1226
1227 sub GetImportRecordOverlayStatus {
1228     my ($import_record_id) = @_;
1229
1230     my $dbh = C4::Context->dbh;
1231     my $sth = $dbh->prepare("SELECT overlay_status FROM import_records WHERE import_record_id = ?");
1232     $sth->execute($import_record_id);
1233     my ($overlay_status) = $sth->fetchrow_array();
1234     $sth->finish();
1235     return $overlay_status;
1236
1237 }
1238
1239
1240 =head2 SetImportRecordOverlayStatus
1241
1242   SetImportRecordOverlayStatus($import_record_id, $new_overlay_status);
1243
1244 =cut
1245
1246 sub SetImportRecordOverlayStatus {
1247     my ($import_record_id, $new_overlay_status) = @_;
1248
1249     my $dbh = C4::Context->dbh;
1250     my $sth = $dbh->prepare("UPDATE import_records SET overlay_status = ? WHERE import_record_id = ?");
1251     $sth->execute($new_overlay_status, $import_record_id);
1252     $sth->finish();
1253
1254 }
1255
1256 =head2 GetImportRecordStatus
1257
1258   my $overlay_status = GetImportRecordStatus($import_record_id);
1259
1260 =cut
1261
1262 sub GetImportRecordStatus {
1263     my ($import_record_id) = @_;
1264
1265     my $dbh = C4::Context->dbh;
1266     my $sth = $dbh->prepare("SELECT status FROM import_records WHERE import_record_id = ?");
1267     $sth->execute($import_record_id);
1268     my ($overlay_status) = $sth->fetchrow_array();
1269     $sth->finish();
1270     return $overlay_status;
1271
1272 }
1273
1274
1275 =head2 SetImportRecordStatus
1276
1277   SetImportRecordStatus($import_record_id, $new_overlay_status);
1278
1279 =cut
1280
1281 sub SetImportRecordStatus {
1282     my ($import_record_id, $new_overlay_status) = @_;
1283
1284     my $dbh = C4::Context->dbh;
1285     my $sth = $dbh->prepare("UPDATE import_records SET status = ? WHERE import_record_id = ?");
1286     $sth->execute($new_overlay_status, $import_record_id);
1287     $sth->finish();
1288
1289 }
1290
1291 =head2 GetImportRecordMatches
1292
1293   my $results = GetImportRecordMatches($import_record_id, $best_only);
1294
1295 =cut
1296
1297 sub GetImportRecordMatches {
1298     my $import_record_id = shift;
1299     my $best_only = @_ ? shift : 0;
1300
1301     my $dbh = C4::Context->dbh;
1302     # FIXME currently biblio only
1303     my $sth = $dbh->prepare_cached("SELECT title, author, biblionumber,
1304                                     candidate_match_id, score, record_type
1305                                     FROM import_records
1306                                     JOIN import_record_matches USING (import_record_id)
1307                                     LEFT JOIN biblio ON (biblionumber = candidate_match_id)
1308                                     WHERE import_record_id = ?
1309                                     ORDER BY score DESC, biblionumber DESC");
1310     $sth->bind_param(1, $import_record_id);
1311     my $results = [];
1312     $sth->execute();
1313     while (my $row = $sth->fetchrow_hashref) {
1314         if ($row->{'record_type'} eq 'auth') {
1315             $row->{'authorized_heading'} = C4::AuthoritiesMarc::GetAuthorizedHeading( { authid => $row->{'candidate_match_id'} } );
1316         }
1317         next if ($row->{'record_type'} eq 'biblio' && not $row->{'biblionumber'});
1318         push @$results, $row;
1319         last if $best_only;
1320     }
1321     $sth->finish();
1322
1323     return $results;
1324     
1325 }
1326
1327
1328 =head2 SetImportRecordMatches
1329
1330   SetImportRecordMatches($import_record_id, @matches);
1331
1332 =cut
1333
1334 sub SetImportRecordMatches {
1335     my $import_record_id = shift;
1336     my @matches = @_;
1337
1338     my $dbh = C4::Context->dbh;
1339     my $delsth = $dbh->prepare("DELETE FROM import_record_matches WHERE import_record_id = ?");
1340     $delsth->execute($import_record_id);
1341     $delsth->finish();
1342
1343     my $sth = $dbh->prepare("INSERT INTO import_record_matches (import_record_id, candidate_match_id, score)
1344                                     VALUES (?, ?, ?)");
1345     foreach my $match (@matches) {
1346         $sth->execute($import_record_id, $match->{'record_id'}, $match->{'score'});
1347     }
1348 }
1349
1350
1351 # internal functions
1352
1353 sub _create_import_record {
1354     my ($batch_id, $record_sequence, $marc_record, $record_type, $encoding, $z3950random, $marc_type) = @_;
1355
1356     my $dbh = C4::Context->dbh;
1357     my $sth = $dbh->prepare("INSERT INTO import_records (import_batch_id, record_sequence, marc, marcxml, 
1358                                                          record_type, encoding, z3950random)
1359                                     VALUES (?, ?, ?, ?, ?, ?, ?)");
1360     $sth->execute($batch_id, $record_sequence, $marc_record->as_usmarc(), $marc_record->as_xml($marc_type),
1361                   $record_type, $encoding, $z3950random);
1362     my $import_record_id = $dbh->{'mysql_insertid'};
1363     $sth->finish();
1364     return $import_record_id;
1365 }
1366
1367 sub _update_import_record_marc {
1368     my ($import_record_id, $marc_record, $marc_type) = @_;
1369
1370     my $dbh = C4::Context->dbh;
1371     my $sth = $dbh->prepare("UPDATE import_records SET marc = ?, marcxml = ?
1372                              WHERE  import_record_id = ?");
1373     $sth->execute($marc_record->as_usmarc(), $marc_record->as_xml($marc_type), $import_record_id);
1374     $sth->finish();
1375 }
1376
1377 sub _add_auth_fields {
1378     my ($import_record_id, $marc_record) = @_;
1379
1380     my $controlnumber;
1381     if ($marc_record->field('001')) {
1382         $controlnumber = $marc_record->field('001')->data();
1383     }
1384     my $authorized_heading = C4::AuthoritiesMarc::GetAuthorizedHeading({ record => $marc_record });
1385     my $dbh = C4::Context->dbh;
1386     my $sth = $dbh->prepare("INSERT INTO import_auths (import_record_id, control_number, authorized_heading) VALUES (?, ?, ?)");
1387     $sth->execute($import_record_id, $controlnumber, $authorized_heading);
1388     $sth->finish();
1389 }
1390
1391 sub _add_biblio_fields {
1392     my ($import_record_id, $marc_record) = @_;
1393
1394     my ($title, $author, $isbn, $issn) = _parse_biblio_fields($marc_record);
1395     my $dbh = C4::Context->dbh;
1396     # FIXME no controlnumber, originalsource
1397     $isbn = C4::Koha::_isbn_cleanup($isbn); # FIXME C4::Koha::_isbn_cleanup should be made public
1398     my $sth = $dbh->prepare("INSERT INTO import_biblios (import_record_id, title, author, isbn, issn) VALUES (?, ?, ?, ?, ?)");
1399     $sth->execute($import_record_id, $title, $author, $isbn, $issn);
1400     $sth->finish();
1401                 
1402 }
1403
1404 sub _update_biblio_fields {
1405     my ($import_record_id, $marc_record) = @_;
1406
1407     my ($title, $author, $isbn, $issn) = _parse_biblio_fields($marc_record);
1408     my $dbh = C4::Context->dbh;
1409     # FIXME no controlnumber, originalsource
1410     # FIXME 2 - should regularize normalization of ISBN wherever it is done
1411     $isbn =~ s/\(.*$//;
1412     $isbn =~ tr/ -_//;
1413     $isbn = uc $isbn;
1414     my $sth = $dbh->prepare("UPDATE import_biblios SET title = ?, author = ?, isbn = ?, issn = ?
1415                              WHERE  import_record_id = ?");
1416     $sth->execute($title, $author, $isbn, $issn, $import_record_id);
1417     $sth->finish();
1418 }
1419
1420 sub _parse_biblio_fields {
1421     my ($marc_record) = @_;
1422
1423     my $dbh = C4::Context->dbh;
1424     my $bibliofields = TransformMarcToKoha($dbh, $marc_record, '');
1425     return ($bibliofields->{'title'}, $bibliofields->{'author'}, $bibliofields->{'isbn'}, $bibliofields->{'issn'});
1426
1427 }
1428
1429 sub _update_batch_record_counts {
1430     my ($batch_id) = @_;
1431
1432     my $dbh = C4::Context->dbh;
1433     my $sth = $dbh->prepare_cached("UPDATE import_batches SET
1434                                         num_records = (
1435                                             SELECT COUNT(*)
1436                                             FROM import_records
1437                                             WHERE import_batch_id = import_batches.import_batch_id),
1438                                         num_items = (
1439                                             SELECT COUNT(*)
1440                                             FROM import_records
1441                                             JOIN import_items USING (import_record_id)
1442                                             WHERE import_batch_id = import_batches.import_batch_id
1443                                             AND record_type = 'biblio')
1444                                     WHERE import_batch_id = ?");
1445     $sth->bind_param(1, $batch_id);
1446     $sth->execute();
1447     $sth->finish();
1448 }
1449
1450 sub _get_commit_action {
1451     my ($overlay_action, $nomatch_action, $item_action, $overlay_status, $import_record_id, $record_type) = @_;
1452     
1453     if ($record_type eq 'biblio') {
1454         my ($bib_result, $bib_match, $item_result);
1455
1456         if ($overlay_status ne 'no_match') {
1457             $bib_match = GetBestRecordMatch($import_record_id);
1458             if ($overlay_action eq 'replace') {
1459                 $bib_result  = defined($bib_match) ? 'replace' : 'create_new';
1460             } elsif ($overlay_action eq 'create_new') {
1461                 $bib_result  = 'create_new';
1462             } elsif ($overlay_action eq 'ignore') {
1463                 $bib_result  = 'ignore';
1464             }
1465             $item_result = ($item_action eq 'always_add' or $item_action eq 'add_only_for_matches') ? 'create_new' : 'ignore';
1466         } else {
1467             $bib_result = $nomatch_action;
1468             $item_result = ($item_action eq 'always_add' or $item_action eq 'add_only_for_new')     ? 'create_new' : 'ignore';
1469         }
1470         return ($bib_result, $item_result, $bib_match);
1471     } else { # must be auths
1472         my ($auth_result, $auth_match);
1473
1474         if ($overlay_status ne 'no_match') {
1475             $auth_match = GetBestRecordMatch($import_record_id);
1476             if ($overlay_action eq 'replace') {
1477                 $auth_result  = defined($auth_match) ? 'replace' : 'create_new';
1478             } elsif ($overlay_action eq 'create_new') {
1479                 $auth_result  = 'create_new';
1480             } elsif ($overlay_action eq 'ignore') {
1481                 $auth_result  = 'ignore';
1482             }
1483         } else {
1484             $auth_result = $nomatch_action;
1485         }
1486
1487         return ($auth_result, undef, $auth_match);
1488
1489     }
1490 }
1491
1492 sub _get_revert_action {
1493     my ($overlay_action, $overlay_status, $status) = @_;
1494
1495     my $bib_result;
1496
1497     if ($status eq 'ignored') {
1498         $bib_result = 'ignore';
1499     } else {
1500         if ($overlay_action eq 'create_new') {
1501             $bib_result = 'delete';
1502         } else {
1503             $bib_result = ($overlay_status eq 'match_applied') ? 'restore' : 'delete';
1504         }
1505     }
1506     return $bib_result;
1507 }
1508
1509 1;
1510 __END__
1511
1512 =head1 AUTHOR
1513
1514 Koha Development Team <http://koha-community.org/>
1515
1516 Galen Charlton <galen.charlton@liblime.com>
1517
1518 =cut