Merge remote-tracking branch 'origin/new/bug_7986'
[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                 my $old_marc = MARC::Record->new_from_xml(StripNonXmlChars($oldxml), 'UTF-8', $rowref->{'encoding'}, $marc_type);
625                 foreach my $item_field ($old_marc->field($item_tag)) {
626                     $old_marc->delete_field($item_field);
627                 }
628                 $oldxml = $old_marc->as_xml($marc_type);
629
630                 ModBiblio($marc_record, $recordid, $oldbiblio->{'frameworkcode'});
631                 $query = "UPDATE import_biblios SET matched_biblionumber = ? WHERE import_record_id = ?";
632
633                 if ($item_result eq 'create_new') {
634                     my ($bib_items_added, $bib_items_errored) = BatchCommitItems($rowref->{'import_record_id'}, $recordid);
635                     $num_items_added += $bib_items_added;
636                     $num_items_errored += $bib_items_errored;
637                 }
638             } else {
639                 $oldxml = GetAuthorityXML($recordid);
640
641                 ModAuthority($recordid, $marc_record, GuessAuthTypeCode($marc_record));
642                 $query = "UPDATE import_auths SET matched_authid = ? WHERE import_record_id = ?";
643             }
644             my $sth = $dbh->prepare_cached("UPDATE import_records SET marcxml_old = ? WHERE import_record_id = ?");
645             $sth->execute($oldxml, $rowref->{'import_record_id'});
646             $sth->finish();
647             my $sth2 = $dbh->prepare_cached($query);
648             $sth2->execute($recordid, $rowref->{'import_record_id'});
649             $sth2->finish();
650             SetImportRecordOverlayStatus($rowref->{'import_record_id'}, 'match_applied');
651             SetImportRecordStatus($rowref->{'import_record_id'}, 'imported');
652         } elsif ($record_result eq 'ignore') {
653             $num_ignored++;
654             if ($record_type eq 'biblio' and defined $recordid and $item_result eq 'create_new') {
655                 my ($bib_items_added, $bib_items_errored) = BatchCommitItems($rowref->{'import_record_id'}, $recordid);
656                 $num_items_added += $bib_items_added;
657                 $num_items_errored += $bib_items_errored;
658                 # still need to record the matched biblionumber so that the
659                 # items can be reverted
660                 my $sth2 = $dbh->prepare_cached("UPDATE import_biblios SET matched_biblionumber = ? WHERE import_record_id = ?");
661                 $sth2->execute($recordid, $rowref->{'import_record_id'});
662                 SetImportRecordOverlayStatus($rowref->{'import_record_id'}, 'match_applied');
663             }
664             SetImportRecordStatus($rowref->{'import_record_id'}, 'ignored');
665         }
666     }
667     $sth->finish();
668     SetImportBatchStatus($batch_id, 'imported');
669     return ($num_added, $num_updated, $num_items_added, $num_items_errored, $num_ignored);
670 }
671
672 =head2 BatchCommitItems
673
674   ($num_items_added, $num_items_errored) = 
675          BatchCommitItems($import_record_id, $biblionumber);
676
677 =cut
678
679 sub BatchCommitItems {
680     my ($import_record_id, $biblionumber) = @_;
681
682     my $dbh = C4::Context->dbh;
683
684     my $num_items_added = 0;
685     my $num_items_errored = 0;
686     my $sth = $dbh->prepare("SELECT import_items_id, import_items.marcxml, encoding
687                              FROM import_items
688                              JOIN import_records USING (import_record_id)
689                              WHERE import_record_id = ?
690                              ORDER BY import_items_id");
691     $sth->bind_param(1, $import_record_id);
692     $sth->execute();
693     while (my $row = $sth->fetchrow_hashref()) {
694         my $item_marc = MARC::Record->new_from_xml(StripNonXmlChars($row->{'marcxml'}), 'UTF-8', $row->{'encoding'});
695         # FIXME - duplicate barcode check needs to become part of AddItemFromMarc()
696         my $item = TransformMarcToKoha($dbh, $item_marc);
697         my $duplicate_barcode = exists($item->{'barcode'}) && GetItemnumberFromBarcode($item->{'barcode'});
698         if ($duplicate_barcode) {
699             my $updsth = $dbh->prepare("UPDATE import_items SET status = ?, import_error = ? WHERE import_items_id = ?");
700             $updsth->bind_param(1, 'error');
701             $updsth->bind_param(2, 'duplicate item barcode');
702             $updsth->bind_param(3, $row->{'import_items_id'});
703             $updsth->execute();
704             $num_items_errored++;
705         } else {
706             my ($item_biblionumber, $biblioitemnumber, $itemnumber) = AddItemFromMarc($item_marc, $biblionumber);
707             my $updsth = $dbh->prepare("UPDATE import_items SET status = ?, itemnumber = ? WHERE import_items_id = ?");
708             $updsth->bind_param(1, 'imported');
709             $updsth->bind_param(2, $itemnumber);
710             $updsth->bind_param(3, $row->{'import_items_id'});
711             $updsth->execute();
712             $updsth->finish();
713             $num_items_added++;
714         }
715     }
716     $sth->finish();
717     return ($num_items_added, $num_items_errored);
718 }
719
720 =head2 BatchRevertRecords
721
722   my ($num_deleted, $num_errors, $num_reverted, $num_items_deleted, 
723       $num_ignored) = BatchRevertRecords($batch_id);
724
725 =cut
726
727 sub BatchRevertRecords {
728     my $batch_id = shift;
729
730     my $record_type;
731     my $num_deleted = 0;
732     my $num_errors = 0;
733     my $num_reverted = 0;
734     my $num_ignored = 0;
735     my $num_items_deleted = 0;
736     # commit (i.e., save, all records in the batch)
737     SetImportBatchStatus('reverting');
738     my $overlay_action = GetImportBatchOverlayAction($batch_id);
739     my $nomatch_action = GetImportBatchNoMatchAction($batch_id);
740     my $dbh = C4::Context->dbh;
741     my $sth = $dbh->prepare("SELECT import_records.import_record_id, record_type, status, overlay_status, marcxml_old, encoding, matched_biblionumber, matched_authid
742                              FROM import_records
743                              LEFT JOIN import_auths ON (import_records.import_record_id=import_auths.import_record_id)
744                              LEFT JOIN import_biblios ON (import_records.import_record_id=import_biblios.import_record_id)
745                              WHERE import_batch_id = ?");
746     $sth->execute($batch_id);
747     my $marc_type;
748     my $marcflavour = C4::Context->preference('marcflavour');
749     while (my $rowref = $sth->fetchrow_hashref) {
750         $record_type = $rowref->{'record_type'};
751         if ($rowref->{'status'} eq 'error' or $rowref->{'status'} eq 'reverted') {
752             $num_ignored++;
753             next;
754         }
755         if ($marcflavour eq 'UNIMARC' && $record_type eq 'auth') {
756             $marc_type = 'UNIMARCAUTH';
757         } elsif ($marcflavour eq 'UNIMARC') {
758             $marc_type = 'UNIMARC';
759         } else {
760             $marc_type = 'USMARC';
761         }
762
763         my $record_result = _get_revert_action($overlay_action, $rowref->{'overlay_status'}, $rowref->{'status'});
764
765         if ($record_result eq 'delete') {
766             my $error = undef;
767             if  ($record_type eq 'biblio') {
768                 $num_items_deleted += BatchRevertItems($rowref->{'import_record_id'}, $rowref->{'matched_biblionumber'});
769                 $error = DelBiblio($rowref->{'matched_biblionumber'});
770             } else {
771                 my $deletedauthid = DelAuthority($rowref->{'matched_authid'});
772             }
773             if (defined $error) {
774                 $num_errors++;
775             } else {
776                 $num_deleted++;
777                 SetImportRecordStatus($rowref->{'import_record_id'}, 'reverted');
778             }
779         } elsif ($record_result eq 'restore') {
780             $num_reverted++;
781             my $old_record = MARC::Record->new_from_xml(StripNonXmlChars($rowref->{'marcxml_old'}), 'UTF-8', $rowref->{'encoding'}, $marc_type);
782             if ($record_type eq 'biblio') {
783                 my $biblionumber = $rowref->{'matched_biblionumber'};
784                 my ($count, $oldbiblio) = GetBiblio($biblionumber);
785                 $num_items_deleted += BatchRevertItems($rowref->{'import_record_id'}, $rowref->{'matched_biblionumber'});
786                 ModBiblio($old_record, $biblionumber, $oldbiblio->{'frameworkcode'});
787             } else {
788                 my $authid = $rowref->{'matched_authid'};
789                 ModAuthority($authid, $old_record, GuessAuthTypeCode($old_record));
790             }
791             SetImportRecordStatus($rowref->{'import_record_id'}, 'reverted');
792         } elsif ($record_result eq 'ignore') {
793             if ($record_type eq 'biblio') {
794                 $num_items_deleted += BatchRevertItems($rowref->{'import_record_id'}, $rowref->{'matched_biblionumber'});
795             }
796             SetImportRecordStatus($rowref->{'import_record_id'}, 'reverted');
797         }
798         my $query;
799         if ($record_type eq 'biblio') {
800             # remove matched_biblionumber only if there is no 'imported' item left
801             $query = "UPDATE import_biblios SET matched_biblionumber = NULL WHERE import_record_id = ?";
802             $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')";
803         } else {
804             $query = "UPDATE import_auths SET matched_authid = NULL WHERE import_record_id = ?";
805         }
806         my $sth2 = $dbh->prepare_cached($query);
807         $sth2->execute($rowref->{'import_record_id'});
808     }
809
810     $sth->finish();
811     SetImportBatchStatus($batch_id, 'reverted');
812     return ($num_deleted, $num_errors, $num_reverted, $num_items_deleted, $num_ignored);
813 }
814
815 =head2 BatchRevertItems
816
817   my $num_items_deleted = BatchRevertItems($import_record_id, $biblionumber);
818
819 =cut
820
821 sub BatchRevertItems {
822     my ($import_record_id, $biblionumber) = @_;
823
824     my $dbh = C4::Context->dbh;
825     my $num_items_deleted = 0;
826
827     my $sth = $dbh->prepare_cached("SELECT import_items_id, itemnumber
828                                    FROM import_items
829                                    JOIN items USING (itemnumber)
830                                    WHERE import_record_id = ?");
831     $sth->bind_param(1, $import_record_id);
832     $sth->execute();
833     while (my $row = $sth->fetchrow_hashref()) {
834         my $error = DelItemCheck($dbh, $biblionumber, $row->{'itemnumber'});
835         if ($error == 1){
836             my $updsth = $dbh->prepare("UPDATE import_items SET status = ? WHERE import_items_id = ?");
837             $updsth->bind_param(1, 'reverted');
838             $updsth->bind_param(2, $row->{'import_items_id'});
839             $updsth->execute();
840             $updsth->finish();
841             $num_items_deleted++;
842         }
843         else {
844             next;
845         }
846     }
847     $sth->finish();
848     return $num_items_deleted;
849 }
850
851 =head2 CleanBatch
852
853   CleanBatch($batch_id)
854
855 Deletes all staged records from the import batch
856 and sets the status of the batch to 'cleaned'.  Note
857 that deleting a stage record does *not* affect
858 any record that has been committed to the database.
859
860 =cut
861
862 sub CleanBatch {
863     my $batch_id = shift;
864     return unless defined $batch_id;
865
866     C4::Context->dbh->do('DELETE FROM import_records WHERE import_batch_id = ?', {}, $batch_id);
867     SetImportBatchStatus($batch_id, 'cleaned');
868 }
869
870 =head2 GetAllImportBatches
871
872   my $results = GetAllImportBatches();
873
874 Returns a references to an array of hash references corresponding
875 to all import_batches rows (of batch_type 'batch'), sorted in 
876 ascending order by import_batch_id.
877
878 =cut
879
880 sub  GetAllImportBatches {
881     my $dbh = C4::Context->dbh;
882     my $sth = $dbh->prepare_cached("SELECT * FROM import_batches
883                                     WHERE batch_type IN ('batch', 'webservice')
884                                     ORDER BY import_batch_id ASC");
885
886     my $results = [];
887     $sth->execute();
888     while (my $row = $sth->fetchrow_hashref) {
889         push @$results, $row;
890     }
891     $sth->finish();
892     return $results;
893 }
894
895 =head2 GetStagedWebserviceBatches
896
897   my $batch_ids = GetStagedWebserviceBatches();
898
899 Returns a references to an array of batch id's
900 of batch_type 'webservice' that are not imported
901
902 =cut
903
904 my $PENDING_WEBSERVICE_BATCHES_QRY = <<EOQ;
905 SELECT import_batch_id FROM import_batches
906 WHERE batch_type = 'webservice'
907 AND import_status = 'staged'
908 EOQ
909 sub  GetStagedWebserviceBatches {
910     my $dbh = C4::Context->dbh;
911     return $dbh->selectcol_arrayref($PENDING_WEBSERVICE_BATCHES_QRY);
912 }
913
914 =head2 GetImportBatchRangeDesc
915
916   my $results = GetImportBatchRangeDesc($offset, $results_per_group);
917
918 Returns a reference to an array of hash references corresponding to
919 import_batches rows (sorted in descending order by import_batch_id)
920 start at the given offset.
921
922 =cut
923
924 sub GetImportBatchRangeDesc {
925     my ($offset, $results_per_group) = @_;
926
927     my $dbh = C4::Context->dbh;
928     my $query = "SELECT * FROM import_batches
929                                     WHERE batch_type IN ('batch', 'webservice')
930                                     ORDER BY import_batch_id DESC";
931     my @params;
932     if ($results_per_group){
933         $query .= " LIMIT ?";
934         push(@params, $results_per_group);
935     }
936     if ($offset){
937         $query .= " OFFSET ?";
938         push(@params, $offset);
939     }
940     my $sth = $dbh->prepare_cached($query);
941     $sth->execute(@params);
942     my $results = $sth->fetchall_arrayref({});
943     $sth->finish();
944     return $results;
945 }
946
947 =head2 GetItemNumbersFromImportBatch
948
949   my @itemsnos = GetItemNumbersFromImportBatch($batch_id);
950
951 =cut
952
953 sub GetItemNumbersFromImportBatch {
954         my ($batch_id) = @_;
955         my $dbh = C4::Context->dbh;
956         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=?");
957         $sth->execute($batch_id);
958         my @items ;
959         while ( my ($itm) = $sth->fetchrow_array ) {
960                 push @items, $itm;
961         }
962         return @items;
963 }
964
965 =head2 GetNumberOfImportBatches 
966
967   my $count = GetNumberOfImportBatches();
968
969 =cut
970
971 sub GetNumberOfNonZ3950ImportBatches {
972     my $dbh = C4::Context->dbh;
973     my $sth = $dbh->prepare("SELECT COUNT(*) FROM import_batches WHERE batch_type != 'z3950'");
974     $sth->execute();
975     my ($count) = $sth->fetchrow_array();
976     $sth->finish();
977     return $count;
978 }
979
980 =head2 GetImportRecordsRange
981
982   my $results = GetImportRecordsRange($batch_id, $offset, $results_per_group);
983
984 Returns a reference to an array of hash references corresponding to
985 import_biblios/import_auths/import_records rows for a given batch
986 starting at the given offset.
987
988 =cut
989
990 sub GetImportRecordsRange {
991     my ($batch_id, $offset, $results_per_group, $status) = @_;
992
993     my $dbh = C4::Context->dbh;
994     my $query = "SELECT title, author, isbn, issn, authorized_heading, import_records.import_record_id,
995                                            record_sequence, status, overlay_status,
996                                            matched_biblionumber, matched_authid, record_type
997                                     FROM   import_records
998                                     LEFT JOIN import_auths ON (import_records.import_record_id=import_auths.import_record_id)
999                                     LEFT JOIN import_biblios ON (import_records.import_record_id=import_biblios.import_record_id)
1000                                     WHERE  import_batch_id = ?";
1001     my @params;
1002     push(@params, $batch_id);
1003     if ($status) {
1004         $query .= " AND status=?";
1005         push(@params,$status);
1006     }
1007     $query.=" ORDER BY import_record_id";
1008
1009     if($results_per_group){
1010         $query .= " LIMIT ?";
1011         push(@params, $results_per_group);
1012     }
1013     if($offset){
1014         $query .= " OFFSET ?";
1015         push(@params, $offset);
1016     }
1017     my $sth = $dbh->prepare_cached($query);
1018     $sth->execute(@params);
1019     my $results = $sth->fetchall_arrayref({});
1020     $sth->finish();
1021     return $results;
1022
1023 }
1024
1025 =head2 GetBestRecordMatch
1026
1027   my $record_id = GetBestRecordMatch($import_record_id);
1028
1029 =cut
1030
1031 sub GetBestRecordMatch {
1032     my ($import_record_id) = @_;
1033
1034     my $dbh = C4::Context->dbh;
1035     my $sth = $dbh->prepare("SELECT candidate_match_id
1036                              FROM   import_record_matches
1037                              WHERE  import_record_id = ?
1038                              ORDER BY score DESC, candidate_match_id DESC");
1039     $sth->execute($import_record_id);
1040     my ($record_id) = $sth->fetchrow_array();
1041     $sth->finish();
1042     return $record_id;
1043 }
1044
1045 =head2 GetImportBatchStatus
1046
1047   my $status = GetImportBatchStatus($batch_id);
1048
1049 =cut
1050
1051 sub GetImportBatchStatus {
1052     my ($batch_id) = @_;
1053
1054     my $dbh = C4::Context->dbh;
1055     my $sth = $dbh->prepare("SELECT import_status FROM import_batches WHERE import_batch_id = ?");
1056     $sth->execute($batch_id);
1057     my ($status) = $sth->fetchrow_array();
1058     $sth->finish();
1059     return $status;
1060
1061 }
1062
1063 =head2 SetImportBatchStatus
1064
1065   SetImportBatchStatus($batch_id, $new_status);
1066
1067 =cut
1068
1069 sub SetImportBatchStatus {
1070     my ($batch_id, $new_status) = @_;
1071
1072     my $dbh = C4::Context->dbh;
1073     my $sth = $dbh->prepare("UPDATE import_batches SET import_status = ? WHERE import_batch_id = ?");
1074     $sth->execute($new_status, $batch_id);
1075     $sth->finish();
1076
1077 }
1078
1079 =head2 GetImportBatchOverlayAction
1080
1081   my $overlay_action = GetImportBatchOverlayAction($batch_id);
1082
1083 =cut
1084
1085 sub GetImportBatchOverlayAction {
1086     my ($batch_id) = @_;
1087
1088     my $dbh = C4::Context->dbh;
1089     my $sth = $dbh->prepare("SELECT overlay_action FROM import_batches WHERE import_batch_id = ?");
1090     $sth->execute($batch_id);
1091     my ($overlay_action) = $sth->fetchrow_array();
1092     $sth->finish();
1093     return $overlay_action;
1094
1095 }
1096
1097
1098 =head2 SetImportBatchOverlayAction
1099
1100   SetImportBatchOverlayAction($batch_id, $new_overlay_action);
1101
1102 =cut
1103
1104 sub SetImportBatchOverlayAction {
1105     my ($batch_id, $new_overlay_action) = @_;
1106
1107     my $dbh = C4::Context->dbh;
1108     my $sth = $dbh->prepare("UPDATE import_batches SET overlay_action = ? WHERE import_batch_id = ?");
1109     $sth->execute($new_overlay_action, $batch_id);
1110     $sth->finish();
1111
1112 }
1113
1114 =head2 GetImportBatchNoMatchAction
1115
1116   my $nomatch_action = GetImportBatchNoMatchAction($batch_id);
1117
1118 =cut
1119
1120 sub GetImportBatchNoMatchAction {
1121     my ($batch_id) = @_;
1122
1123     my $dbh = C4::Context->dbh;
1124     my $sth = $dbh->prepare("SELECT nomatch_action FROM import_batches WHERE import_batch_id = ?");
1125     $sth->execute($batch_id);
1126     my ($nomatch_action) = $sth->fetchrow_array();
1127     $sth->finish();
1128     return $nomatch_action;
1129
1130 }
1131
1132
1133 =head2 SetImportBatchNoMatchAction
1134
1135   SetImportBatchNoMatchAction($batch_id, $new_nomatch_action);
1136
1137 =cut
1138
1139 sub SetImportBatchNoMatchAction {
1140     my ($batch_id, $new_nomatch_action) = @_;
1141
1142     my $dbh = C4::Context->dbh;
1143     my $sth = $dbh->prepare("UPDATE import_batches SET nomatch_action = ? WHERE import_batch_id = ?");
1144     $sth->execute($new_nomatch_action, $batch_id);
1145     $sth->finish();
1146
1147 }
1148
1149 =head2 GetImportBatchItemAction
1150
1151   my $item_action = GetImportBatchItemAction($batch_id);
1152
1153 =cut
1154
1155 sub GetImportBatchItemAction {
1156     my ($batch_id) = @_;
1157
1158     my $dbh = C4::Context->dbh;
1159     my $sth = $dbh->prepare("SELECT item_action FROM import_batches WHERE import_batch_id = ?");
1160     $sth->execute($batch_id);
1161     my ($item_action) = $sth->fetchrow_array();
1162     $sth->finish();
1163     return $item_action;
1164
1165 }
1166
1167
1168 =head2 SetImportBatchItemAction
1169
1170   SetImportBatchItemAction($batch_id, $new_item_action);
1171
1172 =cut
1173
1174 sub SetImportBatchItemAction {
1175     my ($batch_id, $new_item_action) = @_;
1176
1177     my $dbh = C4::Context->dbh;
1178     my $sth = $dbh->prepare("UPDATE import_batches SET item_action = ? WHERE import_batch_id = ?");
1179     $sth->execute($new_item_action, $batch_id);
1180     $sth->finish();
1181
1182 }
1183
1184 =head2 GetImportBatchMatcher
1185
1186   my $matcher_id = GetImportBatchMatcher($batch_id);
1187
1188 =cut
1189
1190 sub GetImportBatchMatcher {
1191     my ($batch_id) = @_;
1192
1193     my $dbh = C4::Context->dbh;
1194     my $sth = $dbh->prepare("SELECT matcher_id FROM import_batches WHERE import_batch_id = ?");
1195     $sth->execute($batch_id);
1196     my ($matcher_id) = $sth->fetchrow_array();
1197     $sth->finish();
1198     return $matcher_id;
1199
1200 }
1201
1202
1203 =head2 SetImportBatchMatcher
1204
1205   SetImportBatchMatcher($batch_id, $new_matcher_id);
1206
1207 =cut
1208
1209 sub SetImportBatchMatcher {
1210     my ($batch_id, $new_matcher_id) = @_;
1211
1212     my $dbh = C4::Context->dbh;
1213     my $sth = $dbh->prepare("UPDATE import_batches SET matcher_id = ? WHERE import_batch_id = ?");
1214     $sth->execute($new_matcher_id, $batch_id);
1215     $sth->finish();
1216
1217 }
1218
1219 =head2 GetImportRecordOverlayStatus
1220
1221   my $overlay_status = GetImportRecordOverlayStatus($import_record_id);
1222
1223 =cut
1224
1225 sub GetImportRecordOverlayStatus {
1226     my ($import_record_id) = @_;
1227
1228     my $dbh = C4::Context->dbh;
1229     my $sth = $dbh->prepare("SELECT overlay_status FROM import_records WHERE import_record_id = ?");
1230     $sth->execute($import_record_id);
1231     my ($overlay_status) = $sth->fetchrow_array();
1232     $sth->finish();
1233     return $overlay_status;
1234
1235 }
1236
1237
1238 =head2 SetImportRecordOverlayStatus
1239
1240   SetImportRecordOverlayStatus($import_record_id, $new_overlay_status);
1241
1242 =cut
1243
1244 sub SetImportRecordOverlayStatus {
1245     my ($import_record_id, $new_overlay_status) = @_;
1246
1247     my $dbh = C4::Context->dbh;
1248     my $sth = $dbh->prepare("UPDATE import_records SET overlay_status = ? WHERE import_record_id = ?");
1249     $sth->execute($new_overlay_status, $import_record_id);
1250     $sth->finish();
1251
1252 }
1253
1254 =head2 GetImportRecordStatus
1255
1256   my $overlay_status = GetImportRecordStatus($import_record_id);
1257
1258 =cut
1259
1260 sub GetImportRecordStatus {
1261     my ($import_record_id) = @_;
1262
1263     my $dbh = C4::Context->dbh;
1264     my $sth = $dbh->prepare("SELECT status FROM import_records WHERE import_record_id = ?");
1265     $sth->execute($import_record_id);
1266     my ($overlay_status) = $sth->fetchrow_array();
1267     $sth->finish();
1268     return $overlay_status;
1269
1270 }
1271
1272
1273 =head2 SetImportRecordStatus
1274
1275   SetImportRecordStatus($import_record_id, $new_overlay_status);
1276
1277 =cut
1278
1279 sub SetImportRecordStatus {
1280     my ($import_record_id, $new_overlay_status) = @_;
1281
1282     my $dbh = C4::Context->dbh;
1283     my $sth = $dbh->prepare("UPDATE import_records SET status = ? WHERE import_record_id = ?");
1284     $sth->execute($new_overlay_status, $import_record_id);
1285     $sth->finish();
1286
1287 }
1288
1289 =head2 GetImportRecordMatches
1290
1291   my $results = GetImportRecordMatches($import_record_id, $best_only);
1292
1293 =cut
1294
1295 sub GetImportRecordMatches {
1296     my $import_record_id = shift;
1297     my $best_only = @_ ? shift : 0;
1298
1299     my $dbh = C4::Context->dbh;
1300     # FIXME currently biblio only
1301     my $sth = $dbh->prepare_cached("SELECT title, author, biblionumber,
1302                                     candidate_match_id, score, record_type
1303                                     FROM import_records
1304                                     JOIN import_record_matches USING (import_record_id)
1305                                     LEFT JOIN biblio ON (biblionumber = candidate_match_id)
1306                                     WHERE import_record_id = ?
1307                                     ORDER BY score DESC, biblionumber DESC");
1308     $sth->bind_param(1, $import_record_id);
1309     my $results = [];
1310     $sth->execute();
1311     while (my $row = $sth->fetchrow_hashref) {
1312         if ($row->{'record_type'} eq 'auth') {
1313             $row->{'authorized_heading'} = C4::AuthoritiesMarc::GetAuthorizedHeading( { authid => $row->{'candidate_match_id'} } );
1314         }
1315         next if ($row->{'record_type'} eq 'biblio' && not $row->{'biblionumber'});
1316         push @$results, $row;
1317         last if $best_only;
1318     }
1319     $sth->finish();
1320
1321     return $results;
1322     
1323 }
1324
1325
1326 =head2 SetImportRecordMatches
1327
1328   SetImportRecordMatches($import_record_id, @matches);
1329
1330 =cut
1331
1332 sub SetImportRecordMatches {
1333     my $import_record_id = shift;
1334     my @matches = @_;
1335
1336     my $dbh = C4::Context->dbh;
1337     my $delsth = $dbh->prepare("DELETE FROM import_record_matches WHERE import_record_id = ?");
1338     $delsth->execute($import_record_id);
1339     $delsth->finish();
1340
1341     my $sth = $dbh->prepare("INSERT INTO import_record_matches (import_record_id, candidate_match_id, score)
1342                                     VALUES (?, ?, ?)");
1343     foreach my $match (@matches) {
1344         $sth->execute($import_record_id, $match->{'record_id'}, $match->{'score'});
1345     }
1346 }
1347
1348
1349 # internal functions
1350
1351 sub _create_import_record {
1352     my ($batch_id, $record_sequence, $marc_record, $record_type, $encoding, $z3950random, $marc_type) = @_;
1353
1354     my $dbh = C4::Context->dbh;
1355     my $sth = $dbh->prepare("INSERT INTO import_records (import_batch_id, record_sequence, marc, marcxml, 
1356                                                          record_type, encoding, z3950random)
1357                                     VALUES (?, ?, ?, ?, ?, ?, ?)");
1358     $sth->execute($batch_id, $record_sequence, $marc_record->as_usmarc(), $marc_record->as_xml($marc_type),
1359                   $record_type, $encoding, $z3950random);
1360     my $import_record_id = $dbh->{'mysql_insertid'};
1361     $sth->finish();
1362     return $import_record_id;
1363 }
1364
1365 sub _update_import_record_marc {
1366     my ($import_record_id, $marc_record, $marc_type) = @_;
1367
1368     my $dbh = C4::Context->dbh;
1369     my $sth = $dbh->prepare("UPDATE import_records SET marc = ?, marcxml = ?
1370                              WHERE  import_record_id = ?");
1371     $sth->execute($marc_record->as_usmarc(), $marc_record->as_xml($marc_type), $import_record_id);
1372     $sth->finish();
1373 }
1374
1375 sub _add_auth_fields {
1376     my ($import_record_id, $marc_record) = @_;
1377
1378     my $controlnumber;
1379     if ($marc_record->field('001')) {
1380         $controlnumber = $marc_record->field('001')->data();
1381     }
1382     my $authorized_heading = C4::AuthoritiesMarc::GetAuthorizedHeading({ record => $marc_record });
1383     my $dbh = C4::Context->dbh;
1384     my $sth = $dbh->prepare("INSERT INTO import_auths (import_record_id, control_number, authorized_heading) VALUES (?, ?, ?)");
1385     $sth->execute($import_record_id, $controlnumber, $authorized_heading);
1386     $sth->finish();
1387 }
1388
1389 sub _add_biblio_fields {
1390     my ($import_record_id, $marc_record) = @_;
1391
1392     my ($title, $author, $isbn, $issn) = _parse_biblio_fields($marc_record);
1393     my $dbh = C4::Context->dbh;
1394     # FIXME no controlnumber, originalsource
1395     $isbn = C4::Koha::_isbn_cleanup($isbn); # FIXME C4::Koha::_isbn_cleanup should be made public
1396     my $sth = $dbh->prepare("INSERT INTO import_biblios (import_record_id, title, author, isbn, issn) VALUES (?, ?, ?, ?, ?)");
1397     $sth->execute($import_record_id, $title, $author, $isbn, $issn);
1398     $sth->finish();
1399                 
1400 }
1401
1402 sub _update_biblio_fields {
1403     my ($import_record_id, $marc_record) = @_;
1404
1405     my ($title, $author, $isbn, $issn) = _parse_biblio_fields($marc_record);
1406     my $dbh = C4::Context->dbh;
1407     # FIXME no controlnumber, originalsource
1408     # FIXME 2 - should regularize normalization of ISBN wherever it is done
1409     $isbn =~ s/\(.*$//;
1410     $isbn =~ tr/ -_//;
1411     $isbn = uc $isbn;
1412     my $sth = $dbh->prepare("UPDATE import_biblios SET title = ?, author = ?, isbn = ?, issn = ?
1413                              WHERE  import_record_id = ?");
1414     $sth->execute($title, $author, $isbn, $issn, $import_record_id);
1415     $sth->finish();
1416 }
1417
1418 sub _parse_biblio_fields {
1419     my ($marc_record) = @_;
1420
1421     my $dbh = C4::Context->dbh;
1422     my $bibliofields = TransformMarcToKoha($dbh, $marc_record, '');
1423     return ($bibliofields->{'title'}, $bibliofields->{'author'}, $bibliofields->{'isbn'}, $bibliofields->{'issn'});
1424
1425 }
1426
1427 sub _update_batch_record_counts {
1428     my ($batch_id) = @_;
1429
1430     my $dbh = C4::Context->dbh;
1431     my $sth = $dbh->prepare_cached("UPDATE import_batches SET
1432                                         num_records = (
1433                                             SELECT COUNT(*)
1434                                             FROM import_records
1435                                             WHERE import_batch_id = import_batches.import_batch_id),
1436                                         num_items = (
1437                                             SELECT COUNT(*)
1438                                             FROM import_records
1439                                             JOIN import_items USING (import_record_id)
1440                                             WHERE import_batch_id = import_batches.import_batch_id
1441                                             AND record_type = 'biblio')
1442                                     WHERE import_batch_id = ?");
1443     $sth->bind_param(1, $batch_id);
1444     $sth->execute();
1445     $sth->finish();
1446 }
1447
1448 sub _get_commit_action {
1449     my ($overlay_action, $nomatch_action, $item_action, $overlay_status, $import_record_id, $record_type) = @_;
1450     
1451     if ($record_type eq 'biblio') {
1452         my ($bib_result, $bib_match, $item_result);
1453
1454         if ($overlay_status ne 'no_match') {
1455             $bib_match = GetBestRecordMatch($import_record_id);
1456             if ($overlay_action eq 'replace') {
1457                 $bib_result  = defined($bib_match) ? 'replace' : 'create_new';
1458             } elsif ($overlay_action eq 'create_new') {
1459                 $bib_result  = 'create_new';
1460             } elsif ($overlay_action eq 'ignore') {
1461                 $bib_result  = 'ignore';
1462             }
1463             $item_result = ($item_action eq 'always_add' or $item_action eq 'add_only_for_matches') ? 'create_new' : 'ignore';
1464         } else {
1465             $bib_result = $nomatch_action;
1466             $item_result = ($item_action eq 'always_add' or $item_action eq 'add_only_for_new')     ? 'create_new' : 'ignore';
1467         }
1468         return ($bib_result, $item_result, $bib_match);
1469     } else { # must be auths
1470         my ($auth_result, $auth_match);
1471
1472         if ($overlay_status ne 'no_match') {
1473             $auth_match = GetBestRecordMatch($import_record_id);
1474             if ($overlay_action eq 'replace') {
1475                 $auth_result  = defined($auth_match) ? 'replace' : 'create_new';
1476             } elsif ($overlay_action eq 'create_new') {
1477                 $auth_result  = 'create_new';
1478             } elsif ($overlay_action eq 'ignore') {
1479                 $auth_result  = 'ignore';
1480             }
1481         } else {
1482             $auth_result = $nomatch_action;
1483         }
1484
1485         return ($auth_result, undef, $auth_match);
1486
1487     }
1488 }
1489
1490 sub _get_revert_action {
1491     my ($overlay_action, $overlay_status, $status) = @_;
1492
1493     my $bib_result;
1494
1495     if ($status eq 'ignored') {
1496         $bib_result = 'ignore';
1497     } else {
1498         if ($overlay_action eq 'create_new') {
1499             $bib_result = 'delete';
1500         } else {
1501             $bib_result = ($overlay_status eq 'match_applied') ? 'restore' : 'delete';
1502         }
1503     }
1504     return $bib_result;
1505 }
1506
1507 1;
1508 __END__
1509
1510 =head1 AUTHOR
1511
1512 Koha Development Team <http://koha-community.org/>
1513
1514 Galen Charlton <galen.charlton@liblime.com>
1515
1516 =cut