2 # Import an iso2709 file into Koha 3
7 # find Koha's Perl modules
8 # test carefully before changing this
10 eval { require "$FindBin::Bin/../kohalib.pl" };
14 use MARC::File::USMARC;
26 use C4::MarcModificationTemplates;
29 use Unicode::Normalize;
30 use Time::HiRes qw(gettimeofday);
36 use Koha::SearchEngine;
37 use Koha::SearchEngine::Search;
39 use open qw( :std :encoding(UTF-8) );
40 binmode( STDOUT, ":encoding(UTF-8)" );
41 my ( $input_marc_file, $number, $offset) = ('',0,0);
42 my ($version, $delete, $test_parameter, $skip_marc8_conversion, $char_encoding, $verbose, $commit, $fk_off,$format,$biblios,$authorities,$keepids,$match, $isbn_check, $logfile);
43 my ( $insert, $filters, $update, $all, $yamlfile, $authtypes, $append );
45 my ($sourcetag,$sourcesubfield,$idmapfl, $dedup_barcode);
48 my $marc_mod_template = '';
49 my $marc_mod_template_id = -1;
54 'commit:f' => \$commit,
55 'file:s' => \$input_marc_file,
57 'o|offset:f' => \$offset,
60 't|test' => \$test_parameter,
61 's' => \$skip_marc8_conversion,
62 'c:s' => \$char_encoding,
68 'k|keepids:s' => \$keepids,
69 'b|biblios' => \$biblios,
70 'a|authorities' => \$authorities,
71 'authtypes:s' => \$authtypes,
72 'filter=s@' => \$filters,
76 'match=s@' => \$match,
77 'i|isbn' => \$isbn_check,
79 'y:s' => \$sourcesubfield,
80 'idmap:s' => \$idmapfl,
81 'cleanisbn!' => \$cleanisbn,
82 'yaml:s' => \$yamlfile,
83 'dedupbarcode' => \$dedup_barcode,
84 'framework=s' => \$framework,
85 'custom:s' => \$localcust,
86 'marcmodtemplate:s' => \$marc_mod_template,
88 $biblios ||= !$authorities;
90 my $writemode = ($append) ? "a" : "w";
92 pod2usage( -msg => "\nYou must specify either --biblios or --authorities, not both.\n", -exitval ) if $biblios && $authorities;
99 if ($version || ($input_marc_file eq '')) {
100 pod2usage( -verbose => 2 );
104 if(defined $localcust) { #local customize module
106 $localcust= $localcust||'LocalChanges'; #default name
107 $localcust=~ s/^.*\/([^\/]+)$/$1/; #extract file name only
108 $localcust=~ s/\.pm$//; #remove extension
109 my $fqcust= $FindBin::Bin."/$localcust.pm"; #try migration_tools dir
114 print "WARNING: customize module $localcust.pm not found!\n";
118 require $localcust if $localcust;
119 $localcust=\&customize if $localcust;
122 if($marc_mod_template ne '') {
123 my @templates = GetModificationTemplates();
124 foreach my $this_template (@templates) {
125 if($this_template->{'name'} eq $marc_mod_template) {
126 $marc_mod_template_id = $this_template->{'template_id'};
130 if($marc_mod_template_id < 0) {
131 die "Can't located MARC modification template '$marc_mod_template'\n";
135 my $dbh = C4::Context->dbh;
136 my $heading_fields=get_heading_fields();
138 if (defined $idmapfl) {
139 open(IDMAP,">$idmapfl") or die "cannot open $idmapfl \n";
142 if ((not defined $sourcesubfield) && (not defined $sourcetag)){
148 # Disable logging for the biblios and authorities import operation. It would unnecessarily
151 # Disable the syspref cache so we can change logging settings
152 C4::Context->disable_syspref_cache();
153 # Save current CataloguingLog and AuthoritiesLog sysprefs values
154 my $CataloguingLog = C4::Context->preference( 'CataloguingLog' );
155 my $AuthoritiesLog = C4::Context->preference( 'AuthoritiesLog' );
156 # Disable logging for both
157 C4::Context->set_preference( 'CataloguingLog', 0 );
158 C4::Context->set_preference( 'AuthoritiesLog', 0 );
161 $dbh->do("SET FOREIGN_KEY_CHECKS = 0");
167 print "deleting biblios\n";
168 $dbh->do("truncate biblio");
169 $dbh->do("truncate biblioitems");
170 $dbh->do("truncate items");
173 print "deleting authorities\n";
174 $dbh->do("truncate auth_header");
176 $dbh->do("truncate zebraqueue");
181 if ($test_parameter) {
182 print "TESTING MODE ONLY\n DOING NOTHING\n===============\n";
185 my $marcFlavour = C4::Context->preference('marcflavour') || 'MARC21';
187 print "Characteristic MARC flavour: $marcFlavour\n" if $verbose;
188 my $starttime = gettimeofday;
190 my $fh = IO::File->new($input_marc_file); # don't let MARC::Batch open the file, as it applies the ':utf8' IO layer
191 if (defined $format && $format =~ /XML/i) {
192 # ugly hack follows -- MARC::File::XML, when used by MARC::Batch,
193 # appears to try to convert incoming XML records from MARC-8
194 # to UTF-8. Setting the BinaryEncoding key turns that off
195 # TODO: see what happens to ISO-8859-1 XML files.
196 # TODO: determine if MARC::Batch can be fixed to handle
197 # XML records properly -- it probably should be
198 # be using a proper push or pull XML parser to
199 # extract the records, not using regexes to look
200 # for <record>.*</record>.
201 $MARC::File::XML::_load_args{BinaryEncoding} = 'utf-8';
202 my $recordformat= ($marcFlavour eq "MARC21"?"USMARC":uc($marcFlavour));
203 #UNIMARC Authorities have a different way to manage encoding than UNIMARC biblios.
204 $recordformat=$recordformat."AUTH" if ($authorities and $marcFlavour ne "MARC21");
205 $MARC::File::XML::_load_args{RecordFormat} = $recordformat;
206 $batch = MARC::Batch->new( 'XML', $fh );
208 $batch = MARC::Batch->new( 'USMARC', $fh );
210 $batch->warnings_off();
211 $batch->strict_off();
213 my $commitnum = $commit ? $commit : 50;
218 print "Skipping file offset: $offset records\n";
219 $batch->next() while ($offset--);
222 my ($tagid,$subfieldid);
227 ( $tagid, $subfieldid ) =
228 GetMarcFromKohaField( "biblio.biblionumber", $framework );
232 # the SQL query to search on isbn
233 my $sth_isbn = $dbh->prepare("SELECT biblionumber,biblioitemnumber FROM biblioitems WHERE isbn=?");
235 $dbh->{AutoCommit} = 0;
238 $loghandle= IO::File->new($logfile, $writemode) ;
239 print $loghandle "id;operation;status\n";
242 my $searcher = Koha::SearchEngine::Search->new(
246 ? $Koha::SearchEngine::AUTHORITIES_INDEX
247 : $Koha::SearchEngine::BIBLIOS_INDEX
255 eval { $record = $batch->next() };
257 print "Bad MARC record $i: $@ skipped\n";
258 # FIXME - because MARC::Batch->next() combines grabbing the next
259 # blob and parsing it into one operation, a correctable condition
260 # such as a MARC-8 record claiming that it's UTF-8 can't be recovered
261 # from because we don't have access to the original blob. Note
262 # that the staging import can deal with this condition (via
263 # C4::Charset::MarcToUTF8Record) because it doesn't use MARC::Batch.
266 # skip if we get an empty record (that is MARC valid, but will result in AddBiblio failure
267 last unless ( $record );
269 if( ($verbose//1)==1 ) { #no dot for verbose==2
270 print "." . ( $i % 100==0 ? "\n$i" : '' );
273 # transcode the record to UTF8 if needed & applicable.
274 if ($record->encoding() eq 'MARC-8' and not $skip_marc8_conversion) {
275 # FIXME update condition
276 my ($guessed_charset, $charset_errors);
277 ($record, $guessed_charset, $charset_errors) = MarcToUTF8Record($record, $marcFlavour.(($authorities and $marcFlavour ne "MARC21")?'AUTH':''));
278 if ($guessed_charset eq 'failed') {
279 warn "ERROR: failed to perform character conversion for record $i\n";
283 SetUTF8Flag($record);
284 if($marc_mod_template_id > 0) {
285 print "Modifying MARC\n";
286 ModifyRecordWithTemplate( $marc_mod_template_id, $record );
288 &$localcust($record) if $localcust;
290 # remove trailing - in isbn (only for biblios, of course)
291 if ($biblios && $cleanisbn) {
292 my $tag = $marcFlavour eq 'UNIMARC' ? '010' : '020';
293 my $field = $record->field($tag);
294 my $isbn = $field && $field->subfield('a');
297 $field->update('a' => $isbn);
301 # search for duplicates (based on Local-number)
303 $originalid = GetRecordId( $record, $tagid, $subfieldid );
306 my $query = build_query( $match, $record );
307 my $server = ( $authorities ? 'authorityserver' : 'biblioserver' );
308 $debug && warn $query;
309 my ( $error, $results, $totalhits ) = $searcher->simple_search_compat( $query, 0, 3, [$server] );
310 # changed to warn so able to continue with one broken record
311 if ( defined $error ) {
312 warn "unable to search the database for duplicates : $error";
313 printlog( { id => $id || $originalid || $match, op => "match", status => "ERROR" } ) if ($logfile);
316 $debug && warn "$query $server : $totalhits";
317 if ( $results && scalar(@$results) == 1 ) {
318 my $marcrecord = C4::Search::new_record_from_zebra( $server, $results->[0] );
319 SetUTF8Flag($marcrecord);
320 $id = GetRecordId( $marcrecord, $tagid, $subfieldid );
321 if ( $authorities && $marcFlavour ) {
322 #Skip if authority in database is the same as the on in database
323 if ( $marcrecord->field('005') && $record->field('005') &&
324 $marcrecord->field('005')->data && $record->field('005')->data &&
325 $marcrecord->field('005')->data >= $record->field('005')->data ) {
327 $yamlhash->{$originalid}->{'authid'} = $id;
329 # we recover all subfields of the heading authorities
331 foreach my $field ( $marcrecord->field("2..") ) {
332 push @subfields, map { ( $_->[0] =~ /[a-z]/ ? $_->[1] : () ) } $field->subfields();
334 $yamlhash->{$originalid}->{'subfields'} = \@subfields;
339 } elsif ( $results && scalar(@$results) > 1 ) {
340 $debug && warn "more than one match for $query";
342 $debug && warn "nomatch for $query";
345 if ($keepids && $originalid) {
347 if ( length($keepids) == 3 ) {
348 $storeidfield = MARC::Field->new( $keepids, $originalid );
350 $storeidfield = MARC::Field->new( substr( $keepids, 0, 3 ), "", "", substr( $keepids, 3, 1 ), $originalid );
352 $record->insert_fields_ordered($storeidfield);
353 $record->delete_field( $record->field($tagid) );
355 foreach my $stringfilter (@$filters) {
356 if ( length($stringfilter) == 3 ) {
357 foreach my $field ( $record->field($stringfilter) ) {
358 $record->delete_field($field);
359 $debug && warn "removed : ", $field->as_string;
361 } elsif ($stringfilter =~ /([0-9]{3})([a-z0-9])(.*)/) {
363 my $removesubfield = $2;
364 my $removematch = $3;
365 if ( ( $removetag > "010" ) && $removesubfield ) {
366 foreach my $field ( $record->field($removetag) ) {
367 $field->delete_subfield( code => "$removesubfield", match => $removematch );
368 $debug && warn "Potentially removed : ", $field->subfield($removesubfield);
373 unless ($test_parameter) {
375 use C4::AuthoritiesMarc;
376 my $authtypecode=GuessAuthTypeCode($record, $heading_fields);
377 my $authid= ($id?$id:GuessAuthId($record));
378 if ($authid && GetAuthority($authid) && $update ){
379 ## Authority has an id and is in database : Replace
380 eval { ( $authid ) = ModAuthority($authid,$record, $authtypecode) };
382 warn "Problem with authority $authid Cannot Modify";
383 printlog({id=>$originalid||$id||$authid, op=>"edit",status=>"ERROR"}) if ($logfile);
386 printlog({id=>$originalid||$id||$authid, op=>"edit",status=>"ok"}) if ($logfile);
389 elsif (defined $authid) {
390 ## An authid is defined but no authority in database : add
391 eval { ( $authid ) = AddAuthority($record,$authid, $authtypecode) };
393 warn "Problem with authority $authid Cannot Add ".$@;
394 printlog({id=>$originalid||$id||$authid, op=>"insert",status=>"ERROR"}) if ($logfile);
397 printlog({id=>$originalid||$id||$authid, op=>"insert",status=>"ok"}) if ($logfile);
401 ## True insert in database
402 eval { ( $authid ) = AddAuthority($record,"", $authtypecode) };
404 warn "Problem with authority $authid Cannot Add".$@;
405 printlog({id=>$originalid||$id||$authid, op=>"insert",status=>"ERROR"}) if ($logfile);
408 printlog({id=>$originalid||$id||$authid, op=>"insert",status=>"ok"}) if ($logfile);
412 $yamlhash->{$originalid}->{'authid'} = $authid;
414 foreach my $field ( $record->field("2..") ) {
415 push @subfields, map { ( $_->[0] =~ /[a-z]/ ? $_->[1] : () ) } $field->subfields();
417 $yamlhash->{$originalid}->{'subfields'} = \@subfields;
421 my ( $biblionumber, $biblioitemnumber, $itemnumbers_ref, $errors_ref );
423 # check for duplicate, based on ISBN (skip it if we already have found a duplicate with match parameter
424 if (!$biblionumber && $isbn_check && $isbn) {
425 # warn "search ISBN : $isbn";
426 $sth_isbn->execute($isbn);
427 ($biblionumber,$biblioitemnumber) = $sth_isbn->fetchrow;
429 if (defined $idmapfl) {
430 if ($sourcetag < "010"){
431 if ($record->field($sourcetag)){
432 my $source = $record->field($sourcetag)->data();
433 printf(IDMAP "%s|%s\n",$source,$biblionumber);
436 my $source=$record->subfield($sourcetag,$sourcesubfield);
437 printf(IDMAP "%s|%s\n",$source,$biblionumber);
440 # create biblio, unless we already have it ( either match or isbn )
443 $biblioitemnumber = Koha::Biblios->find( $biblionumber )->biblioitem->biblioitemnumber;
446 eval { ( $biblionumber, $biblioitemnumber ) = ModBiblio( $record, $biblionumber, GetFrameworkCode($biblionumber) ) };
448 warn "ERROR: Edit biblio $biblionumber failed: $@\n";
449 printlog( { id => $id || $originalid || $biblionumber, op => "update", status => "ERROR" } ) if ($logfile);
452 printlog( { id => $id || $originalid || $biblionumber, op => "update", status => "ok" } ) if ($logfile);
455 printlog( { id => $id || $originalid || $biblionumber, op => "insert", status => "warning : already in database" } ) if ($logfile);
459 eval { ( $biblionumber, $biblioitemnumber ) = AddBiblio( $record, '', { defer_marc_save => 1 } ) };
461 warn "ERROR: Adding biblio $biblionumber failed: $@\n";
462 printlog( { id => $id || $originalid || $biblionumber, op => "insert", status => "ERROR" } ) if ($logfile);
465 printlog( { id => $id || $originalid || $biblionumber, op => "insert", status => "ok" } ) if ($logfile);
468 printlog( { id => $id || $originalid || $biblionumber, op => "update", status => "warning : not in database" } ) if ($logfile);
471 eval { ( $itemnumbers_ref, $errors_ref ) = AddItemBatchFromMarc( $record, $biblionumber, $biblioitemnumber, '' ); };
472 my $error_adding = $@;
473 # Work on a clone so that if there are real errors, we can maybe
475 my $clone_record = $record->clone();
476 C4::Biblio::_strip_item_fields($clone_record, '');
477 # This sets the marc fields if there was an error, and also calls
479 ModBiblioMarc( $clone_record, $biblionumber, $framework );
480 if ( $error_adding ) {
481 warn "ERROR: Adding items to bib $biblionumber failed: $error_adding";
482 printlog({id=>$id||$originalid||$biblionumber, op=>"insertitem",status=>"ERROR"}) if ($logfile);
483 # if we failed because of an exception, assume that
484 # the MARC columns in biblioitems were not set.
488 printlog({id=>$id||$originalid||$biblionumber, op=>"insert",status=>"ok"}) if ($logfile);
490 if ($dedup_barcode && grep { exists $_->{error_code} && $_->{error_code} eq 'duplicate_barcode' } @$errors_ref) {
491 # Find the record called 'barcode'
492 my ($tag, $sub) = C4::Biblio::GetMarcFromKohaField('items.barcode', $framework);
493 # Now remove any items that didn't have a duplicate_barcode error,
494 # erase the barcodes on items that did, and re-add those items.
496 foreach my $i (0 .. $#{$errors_ref}) {
497 my $ref = $errors_ref->[$i];
498 if ($ref && ($ref->{error_code} eq 'duplicate_barcode')) {
499 $dupes{$ref->{item_sequence}} = 1;
500 # Delete the error message because we're going to
502 delete $errors_ref->[$i];
506 foreach my $field ($record->field($tag)) {
509 # Here we remove the barcode
510 $field->delete_subfield(code => $sub);
512 # otherwise we delete the field because we don't want
514 $record->delete_fields($field);
517 # Now re-add the record as before, adding errors to the prev list
519 eval { ( $itemnumbers_ref, $more_errors ) = AddItemBatchFromMarc( $record, $biblionumber, $biblioitemnumber, '' ); };
521 warn "ERROR: Adding items to bib $biblionumber failed: $@\n";
522 printlog({id=>$id||$originalid||$biblionumber, op=>"insertitem",status=>"ERROR"}) if ($logfile);
523 # if we failed because of an exception, assume that
524 # the MARC columns in biblioitems were not set.
525 ModBiblioMarc( $record, $biblionumber, $framework );
528 printlog({id=>$id||$originalid||$biblionumber, op=>"insert",status=>"ok"}) if ($logfile);
530 push @$errors_ref, @{ $more_errors };
532 if ($#{ $errors_ref } > -1) {
533 report_item_errors($biblionumber, $errors_ref);
535 $yamlhash->{$originalid} = $biblionumber if ($yamlfile);
537 $dbh->commit() if (0 == $i % $commitnum);
539 print $record->as_formatted()."\n" if ($verbose//0)==2;
540 last if $i == $number;
543 $dbh->{AutoCommit} = 1;
547 $dbh->do("SET FOREIGN_KEY_CHECKS = 1");
550 # Restore CataloguingLog
551 C4::Context->set_preference( 'CataloguingLog', $CataloguingLog );
552 # Restore AuthoritiesLog
553 C4::Context->set_preference( 'AuthoritiesLog', $AuthoritiesLog );
555 my $timeneeded = gettimeofday - $starttime;
556 print "\n$i MARC records done in $timeneeded seconds\n";
558 print $loghandle "file : $input_marc_file\n";
559 print $loghandle "$i MARC records done in $timeneeded seconds\n";
563 open my $yamlfileout, q{>}, "$yamlfile" or die "cannot open $yamlfile \n";
564 print $yamlfileout Dump($yamlhash);
569 my $marcrecord=shift;
574 return $marcrecord->field($tag)->data() if $marcrecord->field($tag);
577 if ($marcrecord->field($tag)){
578 return $marcrecord->subfield($tag,$subfield);
587 foreach my $matchingpoint (@$match){
588 my $string = build_simplequery($matchingpoint,$record);
589 push @searchstrings,$string if (length($string)>0);
592 $QParser = C4::Context->queryparser if (C4::Context->preference('UseQueryParser'));
599 return join(" $op ",@searchstrings);
601 sub build_simplequery {
605 my ($index,$recorddata)=split /,/,$element;
606 if ($recorddata=~/(\d{3})(.*)/) {
607 my ($tag,$subfields) =($1,$2);
608 foreach my $field ($record->field($tag)){
609 if (length($field->as_string("$subfields"))>0){
610 push @searchstrings,"$index:\"".$field->as_string("$subfields")."\"";
615 $QParser = C4::Context->queryparser if (C4::Context->preference('UseQueryParser'));
622 return join(" $op ",@searchstrings);
624 sub report_item_errors {
625 my $biblionumber = shift;
626 my $errors_ref = shift;
628 foreach my $error (@{ $errors_ref }) {
630 my $msg = "Item not added (bib $biblionumber, item tag #$error->{'item_sequence'}, barcode $error->{'item_barcode'}): ";
631 my $error_code = $error->{'error_code'};
632 $error_code =~ s/_/ /g;
633 $msg .= "$error_code $error->{'error_information'}";
638 my $logelements=shift;
639 print $loghandle join( ";", map { defined $_ ? $_ : "" } @$logelements{qw<id op status>} ), "\n";
641 sub get_heading_fields{
644 $headingfields=YAML::LoadFile($authtypes);
645 $headingfields={C4::Context->preference('marcflavour')=>$headingfields};
646 $debug && warn YAML::Dump($headingfields);
648 unless ($headingfields){
649 $headingfields=$dbh->selectall_hashref("SELECT auth_tag_to_report, authtypecode from auth_types",'auth_tag_to_report',{Slice=>{}});
650 $headingfields={C4::Context->preference('marcflavour')=>$headingfields};
652 return $headingfields;
657 bulkmarcimport.pl - Import bibliographic/authority records into Koha
661 $ export KOHA_CONF=/etc/koha.conf
662 $ perl misc/migration_tools/bulkmarcimport.pl -d -commit 1000 \\
663 -file /home/jmf/koha.mrc -n 3000
667 Don't use this script before you've entered and checked your MARC parameters
668 tables twice (or more!). Otherwise, the import won't work correctly and you
669 will get invalid data.
677 This version/help screen
679 =item B<-b, -biblios>
681 Type of import: bibliographic records
683 =item B<-a, -authorities>
685 Type of import: authority records
687 =item B<-file>=I<FILE>
689 The I<FILE> to import
693 Verbose mode. 1 means "some infos", 2 means "MARC dumping"
697 Turn off foreign key checks during import.
699 =item B<-n>=I<NUMBER>
701 The I<NUMBER> of records to import. If missing, all the file is imported
703 =item B<-o, -offset>=I<NUMBER>
705 File offset before importing, ie I<NUMBER> of records to skip.
707 =item B<-commit>=I<NUMBER>
709 The I<NUMBER> of records to wait before performing a 'commit' operation
713 File logs actions done for each record and their status into file
717 If specified, data will be appended to the logfile. If not, the logfile will be erased for each execution.
721 Test mode: parses the file, saying what it would do, but doing nothing.
725 Skip automatic conversion of MARC-8 to UTF-8. This option is provided for
728 =item B<-c>=I<CHARACTERISTIC>
730 The I<CHARACTERISTIC> MARC flavour. At the moment, only I<MARC21> and
731 I<UNIMARC> are supported. MARC21 by default.
735 Delete EVERYTHING related to biblio in koha-DB before import. Tables: biblio,
738 =item B<-m>=I<FORMAT>
740 Input file I<FORMAT>: I<MARCXML> or I<ISO2709> (defaults to ISO2709)
744 file yamlfile with authoritiesTypes and distinguishable record field in order
745 to store the correct authtype
749 yaml file format a yaml file with ids
753 list of fields that will not be imported. Can be any from 000 to 999 or field,
754 subfield and subfield's matching value such as 200avalue
758 if set, only insert when possible
762 if set, only updates (any biblio should have a matching record)
766 if set, do whatever is required
768 =item B<-k, -keepids>=<FIELD>
770 Field store ids in I<FIELD> (useful for authorities, where 001 contains the
771 authid for Koha, that can contain a very valuable info for authorities coming
772 from LOC or BNF. useless for biblios probably)
774 =item B<-match>=<FIELD>
776 I<FIELD> matchindex,fieldtomatch matchpoint to use to deduplicate fieldtomatch
777 can be either 001 to 999 or field and list of subfields as such 100abcde
781 If set, a search will be done on isbn, and, if the same isbn is found, the
782 biblio is not added. It's another method to deduplicate. B<-match> & B<-isbn>
787 Clean ISBN fields from entering biblio records, ie removes hyphens. By default,
788 ISBN are cleaned. --nocleanisbn will keep ISBN unchanged.
792 Source bib I<TAG> for reporting the source bib number
794 =item B<-y>=I<SUBFIELD>
796 Source I<SUBFIELD> for reporting the source bib number
798 =item B<-idmap>=I<FILE>
800 I<FILE> for the koha bib and source id
804 Store ids in 009 (useful for authorities, where 001 contains the authid for
805 Koha, that can contain a very valuable info for authorities coming from LOC or
806 BNF. useless for biblios probably)
808 =item B<-dedupbarcode>
810 If set, whenever a duplicate barcode is detected, it is removed and the attempt
811 to add the record is retried, thereby giving the record a blank barcode. This
812 is useful when something has set barcodes to be a biblio ID, or similar
813 (usually other software.)
817 This is the code for the framework that the requested records will have attached
818 to them when they are created. If not specified, then the default framework
821 =item B<-custom>=I<MODULE>
823 This parameter allows you to use a local module with a customize subroutine
824 that is called for each MARC record.
825 If no filename is passed, LocalChanges.pm is assumed to be in the
826 migration_tools subdirectory. You may pass an absolute file name or a file name
827 from the migration_tools directory.
829 =item B<-marcmodtemplate>=I<TEMPLATE>
831 This parameter allows you to specify the name of an existing MARC
832 modification template to apply as the MARC records are imported (these
833 templates are created in the "MARC modification templates" tool in Koha).
834 If not specified, no MARC modification templates are used (default).