Line 0
Link Here
|
|
|
1 |
package Koha::MarcOrder; |
2 |
|
3 |
# Copyright 2023, PTFS-Europe Ltd |
4 |
# |
5 |
# This file is part of Koha. |
6 |
# |
7 |
# Koha is free software; you can redistribute it and/or modify it |
8 |
# under the terms of the GNU General Public License as published by |
9 |
# the Free Software Foundation; either version 3 of the License, or |
10 |
# (at your option) any later version. |
11 |
# |
12 |
# Koha is distributed in the hope that it will be useful, but |
13 |
# WITHOUT ANY WARRANTY; without even the implied warranty of |
14 |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
15 |
# GNU General Public License for more details. |
16 |
# |
17 |
# You should have received a copy of the GNU General Public License |
18 |
# along with Koha; if not, see <http://www.gnu.org/licenses>. |
19 |
|
20 |
use Modern::Perl; |
21 |
use Try::Tiny qw( catch try ); |
22 |
use Net::FTP; |
23 |
|
24 |
use base qw(Koha::Object); |
25 |
|
26 |
use C4::Matcher; |
27 |
use C4::ImportBatch qw( |
28 |
RecordsFromMARCXMLFile |
29 |
RecordsFromISO2709File |
30 |
RecordsFromMarcPlugin |
31 |
BatchStageMarcRecords |
32 |
BatchFindDuplicates |
33 |
SetImportBatchMatcher |
34 |
SetImportBatchOverlayAction |
35 |
SetImportBatchNoMatchAction |
36 |
SetImportBatchItemAction |
37 |
SetImportBatchStatus |
38 |
); |
39 |
use C4::Search qw( FindDuplicate ); |
40 |
use C4::Acquisition qw( NewBasket ); |
41 |
use C4::Biblio qw( |
42 |
AddBiblio |
43 |
GetMarcFromKohaField |
44 |
TransformHtmlToXml |
45 |
); |
46 |
use C4::Items qw( AddItemFromMarc ); |
47 |
use C4::Budgets qw( GetBudgetByCode ); |
48 |
|
49 |
use Koha::Database; |
50 |
use Koha::ImportBatchProfiles; |
51 |
use Koha::ImportBatches; |
52 |
use Koha::Import::Records; |
53 |
use Koha::Acquisition::Currencies; |
54 |
use Koha::Acquisition::Booksellers; |
55 |
use Koha::Acquisition::Baskets; |
56 |
|
57 |
=head1 NAME |
58 |
|
59 |
Koha::MarcOrder - Koha Marc Order Object class |
60 |
|
61 |
=head1 API |
62 |
|
63 |
=head2 Class methods |
64 |
|
65 |
=cut |
66 |
|
67 |
=head3 create_order_lines_from_file |
68 |
|
69 |
my $result = Koha::MarcOrder->create_order_lines_from_file($args); |
70 |
|
71 |
Controller for file staging, basket creation and order line creation when using the cronjob in marc_ordering_process.pl |
72 |
|
73 |
=cut |
74 |
|
75 |
sub create_order_lines_from_file { |
76 |
my ( $self, $args ) = @_; |
77 |
|
78 |
my $filename = $args->{filename}; |
79 |
my $filepath = $args->{filepath}; |
80 |
my $profile = $args->{profile}; |
81 |
my $agent = $args->{agent}; |
82 |
|
83 |
my $success; |
84 |
my $error; |
85 |
|
86 |
my $vendor_id = $profile->vendor_id; |
87 |
my $budget_id = $profile->budget_id; |
88 |
|
89 |
my $vendor_record = Koha::Acquisition::Booksellers->find({ id => $vendor_id }); |
90 |
|
91 |
my $basket_id = _create_basket_for_file({ |
92 |
filename => $filename, |
93 |
vendor_id => $vendor_id |
94 |
}); |
95 |
|
96 |
my $format = index($filename, '.mrc') != -1 ? 'ISO2709' : 'MARCXML'; |
97 |
my $params = { |
98 |
record_type => $profile->record_type, |
99 |
encoding => $profile->encoding, |
100 |
format => $format, |
101 |
filepath => $filepath, |
102 |
filename => $filename, |
103 |
comments => undef, |
104 |
parse_items => $profile->parse_items, |
105 |
matcher_id => $profile->matcher_id, |
106 |
overlay_action => $profile->overlay_action, |
107 |
nomatch_action => $profile->nomatch_action, |
108 |
item_action => $profile->item_action, |
109 |
}; |
110 |
|
111 |
try { |
112 |
my $import_batch_id = _stage_file($params); |
113 |
|
114 |
my $import_records = Koha::Import::Records->search({ |
115 |
import_batch_id => $import_batch_id, |
116 |
}); |
117 |
|
118 |
while( my $import_record = $import_records->next ){ |
119 |
my $result = add_biblios_from_import_record({ |
120 |
import_batch_id => $import_batch_id, |
121 |
import_record => $import_record, |
122 |
matcher_id => $params->{matcher_id}, |
123 |
overlay_action => $params->{overlay_action}, |
124 |
agent => $agent, |
125 |
}); |
126 |
warn "Duplicates found in $result->{duplicates_in_batch}, record was skipped." if $result->{duplicates_in_batch}; |
127 |
next if $result->{skip}; |
128 |
|
129 |
my $order_line_details = add_items_from_import_record({ |
130 |
record_result => $result->{record_result}, |
131 |
basket_id => $basket_id, |
132 |
vendor => $vendor_record, |
133 |
budget_id => $budget_id, |
134 |
agent => $agent, |
135 |
}); |
136 |
|
137 |
my $order_lines = create_order_lines({ |
138 |
order_line_details => $order_line_details |
139 |
}); |
140 |
}; |
141 |
SetImportBatchStatus( $import_batch_id, 'imported' ) |
142 |
if Koha::Import::Records->search({import_batch_id => $import_batch_id, status => 'imported' })->count |
143 |
== Koha::Import::Records->search({import_batch_id => $import_batch_id})->count; |
144 |
|
145 |
$success = 1; |
146 |
} catch { |
147 |
$success = 0; |
148 |
$error = $_; |
149 |
}; |
150 |
|
151 |
return $success ? { success => 1, error => ''} : { success => 0, error => $error }; |
152 |
} |
153 |
|
154 |
=head3 import_record_and_create_order_lines |
155 |
|
156 |
my $result = Koha::MarcOrder->import_record_and_create_order_lines($args); |
157 |
|
158 |
Controller for record import and order line creation when using the interface in addorderiso2709.pl |
159 |
|
160 |
=cut |
161 |
|
162 |
sub import_record_and_create_order_lines { |
163 |
my ( $self, $args ) = @_; |
164 |
|
165 |
my $import_batch_id = $args->{import_batch_id}; |
166 |
my @import_record_id_selected = $args->{import_record_id_selected} || (); |
167 |
my $matcher_id = $args->{matcher_id}; |
168 |
my $overlay_action = $args->{overlay_action}; |
169 |
my $import_record = $args->{import_record}; |
170 |
my $client_item_fields = $args->{client_item_fields}; |
171 |
my $agent = $args->{agent}; |
172 |
my $basket_id = $args->{basket_id}; |
173 |
my $budget_id = $args->{budget_id}; |
174 |
my $vendor = $args->{vendor}; |
175 |
|
176 |
my $result = add_biblios_from_import_record({ |
177 |
import_batch_id => $import_batch_id, |
178 |
import_record => $import_record, |
179 |
matcher_id => $matcher_id, |
180 |
overlay_action => $overlay_action, |
181 |
agent => $agent, |
182 |
import_record_id_selected => @import_record_id_selected, |
183 |
}); |
184 |
|
185 |
return { |
186 |
duplicates_in_batch => $result->{duplicates_in_batch}, |
187 |
skip => $result->{skip} |
188 |
} if $result->{skip}; |
189 |
|
190 |
my $order_line_details = add_items_from_import_record({ |
191 |
record_result => $result->{record_result}, |
192 |
basket_id => $basket_id, |
193 |
vendor => $vendor, |
194 |
budget_id => $budget_id, |
195 |
agent => $agent, |
196 |
client_item_fields => $client_item_fields |
197 |
}); |
198 |
|
199 |
my $order_lines = create_order_lines({ |
200 |
order_line_details => $order_line_details |
201 |
}); |
202 |
|
203 |
return { |
204 |
duplicates_in_batch => 0, |
205 |
skip => 0 |
206 |
} |
207 |
} |
208 |
|
209 |
=head3 _create_basket_for_file |
210 |
|
211 |
my $basket_id = _create_basket_for_file({ |
212 |
filename => $filename, |
213 |
vendor_id => $vendor_id |
214 |
}); |
215 |
|
216 |
Creates a basket ready to receive order lines based on the imported file |
217 |
|
218 |
=cut |
219 |
|
220 |
sub _create_basket_for_file { |
221 |
my ( $args ) = @_; |
222 |
|
223 |
my $filename = $args->{filename}; |
224 |
my $vendor_id = $args->{vendor_id}; |
225 |
|
226 |
# aqbasketname.basketname has a max length of 50 characters so long file names will need to be truncated |
227 |
my $basketname = length($filename) > 50 ? substr( $filename, 0, 50 ): $filename; |
228 |
|
229 |
my $basketno = |
230 |
NewBasket( $vendor_id, 0, $basketname, q{}, |
231 |
q{} . q{} ); |
232 |
|
233 |
return $basketno; |
234 |
} |
235 |
|
236 |
=head3 _stage_file |
237 |
|
238 |
$file->_stage_file($params) |
239 |
|
240 |
Stages a file directly using parameters from a marc ordering account and without using the background job |
241 |
This function is a mirror of Koha::BackgroundJob::StageMARCForImport->process but with the background job functionality removed |
242 |
|
243 |
=cut |
244 |
|
245 |
sub _stage_file { |
246 |
my ( $args ) = @_; |
247 |
|
248 |
my $record_type = $args->{record_type}; |
249 |
my $encoding = $args->{encoding}; |
250 |
my $format = $args->{format}; |
251 |
my $filepath = $args->{filepath}; |
252 |
my $filename = $args->{filename}; |
253 |
my $marc_modification_template = $args->{marc_modification_template}; |
254 |
my $comments = $args->{comments}; |
255 |
my $parse_items = $args->{parse_items}; |
256 |
my $matcher_id = $args->{matcher_id}; |
257 |
my $overlay_action = $args->{overlay_action}; |
258 |
my $nomatch_action = $args->{nomatch_action}; |
259 |
my $item_action = $args->{item_action}; |
260 |
|
261 |
my @messages; |
262 |
my ( $batch_id, $num_valid, $num_items, @import_errors ); |
263 |
my $num_with_matches = 0; |
264 |
my $checked_matches = 0; |
265 |
my $matcher_failed = 0; |
266 |
my $matcher_code = ""; |
267 |
|
268 |
my $schema = Koha::Database->new->schema; |
269 |
try { |
270 |
$schema->storage->txn_begin; |
271 |
|
272 |
my ( $errors, $marcrecords ); |
273 |
if ( $format eq 'MARCXML' ) { |
274 |
( $errors, $marcrecords ) = |
275 |
C4::ImportBatch::RecordsFromMARCXMLFile( $filepath, $encoding ); |
276 |
} |
277 |
elsif ( $format eq 'ISO2709' ) { |
278 |
( $errors, $marcrecords ) = |
279 |
C4::ImportBatch::RecordsFromISO2709File( $filepath, $record_type, |
280 |
$encoding ); |
281 |
} |
282 |
else { # plugin based |
283 |
$errors = []; |
284 |
$marcrecords = |
285 |
C4::ImportBatch::RecordsFromMarcPlugin( $filepath, $format, |
286 |
$encoding ); |
287 |
} |
288 |
|
289 |
( $batch_id, $num_valid, $num_items, @import_errors ) = BatchStageMarcRecords( |
290 |
$record_type, $encoding, |
291 |
$marcrecords, $filename, |
292 |
$marc_modification_template, $comments, |
293 |
'', $parse_items, |
294 |
0 |
295 |
); |
296 |
|
297 |
if ($matcher_id) { |
298 |
my $matcher = C4::Matcher->fetch($matcher_id); |
299 |
if ( defined $matcher ) { |
300 |
$checked_matches = 1; |
301 |
$matcher_code = $matcher->code(); |
302 |
$num_with_matches = |
303 |
BatchFindDuplicates( $batch_id, $matcher, 10); |
304 |
SetImportBatchMatcher( $batch_id, $matcher_id ); |
305 |
SetImportBatchOverlayAction( $batch_id, $overlay_action ); |
306 |
SetImportBatchNoMatchAction( $batch_id, $nomatch_action ); |
307 |
SetImportBatchItemAction( $batch_id, $item_action ); |
308 |
$schema->storage->txn_commit; |
309 |
} |
310 |
else { |
311 |
$matcher_failed = 1; |
312 |
$schema->storage->txn_rollback; |
313 |
} |
314 |
} else { |
315 |
$schema->storage->txn_commit; |
316 |
} |
317 |
|
318 |
return $batch_id; |
319 |
} |
320 |
catch { |
321 |
warn $_; |
322 |
$schema->storage->txn_rollback; |
323 |
die "Something terrible has happened!" |
324 |
if ( $_ =~ /Rollback failed/ ); # TODO Check test: Rollback failed |
325 |
}; |
326 |
} |
327 |
|
328 |
=head3 _get_MarcItemFieldsToOrder_syspref_data |
329 |
|
330 |
my $marc_item_fields_to_order = _get_MarcItemFieldsToOrder_syspref_data('MarcItemFieldsToOrder', $marcrecord, $fields); |
331 |
|
332 |
Fetches data from a marc record based on the mappings in the syspref MarcItemFieldsToOrder using the fields selected in $fields (array). |
333 |
|
334 |
=cut |
335 |
|
336 |
sub _get_MarcItemFieldsToOrder_syspref_data { |
337 |
my ($syspref_name, $record, $field_list) = @_; |
338 |
my $syspref = C4::Context->preference($syspref_name); |
339 |
$syspref = "$syspref\n\n"; |
340 |
my $yaml = eval { |
341 |
YAML::XS::Load(Encode::encode_utf8($syspref)); |
342 |
}; |
343 |
if ( $@ ) { |
344 |
warn "Unable to parse $syspref syspref : $@"; |
345 |
return (); |
346 |
} |
347 |
my @result; |
348 |
my @tags_list; |
349 |
|
350 |
# Check tags in syspref definition |
351 |
for my $field_name ( @$field_list ) { |
352 |
next unless exists $yaml->{$field_name}; |
353 |
my @fields = split /\|/, $yaml->{$field_name}; |
354 |
for my $field ( @fields ) { |
355 |
my ( $f, $sf ) = split /\$/, $field; |
356 |
next unless $f and $sf; |
357 |
push @tags_list, $f; |
358 |
} |
359 |
} |
360 |
@tags_list = List::MoreUtils::uniq(@tags_list); |
361 |
|
362 |
die "System preference MarcItemFieldsToOrder has not been filled in. Please set the mapping values to use this cron script." if scalar(@tags_list == 0); |
363 |
|
364 |
my $tags_count = _verify_number_of_fields(\@tags_list, $record); |
365 |
# Return if the number of these fields in the record is not the same. |
366 |
die "Invalid number of fields detected on field $tags_count->{key}, please check this file" if $tags_count->{error}; |
367 |
|
368 |
# Gather the fields |
369 |
my $fields_hash; |
370 |
foreach my $tag (@tags_list) { |
371 |
my @tmp_fields; |
372 |
foreach my $field ($record->field($tag)) { |
373 |
push @tmp_fields, $field; |
374 |
} |
375 |
$fields_hash->{$tag} = \@tmp_fields; |
376 |
} |
377 |
|
378 |
for (my $i = 0; $i < $tags_count->{count}; $i++) { |
379 |
my $r; |
380 |
for my $field_name ( @$field_list ) { |
381 |
next unless exists $yaml->{$field_name}; |
382 |
my @fields = split /\|/, $yaml->{$field_name}; |
383 |
for my $field ( @fields ) { |
384 |
my ( $f, $sf ) = split /\$/, $field; |
385 |
next unless $f and $sf; |
386 |
my $v = $fields_hash->{$f}[$i] ? $fields_hash->{$f}[$i]->subfield( $sf ) : undef; |
387 |
$r->{$field_name} = $v if (defined $v); |
388 |
last if $yaml->{$field}; |
389 |
} |
390 |
} |
391 |
push @result, $r; |
392 |
} |
393 |
return $result[0]; |
394 |
} |
395 |
|
396 |
=head3 _verify_number_of_fields |
397 |
|
398 |
my $tags_count = _verify_number_of_fields(\@tags_list, $record); |
399 |
|
400 |
Verifies that the number of fields in the record is consistent for each field |
401 |
|
402 |
=cut |
403 |
|
404 |
sub _verify_number_of_fields { |
405 |
my ($tags_list, $record) = @_; |
406 |
my $tag_fields_count; |
407 |
for my $tag (@$tags_list) { |
408 |
my @fields = $record->field($tag); |
409 |
$tag_fields_count->{$tag} = scalar @fields; |
410 |
} |
411 |
|
412 |
my $tags_count; |
413 |
foreach my $key ( keys %$tag_fields_count ) { |
414 |
if ( $tag_fields_count->{$key} > 0 ) { # Having 0 of a field is ok |
415 |
$tags_count //= $tag_fields_count->{$key}; # Start with the count from the first occurrence |
416 |
return { error => 1, key => $key } if $tag_fields_count->{$key} != $tags_count; # All counts of various fields should be equal if they exist |
417 |
} |
418 |
} |
419 |
|
420 |
return { error => 0, count => $tags_count }; |
421 |
} |
422 |
|
423 |
=head3 add_biblios_from_import_record |
424 |
|
425 |
my ($record_results, $duplicates_in_batch) = add_biblios_from_import_record({ |
426 |
import_record => $import_record, |
427 |
matcher_id => $matcher_id, |
428 |
overlay_action => $overlay_action, |
429 |
import_record_id_selected => $import_record_id_selected, |
430 |
agent => $agent, |
431 |
import_batch_id => $import_batch_id |
432 |
}); |
433 |
|
434 |
Takes a set of import records and adds biblio records based on the file content. |
435 |
Params matcher_id and overlay_action are taken from the marc ordering account. |
436 |
Returns the new or matched biblionumber and the marc record for each import record. |
437 |
|
438 |
=cut |
439 |
|
440 |
sub add_biblios_from_import_record { |
441 |
my ( $args ) = @_; |
442 |
|
443 |
my $import_batch_id = $args->{import_batch_id}; |
444 |
my @import_record_id_selected = $args->{import_record_id_selected} || (); |
445 |
my $matcher_id = $args->{matcher_id}; |
446 |
my $overlay_action = $args->{overlay_action}; |
447 |
my $import_record = $args->{import_record}; |
448 |
my $agent = $args->{agent} || ""; |
449 |
my $duplicates_in_batch; |
450 |
|
451 |
my $duplicates_found = 0; |
452 |
if($agent eq 'client') { |
453 |
return { |
454 |
record_result => 0, |
455 |
duplicates_in_batch => 0, |
456 |
skip => 1 |
457 |
} if not grep { $_ eq $import_record->import_record_id } @import_record_id_selected; |
458 |
} |
459 |
|
460 |
my $marcrecord = $import_record->get_marc_record || die "Couldn't translate marc information"; |
461 |
my $matches = $import_record->get_import_record_matches({ chosen => 1 }); |
462 |
my $match = $matches->count ? $matches->next : undef; |
463 |
my $biblionumber = $match ? $match->candidate_match_id : 0; |
464 |
|
465 |
if ( $biblionumber ) { |
466 |
$import_record->status('imported')->store; |
467 |
if( $overlay_action eq 'replace' ){ |
468 |
my $biblio = Koha::Biblios->find( $biblionumber ); |
469 |
$import_record->replace({ biblio => $biblio }); |
470 |
} |
471 |
} else { |
472 |
if ($matcher_id) { |
473 |
if ( $matcher_id eq '_TITLE_AUTHOR_' ) { |
474 |
my @matches = FindDuplicate($marcrecord); |
475 |
$duplicates_found = 1 if @matches; |
476 |
} |
477 |
else { |
478 |
my $matcher = C4::Matcher->fetch($matcher_id); |
479 |
my @matches = $matcher->get_matches( $marcrecord, my $max_matches = 1 ); |
480 |
$duplicates_found = 1 if @matches; |
481 |
} |
482 |
return { |
483 |
record_result => 0, |
484 |
duplicates_in_batch => $import_batch_id, |
485 |
skip => 1 |
486 |
} if $duplicates_found; |
487 |
} |
488 |
|
489 |
# add the biblio if no matches were found |
490 |
if( !$duplicates_found ) { |
491 |
( $biblionumber, undef ) = AddBiblio( $marcrecord, '' ); |
492 |
$import_record->status('imported')->store; |
493 |
} |
494 |
} |
495 |
$import_record->import_biblio->matched_biblionumber($biblionumber)->store; |
496 |
|
497 |
my $record_result = { |
498 |
biblionumber => $biblionumber, |
499 |
marcrecord => $marcrecord, |
500 |
import_record_id => $import_record->import_record_id, |
501 |
}; |
502 |
|
503 |
return { |
504 |
record_result => $record_result, |
505 |
duplicates_in_batch => $duplicates_in_batch, |
506 |
skip => 0 |
507 |
}; |
508 |
} |
509 |
|
510 |
=head3 add_items_from_import_record |
511 |
|
512 |
my $order_line_details = add_items_from_import_record({ |
513 |
record_result => $record_result, |
514 |
basket_id => $basket_id, |
515 |
vendor => $vendor, |
516 |
budget_id => $budget_id, |
517 |
agent => $agent, |
518 |
client_item_fields => $client_item_fields |
519 |
}); |
520 |
|
521 |
Adds items to biblio records based on mappings in MarcItemFieldsToOrder. |
522 |
Returns an array of order line details based on newly added items. |
523 |
If being called from addorderiso2709.pl then client_item_fields is a hash of all the UI form inputs needed by the script. |
524 |
|
525 |
=cut |
526 |
|
527 |
sub add_items_from_import_record { |
528 |
my ( $args ) = @_; |
529 |
|
530 |
my $record_result = $args->{record_result}; |
531 |
my $basket_id = $args->{basket_id}; |
532 |
my $budget_id = $args->{budget_id}; |
533 |
my $vendor = $args->{vendor}; |
534 |
my $agent = $args->{agent}; |
535 |
my $client_item_fields = $args->{client_item_fields} || undef; |
536 |
my $active_currency = Koha::Acquisition::Currencies->get_active; |
537 |
my $biblionumber = $record_result->{biblionumber}; |
538 |
my $marcrecord = $record_result->{marcrecord}; |
539 |
my @order_line_details; |
540 |
|
541 |
if($agent eq 'cron') { |
542 |
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']); |
543 |
my $item_homebranch = $marc_item_fields_to_order->{homebranch}; |
544 |
my $item_holdingbranch = $marc_item_fields_to_order->{holdingbranch}; |
545 |
my $item_itype = $marc_item_fields_to_order->{itype}; |
546 |
my $item_nonpublic_note = $marc_item_fields_to_order->{nonpublic_note}; |
547 |
my $item_public_note = $marc_item_fields_to_order->{public_note}; |
548 |
my $item_loc = $marc_item_fields_to_order->{loc}; |
549 |
my $item_ccode = $marc_item_fields_to_order->{ccode}; |
550 |
my $item_notforloan = $marc_item_fields_to_order->{notforloan}; |
551 |
my $item_uri = $marc_item_fields_to_order->{uri}; |
552 |
my $item_copyno = $marc_item_fields_to_order->{copyno}; |
553 |
my $item_quantity = $marc_item_fields_to_order->{quantity}; |
554 |
my $item_budget_code = $marc_item_fields_to_order->{budget_code}; |
555 |
my $item_budget_id; |
556 |
if ( $marc_item_fields_to_order->{budget_code} ) { |
557 |
my $item_budget = GetBudgetByCode( $marc_item_fields_to_order->{budget_code} ); |
558 |
if ( $item_budget ) { |
559 |
$item_budget_id = $item_budget->{budget_id}; |
560 |
} else { |
561 |
$item_budget_id = $budget_id; |
562 |
} |
563 |
} else { |
564 |
$item_budget_id = $budget_id; |
565 |
} |
566 |
my $item_price = $marc_item_fields_to_order->{price}; |
567 |
my $item_replacement_price = $marc_item_fields_to_order->{replacementprice}; |
568 |
my $item_callnumber = $marc_item_fields_to_order->{itemcallnumber}; |
569 |
|
570 |
if(!$item_quantity) { |
571 |
my $isbn = $marcrecord->subfield( '020', "a" ); |
572 |
warn "No quantity found for record with ISBN: $isbn. No items will be added."; |
573 |
} |
574 |
|
575 |
for (my $i = 0; $i < $item_quantity; $i++) { |
576 |
my $item = Koha::Item->new({ |
577 |
biblionumber => $biblionumber, |
578 |
homebranch => $item_homebranch, |
579 |
holdingbranch => $item_holdingbranch, |
580 |
itype => $item_itype, |
581 |
itemnotes_nonpublic => $item_nonpublic_note, |
582 |
itemnotes => $item_public_note, |
583 |
location => $item_loc, |
584 |
ccode => $item_ccode, |
585 |
notforloan => $item_notforloan, |
586 |
uri => $item_uri, |
587 |
copynumber => $item_copyno, |
588 |
price => $item_price, |
589 |
replacementprice => $item_replacement_price, |
590 |
itemcallnumber => $item_callnumber, |
591 |
})->store; |
592 |
|
593 |
my %order_detail_hash = ( |
594 |
biblionumber => $biblionumber, |
595 |
itemnumbers => ($item->itemnumber), |
596 |
basketno => $basket_id, |
597 |
quantity => 1, |
598 |
budget_id => $item_budget_id, |
599 |
currency => $vendor->listprice, |
600 |
); |
601 |
|
602 |
if($item_price) { |
603 |
$order_detail_hash{tax_rate_on_ordering} = $vendor->tax_rate; |
604 |
$order_detail_hash{tax_rate_on_receiving} = $vendor->tax_rate; |
605 |
$order_detail_hash{discount} = $vendor->discount; |
606 |
$order_detail_hash{rrp} = $item_price; |
607 |
$order_detail_hash{ecost} = $vendor->discount ? $item_price * ( 1 - $vendor->discount / 100 ) : $item_price; |
608 |
$order_detail_hash{listprice} = $order_detail_hash{rrp} / $active_currency->rate; |
609 |
$order_detail_hash{unitprice} = $order_detail_hash{ecost}; |
610 |
} else { |
611 |
$order_detail_hash{listprice} = 0; |
612 |
} |
613 |
$order_detail_hash{replacementprice} = $item_replacement_price || 0; |
614 |
$order_detail_hash{uncertainprice} = 0 if $order_detail_hash{listprice}; |
615 |
|
616 |
push @order_line_details, \%order_detail_hash; |
617 |
} |
618 |
} |
619 |
|
620 |
if($agent eq 'client') { |
621 |
my $homebranches = $client_item_fields->{homebranches}; |
622 |
my $count = scalar @$homebranches; |
623 |
my $holdingbranches = $client_item_fields->{holdingbranches}; |
624 |
my $itypes = $client_item_fields->{itypes}; |
625 |
my $nonpublic_notes = $client_item_fields->{nonpublic_notes}; |
626 |
my $public_notes = $client_item_fields->{public_notes}; |
627 |
my $locs = $client_item_fields->{locs}; |
628 |
my $ccodes = $client_item_fields->{ccodes}; |
629 |
my $notforloans = $client_item_fields->{notforloans}; |
630 |
my $uris = $client_item_fields->{uris}; |
631 |
my $copynos = $client_item_fields->{copynos}; |
632 |
my $budget_codes = $client_item_fields->{budget_codes}; |
633 |
my $itemprices = $client_item_fields->{itemprices}; |
634 |
my $replacementprices = $client_item_fields->{replacementprices}; |
635 |
my $itemcallnumbers = $client_item_fields->{itemcallnumbers}; |
636 |
|
637 |
my $itemcreation; |
638 |
for (my $i = 0; $i < $count; $i++) { |
639 |
$itemcreation = 1; |
640 |
my $item = Koha::Item->new( |
641 |
{ |
642 |
biblionumber => $biblionumber, |
643 |
homebranch => @$homebranches[$i], |
644 |
holdingbranch => @$holdingbranches[$i], |
645 |
itemnotes_nonpublic => @$nonpublic_notes[$i], |
646 |
itemnotes => @$public_notes[$i], |
647 |
location => @$locs[$i], |
648 |
ccode => @$ccodes[$i], |
649 |
itype => @$itypes[$i], |
650 |
notforloan => @$notforloans[$i], |
651 |
uri => @$uris[$i], |
652 |
copynumber => @$copynos[$i], |
653 |
price => @$itemprices[$i], |
654 |
replacementprice => @$replacementprices[$i], |
655 |
itemcallnumber => @$itemcallnumbers[$i], |
656 |
} |
657 |
)->store; |
658 |
|
659 |
my %order_detail_hash = ( |
660 |
biblionumber => $biblionumber, |
661 |
itemnumbers => ($item->itemnumber), |
662 |
basketno => $basket_id, |
663 |
quantity => 1, |
664 |
budget_id => @$budget_codes[$i] || $budget_id, # If no budget selected in the UI, default to the budget on the ordering account |
665 |
currency => $vendor->listprice, |
666 |
); |
667 |
|
668 |
if(@$itemprices[$i]) { |
669 |
$order_detail_hash{tax_rate_on_ordering} = $vendor->tax_rate; |
670 |
$order_detail_hash{tax_rate_on_receiving} = $vendor->tax_rate; |
671 |
my $order_discount = $client_item_fields->{c_discount} ? $client_item_fields->{c_discount} : $vendor->discount; |
672 |
$order_detail_hash{discount} = $order_discount; |
673 |
$order_detail_hash{rrp} = @$itemprices[$i]; |
674 |
$order_detail_hash{ecost} = $order_discount ? @$itemprices[$i] * ( 1 - $order_discount / 100 ) : @$itemprices[$i]; |
675 |
$order_detail_hash{listprice} = $order_detail_hash{rrp} / $active_currency->rate; |
676 |
$order_detail_hash{unitprice} = $order_detail_hash{ecost}; |
677 |
} else { |
678 |
$order_detail_hash{listprice} = 0; |
679 |
} |
680 |
$order_detail_hash{replacementprice} = @$replacementprices[$i] || 0; |
681 |
$order_detail_hash{uncertainprice} = 0 if $order_detail_hash{listprice}; |
682 |
|
683 |
push @order_line_details, \%order_detail_hash; |
684 |
} |
685 |
|
686 |
if(!$itemcreation) { |
687 |
my $quantity = GetMarcQuantity($marcrecord, C4::Context->preference('marcflavour')) || 1; |
688 |
my %order_detail_hash = ( |
689 |
biblionumber => $biblionumber, |
690 |
basketno => $basket_id, |
691 |
quantity => $client_item_fields->{c_quantity}, |
692 |
budget_id => $client_item_fields->{c_budget_id}, |
693 |
uncertainprice => 1, |
694 |
sort1 => $client_item_fields->{c_sort1}, |
695 |
sort2 => $client_item_fields->{c_sort2}, |
696 |
order_internalnote => $client_item_fields->{all_order_internalnote}, |
697 |
order_vendornote => $client_item_fields->{all_order_vendornote}, |
698 |
currency => $client_item_fields->{all_currency}, |
699 |
replacementprice => $client_item_fields->{c_replacement_price}, |
700 |
); |
701 |
if ($client_item_fields->{c_price}){ |
702 |
$order_detail_hash{tax_rate_on_ordering} = $vendor->tax_rate; |
703 |
$order_detail_hash{tax_rate_on_receiving} = $vendor->tax_rate; |
704 |
my $order_discount = $client_item_fields->{c_discount} ? $client_item_fields->{c_discount} : $vendor->discount; |
705 |
$order_detail_hash{discount} = $order_discount; |
706 |
$order_detail_hash{rrp} = $client_item_fields->{c_price}; |
707 |
$order_detail_hash{ecost} = $order_discount ? $client_item_fields->{c_price} * ( 1 - $order_discount / 100 ) : $client_item_fields->{c_price}; |
708 |
$order_detail_hash{listprice} = $order_detail_hash{rrp} / $active_currency->rate; |
709 |
$order_detail_hash{unitprice} = $order_detail_hash{ecost}; |
710 |
} else { |
711 |
$order_detail_hash{listprice} = 0; |
712 |
} |
713 |
|
714 |
$order_detail_hash{uncertainprice} = 0 if $order_detail_hash{listprice}; |
715 |
|
716 |
# 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 |
717 |
my $basket = Koha::Acquisition::Baskets->find( $basket_id ); |
718 |
$order_detail_hash{itemnumbers} = (); |
719 |
if ( $basket->effective_create_items eq 'ordering' && !$basket->is_standing ) { |
720 |
my @tags = $client_item_fields->{tag}; |
721 |
my @subfields = $client_item_fields->{subfield}; |
722 |
my @field_values = $client_item_fields->{field_value}; |
723 |
my @serials = $client_item_fields->{serial}; |
724 |
my $xml = TransformHtmlToXml( \@tags, \@subfields, \@field_values ); |
725 |
my $record = MARC::Record::new_from_xml( $xml, 'UTF-8' ); |
726 |
for ( my $qtyloop=1; $qtyloop <= $client_item_fields->{c_quantity}; $qtyloop++ ) { |
727 |
my ( $biblionumber, undef, $itemnumber ) = AddItemFromMarc( $record, $biblionumber ); |
728 |
push @{ $order_detail_hash{itemnumbers} }, $itemnumber; |
729 |
} |
730 |
} |
731 |
push @order_line_details, \%order_detail_hash; |
732 |
} |
733 |
} |
734 |
return \@order_line_details; |
735 |
} |
736 |
|
737 |
=head3 create_order_lines |
738 |
|
739 |
my $order_lines = create_order_lines({ |
740 |
order_line_details => $order_line_details |
741 |
}); |
742 |
|
743 |
Creates order lines based on an array of order line details |
744 |
|
745 |
=cut |
746 |
|
747 |
sub create_order_lines { |
748 |
my ( $args ) = @_; |
749 |
|
750 |
my $order_line_details = $args->{order_line_details}; |
751 |
|
752 |
foreach my $order_detail ( @{ $order_line_details } ) { |
753 |
my @itemnumbers = $order_detail->{itemnumbers}; |
754 |
delete($order_detail->{itemnumber}); |
755 |
my $order = Koha::Acquisition::Order->new( \%{ $order_detail } ); |
756 |
$order->populate_with_prices_for_ordering(); |
757 |
$order->populate_with_prices_for_receiving(); |
758 |
$order->store; |
759 |
foreach my $itemnumber ( @itemnumbers ) { |
760 |
$order->add_item( $itemnumber ); |
761 |
} |
762 |
} |
763 |
return; |
764 |
} |
765 |
|
766 |
1; |