Bug 7475: Teach matching rules to handle authorities
[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 )) {
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);
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);
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);
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
293     my $import_record_id = _create_import_record($batch_id, $record_sequence, $marc_record, 'auth', $encoding, $z3950random);
294     _add_auth_fields($import_record_id, $marc_record);
295     _update_batch_record_counts($batch_id) if $update_counts;
296     return $import_record_id;
297 }
298
299 =head2 ModAuthInBatch
300
301   ModAuthInBatch($import_record_id, $marc_record);
302
303 =cut
304
305 sub ModAuthInBatch {
306     my ($import_record_id, $marc_record) = @_;
307
308     _update_import_record_marc($import_record_id, $marc_record);
309
310 }
311
312 =head2 BatchStageMarcRecords
313
314   ($batch_id, $num_records, $num_items, @invalid_records) = 
315     BatchStageMarcRecords($record_type, $encoding, $marc_records, $file_name,
316                           $comments, $branch_code, $parse_items,
317                           $leave_as_staging, 
318                           $progress_interval, $progress_callback);
319
320 =cut
321
322 sub  BatchStageMarcRecords {
323     my $record_type = shift;
324     my $encoding = shift;
325     my $marc_records = shift;
326     my $file_name = shift;
327     my $comments = shift;
328     my $branch_code = shift;
329     my $parse_items = shift;
330     my $leave_as_staging = shift;
331    
332     # optional callback to monitor status 
333     # of job
334     my $progress_interval = 0;
335     my $progress_callback = undef;
336     if ($#_ == 1) {
337         $progress_interval = shift;
338         $progress_callback = shift;
339         $progress_interval = 0 unless $progress_interval =~ /^\d+$/ and $progress_interval > 0;
340         $progress_interval = 0 unless 'CODE' eq ref $progress_callback;
341     } 
342     
343     my $batch_id = AddImportBatch( {
344             overlay_action => 'create_new',
345             import_status => 'staging',
346             batch_type => 'batch',
347             file_name => $file_name,
348             comments => $comments,
349         } );
350     if ($parse_items) {
351         SetImportBatchItemAction($batch_id, 'always_add');
352     } else {
353         SetImportBatchItemAction($batch_id, 'ignore');
354     }
355
356     my @invalid_records = ();
357     my $num_valid = 0;
358     my $num_items = 0;
359     # FIXME - for now, we're dealing only with bibs
360     my $rec_num = 0;
361     foreach my $marc_blob (split(/\x1D/, $marc_records)) {
362         $marc_blob =~ s/^\s+//g;
363         $marc_blob =~ s/\s+$//g;
364         next unless $marc_blob;
365         $rec_num++;
366         if ($progress_interval and (0 == ($rec_num % $progress_interval))) {
367             &$progress_callback($rec_num);
368         }
369         my ($marc_record, $charset_guessed, $char_errors) =
370             MarcToUTF8Record($marc_blob, C4::Context->preference("marcflavour"), $encoding);
371
372         $encoding = $charset_guessed unless $encoding;
373
374         my $import_record_id;
375         if (scalar($marc_record->fields()) == 0) {
376             push @invalid_records, $marc_blob;
377         } else {
378             $num_valid++;
379             if ($record_type eq 'biblio') {
380                 $import_record_id = AddBiblioToBatch($batch_id, $rec_num, $marc_record, $encoding, int(rand(99999)), 0);
381                 if ($parse_items) {
382                     my @import_items_ids = AddItemsToImportBiblio($batch_id, $import_record_id, $marc_record, 0);
383                     $num_items += scalar(@import_items_ids);
384                 }
385             } elsif ($record_type eq 'auth') {
386                 $import_record_id = AddAuthToBatch($batch_id, $rec_num, $marc_record, $encoding, int(rand(99999)), 0);
387             }
388         }
389     }
390     unless ($leave_as_staging) {
391         SetImportBatchStatus($batch_id, 'staged');
392     }
393     # FIXME branch_code, number of bibs, number of items
394     _update_batch_record_counts($batch_id);
395     return ($batch_id, $num_valid, $num_items, @invalid_records);
396 }
397
398 =head2 AddItemsToImportBiblio
399
400   my @import_items_ids = AddItemsToImportBiblio($batch_id, 
401                 $import_record_id, $marc_record, $update_counts);
402
403 =cut
404
405 sub AddItemsToImportBiblio {
406     my $batch_id = shift;
407     my $import_record_id = shift;
408     my $marc_record = shift;
409     my $update_counts = @_ ? shift : 0;
410
411     my @import_items_ids = ();
412    
413     my $dbh = C4::Context->dbh; 
414     my ($item_tag,$item_subfield) = &GetMarcFromKohaField("items.itemnumber",'');
415     foreach my $item_field ($marc_record->field($item_tag)) {
416         my $item_marc = MARC::Record->new();
417         $item_marc->leader("00000    a              "); # must set Leader/09 to 'a'
418         $item_marc->append_fields($item_field);
419         $marc_record->delete_field($item_field);
420         my $sth = $dbh->prepare_cached("INSERT INTO import_items (import_record_id, status, marcxml)
421                                         VALUES (?, ?, ?)");
422         $sth->bind_param(1, $import_record_id);
423         $sth->bind_param(2, 'staged');
424         $sth->bind_param(3, $item_marc->as_xml());
425         $sth->execute();
426         push @import_items_ids, $dbh->{'mysql_insertid'};
427         $sth->finish();
428     }
429
430     if ($#import_items_ids > -1) {
431         _update_batch_record_counts($batch_id) if $update_counts;
432         _update_import_record_marc($import_record_id, $marc_record);
433     }
434     return @import_items_ids;
435 }
436
437 =head2 BatchFindDuplicates
438
439   my $num_with_matches = BatchFindDuplicates($batch_id, $matcher,
440              $max_matches, $progress_interval, $progress_callback);
441
442 Goes through the records loaded in the batch and attempts to 
443 find duplicates for each one.  Sets the matching status 
444 of each record to "no_match" or "auto_match" as appropriate.
445
446 The $max_matches parameter is optional; if it is not supplied,
447 it defaults to 10.
448
449 The $progress_interval and $progress_callback parameters are 
450 optional; if both are supplied, the sub referred to by
451 $progress_callback will be invoked every $progress_interval
452 records using the number of records processed as the 
453 singular argument.
454
455 =cut
456
457 sub BatchFindDuplicates {
458     my $batch_id = shift;
459     my $matcher = shift;
460     my $max_matches = @_ ? shift : 10;
461
462     # optional callback to monitor status 
463     # of job
464     my $progress_interval = 0;
465     my $progress_callback = undef;
466     if ($#_ == 1) {
467         $progress_interval = shift;
468         $progress_callback = shift;
469         $progress_interval = 0 unless $progress_interval =~ /^\d+$/ and $progress_interval > 0;
470         $progress_interval = 0 unless 'CODE' eq ref $progress_callback;
471     }
472
473     my $dbh = C4::Context->dbh;
474
475     my $sth = $dbh->prepare("SELECT import_record_id, record_type, marc
476                              FROM import_records
477                              WHERE import_batch_id = ?");
478     $sth->execute($batch_id);
479     my $num_with_matches = 0;
480     my $rec_num = 0;
481     while (my $rowref = $sth->fetchrow_hashref) {
482         $rec_num++;
483         if ($progress_interval and (0 == ($rec_num % $progress_interval))) {
484             &$progress_callback($rec_num);
485         }
486         my $marc_record = MARC::Record->new_from_usmarc($rowref->{'marc'});
487         my @matches = ();
488         if (defined $matcher) {
489             @matches = $matcher->get_matches($marc_record, $max_matches);
490         }
491         if (scalar(@matches) > 0) {
492             $num_with_matches++;
493             SetImportRecordMatches($rowref->{'import_record_id'}, @matches);
494             SetImportRecordOverlayStatus($rowref->{'import_record_id'}, 'auto_match');
495         } else {
496             SetImportRecordMatches($rowref->{'import_record_id'}, ());
497             SetImportRecordOverlayStatus($rowref->{'import_record_id'}, 'no_match');
498         }
499     }
500     $sth->finish();
501     return $num_with_matches;
502 }
503
504 =head2 BatchCommitRecords
505
506   my ($num_added, $num_updated, $num_items_added, $num_items_errored, $num_ignored) =
507         BatchCommitRecords($batch_id, $framework,
508         $progress_interval, $progress_callback);
509
510 =cut
511
512 sub BatchCommitRecords {
513     my $batch_id = shift;
514     my $framework = shift;
515
516     # optional callback to monitor status 
517     # of job
518     my $progress_interval = 0;
519     my $progress_callback = undef;
520     if ($#_ == 1) {
521         $progress_interval = shift;
522         $progress_callback = shift;
523         $progress_interval = 0 unless $progress_interval =~ /^\d+$/ and $progress_interval > 0;
524         $progress_interval = 0 unless 'CODE' eq ref $progress_callback;
525     }
526
527     my $record_type;
528     my $num_added = 0;
529     my $num_updated = 0;
530     my $num_items_added = 0;
531     my $num_items_errored = 0;
532     my $num_ignored = 0;
533     # commit (i.e., save, all records in the batch)
534     SetImportBatchStatus('importing');
535     my $overlay_action = GetImportBatchOverlayAction($batch_id);
536     my $nomatch_action = GetImportBatchNoMatchAction($batch_id);
537     my $item_action = GetImportBatchItemAction($batch_id);
538     my $item_tag;
539     my $item_subfield;
540     my $dbh = C4::Context->dbh;
541     my $sth = $dbh->prepare("SELECT import_records.import_record_id, record_type, status, overlay_status, marc, encoding
542                              FROM import_records
543                              LEFT JOIN import_auths ON (import_records.import_record_id=import_auths.import_record_id)
544                              LEFT JOIN import_biblios ON (import_records.import_record_id=import_biblios.import_record_id)
545                              WHERE import_batch_id = ?");
546     $sth->execute($batch_id);
547     my $rec_num = 0;
548     while (my $rowref = $sth->fetchrow_hashref) {
549         $record_type = $rowref->{'record_type'};
550         $rec_num++;
551         if ($progress_interval and (0 == ($rec_num % $progress_interval))) {
552             &$progress_callback($rec_num);
553         }
554         if ($rowref->{'status'} eq 'error' or $rowref->{'status'} eq 'imported') {
555             $num_ignored++;
556             next;
557         }
558
559         my $marc_record = MARC::Record->new_from_usmarc($rowref->{'marc'});
560
561         if ($record_type eq 'biblio') {
562             # remove any item tags - rely on BatchCommitItems
563             ($item_tag,$item_subfield) = &GetMarcFromKohaField("items.itemnumber",'');
564             foreach my $item_field ($marc_record->field($item_tag)) {
565                 $marc_record->delete_field($item_field);
566             }
567         }
568
569         my ($record_result, $item_result, $record_match) =
570             _get_commit_action($overlay_action, $nomatch_action, $item_action, 
571                                $rowref->{'overlay_status'}, $rowref->{'import_record_id'}, $record_type);
572
573         my $recordid;
574         my $query;
575         if ($record_result eq 'create_new') {
576             $num_added++;
577             if ($record_type eq 'biblio') {
578                 my $biblioitemnumber;
579                 ($recordid, $biblioitemnumber) = AddBiblio($marc_record, $framework);
580                 $query = "UPDATE import_biblios SET matched_biblionumber = ? WHERE import_record_id = ?";
581                 if ($item_result eq 'create_new') {
582                     my ($bib_items_added, $bib_items_errored) = BatchCommitItems($rowref->{'import_record_id'}, $recordid);
583                     $num_items_added += $bib_items_added;
584                     $num_items_errored += $bib_items_errored;
585                 }
586             } else {
587                 my $authid = AddAuthority($marc_record, undef, GuessAuthTypeCode($marc_record));
588                 $query = "UPDATE import_auths SET matched_authid = ? WHERE import_record_id = ?";
589             }
590             my $sth = $dbh->prepare_cached($query);
591             $sth->execute($recordid, $rowref->{'import_record_id'});
592             $sth->finish();
593             SetImportRecordStatus($rowref->{'import_record_id'}, 'imported');
594         } elsif ($record_result eq 'replace') {
595             $num_updated++;
596             $recordid = $record_match;
597             my $oldxml;
598             if ($record_type eq 'biblio') {
599                 my ($count, $oldbiblio) = GetBiblio($recordid);
600                 $oldxml = GetXmlBiblio($recordid);
601
602                 # remove item fields so that they don't get
603                 # added again if record is reverted
604                 my $old_marc = MARC::Record->new_from_xml(StripNonXmlChars($oldxml), 'UTF-8', $rowref->{'encoding'});
605                 foreach my $item_field ($old_marc->field($item_tag)) {
606                     $old_marc->delete_field($item_field);
607                 }
608                 $oldxml = $old_marc->as_xml();
609
610                 ModBiblio($marc_record, $recordid, $oldbiblio->{'frameworkcode'});
611                 $query = "UPDATE import_biblios SET matched_biblionumber = ? WHERE import_record_id = ?";
612
613                 if ($item_result eq 'create_new') {
614                     my ($bib_items_added, $bib_items_errored) = BatchCommitItems($rowref->{'import_record_id'}, $recordid);
615                     $num_items_added += $bib_items_added;
616                     $num_items_errored += $bib_items_errored;
617                 }
618             } else {
619                 my $oldxml = GetAuthorityXML($recordid);
620
621                 ModAuthority($recordid, $marc_record, GuessAuthTypeCode($marc_record));
622                 $query = "UPDATE import_auths SET matched_authid = ? WHERE import_record_id = ?";
623             }
624             my $sth = $dbh->prepare_cached("UPDATE import_records SET marcxml_old = ? WHERE import_record_id = ?");
625             $sth->execute($oldxml, $rowref->{'import_record_id'});
626             $sth->finish();
627             my $sth2 = $dbh->prepare_cached($query);
628             $sth2->execute($recordid, $rowref->{'import_record_id'});
629             $sth2->finish();
630             SetImportRecordOverlayStatus($rowref->{'import_record_id'}, 'match_applied');
631             SetImportRecordStatus($rowref->{'import_record_id'}, 'imported');
632         } elsif ($record_result eq 'ignore') {
633             $num_ignored++;
634             if ($record_type eq 'biblio' and defined $recordid and $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                 # still need to record the matched biblionumber so that the
639                 # items can be reverted
640                 my $sth2 = $dbh->prepare_cached("UPDATE import_biblios SET matched_biblionumber = ? WHERE import_record_id = ?");
641                 $sth2->execute($recordid, $rowref->{'import_record_id'});
642                 SetImportRecordOverlayStatus($rowref->{'import_record_id'}, 'match_applied');
643             }
644             SetImportRecordStatus($rowref->{'import_record_id'}, 'ignored');
645         }
646     }
647     $sth->finish();
648     SetImportBatchStatus($batch_id, 'imported');
649     return ($num_added, $num_updated, $num_items_added, $num_items_errored, $num_ignored);
650 }
651
652 =head2 BatchCommitItems
653
654   ($num_items_added, $num_items_errored) = 
655          BatchCommitItems($import_record_id, $biblionumber);
656
657 =cut
658
659 sub BatchCommitItems {
660     my ($import_record_id, $biblionumber) = @_;
661
662     my $dbh = C4::Context->dbh;
663
664     my $num_items_added = 0;
665     my $num_items_errored = 0;
666     my $sth = $dbh->prepare("SELECT import_items_id, import_items.marcxml, encoding
667                              FROM import_items
668                              JOIN import_records USING (import_record_id)
669                              WHERE import_record_id = ?
670                              ORDER BY import_items_id");
671     $sth->bind_param(1, $import_record_id);
672     $sth->execute();
673     while (my $row = $sth->fetchrow_hashref()) {
674         my $item_marc = MARC::Record->new_from_xml(StripNonXmlChars($row->{'marcxml'}), 'UTF-8', $row->{'encoding'});
675         # FIXME - duplicate barcode check needs to become part of AddItemFromMarc()
676         my $item = TransformMarcToKoha($dbh, $item_marc);
677         my $duplicate_barcode = exists($item->{'barcode'}) && GetItemnumberFromBarcode($item->{'barcode'});
678         if ($duplicate_barcode) {
679             my $updsth = $dbh->prepare("UPDATE import_items SET status = ?, import_error = ? WHERE import_items_id = ?");
680             $updsth->bind_param(1, 'error');
681             $updsth->bind_param(2, 'duplicate item barcode');
682             $updsth->bind_param(3, $row->{'import_items_id'});
683             $updsth->execute();
684             $num_items_errored++;
685         } else {
686             my ($item_biblionumber, $biblioitemnumber, $itemnumber) = AddItemFromMarc($item_marc, $biblionumber);
687             my $updsth = $dbh->prepare("UPDATE import_items SET status = ?, itemnumber = ? WHERE import_items_id = ?");
688             $updsth->bind_param(1, 'imported');
689             $updsth->bind_param(2, $itemnumber);
690             $updsth->bind_param(3, $row->{'import_items_id'});
691             $updsth->execute();
692             $updsth->finish();
693             $num_items_added++;
694         }
695     }
696     $sth->finish();
697     return ($num_items_added, $num_items_errored);
698 }
699
700 =head2 BatchRevertRecords
701
702   my ($num_deleted, $num_errors, $num_reverted, $num_items_deleted, 
703       $num_ignored) = BatchRevertRecords($batch_id);
704
705 =cut
706
707 sub BatchRevertRecords {
708     my $batch_id = shift;
709
710     my $record_type;
711     my $num_deleted = 0;
712     my $num_errors = 0;
713     my $num_reverted = 0;
714     my $num_ignored = 0;
715     my $num_items_deleted = 0;
716     # commit (i.e., save, all records in the batch)
717     SetImportBatchStatus('reverting');
718     my $overlay_action = GetImportBatchOverlayAction($batch_id);
719     my $nomatch_action = GetImportBatchNoMatchAction($batch_id);
720     my $dbh = C4::Context->dbh;
721     my $sth = $dbh->prepare("SELECT import_records.import_record_id, record_type, status, overlay_status, marcxml_old, encoding, matched_biblionumber, matched_authid
722                              FROM import_records
723                              LEFT JOIN import_auths ON (import_records.import_record_id=import_auths.import_record_id)
724                              LEFT JOIN import_biblios ON (import_records.import_record_id=import_biblios.import_record_id)
725                              WHERE import_batch_id = ?");
726     $sth->execute($batch_id);
727     while (my $rowref = $sth->fetchrow_hashref) {
728         $record_type = $rowref->{'record_type'};
729         if ($rowref->{'status'} eq 'error' or $rowref->{'status'} eq 'reverted') {
730             $num_ignored++;
731             next;
732         }
733
734         my $record_result = _get_revert_action($overlay_action, $rowref->{'overlay_status'}, $rowref->{'status'});
735
736         if ($record_result eq 'delete') {
737             my $error = undef;
738             if  ($record_type eq 'biblio') {
739                 $num_items_deleted += BatchRevertItems($rowref->{'import_record_id'}, $rowref->{'matched_biblionumber'});
740                 $error = DelBiblio($rowref->{'matched_biblionumber'});
741             } else {
742                 my $deletedauthid = DelAuthority($rowref->{'matched_authid'});
743             }
744             if (defined $error) {
745                 $num_errors++;
746             } else {
747                 $num_deleted++;
748                 SetImportRecordStatus($rowref->{'import_record_id'}, 'reverted');
749             }
750         } elsif ($record_result eq 'restore') {
751             $num_reverted++;
752             my $old_record = MARC::Record->new_from_xml(StripNonXmlChars($rowref->{'marcxml_old'}), 'UTF-8', $rowref->{'encoding'});
753             if ($record_type eq 'biblio') {
754                 my $biblionumber = $rowref->{'matched_biblionumber'};
755                 my ($count, $oldbiblio) = GetBiblio($biblionumber);
756                 $num_items_deleted += BatchRevertItems($rowref->{'import_record_id'}, $rowref->{'matched_biblionumber'});
757                 ModBiblio($old_record, $biblionumber, $oldbiblio->{'frameworkcode'});
758             } else {
759                 my $authid = $rowref->{'matched_authid'};
760                 ModAuthority($authid, $old_record, GuessAuthTypeCode($old_record));
761             }
762             SetImportRecordStatus($rowref->{'import_record_id'}, 'reverted');
763         } elsif ($record_result eq 'ignore') {
764             if ($record_type eq 'biblio') {
765                 $num_items_deleted += BatchRevertItems($rowref->{'import_record_id'}, $rowref->{'matched_biblionumber'});
766             }
767             SetImportRecordStatus($rowref->{'import_record_id'}, 'reverted');
768         }
769         my $query;
770         if ($record_type eq 'biblio') {
771             # remove matched_biblionumber only if there is no 'imported' item left
772             $query = "UPDATE import_biblios SET matched_biblionumber = NULL WHERE import_record_id = ?";
773             $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')";
774         } else {
775             $query = "UPDATE import_auths SET matched_authid = NULL WHERE import_record_id = ?";
776         }
777         my $sth2 = $dbh->prepare_cached($query);
778         $sth2->execute($rowref->{'import_record_id'});
779     }
780
781     $sth->finish();
782     SetImportBatchStatus($batch_id, 'reverted');
783     return ($num_deleted, $num_errors, $num_reverted, $num_items_deleted, $num_ignored);
784 }
785
786 =head2 BatchRevertItems
787
788   my $num_items_deleted = BatchRevertItems($import_record_id, $biblionumber);
789
790 =cut
791
792 sub BatchRevertItems {
793     my ($import_record_id, $biblionumber) = @_;
794
795     my $dbh = C4::Context->dbh;
796     my $num_items_deleted = 0;
797
798     my $sth = $dbh->prepare_cached("SELECT import_items_id, itemnumber
799                                    FROM import_items
800                                    JOIN items USING (itemnumber)
801                                    WHERE import_record_id = ?");
802     $sth->bind_param(1, $import_record_id);
803     $sth->execute();
804     while (my $row = $sth->fetchrow_hashref()) {
805         my $error = DelItemCheck($dbh, $biblionumber, $row->{'itemnumber'});
806         if ($error == 1){
807             my $updsth = $dbh->prepare("UPDATE import_items SET status = ? WHERE import_items_id = ?");
808             $updsth->bind_param(1, 'reverted');
809             $updsth->bind_param(2, $row->{'import_items_id'});
810             $updsth->execute();
811             $updsth->finish();
812             $num_items_deleted++;
813         }
814         else {
815             next;
816         }
817     }
818     $sth->finish();
819     return $num_items_deleted;
820 }
821
822 =head2 CleanBatch
823
824   CleanBatch($batch_id)
825
826 Deletes all staged records from the import batch
827 and sets the status of the batch to 'cleaned'.  Note
828 that deleting a stage record does *not* affect
829 any record that has been committed to the database.
830
831 =cut
832
833 sub CleanBatch {
834     my $batch_id = shift;
835     return unless defined $batch_id;
836
837     C4::Context->dbh->do('DELETE FROM import_records WHERE import_batch_id = ?', {}, $batch_id);
838     SetImportBatchStatus($batch_id, 'cleaned');
839 }
840
841 =head2 GetAllImportBatches
842
843   my $results = GetAllImportBatches();
844
845 Returns a references to an array of hash references corresponding
846 to all import_batches rows (of batch_type 'batch'), sorted in 
847 ascending order by import_batch_id.
848
849 =cut
850
851 sub  GetAllImportBatches {
852     my $dbh = C4::Context->dbh;
853     my $sth = $dbh->prepare_cached("SELECT * FROM import_batches
854                                     WHERE batch_type IN ('batch', 'webservice')
855                                     ORDER BY import_batch_id ASC");
856
857     my $results = [];
858     $sth->execute();
859     while (my $row = $sth->fetchrow_hashref) {
860         push @$results, $row;
861     }
862     $sth->finish();
863     return $results;
864 }
865
866 =head2 GetStagedWebserviceBatches
867
868   my $batch_ids = GetStagedWebserviceBatches();
869
870 Returns a references to an array of batch id's
871 of batch_type 'webservice' that are not imported
872
873 =cut
874
875 my $PENDING_WEBSERVICE_BATCHES_QRY = <<EOQ;
876 SELECT import_batch_id FROM import_batches
877 WHERE batch_type = 'webservice'
878 AND import_status = 'staged'
879 EOQ
880 sub  GetStagedWebserviceBatches {
881     my $dbh = C4::Context->dbh;
882     return $dbh->selectcol_arrayref($PENDING_WEBSERVICE_BATCHES_QRY);
883 }
884
885 =head2 GetImportBatchRangeDesc
886
887   my $results = GetImportBatchRangeDesc($offset, $results_per_group);
888
889 Returns a reference to an array of hash references corresponding to
890 import_batches rows (sorted in descending order by import_batch_id)
891 start at the given offset.
892
893 =cut
894
895 sub GetImportBatchRangeDesc {
896     my ($offset, $results_per_group) = @_;
897
898     my $dbh = C4::Context->dbh;
899     my $query = "SELECT * FROM import_batches
900                                     WHERE batch_type IN ('batch', 'webservice')
901                                     ORDER BY import_batch_id DESC";
902     my @params;
903     if ($results_per_group){
904         $query .= " LIMIT ?";
905         push(@params, $results_per_group);
906     }
907     if ($offset){
908         $query .= " OFFSET ?";
909         push(@params, $offset);
910     }
911     my $sth = $dbh->prepare_cached($query);
912     $sth->execute(@params);
913     my $results = $sth->fetchall_arrayref({});
914     $sth->finish();
915     return $results;
916 }
917
918 =head2 GetItemNumbersFromImportBatch
919
920   my @itemsnos = GetItemNumbersFromImportBatch($batch_id);
921
922 =cut
923
924 sub GetItemNumbersFromImportBatch {
925         my ($batch_id) = @_;
926         my $dbh = C4::Context->dbh;
927         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=?");
928         $sth->execute($batch_id);
929         my @items ;
930         while ( my ($itm) = $sth->fetchrow_array ) {
931                 push @items, $itm;
932         }
933         return @items;
934 }
935
936 =head2 GetNumberOfImportBatches 
937
938   my $count = GetNumberOfImportBatches();
939
940 =cut
941
942 sub GetNumberOfNonZ3950ImportBatches {
943     my $dbh = C4::Context->dbh;
944     my $sth = $dbh->prepare("SELECT COUNT(*) FROM import_batches WHERE batch_type != 'z3950'");
945     $sth->execute();
946     my ($count) = $sth->fetchrow_array();
947     $sth->finish();
948     return $count;
949 }
950
951 =head2 GetImportRecordsRange
952
953   my $results = GetImportRecordsRange($batch_id, $offset, $results_per_group);
954
955 Returns a reference to an array of hash references corresponding to
956 import_biblios/import_auths/import_records rows for a given batch
957 starting at the given offset.
958
959 =cut
960
961 sub GetImportRecordsRange {
962     my ($batch_id, $offset, $results_per_group, $status) = @_;
963
964     my $dbh = C4::Context->dbh;
965     my $query = "SELECT title, author, isbn, issn, authorized_heading, import_records.import_record_id,
966                                            record_sequence, status, overlay_status,
967                                            matched_biblionumber, matched_authid, record_type
968                                     FROM   import_records
969                                     LEFT JOIN import_auths ON (import_records.import_record_id=import_auths.import_record_id)
970                                     LEFT JOIN import_biblios ON (import_records.import_record_id=import_biblios.import_record_id)
971                                     WHERE  import_batch_id = ?";
972     my @params;
973     push(@params, $batch_id);
974     if ($status) {
975         $query .= " AND status=?";
976         push(@params,$status);
977     }
978     $query.=" ORDER BY import_record_id";
979
980     if($results_per_group){
981         $query .= " LIMIT ?";
982         push(@params, $results_per_group);
983     }
984     if($offset){
985         $query .= " OFFSET ?";
986         push(@params, $offset);
987     }
988     my $sth = $dbh->prepare_cached($query);
989     $sth->execute(@params);
990     my $results = $sth->fetchall_arrayref({});
991     $sth->finish();
992     return $results;
993
994 }
995
996 =head2 GetBestRecordMatch
997
998   my $record_id = GetBestRecordMatch($import_record_id);
999
1000 =cut
1001
1002 sub GetBestRecordMatch {
1003     my ($import_record_id) = @_;
1004
1005     my $dbh = C4::Context->dbh;
1006     my $sth = $dbh->prepare("SELECT candidate_match_id
1007                              FROM   import_record_matches
1008                              WHERE  import_record_id = ?
1009                              ORDER BY score DESC, candidate_match_id DESC");
1010     $sth->execute($import_record_id);
1011     my ($record_id) = $sth->fetchrow_array();
1012     $sth->finish();
1013     return $record_id;
1014 }
1015
1016 =head2 GetImportBatchStatus
1017
1018   my $status = GetImportBatchStatus($batch_id);
1019
1020 =cut
1021
1022 sub GetImportBatchStatus {
1023     my ($batch_id) = @_;
1024
1025     my $dbh = C4::Context->dbh;
1026     my $sth = $dbh->prepare("SELECT import_status FROM import_batches WHERE import_batch_id = ?");
1027     $sth->execute($batch_id);
1028     my ($status) = $sth->fetchrow_array();
1029     $sth->finish();
1030     return $status;
1031
1032 }
1033
1034 =head2 SetImportBatchStatus
1035
1036   SetImportBatchStatus($batch_id, $new_status);
1037
1038 =cut
1039
1040 sub SetImportBatchStatus {
1041     my ($batch_id, $new_status) = @_;
1042
1043     my $dbh = C4::Context->dbh;
1044     my $sth = $dbh->prepare("UPDATE import_batches SET import_status = ? WHERE import_batch_id = ?");
1045     $sth->execute($new_status, $batch_id);
1046     $sth->finish();
1047
1048 }
1049
1050 =head2 GetImportBatchOverlayAction
1051
1052   my $overlay_action = GetImportBatchOverlayAction($batch_id);
1053
1054 =cut
1055
1056 sub GetImportBatchOverlayAction {
1057     my ($batch_id) = @_;
1058
1059     my $dbh = C4::Context->dbh;
1060     my $sth = $dbh->prepare("SELECT overlay_action FROM import_batches WHERE import_batch_id = ?");
1061     $sth->execute($batch_id);
1062     my ($overlay_action) = $sth->fetchrow_array();
1063     $sth->finish();
1064     return $overlay_action;
1065
1066 }
1067
1068
1069 =head2 SetImportBatchOverlayAction
1070
1071   SetImportBatchOverlayAction($batch_id, $new_overlay_action);
1072
1073 =cut
1074
1075 sub SetImportBatchOverlayAction {
1076     my ($batch_id, $new_overlay_action) = @_;
1077
1078     my $dbh = C4::Context->dbh;
1079     my $sth = $dbh->prepare("UPDATE import_batches SET overlay_action = ? WHERE import_batch_id = ?");
1080     $sth->execute($new_overlay_action, $batch_id);
1081     $sth->finish();
1082
1083 }
1084
1085 =head2 GetImportBatchNoMatchAction
1086
1087   my $nomatch_action = GetImportBatchNoMatchAction($batch_id);
1088
1089 =cut
1090
1091 sub GetImportBatchNoMatchAction {
1092     my ($batch_id) = @_;
1093
1094     my $dbh = C4::Context->dbh;
1095     my $sth = $dbh->prepare("SELECT nomatch_action FROM import_batches WHERE import_batch_id = ?");
1096     $sth->execute($batch_id);
1097     my ($nomatch_action) = $sth->fetchrow_array();
1098     $sth->finish();
1099     return $nomatch_action;
1100
1101 }
1102
1103
1104 =head2 SetImportBatchNoMatchAction
1105
1106   SetImportBatchNoMatchAction($batch_id, $new_nomatch_action);
1107
1108 =cut
1109
1110 sub SetImportBatchNoMatchAction {
1111     my ($batch_id, $new_nomatch_action) = @_;
1112
1113     my $dbh = C4::Context->dbh;
1114     my $sth = $dbh->prepare("UPDATE import_batches SET nomatch_action = ? WHERE import_batch_id = ?");
1115     $sth->execute($new_nomatch_action, $batch_id);
1116     $sth->finish();
1117
1118 }
1119
1120 =head2 GetImportBatchItemAction
1121
1122   my $item_action = GetImportBatchItemAction($batch_id);
1123
1124 =cut
1125
1126 sub GetImportBatchItemAction {
1127     my ($batch_id) = @_;
1128
1129     my $dbh = C4::Context->dbh;
1130     my $sth = $dbh->prepare("SELECT item_action FROM import_batches WHERE import_batch_id = ?");
1131     $sth->execute($batch_id);
1132     my ($item_action) = $sth->fetchrow_array();
1133     $sth->finish();
1134     return $item_action;
1135
1136 }
1137
1138
1139 =head2 SetImportBatchItemAction
1140
1141   SetImportBatchItemAction($batch_id, $new_item_action);
1142
1143 =cut
1144
1145 sub SetImportBatchItemAction {
1146     my ($batch_id, $new_item_action) = @_;
1147
1148     my $dbh = C4::Context->dbh;
1149     my $sth = $dbh->prepare("UPDATE import_batches SET item_action = ? WHERE import_batch_id = ?");
1150     $sth->execute($new_item_action, $batch_id);
1151     $sth->finish();
1152
1153 }
1154
1155 =head2 GetImportBatchMatcher
1156
1157   my $matcher_id = GetImportBatchMatcher($batch_id);
1158
1159 =cut
1160
1161 sub GetImportBatchMatcher {
1162     my ($batch_id) = @_;
1163
1164     my $dbh = C4::Context->dbh;
1165     my $sth = $dbh->prepare("SELECT matcher_id FROM import_batches WHERE import_batch_id = ?");
1166     $sth->execute($batch_id);
1167     my ($matcher_id) = $sth->fetchrow_array();
1168     $sth->finish();
1169     return $matcher_id;
1170
1171 }
1172
1173
1174 =head2 SetImportBatchMatcher
1175
1176   SetImportBatchMatcher($batch_id, $new_matcher_id);
1177
1178 =cut
1179
1180 sub SetImportBatchMatcher {
1181     my ($batch_id, $new_matcher_id) = @_;
1182
1183     my $dbh = C4::Context->dbh;
1184     my $sth = $dbh->prepare("UPDATE import_batches SET matcher_id = ? WHERE import_batch_id = ?");
1185     $sth->execute($new_matcher_id, $batch_id);
1186     $sth->finish();
1187
1188 }
1189
1190 =head2 GetImportRecordOverlayStatus
1191
1192   my $overlay_status = GetImportRecordOverlayStatus($import_record_id);
1193
1194 =cut
1195
1196 sub GetImportRecordOverlayStatus {
1197     my ($import_record_id) = @_;
1198
1199     my $dbh = C4::Context->dbh;
1200     my $sth = $dbh->prepare("SELECT overlay_status FROM import_records WHERE import_record_id = ?");
1201     $sth->execute($import_record_id);
1202     my ($overlay_status) = $sth->fetchrow_array();
1203     $sth->finish();
1204     return $overlay_status;
1205
1206 }
1207
1208
1209 =head2 SetImportRecordOverlayStatus
1210
1211   SetImportRecordOverlayStatus($import_record_id, $new_overlay_status);
1212
1213 =cut
1214
1215 sub SetImportRecordOverlayStatus {
1216     my ($import_record_id, $new_overlay_status) = @_;
1217
1218     my $dbh = C4::Context->dbh;
1219     my $sth = $dbh->prepare("UPDATE import_records SET overlay_status = ? WHERE import_record_id = ?");
1220     $sth->execute($new_overlay_status, $import_record_id);
1221     $sth->finish();
1222
1223 }
1224
1225 =head2 GetImportRecordStatus
1226
1227   my $overlay_status = GetImportRecordStatus($import_record_id);
1228
1229 =cut
1230
1231 sub GetImportRecordStatus {
1232     my ($import_record_id) = @_;
1233
1234     my $dbh = C4::Context->dbh;
1235     my $sth = $dbh->prepare("SELECT status FROM import_records WHERE import_record_id = ?");
1236     $sth->execute($import_record_id);
1237     my ($overlay_status) = $sth->fetchrow_array();
1238     $sth->finish();
1239     return $overlay_status;
1240
1241 }
1242
1243
1244 =head2 SetImportRecordStatus
1245
1246   SetImportRecordStatus($import_record_id, $new_overlay_status);
1247
1248 =cut
1249
1250 sub SetImportRecordStatus {
1251     my ($import_record_id, $new_overlay_status) = @_;
1252
1253     my $dbh = C4::Context->dbh;
1254     my $sth = $dbh->prepare("UPDATE import_records SET status = ? WHERE import_record_id = ?");
1255     $sth->execute($new_overlay_status, $import_record_id);
1256     $sth->finish();
1257
1258 }
1259
1260 =head2 GetImportRecordMatches
1261
1262   my $results = GetImportRecordMatches($import_record_id, $best_only);
1263
1264 =cut
1265
1266 sub GetImportRecordMatches {
1267     my $import_record_id = shift;
1268     my $best_only = @_ ? shift : 0;
1269
1270     my $dbh = C4::Context->dbh;
1271     # FIXME currently biblio only
1272     my $sth = $dbh->prepare_cached("SELECT title, author, biblionumber,
1273                                     candidate_match_id, score, record_type
1274                                     FROM import_records
1275                                     JOIN import_record_matches USING (import_record_id)
1276                                     LEFT JOIN biblio ON (biblionumber = candidate_match_id)
1277                                     WHERE import_record_id = ?
1278                                     ORDER BY score DESC, biblionumber DESC");
1279     $sth->bind_param(1, $import_record_id);
1280     my $results = [];
1281     $sth->execute();
1282     while (my $row = $sth->fetchrow_hashref) {
1283         if ($row->{'record_type'} eq 'auth') {
1284             $row->{'authorized_heading'} = GetAuthorizedHeading( { authid => $row->{'candidate_match_id'} } );
1285         }
1286         next if ($row->{'record_type'} eq 'biblio' && not $row->{'biblionumber'});
1287         push @$results, $row;
1288         last if $best_only;
1289     }
1290     $sth->finish();
1291
1292     return $results;
1293     
1294 }
1295
1296
1297 =head2 SetImportRecordMatches
1298
1299   SetImportRecordMatches($import_record_id, @matches);
1300
1301 =cut
1302
1303 sub SetImportRecordMatches {
1304     my $import_record_id = shift;
1305     my @matches = @_;
1306
1307     my $dbh = C4::Context->dbh;
1308     my $delsth = $dbh->prepare("DELETE FROM import_record_matches WHERE import_record_id = ?");
1309     $delsth->execute($import_record_id);
1310     $delsth->finish();
1311
1312     my $sth = $dbh->prepare("INSERT INTO import_record_matches (import_record_id, candidate_match_id, score)
1313                                     VALUES (?, ?, ?)");
1314     foreach my $match (@matches) {
1315         $sth->execute($import_record_id, $match->{'record_id'}, $match->{'score'});
1316     }
1317 }
1318
1319
1320 # internal functions
1321
1322 sub _create_import_record {
1323     my ($batch_id, $record_sequence, $marc_record, $record_type, $encoding, $z3950random) = @_;
1324
1325     my $dbh = C4::Context->dbh;
1326     my $sth = $dbh->prepare("INSERT INTO import_records (import_batch_id, record_sequence, marc, marcxml, 
1327                                                          record_type, encoding, z3950random)
1328                                     VALUES (?, ?, ?, ?, ?, ?, ?)");
1329     $sth->execute($batch_id, $record_sequence, $marc_record->as_usmarc(), $marc_record->as_xml(),
1330                   $record_type, $encoding, $z3950random);
1331     my $import_record_id = $dbh->{'mysql_insertid'};
1332     $sth->finish();
1333     return $import_record_id;
1334 }
1335
1336 sub _update_import_record_marc {
1337     my ($import_record_id, $marc_record) = @_;
1338
1339     my $dbh = C4::Context->dbh;
1340     my $sth = $dbh->prepare("UPDATE import_records SET marc = ?, marcxml = ?
1341                              WHERE  import_record_id = ?");
1342     $sth->execute($marc_record->as_usmarc(), $marc_record->as_xml(C4::Context->preference('marcflavour')), $import_record_id);
1343     $sth->finish();
1344 }
1345
1346 sub _add_auth_fields {
1347     my ($import_record_id, $marc_record) = @_;
1348
1349     my $controlnumber;
1350     if ($marc_record->field('001')) {
1351         $controlnumber = $marc_record->field('001')->data();
1352     }
1353     my $authorized_heading = GetAuthorizedHeading($marc_record);
1354     my $dbh = C4::Context->dbh;
1355     my $sth = $dbh->prepare("INSERT INTO import_auths (import_record_id, controlnumber, authorized_heading) VALUES (?, ?, ?)");
1356     $sth->execute($import_record_id, $controlnumber, $authorized_heading);
1357     $sth->finish();
1358 }
1359
1360 sub _add_biblio_fields {
1361     my ($import_record_id, $marc_record) = @_;
1362
1363     my ($title, $author, $isbn, $issn) = _parse_biblio_fields($marc_record);
1364     my $dbh = C4::Context->dbh;
1365     # FIXME no controlnumber, originalsource
1366     $isbn = C4::Koha::_isbn_cleanup($isbn); # FIXME C4::Koha::_isbn_cleanup should be made public
1367     my $sth = $dbh->prepare("INSERT INTO import_biblios (import_record_id, title, author, isbn, issn) VALUES (?, ?, ?, ?, ?)");
1368     $sth->execute($import_record_id, $title, $author, $isbn, $issn);
1369     $sth->finish();
1370                 
1371 }
1372
1373 sub _update_biblio_fields {
1374     my ($import_record_id, $marc_record) = @_;
1375
1376     my ($title, $author, $isbn, $issn) = _parse_biblio_fields($marc_record);
1377     my $dbh = C4::Context->dbh;
1378     # FIXME no controlnumber, originalsource
1379     # FIXME 2 - should regularize normalization of ISBN wherever it is done
1380     $isbn =~ s/\(.*$//;
1381     $isbn =~ tr/ -_//;
1382     $isbn = uc $isbn;
1383     my $sth = $dbh->prepare("UPDATE import_biblios SET title = ?, author = ?, isbn = ?, issn = ?
1384                              WHERE  import_record_id = ?");
1385     $sth->execute($title, $author, $isbn, $issn, $import_record_id);
1386     $sth->finish();
1387 }
1388
1389 sub _parse_biblio_fields {
1390     my ($marc_record) = @_;
1391
1392     my $dbh = C4::Context->dbh;
1393     my $bibliofields = TransformMarcToKoha($dbh, $marc_record, '');
1394     return ($bibliofields->{'title'}, $bibliofields->{'author'}, $bibliofields->{'isbn'}, $bibliofields->{'issn'});
1395
1396 }
1397
1398 sub _update_batch_record_counts {
1399     my ($batch_id) = @_;
1400
1401     my $dbh = C4::Context->dbh;
1402     my $sth = $dbh->prepare_cached("UPDATE import_batches SET
1403                                         num_records = (
1404                                             SELECT COUNT(*)
1405                                             FROM import_records
1406                                             WHERE import_batch_id = import_batches.import_batch_id
1407                                             AND record_type = 'biblio'),
1408                                         num_items = (
1409                                             SELECT COUNT(*)
1410                                             FROM import_records
1411                                             JOIN import_items USING (import_record_id)
1412                                             WHERE import_batch_id = import_batches.import_batch_id
1413                                             AND record_type = 'biblio')
1414                                     WHERE import_batch_id = ?");
1415     $sth->bind_param(1, $batch_id);
1416     $sth->execute();
1417     $sth->finish();
1418 }
1419
1420 sub _get_commit_action {
1421     my ($overlay_action, $nomatch_action, $item_action, $overlay_status, $import_record_id, $record_type) = @_;
1422     
1423     if ($record_type eq 'biblio') {
1424         my ($bib_result, $bib_match, $item_result);
1425
1426         if ($overlay_status ne 'no_match') {
1427             $bib_match = GetBestRecordMatch($import_record_id);
1428             if ($overlay_action eq 'replace') {
1429                 $bib_result  = defined($bib_match) ? 'replace' : 'create_new';
1430             } elsif ($overlay_action eq 'create_new') {
1431                 $bib_result  = 'create_new';
1432             } elsif ($overlay_action eq 'ignore') {
1433                 $bib_result  = 'ignore';
1434             }
1435             $item_result = ($item_action eq 'always_add' or $item_action eq 'add_only_for_matches') ? 'create_new' : 'ignore';
1436         } else {
1437             $bib_result = $nomatch_action;
1438             $item_result = ($item_action eq 'always_add' or $item_action eq 'add_only_for_new')     ? 'create_new' : 'ignore';
1439         }
1440         return ($bib_result, $item_result, $bib_match);
1441     } else { # must be auths
1442         my ($auth_result, $auth_match);
1443
1444         if ($overlay_status ne 'no_match') {
1445             $auth_match = GetBestRecordMatch($import_record_id);
1446             if ($overlay_action eq 'replace') {
1447                 $auth_result  = defined($auth_match) ? 'replace' : 'create_new';
1448             } elsif ($overlay_action eq 'create_new') {
1449                 $auth_result  = 'create_new';
1450             } elsif ($overlay_action eq 'ignore') {
1451                 $auth_result  = 'ignore';
1452             }
1453         } else {
1454             $auth_result = $nomatch_action;
1455         }
1456
1457         return ($auth_result, undef, $auth_match);
1458
1459     }
1460 }
1461
1462 sub _get_revert_action {
1463     my ($overlay_action, $overlay_status, $status) = @_;
1464
1465     my $bib_result;
1466
1467     if ($status eq 'ignored') {
1468         $bib_result = 'ignore';
1469     } else {
1470         if ($overlay_action eq 'create_new') {
1471             $bib_result = 'delete';
1472         } else {
1473             $bib_result = ($overlay_status eq 'match_applied') ? 'restore' : 'delete';
1474         }
1475     }
1476     return $bib_result;
1477 }
1478
1479 1;
1480 __END__
1481
1482 =head1 AUTHOR
1483
1484 Koha Development Team <http://koha-community.org/>
1485
1486 Galen Charlton <galen.charlton@liblime.com>
1487
1488 =cut