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