Bugzilla – Attachment 157095 Details for
Bug 34355
Automated MARC record ordering process
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Help
|
New Account
|
Log In
[x]
|
Forgot Password
Login:
[x]
[patch]
Bug 34355: Add cronjob, MarcOrder object and refactored addorderiso2709 script
Bug-34355-Add-cronjob-MarcOrder-object-and-refacto.patch (text/plain), 42.78 KB, created by
Matt Blenkinsop
on 2023-10-13 13:51:22 UTC
(
hide
)
Description:
Bug 34355: Add cronjob, MarcOrder object and refactored addorderiso2709 script
Filename:
MIME Type:
Creator:
Matt Blenkinsop
Created:
2023-10-13 13:51:22 UTC
Size:
42.78 KB
patch
obsolete
>From 939031abf3a23f5f4d74debe572cefdc5527a417 Mon Sep 17 00:00:00 2001 >From: Matt Blenkinsop <matt.blenkinsop@ptfs-europe.com> >Date: Mon, 24 Jul 2023 13:53:22 +0000 >Subject: [PATCH] Bug 34355: Add cronjob, MarcOrder object and refactored > addorderiso2709 script > >--- > Koha/MarcOrder.pm | 600 +++++++++++++++---------- > misc/cronjobs/marc_ordering_process.pl | 128 ++++++ > 2 files changed, 494 insertions(+), 234 deletions(-) > create mode 100644 misc/cronjobs/marc_ordering_process.pl > >diff --git a/Koha/MarcOrder.pm b/Koha/MarcOrder.pm >index 79b1d9a377..4f48d1a5fd 100644 >--- a/Koha/MarcOrder.pm >+++ b/Koha/MarcOrder.pm >@@ -19,6 +19,7 @@ package Koha::MarcOrder; > > use Modern::Perl; > use Try::Tiny qw( catch try ); >+use Net::FTP; > > use base qw(Koha::Object); > >@@ -34,20 +35,15 @@ use C4::ImportBatch qw( > SetImportBatchNoMatchAction > SetImportBatchItemAction > SetImportBatchStatus >- GetImportBatchOverlayAction >- GetImportBatchNoMatchAction >- GetImportBatchItemAction >- GetImportBatch > ); >-use C4::Search qw( FindDuplicate ); >+use C4::Search qw( FindDuplicate ); > use C4::Acquisition qw( NewBasket ); >-use C4::Biblio qw( >+use C4::Biblio qw( > AddBiblio > GetMarcFromKohaField > TransformHtmlToXml >- GetMarcQuantity > ); >-use C4::Items qw( AddItemFromMarc ); >+use C4::Items qw( AddItemFromMarc ); > use C4::Budgets qw( GetBudgetByCode ); > > use Koha::Database; >@@ -68,6 +64,93 @@ Koha::MarcOrder - Koha Marc Order Object class > > =cut > >+=head3 create_order_lines_from_file >+ >+ my $result = Koha::MarcOrder->create_order_lines_from_file($args); >+ >+ Controller for file staging, basket creation and order line creation when using the cronjob in marc_ordering_process.pl >+ >+=cut >+ >+sub create_order_lines_from_file { >+ my ( $self, $args ) = @_; >+ >+ my $filename = $args->{filename}; >+ my $filepath = $args->{filepath}; >+ my $profile = $args->{profile}; >+ my $agent = $args->{agent}; >+ >+ my $success; >+ my $error; >+ >+ my $vendor_id = $profile->vendor_id; >+ my $budget_id = $profile->budget_id; >+ >+ my $vendor_record = Koha::Acquisition::Booksellers->find({ id => $vendor_id }); >+ >+ my $basket_id = _create_basket_for_file({ >+ filename => $filename, >+ vendor_id => $vendor_id >+ }); >+ >+ my $format = index($filename, '.mrc') != -1 ? 'ISO2709' : 'MARCXML'; >+ my $params = { >+ record_type => $profile->record_type, >+ encoding => $profile->encoding, >+ format => $format, >+ filepath => $filepath, >+ filename => $filename, >+ comments => undef, >+ parse_items => $profile->parse_items, >+ matcher_id => $profile->matcher_id, >+ overlay_action => $profile->overlay_action, >+ nomatch_action => $profile->nomatch_action, >+ item_action => $profile->item_action, >+ }; >+ >+ try { >+ my $import_batch_id = _stage_file($params); >+ >+ my $import_records = Koha::Import::Records->search({ >+ import_batch_id => $import_batch_id, >+ }); >+ >+ while( my $import_record = $import_records->next ){ >+ my $result = add_biblios_from_import_record({ >+ import_batch_id => $import_batch_id, >+ import_record => $import_record, >+ matcher_id => $params->{matcher_id}, >+ overlay_action => $params->{overlay_action}, >+ agent => $agent, >+ }); >+ warn "Duplicates found in $result->{duplicates_in_batch}, record was skipped." if $result->{duplicates_in_batch}; >+ next if $result->{skip}; >+ >+ my $order_line_details = add_items_from_import_record({ >+ record_result => $result->{record_result}, >+ basket_id => $basket_id, >+ vendor => $vendor_record, >+ budget_id => $budget_id, >+ agent => $agent, >+ }); >+ >+ my $order_lines = create_order_lines({ >+ order_line_details => $order_line_details >+ }); >+ }; >+ SetImportBatchStatus( $import_batch_id, 'imported' ) >+ if Koha::Import::Records->search({import_batch_id => $import_batch_id, status => 'imported' })->count >+ == Koha::Import::Records->search({import_batch_id => $import_batch_id})->count; >+ >+ $success = 1; >+ } catch { >+ $success = 0; >+ $error = $_; >+ }; >+ >+ return $success ? { success => 1, error => ''} : { success => 0, error => $error }; >+} >+ > =head3 import_record_and_create_order_lines > > my $result = Koha::MarcOrder->import_record_and_create_order_lines($args); >@@ -80,7 +163,7 @@ sub import_record_and_create_order_lines { > my ( $self, $args ) = @_; > > my $import_batch_id = $args->{import_batch_id}; >- my $import_record_id_selected = $args->{import_record_id_selected} || (); >+ my @import_record_id_selected = $args->{import_record_id_selected} || (); > my $matcher_id = $args->{matcher_id}; > my $overlay_action = $args->{overlay_action}; > my $import_record = $args->{import_record}; >@@ -90,72 +173,156 @@ sub import_record_and_create_order_lines { > my $budget_id = $args->{budget_id}; > my $vendor = $args->{vendor}; > >- my $result = add_biblios_from_import_record( >- { >- import_batch_id => $import_batch_id, >- import_record => $import_record, >- matcher_id => $matcher_id, >- overlay_action => $overlay_action, >- agent => $agent, >- import_record_id_selected => $import_record_id_selected, >- } >- ); >+ my $result = add_biblios_from_import_record({ >+ import_batch_id => $import_batch_id, >+ import_record => $import_record, >+ matcher_id => $matcher_id, >+ overlay_action => $overlay_action, >+ agent => $agent, >+ import_record_id_selected => @import_record_id_selected, >+ }); > > return { > duplicates_in_batch => $result->{duplicates_in_batch}, > skip => $result->{skip} > } if $result->{skip}; > >- my $order_line_details = add_items_from_import_record( >- { >- record_result => $result->{record_result}, >- basket_id => $basket_id, >- vendor => $vendor, >- budget_id => $budget_id, >- agent => $agent, >- client_item_fields => $client_item_fields >- } >- ); >+ my $order_line_details = add_items_from_import_record({ >+ record_result => $result->{record_result}, >+ basket_id => $basket_id, >+ vendor => $vendor, >+ budget_id => $budget_id, >+ agent => $agent, >+ client_item_fields => $client_item_fields >+ }); > >- my $order_lines = create_order_lines( { order_line_details => $order_line_details } ); >+ my $order_lines = create_order_lines({ >+ order_line_details => $order_line_details >+ }); > > return { > duplicates_in_batch => 0, > skip => 0 >- }; >+ } > } > >-=head3 _get_MarcFieldsToOrder_syspref_data >+=head3 _create_basket_for_file > >- my $marc_fields_to_order = _get_MarcFieldsToOrder_syspref_data('MarcFieldsToOrder', $marcrecord, $fields); >+ my $basket_id = _create_basket_for_file({ >+ filename => $filename, >+ vendor_id => $vendor_id >+ }); > >- Fetches data from a marc record based on the mappings in the syspref MarcFieldsToOrder using the fields selected in $fields (array). >+ Creates a basket ready to receive order lines based on the imported file > > =cut > >-sub _get_MarcFieldsToOrder_syspref_data { >- my ( $syspref_name, $record, $field_list ) = @_; >- my $syspref = C4::Context->preference($syspref_name); >- $syspref = "$syspref\n\n"; >- my $yaml = eval { YAML::XS::Load( Encode::encode_utf8($syspref) ); }; >- if ($@) { >- warn "Unable to parse $syspref syspref : $@"; >- return (); >- } >- my $r; >- for my $field_name (@$field_list) { >- next unless exists $yaml->{$field_name}; >- my @fields = split /\|/, $yaml->{$field_name}; >- for my $field (@fields) { >- my ( $f, $sf ) = split /\$/, $field; >- next unless $f and $sf; >- if ( my $v = $record->subfield( $f, $sf ) ) { >- $r->{$field_name} = $v; >+sub _create_basket_for_file { >+ my ( $args ) = @_; >+ >+ my $filename = $args->{filename}; >+ my $vendor_id = $args->{vendor_id}; >+ >+ # aqbasketname.basketname has a max length of 50 characters so long file names will need to be truncated >+ my $basketname = length($filename) > 50 ? substr( $filename, 0, 50 ): $filename; >+ >+ my $basketno = >+ NewBasket( $vendor_id, 0, $basketname, q{}, >+ q{} . q{} ); >+ >+ return $basketno; >+} >+ >+=head3 _stage_file >+ >+ $file->_stage_file($params) >+ >+ Stages a file directly using parameters from a marc ordering account and without using the background job >+ This function is a mirror of Koha::BackgroundJob::StageMARCForImport->process but with the background job functionality removed >+ >+=cut >+ >+sub _stage_file { >+ my ( $args ) = @_; >+ >+ my $record_type = $args->{record_type}; >+ my $encoding = $args->{encoding}; >+ my $format = $args->{format}; >+ my $filepath = $args->{filepath}; >+ my $filename = $args->{filename}; >+ my $marc_modification_template = $args->{marc_modification_template}; >+ my $comments = $args->{comments}; >+ my $parse_items = $args->{parse_items}; >+ my $matcher_id = $args->{matcher_id}; >+ my $overlay_action = $args->{overlay_action}; >+ my $nomatch_action = $args->{nomatch_action}; >+ my $item_action = $args->{item_action}; >+ >+ my @messages; >+ my ( $batch_id, $num_valid, $num_items, @import_errors ); >+ my $num_with_matches = 0; >+ my $checked_matches = 0; >+ my $matcher_failed = 0; >+ my $matcher_code = ""; >+ >+ my $schema = Koha::Database->new->schema; >+ try { >+ $schema->storage->txn_begin; >+ >+ my ( $errors, $marcrecords ); >+ if ( $format eq 'MARCXML' ) { >+ ( $errors, $marcrecords ) = >+ C4::ImportBatch::RecordsFromMARCXMLFile( $filepath, $encoding ); >+ } >+ elsif ( $format eq 'ISO2709' ) { >+ ( $errors, $marcrecords ) = >+ C4::ImportBatch::RecordsFromISO2709File( $filepath, $record_type, >+ $encoding ); >+ } >+ else { # plugin based >+ $errors = []; >+ $marcrecords = >+ C4::ImportBatch::RecordsFromMarcPlugin( $filepath, $format, >+ $encoding ); >+ } >+ >+ ( $batch_id, $num_valid, $num_items, @import_errors ) = BatchStageMarcRecords( >+ $record_type, $encoding, >+ $marcrecords, $filename, >+ $marc_modification_template, $comments, >+ '', $parse_items, >+ 0 >+ ); >+ >+ if ($matcher_id) { >+ my $matcher = C4::Matcher->fetch($matcher_id); >+ if ( defined $matcher ) { >+ $checked_matches = 1; >+ $matcher_code = $matcher->code(); >+ $num_with_matches = >+ BatchFindDuplicates( $batch_id, $matcher, 10); >+ SetImportBatchMatcher( $batch_id, $matcher_id ); >+ SetImportBatchOverlayAction( $batch_id, $overlay_action ); >+ SetImportBatchNoMatchAction( $batch_id, $nomatch_action ); >+ SetImportBatchItemAction( $batch_id, $item_action ); >+ $schema->storage->txn_commit; >+ } >+ else { >+ $matcher_failed = 1; >+ $schema->storage->txn_rollback; > } >- last if $yaml->{$field}; >+ } else { >+ $schema->storage->txn_commit; > } >+ >+ return $batch_id; > } >- return $r; >+ catch { >+ warn $_; >+ $schema->storage->txn_rollback; >+ die "Something terrible has happened!" >+ if ( $_ =~ /Rollback failed/ ); # TODO Check test: Rollback failed >+ }; > } > > =head3 _get_MarcItemFieldsToOrder_syspref_data >@@ -167,11 +334,13 @@ sub _get_MarcFieldsToOrder_syspref_data { > =cut > > sub _get_MarcItemFieldsToOrder_syspref_data { >- my ( $syspref_name, $record, $field_list ) = @_; >+ my ($syspref_name, $record, $field_list) = @_; > my $syspref = C4::Context->preference($syspref_name); > $syspref = "$syspref\n\n"; >- my $yaml = eval { YAML::XS::Load( Encode::encode_utf8($syspref) ); }; >- if ($@) { >+ my $yaml = eval { >+ YAML::XS::Load(Encode::encode_utf8($syspref)); >+ }; >+ if ( $@ ) { > warn "Unable to parse $syspref syspref : $@"; > return (); > } >@@ -179,10 +348,10 @@ sub _get_MarcItemFieldsToOrder_syspref_data { > my @tags_list; > > # Check tags in syspref definition >- for my $field_name (@$field_list) { >+ for my $field_name ( @$field_list ) { > next unless exists $yaml->{$field_name}; > my @fields = split /\|/, $yaml->{$field_name}; >- for my $field (@fields) { >+ for my $field ( @fields ) { > my ( $f, $sf ) = split /\$/, $field; > next unless $f and $sf; > push @tags_list, $f; >@@ -190,8 +359,9 @@ sub _get_MarcItemFieldsToOrder_syspref_data { > } > @tags_list = List::MoreUtils::uniq(@tags_list); > >- my $tags_count = _verify_number_of_fields( \@tags_list, $record ); >+ die "System preference MarcItemFieldsToOrder has not been filled in. Please set the mapping values to use this cron script." if scalar(@tags_list == 0); > >+ my $tags_count = _verify_number_of_fields(\@tags_list, $record); > # Return if the number of these fields in the record is not the same. > die "Invalid number of fields detected on field $tags_count->{key}, please check this file" if $tags_count->{error}; > >@@ -199,30 +369,27 @@ sub _get_MarcItemFieldsToOrder_syspref_data { > my $fields_hash; > foreach my $tag (@tags_list) { > my @tmp_fields; >- foreach my $field ( $record->field($tag) ) { >+ foreach my $field ($record->field($tag)) { > push @tmp_fields, $field; > } > $fields_hash->{$tag} = \@tmp_fields; > } > >- if ( $tags_count->{count} ) { >- for ( my $i = 0 ; $i < $tags_count->{count} ; $i++ ) { >- my $r; >- for my $field_name (@$field_list) { >- next unless exists $yaml->{$field_name}; >- my @fields = split /\|/, $yaml->{$field_name}; >- for my $field (@fields) { >- my ( $f, $sf ) = split /\$/, $field; >- next unless $f and $sf; >- my $v = $fields_hash->{$f}[$i] ? $fields_hash->{$f}[$i]->subfield($sf) : undef; >- $r->{$field_name} = $v if ( defined $v ); >- last if $yaml->{$field}; >- } >+ for (my $i = 0; $i < $tags_count->{count}; $i++) { >+ my $r; >+ for my $field_name ( @$field_list ) { >+ next unless exists $yaml->{$field_name}; >+ my @fields = split /\|/, $yaml->{$field_name}; >+ for my $field ( @fields ) { >+ my ( $f, $sf ) = split /\$/, $field; >+ next unless $f and $sf; >+ my $v = $fields_hash->{$f}[$i] ? $fields_hash->{$f}[$i]->subfield( $sf ) : undef; >+ $r->{$field_name} = $v if (defined $v); >+ last if $yaml->{$field}; > } >- push @result, $r; > } >+ push @result, $r; > } >- > return $result[0]; > } > >@@ -235,7 +402,7 @@ sub _get_MarcItemFieldsToOrder_syspref_data { > =cut > > sub _verify_number_of_fields { >- my ( $tags_list, $record ) = @_; >+ my ($tags_list, $record) = @_; > my $tag_fields_count; > for my $tag (@$tags_list) { > my @fields = $record->field($tag); >@@ -244,13 +411,12 @@ sub _verify_number_of_fields { > > my $tags_count; > foreach my $key ( keys %$tag_fields_count ) { >- if ( $tag_fields_count->{$key} > 0 ) { # Having 0 of a field is ok >- $tags_count //= $tag_fields_count->{$key}; # Start with the count from the first occurrence >- return { error => 1, key => $key } >- if $tag_fields_count->{$key} != >- $tags_count; # All counts of various fields should be equal if they exist >+ if ( $tag_fields_count->{$key} > 0 ) { # Having 0 of a field is ok >+ $tags_count //= $tag_fields_count->{$key}; # Start with the count from the first occurrence >+ return { error => 1, key => $key } if $tag_fields_count->{$key} != $tags_count; # All counts of various fields should be equal if they exist > } > } >+ > return { error => 0, count => $tags_count }; > } > >@@ -272,10 +438,10 @@ sub _verify_number_of_fields { > =cut > > sub add_biblios_from_import_record { >- my ($args) = @_; >+ my ( $args ) = @_; > > my $import_batch_id = $args->{import_batch_id}; >- my $import_record_id_selected = $args->{import_record_id_selected} || (); >+ my @import_record_id_selected = $args->{import_record_id_selected} || (); > my $matcher_id = $args->{matcher_id}; > my $overlay_action = $args->{overlay_action}; > my $import_record = $args->{import_record}; >@@ -283,31 +449,32 @@ sub add_biblios_from_import_record { > my $duplicates_in_batch; > > my $duplicates_found = 0; >- if ( $agent eq 'client' ) { >+ if($agent eq 'client') { > return { > record_result => 0, > duplicates_in_batch => 0, > skip => 1 >- } if not grep { $_ eq $import_record->import_record_id } @{$import_record_id_selected}; >+ } if not grep { $_ eq $import_record->import_record_id } @import_record_id_selected; > } > > my $marcrecord = $import_record->get_marc_record || die "Couldn't translate marc information"; >- my $matches = $import_record->get_import_record_matches( { chosen => 1 } ); >- my $match = $matches->count ? $matches->next : undef; >- my $biblionumber = $match ? $match->candidate_match_id : 0; >+ my $matches = $import_record->get_import_record_matches({ chosen => 1 }); >+ my $match = $matches->count ? $matches->next : undef; >+ my $biblionumber = $match ? $match->candidate_match_id : 0; > >- if ($biblionumber) { >+ if ( $biblionumber ) { > $import_record->status('imported')->store; >- if ( $overlay_action eq 'replace' ) { >- my $biblio = Koha::Biblios->find($biblionumber); >- $import_record->replace( { biblio => $biblio } ); >+ if( $overlay_action eq 'replace' ){ >+ my $biblio = Koha::Biblios->find( $biblionumber ); >+ $import_record->replace({ biblio => $biblio }); > } > } else { > if ($matcher_id) { > if ( $matcher_id eq '_TITLE_AUTHOR_' ) { > my @matches = FindDuplicate($marcrecord); > $duplicates_found = 1 if @matches; >- } else { >+ } >+ else { > my $matcher = C4::Matcher->fetch($matcher_id); > my @matches = $matcher->get_matches( $marcrecord, my $max_matches = 1 ); > $duplicates_found = 1 if @matches; >@@ -320,7 +487,7 @@ sub add_biblios_from_import_record { > } > > # add the biblio if no matches were found >- if ( !$duplicates_found ) { >+ if( !$duplicates_found ) { > ( $biblionumber, undef ) = AddBiblio( $marcrecord, '' ); > $import_record->status('imported')->store; > } >@@ -358,7 +525,7 @@ sub add_biblios_from_import_record { > =cut > > sub add_items_from_import_record { >- my ($args) = @_; >+ my ( $args ) = @_; > > my $record_result = $args->{record_result}; > my $basket_id = $args->{basket_id}; >@@ -371,54 +538,24 @@ sub add_items_from_import_record { > my $marcrecord = $record_result->{marcrecord}; > my @order_line_details; > >- if ( $agent eq 'cron' ) { >- my $marc_fields_to_order = _get_MarcFieldsToOrder_syspref_data( >- 'MarcFieldsToOrder', $marcrecord, >- [ 'price', 'quantity', 'budget_code', 'discount', 'sort1', 'sort2' ] >- ); >- my $quantity = $marc_fields_to_order->{quantity}; >- my $budget_code = >- $marc_fields_to_order->{budget_code} || $budget_id; # Use fallback from ordering profile if not mapped >- my $price = $marc_fields_to_order->{price}; >- my $discount = $marc_fields_to_order->{discount}; >- my $sort1 = $marc_fields_to_order->{sort1}; >- my $sort2 = $marc_fields_to_order->{sort2}; >- my $mapped_budget; >- >- if ($budget_code) { >- my $biblio_budget = GetBudgetByCode($budget_code); >- if ($biblio_budget) { >- $mapped_budget = $biblio_budget->{budget_id}; >- } else { >- $mapped_budget = $budget_id; >- } >- } >- >- my $marc_item_fields_to_order = _get_MarcItemFieldsToOrder_syspref_data( >- 'MarcItemFieldsToOrder', >- $marcrecord, >- [ >- 'homebranch', 'holdingbranch', 'itype', 'nonpublic_note', 'public_note', 'loc', 'ccode', 'notforloan', >- 'uri', 'copyno', 'price', 'replacementprice', 'itemcallnumber', 'quantity', 'budget_code' >- ] >- ); >- my $item_homebranch = $marc_item_fields_to_order->{homebranch}; >- my $item_holdingbranch = $marc_item_fields_to_order->{holdingbranch}; >- my $item_itype = $marc_item_fields_to_order->{itype}; >- my $item_nonpublic_note = $marc_item_fields_to_order->{nonpublic_note}; >- my $item_public_note = $marc_item_fields_to_order->{public_note}; >- my $item_loc = $marc_item_fields_to_order->{loc}; >- my $item_ccode = $marc_item_fields_to_order->{ccode}; >- my $item_notforloan = $marc_item_fields_to_order->{notforloan}; >- my $item_uri = $marc_item_fields_to_order->{uri}; >- my $item_copyno = $marc_item_fields_to_order->{copyno}; >- my $item_quantity = $marc_item_fields_to_order->{quantity} || 0; >- my $item_budget_code = $marc_item_fields_to_order->{budget_code}; >+ if($agent eq 'cron') { >+ my $marc_item_fields_to_order = _get_MarcItemFieldsToOrder_syspref_data('MarcItemFieldsToOrder', $marcrecord, ['homebranch', 'holdingbranch', 'itype', 'nonpublic_note', 'public_note', 'loc', 'ccode', 'notforloan', 'uri', 'copyno', 'price', 'replacementprice', 'itemcallnumber', 'quantity', 'budget_code']); >+ my $item_homebranch = $marc_item_fields_to_order->{homebranch}; >+ my $item_holdingbranch = $marc_item_fields_to_order->{holdingbranch}; >+ my $item_itype = $marc_item_fields_to_order->{itype}; >+ my $item_nonpublic_note = $marc_item_fields_to_order->{nonpublic_note}; >+ my $item_public_note = $marc_item_fields_to_order->{public_note}; >+ my $item_loc = $marc_item_fields_to_order->{loc}; >+ my $item_ccode = $marc_item_fields_to_order->{ccode}; >+ my $item_notforloan = $marc_item_fields_to_order->{notforloan}; >+ my $item_uri = $marc_item_fields_to_order->{uri}; >+ my $item_copyno = $marc_item_fields_to_order->{copyno}; >+ my $item_quantity = $marc_item_fields_to_order->{quantity}; >+ my $item_budget_code = $marc_item_fields_to_order->{budget_code}; > my $item_budget_id; >- > if ( $marc_item_fields_to_order->{budget_code} ) { > my $item_budget = GetBudgetByCode( $marc_item_fields_to_order->{budget_code} ); >- if ($item_budget) { >+ if ( $item_budget ) { > $item_budget_id = $item_budget->{budget_id}; > } else { > $item_budget_id = $budget_id; >@@ -429,47 +566,47 @@ sub add_items_from_import_record { > my $item_price = $marc_item_fields_to_order->{price}; > my $item_replacement_price = $marc_item_fields_to_order->{replacementprice}; > my $item_callnumber = $marc_item_fields_to_order->{itemcallnumber}; >- my $itemcreation = 0; > >- for ( my $i = 0 ; $i < $item_quantity ; $i++ ) { >- $itemcreation = 1; >- my $item = Koha::Item->new( >- { >- biblionumber => $biblionumber, >- homebranch => $item_homebranch, >- holdingbranch => $item_holdingbranch, >- itype => $item_itype, >- itemnotes_nonpublic => $item_nonpublic_note, >- itemnotes => $item_public_note, >- location => $item_loc, >- ccode => $item_ccode, >- notforloan => $item_notforloan, >- uri => $item_uri, >- copynumber => $item_copyno, >- price => $item_price, >- replacementprice => $item_replacement_price, >- itemcallnumber => $item_callnumber, >- } >- )->store; >+ if(!$item_quantity) { >+ my $isbn = $marcrecord->subfield( '020', "a" ); >+ warn "No quantity found for record with ISBN: $isbn. No items will be added."; >+ } >+ >+ for (my $i = 0; $i < $item_quantity; $i++) { >+ my $item = Koha::Item->new({ >+ biblionumber => $biblionumber, >+ homebranch => $item_homebranch, >+ holdingbranch => $item_holdingbranch, >+ itype => $item_itype, >+ itemnotes_nonpublic => $item_nonpublic_note, >+ itemnotes => $item_public_note, >+ location => $item_loc, >+ ccode => $item_ccode, >+ notforloan => $item_notforloan, >+ uri => $item_uri, >+ copynumber => $item_copyno, >+ price => $item_price, >+ replacementprice => $item_replacement_price, >+ itemcallnumber => $item_callnumber, >+ })->store; > > my %order_detail_hash = ( > biblionumber => $biblionumber, >+ itemnumbers => ($item->itemnumber), > basketno => $basket_id, >- itemnumbers => ( $item->itemnumber ), > quantity => 1, > budget_id => $item_budget_id, > currency => $vendor->listprice, > ); > >- if ($item_price) { >+ if($item_price) { > $order_detail_hash{tax_rate_on_ordering} = $vendor->tax_rate; > $order_detail_hash{tax_rate_on_receiving} = $vendor->tax_rate; > $order_detail_hash{discount} = $vendor->discount; > $order_detail_hash{rrp} = $item_price; >- $order_detail_hash{ecost} = >- $vendor->discount ? $item_price * ( 1 - $vendor->discount / 100 ) : $item_price; >- $order_detail_hash{listprice} = $order_detail_hash{rrp} / $active_currency->rate; >- $order_detail_hash{unitprice} = $order_detail_hash{ecost}; >+ $order_detail_hash{ecost} = $vendor->discount ? $item_price * ( 1 - $vendor->discount / 100 ) : $item_price; >+ $order_detail_hash{listprice} = $order_detail_hash{rrp} / $active_currency->rate; >+ $order_detail_hash{unitprice} = $order_detail_hash{ecost}; > } else { > $order_detail_hash{listprice} = 0; > } >@@ -478,37 +615,9 @@ sub add_items_from_import_record { > > push @order_line_details, \%order_detail_hash; > } >- >- if ( !$itemcreation ) { >- my %order_detail_hash = ( >- biblionumber => $biblionumber, >- basketno => $basket_id, >- quantity => $quantity, >- budget_id => $mapped_budget, >- uncertainprice => 1, >- sort1 => $sort1, >- sort2 => $sort2, >- ); >- >- if ($price) { >- $order_detail_hash{tax_rate_on_ordering} = $vendor->tax_rate; >- $order_detail_hash{tax_rate_on_receiving} = $vendor->tax_rate; >- my $order_discount = $discount ? $discount : $vendor->discount; >- $order_detail_hash{discount} = $order_discount; >- $order_detail_hash{rrp} = $price; >- $order_detail_hash{ecost} = $order_discount ? $price * ( 1 - $order_discount / 100 ) : $price; >- $order_detail_hash{listprice} = $order_detail_hash{rrp} / $active_currency->rate; >- $order_detail_hash{unitprice} = $order_detail_hash{ecost}; >- } else { >- $order_detail_hash{listprice} = 0; >- } >- >- $order_detail_hash{uncertainprice} = 0 if $order_detail_hash{listprice}; >- push @order_line_details, \%order_detail_hash; >- } > } > >- if ( $agent eq 'client' ) { >+ if($agent eq 'client') { > my $homebranches = $client_item_fields->{homebranches}; > my $count = scalar @$homebranches; > my $holdingbranches = $client_item_fields->{holdingbranches}; >@@ -526,7 +635,7 @@ sub add_items_from_import_record { > my $itemcallnumbers = $client_item_fields->{itemcallnumbers}; > > my $itemcreation; >- for ( my $i = 0 ; $i < $count ; $i++ ) { >+ for (my $i = 0; $i < $count; $i++) { > $itemcreation = 1; > my $item = Koha::Item->new( > { >@@ -549,25 +658,22 @@ sub add_items_from_import_record { > > my %order_detail_hash = ( > biblionumber => $biblionumber, >- itemnumbers => ( $item->itemnumber ), >+ itemnumbers => ($item->itemnumber), > basketno => $basket_id, > quantity => 1, >- budget_id => @$budget_codes[$i] >- || $budget_id, # If no budget selected in the UI, default to the budget on the ordering account >- currency => $vendor->listprice, >+ budget_id => @$budget_codes[$i] || $budget_id, # If no budget selected in the UI, default to the budget on the ordering account >+ currency => $vendor->listprice, > ); > >- if ( @$itemprices[$i] ) { >+ if(@$itemprices[$i]) { > $order_detail_hash{tax_rate_on_ordering} = $vendor->tax_rate; > $order_detail_hash{tax_rate_on_receiving} = $vendor->tax_rate; >- my $order_discount = >- $client_item_fields->{c_discount} ? $client_item_fields->{c_discount} : $vendor->discount; >- $order_detail_hash{discount} = $order_discount; >- $order_detail_hash{rrp} = @$itemprices[$i]; >- $order_detail_hash{ecost} = >- $order_discount ? @$itemprices[$i] * ( 1 - $order_discount / 100 ) : @$itemprices[$i]; >- $order_detail_hash{listprice} = $order_detail_hash{rrp} / $active_currency->rate; >- $order_detail_hash{unitprice} = $order_detail_hash{ecost}; >+ my $order_discount = $client_item_fields->{c_discount} ? $client_item_fields->{c_discount} : $vendor->discount; >+ $order_detail_hash{discount} = $order_discount; >+ $order_detail_hash{rrp} = @$itemprices[$i]; >+ $order_detail_hash{ecost} = $order_discount ? @$itemprices[$i] * ( 1 - $order_discount / 100 ) : @$itemprices[$i]; >+ $order_detail_hash{listprice} = $order_detail_hash{rrp} / $active_currency->rate; >+ $order_detail_hash{unitprice} = $order_detail_hash{ecost}; > } else { > $order_detail_hash{listprice} = 0; > } >@@ -577,8 +683,8 @@ sub add_items_from_import_record { > push @order_line_details, \%order_detail_hash; > } > >- if ( !$itemcreation ) { >- my $quantity = GetMarcQuantity( $marcrecord, C4::Context->preference('marcflavour') ) || 1; >+ if(!$itemcreation) { >+ my $quantity = GetMarcQuantity($marcrecord, C4::Context->preference('marcflavour')) || 1; > my %order_detail_hash = ( > biblionumber => $biblionumber, > basketno => $basket_id, >@@ -592,19 +698,15 @@ sub add_items_from_import_record { > currency => $client_item_fields->{all_currency}, > replacementprice => $client_item_fields->{c_replacement_price}, > ); >- if ( $client_item_fields->{c_price} ) { >+ if ($client_item_fields->{c_price}){ > $order_detail_hash{tax_rate_on_ordering} = $vendor->tax_rate; > $order_detail_hash{tax_rate_on_receiving} = $vendor->tax_rate; >- my $order_discount = >- $client_item_fields->{c_discount} ? $client_item_fields->{c_discount} : $vendor->discount; >- $order_detail_hash{discount} = $order_discount; >- $order_detail_hash{rrp} = $client_item_fields->{c_price}; >- $order_detail_hash{ecost} = >- $order_discount >- ? $client_item_fields->{c_price} * ( 1 - $order_discount / 100 ) >- : $client_item_fields->{c_price}; >- $order_detail_hash{listprice} = $order_detail_hash{rrp} / $active_currency->rate; >- $order_detail_hash{unitprice} = $order_detail_hash{ecost}; >+ my $order_discount = $client_item_fields->{c_discount} ? $client_item_fields->{c_discount} : $vendor->discount; >+ $order_detail_hash{discount} = $order_discount; >+ $order_detail_hash{rrp} = $client_item_fields->{c_price}; >+ $order_detail_hash{ecost} = $order_discount ? $client_item_fields->{c_price} * ( 1 - $order_discount / 100 ) : $client_item_fields->{c_price}; >+ $order_detail_hash{listprice} = $order_detail_hash{rrp} / $active_currency->rate; >+ $order_detail_hash{unitprice} = $order_detail_hash{ecost}; > } else { > $order_detail_hash{listprice} = 0; > } >@@ -612,7 +714,7 @@ sub add_items_from_import_record { > $order_detail_hash{uncertainprice} = 0 if $order_detail_hash{listprice}; > > # Add items if applicable parsing the item sent by the form, and create an item just for the import_record_id we are dealing with >- my $basket = Koha::Acquisition::Baskets->find($basket_id); >+ my $basket = Koha::Acquisition::Baskets->find( $basket_id ); > $order_detail_hash{itemnumbers} = (); > if ( $basket->effective_create_items eq 'ordering' && !$basket->is_standing ) { > my @tags = $client_item_fields->{tag}; >@@ -621,7 +723,7 @@ sub add_items_from_import_record { > my @serials = $client_item_fields->{serial}; > my $xml = TransformHtmlToXml( \@tags, \@subfields, \@field_values ); > my $record = MARC::Record::new_from_xml( $xml, 'UTF-8' ); >- for ( my $qtyloop = 1 ; $qtyloop <= $client_item_fields->{c_quantity} ; $qtyloop++ ) { >+ for ( my $qtyloop=1; $qtyloop <= $client_item_fields->{c_quantity}; $qtyloop++ ) { > my ( $biblionumber, undef, $itemnumber ) = AddItemFromMarc( $record, $biblionumber ); > push @{ $order_detail_hash{itemnumbers} }, $itemnumber; > } >@@ -643,24 +745,33 @@ sub add_items_from_import_record { > =cut > > sub create_order_lines { >- my ($args) = @_; >+ my ( $args ) = @_; > > my $order_line_details = $args->{order_line_details}; > >- foreach my $order_detail ( @{$order_line_details} ) { >- my @itemnumbers = $order_detail->{itemnumbers} || (); >- delete( $order_detail->{itemnumbers} ); >- my $order = Koha::Acquisition::Order->new( \%{$order_detail} ); >+ foreach my $order_detail ( @{ $order_line_details } ) { >+ my @itemnumbers = $order_detail->{itemnumbers}; >+ delete($order_detail->{itemnumber}); >+ my $order = Koha::Acquisition::Order->new( \%{ $order_detail } ); > $order->populate_with_prices_for_ordering(); > $order->populate_with_prices_for_receiving(); > $order->store; >- foreach my $itemnumber (@itemnumbers) { >- $order->add_item($itemnumber); >+ foreach my $itemnumber ( @itemnumbers ) { >+ $order->add_item( $itemnumber ); > } > } > return; > } > >+ >+=head3 import_batches_list >+ >+Fetches import batches matching the batch to be added to the basket and adds these to the $template >+ >+Koha::MarcOrder->import_batches_list($template); >+ >+=cut >+ > sub import_batches_list { > my ( $self, $template ) = @_; > my $batches = GetImportBatchRangeDesc(); >@@ -699,6 +810,15 @@ sub import_batches_list { > $template->param( num_results => $num_batches ); > } > >+=head3 >+ >+For an import batch, this function reads the files and creates all the relevant data pertaining to that file >+It then passes this to the $template variable to be shown in the UI >+ >+Koha::MarcOrder->import_biblios_list( $template, $cgiparams->{'import_batch_id'} ); >+ >+=cut >+ > sub import_biblios_list { > my ( $self, $template, $import_batch_id ) = @_; > my $batch = GetImportBatch( $import_batch_id, 'staged' ); >@@ -896,6 +1016,12 @@ sub import_biblios_list { > _batch_info( $template, $batch ); > } > >+=head3 >+ >+Creates a hash of information to be used about an import batch in the template >+ >+=cut >+ > sub _batch_info { > my ( $template, $batch ) = @_; > $template->param( >@@ -928,6 +1054,12 @@ sub _batch_info { > _add_matcher_list( $batch->{'matcher_id'}, $template ); > } > >+=head3 >+ >+Adds a list of available matchers based on an import batch >+ >+=cut >+ > sub _add_matcher_list { > my ( $current_matcher_id, $template ) = @_; > my @matchers = C4::Matcher::GetMatcherList(); >@@ -941,4 +1073,4 @@ sub _add_matcher_list { > $template->param( available_matchers => \@matchers ); > } > >-1; >+1; >\ No newline at end of file >diff --git a/misc/cronjobs/marc_ordering_process.pl b/misc/cronjobs/marc_ordering_process.pl >new file mode 100644 >index 0000000000..498bf01701 >--- /dev/null >+++ b/misc/cronjobs/marc_ordering_process.pl >@@ -0,0 +1,128 @@ >+ >+#!/usr/bin/perl >+ >+# This file is part of Koha. >+# >+# Copyright (C) 2023 PTFS Europe Ltd >+# >+# Koha is free software; you can redistribute it and/or modify it >+# under the terms of the GNU General Public License as published by >+# the Free Software Foundation; either version 3 of the License, or >+# (at your option) any later version. >+# >+# Koha is distributed in the hope that it will be useful, but >+# WITHOUT ANY WARRANTY; without even the implied warranty of >+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the >+# GNU General Public License for more details. >+# >+# You should have received a copy of the GNU General Public License >+# along with Koha; if not, see <http://www.gnu.org/licenses>. >+ >+=head1 NAME >+ >+marc_ordering_process.pl - cron script to retrieve marc files and create order lines >+ >+=head1 SYNOPSIS >+ >+./marc_ordering_process.pl [-c|--confirm] [-v|--verbose] >+ >+or, in crontab: >+# Once every day >+0 3 * * * marc_ordering_process.pl -c >+ >+=head1 DESCRIPTION >+ >+This script searches for new marc files in an SFTP location >+If there are new files, it stages those files, adds bilbios/items and creates order lines >+ >+=head1 OPTIONS >+ >+=over >+ >+=item B<-v|--verbose> >+ >+Print report to standard out. >+ >+=item B<-c|--confirm> >+ >+Without this parameter no changes will be made >+ >+=back >+ >+=cut >+ >+use Modern::Perl; >+use Pod::Usage qw( pod2usage ); >+use Getopt::Long qw( GetOptions ); >+use File::Copy qw( copy move ); >+ >+use Koha::Script -cron; >+use Koha::MarcOrder; >+use Koha::MarcOrderAccounts; >+ >+use C4::Log qw( cronlogaction ); >+ >+my $command_line_options = join(" ",@ARGV); >+ >+my ( $help, $verbose, $confirm ); >+GetOptions( >+ 'h|help' => \$help, >+ 'v|verbose' => \$verbose, >+ 'c|confirm' => \$confirm, >+) || pod2usage(1); >+ >+pod2usage(0) if $help; >+ >+cronlogaction({ info => $command_line_options }); >+ >+$verbose = 1 unless $verbose or $confirm; >+print "Test run only\n" unless $confirm; >+ >+print "Fetching marc ordering accounts\n" if $verbose; >+my @accounts = Koha::MarcOrderAccounts->search( >+ {}, >+ { >+ join => ['vendor', 'budget'] >+ } >+)->as_list; >+ >+if(scalar(@accounts) == 0) { >+ print "No accounts found - you must create a Marc order account for this cronjob to run\n" if $verbose; >+} >+ >+foreach my $acct ( @accounts ) { >+ if($verbose) { >+ say sprintf "Starting marc ordering process for %s", $acct->vendor->name; >+ say sprintf "Looking for new files in %s", $acct->download_directory; >+ } >+ >+ my $working_dir = $acct->download_directory; >+ opendir my $dir, $working_dir or die "Can't open filepath"; >+ my @files = grep { /\.(mrc|marcxml|mrk)/i } readdir $dir; >+ closedir $dir; >+ >+ foreach my $filename ( @files ) { >+ say sprintf "Creating order lines from file %s", $filename if $verbose; >+ if($confirm) { >+ my $full_path = "$working_dir/$filename"; >+ my $args = { >+ filename => $filename, >+ filepath => $full_path, >+ profile => $acct, >+ agent => 'cron' >+ }; >+ my $result = Koha::MarcOrder->create_order_lines_from_file($args); >+ if($result->{success}) { >+ say sprintf "Successfully processed file: %s", $filename if $verbose; >+ unlink $full_path; >+ } else { >+ say sprintf "Error processing file: %s", $filename if $verbose; >+ say sprintf "Error message: %s", $result->{error} if $verbose; >+ }; >+ } >+ } >+ print "All files completed\n"; >+ print "Moving to next account\n\n"; >+} >+print "Process complete\n"; >+cronlogaction({ action => 'End', info => "COMPLETED" }); >-- >2.37.1 (Apple Git-137.1)
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Diff
|
Splinter Review
Attachments on
bug 34355
:
154705
|
154706
|
154707
|
154708
|
154709
|
154710
|
157092
|
157093
|
157094
|
157095
|
157096
|
157097
|
157098
|
157099
|
157100
|
160858
|
160859
|
160860
|
160861
|
160862
|
160863
|
160864
|
160865
|
160866
|
160867
|
160868
|
160869
|
160870
|
160871
|
160872
|
160873
|
160874
|
160875
|
169896
|
169897
|
169898
|
169899
|
169900
|
169901
|
169902
|
169903
|
169904
|
170395
|
170401
|
170402
|
170403
|
170404
|
170405
|
170406
|
170407
|
170408
|
170409
|
173054
|
173055
|
173056
|
173057
|
173058
|
173059
|
173060
|
173061
|
173062
|
173063
|
173064
|
173065
|
173066
|
173709
|
173805
|
173806
|
173809
|
173824
|
173825
|
173993
|
173994
|
173995
|
173996
|
173997
|
173998
|
173999
|
174000
|
174001
|
174002
|
174003
|
174004
|
174005
|
174006
|
174007
|
174008
|
174009
|
174010
|
174362
|
174363
|
174364
|
174365
|
174366
|
174367
|
174368
|
174369
|
174370
|
174371
|
174372
|
174373
|
174374
|
174375
|
174376
|
174377
|
174378
|
174379
|
174380
|
174382
|
174383