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