Lines 24-56
use Try::Tiny qw( catch try );
Link Here
|
24 |
|
24 |
|
25 |
use C4::Auth qw( get_template_and_user haspermission ); |
25 |
use C4::Auth qw( get_template_and_user haspermission ); |
26 |
use C4::Output qw( output_html_with_http_headers ); |
26 |
use C4::Output qw( output_html_with_http_headers ); |
27 |
use C4::Biblio qw( |
|
|
28 |
DelBiblio |
29 |
GetAuthorisedValueDesc |
30 |
GetMarcFromKohaField |
31 |
GetMarcStructure |
32 |
IsMarcStructureInternal |
33 |
TransformHtmlToXml |
34 |
); |
35 |
use C4::Items qw( GetItemsInfo Item2Marc ModItemFromMarc ); |
36 |
use C4::Circulation qw( LostItem IsItemIssued ); |
37 |
use C4::Context; |
27 |
use C4::Context; |
38 |
use C4::Koha; |
|
|
39 |
use C4::BackgroundJob; |
40 |
use C4::ClassSource qw( GetClassSources GetClassSource ); |
41 |
use MARC::File::XML; |
28 |
use MARC::File::XML; |
42 |
use List::MoreUtils qw( uniq ); |
29 |
use List::MoreUtils qw( uniq ); |
|
|
30 |
use Encode qw( encode_utf8 ); |
43 |
|
31 |
|
44 |
use Koha::Database; |
32 |
use Koha::Database; |
|
|
33 |
use Koha::DateUtils qw( dt_from_string ); |
45 |
use Koha::Exceptions::Exception; |
34 |
use Koha::Exceptions::Exception; |
46 |
use Koha::AuthorisedValues; |
|
|
47 |
use Koha::Biblios; |
35 |
use Koha::Biblios; |
48 |
use Koha::DateUtils qw( dt_from_string ); |
|
|
49 |
use Koha::Items; |
36 |
use Koha::Items; |
50 |
use Koha::ItemTypes; |
|
|
51 |
use Koha::Patrons; |
37 |
use Koha::Patrons; |
52 |
use Koha::SearchEngine::Indexer; |
38 |
use Koha::Item::Attributes; |
|
|
39 |
use Koha::BackgroundJob::BatchDeleteItem; |
40 |
use Koha::BackgroundJob::BatchUpdateItem; |
53 |
use Koha::UI::Form::Builder::Item; |
41 |
use Koha::UI::Form::Builder::Item; |
|
|
42 |
use Koha::UI::Table::Builder::Items; |
54 |
|
43 |
|
55 |
my $input = CGI->new; |
44 |
my $input = CGI->new; |
56 |
my $dbh = C4::Context->dbh; |
45 |
my $dbh = C4::Context->dbh; |
Lines 90-346
my $restrictededition = $uid ? haspermission($uid, {'tools' => 'items_batchmod_
Link Here
|
90 |
# In case user is a superlibrarian, edition is not restricted |
79 |
# In case user is a superlibrarian, edition is not restricted |
91 |
$restrictededition = 0 if ($restrictededition != 0 && C4::Context->IsSuperLibrarian()); |
80 |
$restrictededition = 0 if ($restrictededition != 0 && C4::Context->IsSuperLibrarian()); |
92 |
|
81 |
|
93 |
$template->param(del => $del); |
|
|
94 |
|
95 |
my $nextop=""; |
82 |
my $nextop=""; |
96 |
my @errors; # store errors found while checking data BEFORE saving item. |
83 |
my $display_items; |
97 |
my $items_display_hashref; |
|
|
98 |
our $tagslib = &GetMarcStructure(1); |
99 |
|
100 |
my $deleted_items = 0; # Number of deleted items |
101 |
my $deleted_records = 0; # Number of deleted records ( with no items attached ) |
102 |
my $not_deleted_items = 0; # Number of items that could not be deleted |
103 |
my @not_deleted; # List of the itemnumbers that could not be deleted |
104 |
my $modified_items = 0; # Numbers of modified items |
105 |
my $modified_fields = 0; # Numbers of modified fields |
106 |
|
84 |
|
107 |
my %cookies = parse CGI::Cookie($cookie); |
85 |
my %cookies = parse CGI::Cookie($cookie); |
108 |
my $sessionID = $cookies{'CGISESSID'}->value; |
86 |
my $sessionID = $cookies{'CGISESSID'}->value; |
109 |
|
87 |
|
|
|
88 |
my @messages; |
110 |
|
89 |
|
111 |
#--- ---------------------------------------------------------------------------- |
90 |
if ( $op eq "action" ) { |
112 |
if ($op eq "action") { |
91 |
|
113 |
#------------------------------------------------------------------------------- |
92 |
if ($del) { |
114 |
my @tags = $input->multi_param('tag'); |
|
|
115 |
my @subfields = $input->multi_param('subfield'); |
116 |
my @values = $input->multi_param('field_value'); |
117 |
my @searches = $input->multi_param('regex_search'); |
118 |
my @replaces = $input->multi_param('regex_replace'); |
119 |
my @modifiers = $input->multi_param('regex_modifiers'); |
120 |
my @subfields_to_blank = $input->multi_param('disable_input'); |
121 |
# build indicator hash. |
122 |
my @ind_tag = $input->multi_param('ind_tag'); |
123 |
my @indicator = $input->multi_param('indicator'); |
124 |
|
125 |
my $upd_biblionumbers; |
126 |
my $del_biblionumbers; |
127 |
if ( $del ) { |
128 |
try { |
93 |
try { |
129 |
my $schema = Koha::Database->new->schema; |
94 |
my $params = { |
130 |
$schema->txn_do( |
95 |
record_ids => \@itemnumbers, |
131 |
sub { |
96 |
delete_biblios => $del_records, |
132 |
foreach my $itemnumber (@itemnumbers) { |
97 |
}; |
133 |
my $item = Koha::Items->find($itemnumber); |
98 |
my $job_id = |
134 |
next |
99 |
Koha::BackgroundJob::BatchDeleteItem->new->enqueue($params); |
135 |
unless $item |
100 |
$nextop = 'enqueued'; |
136 |
; # Should have been tested earlier, but just in case... |
101 |
$template->param( job_id => $job_id, ); |
137 |
my $itemdata = $item->unblessed; |
|
|
138 |
my $return = $item->safe_delete; |
139 |
if ( ref( $return ) ) { |
140 |
$deleted_items++; |
141 |
push @$upd_biblionumbers, $itemdata->{'biblionumber'}; |
142 |
} |
143 |
else { |
144 |
$not_deleted_items++; |
145 |
push @not_deleted, |
146 |
{ |
147 |
biblionumber => $itemdata->{'biblionumber'}, |
148 |
itemnumber => $itemdata->{'itemnumber'}, |
149 |
barcode => $itemdata->{'barcode'}, |
150 |
title => $itemdata->{'title'}, |
151 |
reason => $return, |
152 |
}; |
153 |
} |
154 |
|
155 |
# If there are no items left, delete the biblio |
156 |
if ($del_records) { |
157 |
my $itemscount = Koha::Biblios->find( $itemdata->{'biblionumber'} )->items->count; |
158 |
if ( $itemscount == 0 ) { |
159 |
my $error = DelBiblio( $itemdata->{'biblionumber'}, { skip_record_index => 1 } ); |
160 |
unless ($error) { |
161 |
$deleted_records++; |
162 |
push @$del_biblionumbers, $itemdata->{'biblionumber'}; |
163 |
if ( $src eq 'CATALOGUING' ) { |
164 |
# We are coming catalogue/detail.pl, there were items from a single bib record |
165 |
$template->param( biblio_deleted => 1 ); |
166 |
} |
167 |
} |
168 |
} |
169 |
} |
170 |
} |
171 |
if (@not_deleted) { |
172 |
Koha::Exceptions::Exception->throw( |
173 |
'Some items have not been deleted, rolling back'); |
174 |
} |
175 |
} |
176 |
); |
177 |
} |
102 |
} |
178 |
catch { |
103 |
catch { |
179 |
warn $_; |
104 |
warn $_; |
180 |
if ( $_->isa('Koha::Exceptions::Exception') ) { |
105 |
push @messages, |
181 |
$template->param( deletion_failed => 1 ); |
106 |
{ |
182 |
} |
107 |
type => 'error', |
183 |
die "Something terrible has happened!" |
108 |
code => 'cannot_enqueue_job', |
184 |
if ($_ =~ /Rollback failed/); # Rollback failed |
109 |
error => $_, |
|
|
110 |
}; |
111 |
$template->param( view => 'errors' ); |
185 |
}; |
112 |
}; |
186 |
} |
113 |
} |
187 |
|
114 |
|
188 |
else { # modification |
115 |
else { # modification |
189 |
|
116 |
|
190 |
my @columns = Koha::Items->columns; |
117 |
my @item_columns = Koha::Items->columns; |
191 |
|
118 |
|
192 |
my $new_item_data; |
119 |
my $new_item_data; |
193 |
my @columns_with_regex; |
120 |
my ( $columns_with_regex ); |
194 |
for my $c ( @columns ) { |
121 |
my @subfields_to_blank = $input->multi_param('disable_input'); |
195 |
if ( $c eq 'more_subfields_xml' ) { |
122 |
my @more_subfields = $input->multi_param("items.more_subfields_xml"); |
196 |
my @more_subfields_xml = $input->multi_param("items.more_subfields_xml"); |
123 |
for my $item_column (@item_columns) { |
197 |
my @unlinked_item_subfields; |
124 |
my @attributes = ($item_column); |
198 |
for my $subfield ( @more_subfields_xml ) { |
125 |
my $cgi_param_prefix = 'items.'; |
199 |
my $v = $input->param('items.more_subfields_xml_' . $subfield); |
126 |
if ( $item_column eq 'more_subfields_xml' ) { |
200 |
push @unlinked_item_subfields, $subfield, $v; |
127 |
@attributes = (); |
201 |
} |
128 |
$cgi_param_prefix = 'items.more_subfields_xml_'; |
202 |
if ( @unlinked_item_subfields ) { |
129 |
for my $subfield (@more_subfields) { |
203 |
my $marc = MARC::Record->new(); |
130 |
push @attributes, $subfield; |
204 |
# use of tag 999 is arbitrary, and doesn't need to match the item tag |
|
|
205 |
# used in the framework |
206 |
$marc->append_fields(MARC::Field->new('999', ' ', ' ', @unlinked_item_subfields)); |
207 |
$marc->encoding("UTF-8"); |
208 |
# FIXME This is WRONG! We need to use the values that haven't been modified by the batch tool! |
209 |
$new_item_data->{more_subfields_xml} = $marc->as_xml("USMARC"); |
210 |
next; |
211 |
} |
131 |
} |
212 |
$new_item_data->{more_subfields_xml} = undef; |
132 |
} |
213 |
# FIXME deal with more_subfields_xml and @subfields_to_blank |
|
|
214 |
} elsif ( grep { $c eq $_ } @subfields_to_blank ) { |
215 |
# Empty this column |
216 |
$new_item_data->{$c} = undef |
217 |
} else { |
218 |
|
133 |
|
219 |
my @v = grep { $_ ne "" } |
134 |
for my $attr (@attributes) { |
220 |
uniq $input->multi_param( "items." . $c ); |
|
|
221 |
|
135 |
|
222 |
next unless @v; |
136 |
my $cgi_var_name = $cgi_param_prefix |
|
|
137 |
. encode_utf8($attr) |
138 |
; # We need to deal correctly with encoding on subfield codes |
223 |
|
139 |
|
224 |
$new_item_data->{$c} = join ' | ', @v; |
140 |
if ( grep { $attr eq $_ } @subfields_to_blank ) { |
225 |
} |
141 |
|
|
|
142 |
# Empty this column |
143 |
$new_item_data->{$attr} = undef; |
144 |
} |
145 |
elsif ( my $regex_search = |
146 |
$input->param( $cgi_var_name . '_regex_search' ) ) |
147 |
{ |
148 |
$columns_with_regex->{$attr} = { |
149 |
search => $regex_search, |
150 |
replace => |
151 |
$input->param( $cgi_var_name . '_regex_replace' ), |
152 |
modifiers => |
153 |
$input->param( $cgi_var_name . '_regex_modifiers' ) |
154 |
}; |
155 |
} |
156 |
else { |
157 |
my @v = |
158 |
grep { $_ ne "" } uniq $input->multi_param($cgi_var_name); |
226 |
|
159 |
|
227 |
if ( my $regex_search = $input->param('items.'.$c.'_regex_search') ) { |
160 |
next unless @v; |
228 |
push @columns_with_regex, $c; |
161 |
|
|
|
162 |
$new_item_data->{$attr} = join '|', @v; |
163 |
} |
229 |
} |
164 |
} |
230 |
} |
165 |
} |
231 |
|
166 |
|
|
|
167 |
my $params = { |
168 |
record_ids => \@itemnumbers, |
169 |
regex_mod => $columns_with_regex, |
170 |
new_values => $new_item_data, |
171 |
exclude_from_local_holds_priority => ( |
172 |
defined $exclude_from_local_holds_priority |
173 |
&& $exclude_from_local_holds_priority ne "" |
174 |
) |
175 |
? $exclude_from_local_holds_priority |
176 |
: undef, |
177 |
|
178 |
}; |
232 |
try { |
179 |
try { |
233 |
my $schema = Koha::Database->new->schema; |
180 |
my $job_id = |
234 |
$schema->txn_do( |
181 |
Koha::BackgroundJob::BatchUpdateItem->new->enqueue($params); |
235 |
sub { |
182 |
$nextop = 'enqueued'; |
236 |
|
183 |
$template->param( job_id => $job_id, ); |
237 |
foreach my $itemnumber (@itemnumbers) { |
|
|
238 |
my $item = Koha::Items->find($itemnumber); |
239 |
next |
240 |
unless $item |
241 |
; # Should have been tested earlier, but just in case... |
242 |
my $itemdata = $item->unblessed; |
243 |
|
244 |
my $modified_holds_priority = 0; |
245 |
if ( defined $exclude_from_local_holds_priority && $exclude_from_local_holds_priority ne "" ) { |
246 |
if(!defined $item->exclude_from_local_holds_priority || $item->exclude_from_local_holds_priority != $exclude_from_local_holds_priority) { |
247 |
$item->exclude_from_local_holds_priority($exclude_from_local_holds_priority)->store; |
248 |
$modified_holds_priority = 1; |
249 |
} |
250 |
} |
251 |
|
252 |
my $modified = 0; |
253 |
for my $c ( @columns_with_regex ) { |
254 |
my $regex_search = $input->param('items.'.$c.'_regex_search'); |
255 |
my $old_value = $item->$c; |
256 |
|
257 |
my $value = apply_regex( |
258 |
{ |
259 |
search => $regex_search, |
260 |
replace => $input->param( |
261 |
'items' . $c . '_regex_replace' |
262 |
), |
263 |
modifiers => $input->param( |
264 |
'items' . $c . '_regex_modifiers' |
265 |
), |
266 |
value => $old_value, |
267 |
} |
268 |
); |
269 |
unless ( $old_value eq $value ) { |
270 |
$modified++; |
271 |
$item->$c($value); |
272 |
} |
273 |
} |
274 |
|
275 |
$modified += scalar(keys %$new_item_data); # FIXME This is incorrect if old value == new value. Should we loop of the keys and compare the before/after values? |
276 |
if ( $modified) { |
277 |
my $itemlost_pre = $item->itemlost; |
278 |
$item->set($new_item_data)->store({skip_record_index => 1}); |
279 |
|
280 |
push @$upd_biblionumbers, $itemdata->{'biblionumber'}; |
281 |
|
282 |
LostItem( |
283 |
$item->itemnumber, 'batchmod', undef, |
284 |
{ skip_record_index => 1 } |
285 |
) if $item->itemlost |
286 |
and not $itemlost_pre; |
287 |
} |
288 |
|
289 |
$modified_items++ if $modified || $modified_holds_priority; |
290 |
$modified_fields += $modified + $modified_holds_priority; |
291 |
} |
292 |
} |
293 |
); |
294 |
} |
184 |
} |
295 |
catch { |
185 |
catch { |
296 |
warn $_; |
186 |
push @messages, |
297 |
die "Something terrible has happened!" |
187 |
{ |
298 |
if ($_ =~ /Rollback failed/); # Rollback failed |
188 |
type => 'error', |
|
|
189 |
code => 'cannot_enqueue_job', |
190 |
error => $_, |
191 |
}; |
192 |
$template->param( view => 'errors' ); |
299 |
}; |
193 |
}; |
300 |
} |
194 |
} |
301 |
|
195 |
|
302 |
$upd_biblionumbers = [ uniq @$upd_biblionumbers ]; # Only update each bib once |
|
|
303 |
|
304 |
# Don't send specialUpdate for records we are going to delete |
305 |
my %del_bib_hash = map{ $_ => undef } @$del_biblionumbers; |
306 |
@$upd_biblionumbers = grep( ! exists( $del_bib_hash{$_} ), @$upd_biblionumbers ); |
307 |
|
308 |
my $indexer = Koha::SearchEngine::Indexer->new({ index => $Koha::SearchEngine::BIBLIOS_INDEX }); |
309 |
$indexer->index_records( $upd_biblionumbers, 'specialUpdate', "biblioserver", undef ) if @$upd_biblionumbers; |
310 |
$indexer->index_records( $del_biblionumbers, 'recordDelete', "biblioserver", undef ) if @$del_biblionumbers; |
311 |
|
312 |
# Once the job is done |
313 |
# If we have a reasonable amount of items, we display them |
314 |
my $max_items = $del ? C4::Context->preference("MaxItemsToDisplayForBatchDel") : C4::Context->preference("MaxItemsToDisplayForBatchMod"); |
315 |
if (scalar(@itemnumbers) <= $max_items ){ |
316 |
if (scalar(@itemnumbers) <= 1000 ) { |
317 |
$items_display_hashref=BuildItemsData(@itemnumbers); |
318 |
} else { |
319 |
# Else, we only display the barcode |
320 |
my @simple_items_display = map { |
321 |
my $itemnumber = $_; |
322 |
my $item = Koha::Items->find($itemnumber); |
323 |
{ |
324 |
itemnumber => $itemnumber, |
325 |
barcode => $item ? ( $item->barcode // q{} ) : q{}, |
326 |
biblionumber => $item ? $item->biblio->biblionumber : q{}, |
327 |
}; |
328 |
} @itemnumbers; |
329 |
$template->param("simple_items_display" => \@simple_items_display); |
330 |
} |
331 |
} else { |
332 |
$template->param( "too_many_items_display" => scalar(@itemnumbers) ); |
333 |
$template->param( "job_completed" => 1 ); |
334 |
} |
335 |
|
336 |
|
337 |
# Calling the template |
338 |
$template->param( |
339 |
modified_items => $modified_items, |
340 |
modified_fields => $modified_fields, |
341 |
); |
342 |
|
343 |
} |
196 |
} |
|
|
197 |
|
198 |
$template->param( |
199 |
messages => \@messages, |
200 |
); |
344 |
# |
201 |
# |
345 |
#------------------------------------------------------------------------------- |
202 |
#------------------------------------------------------------------------------- |
346 |
# build screen with existing items. and "new" one |
203 |
# build screen with existing items. and "new" one |
Lines 376-385
if ($op eq "show"){
Link Here
|
376 |
} |
233 |
} |
377 |
} else { |
234 |
} else { |
378 |
if (defined $biblionumber && !@itemnumbers){ |
235 |
if (defined $biblionumber && !@itemnumbers){ |
379 |
my @all_items = GetItemsInfo( $biblionumber ); |
236 |
my $biblio = Koha::Biblios->find($biblionumber); |
380 |
foreach my $itm (@all_items) { |
237 |
@itemnumbers = $biblio ? $biblio->items->get_column('itemnumber') : (); |
381 |
push @itemnumbers, $itm->{itemnumber}; |
|
|
382 |
} |
383 |
} |
238 |
} |
384 |
if ( my $list = $input->param('barcodelist') ) { |
239 |
if ( my $list = $input->param('barcodelist') ) { |
385 |
my @barcodelist = grep /\S/, ( split /[$split_chars]/, $list ); |
240 |
my @barcodelist = grep /\S/, ( split /[$split_chars]/, $list ); |
Lines 399-405
if ($op eq "show"){
Link Here
|
399 |
: C4::Context->preference("MaxItemsToDisplayForBatchMod"); |
254 |
: C4::Context->preference("MaxItemsToDisplayForBatchMod"); |
400 |
$template->param("too_many_items_process" => scalar(@itemnumbers)) if !$del && scalar(@itemnumbers) > C4::Context->preference("MaxItemsToProcessForBatchMod"); |
255 |
$template->param("too_many_items_process" => scalar(@itemnumbers)) if !$del && scalar(@itemnumbers) > C4::Context->preference("MaxItemsToProcessForBatchMod"); |
401 |
if (scalar(@itemnumbers) <= ( $max_display_items // 1000 ) ) { |
256 |
if (scalar(@itemnumbers) <= ( $max_display_items // 1000 ) ) { |
402 |
$items_display_hashref=BuildItemsData(@itemnumbers); |
257 |
$display_items = 1; |
403 |
} else { |
258 |
} else { |
404 |
$template->param("too_many_items_display" => scalar(@itemnumbers)); |
259 |
$template->param("too_many_items_display" => scalar(@itemnumbers)); |
405 |
# Even if we do not display the items, we need the itemnumbers |
260 |
# Even if we do not display the items, we need the itemnumbers |
Lines 407-416
if ($op eq "show"){
Link Here
|
407 |
} |
262 |
} |
408 |
|
263 |
|
409 |
# now, build the item form for entering a new item |
264 |
# now, build the item form for entering a new item |
410 |
my @loop_data =(); |
|
|
411 |
my $branch_limit = C4::Context->userenv ? C4::Context->userenv->{"branch"} : ""; |
412 |
|
413 |
my $pref_itemcallnumber = C4::Context->preference('itemcallnumber'); |
414 |
|
265 |
|
415 |
# Getting list of subfields to keep when restricted batchmod edit is enabled |
266 |
# Getting list of subfields to keep when restricted batchmod edit is enabled |
416 |
my @subfields_to_allow = $restrictededition ? split ' ', C4::Context->preference('SubfieldsToAllowForRestrictedBatchmod') : (); |
267 |
my @subfields_to_allow = $restrictededition ? split ' ', C4::Context->preference('SubfieldsToAllowForRestrictedBatchmod') : (); |
Lines 423-429
if ($op eq "show"){
Link Here
|
423 |
? ( subfields_to_allow => \@subfields_to_allow ) |
274 |
? ( subfields_to_allow => \@subfields_to_allow ) |
424 |
: () |
275 |
: () |
425 |
), |
276 |
), |
426 |
subfields_to_ignore => ['items.barcode'], |
277 |
ignore_not_allowed_subfields => 1, |
|
|
278 |
kohafields_to_ignore => ['items.barcode'], |
427 |
prefill_with_default_values => $use_default_values, |
279 |
prefill_with_default_values => $use_default_values, |
428 |
default_branches_empty => 1, |
280 |
default_branches_empty => 1, |
429 |
} |
281 |
} |
Lines 438-624
if ($op eq "show"){
Link Here
|
438 |
$nextop="action" |
290 |
$nextop="action" |
439 |
} # -- End action="show" |
291 |
} # -- End action="show" |
440 |
|
292 |
|
441 |
$template->param(%$items_display_hashref) if $items_display_hashref; |
293 |
if ( $display_items ) { |
442 |
$template->param( |
294 |
my $items_table = |
443 |
op => $nextop, |
295 |
Koha::UI::Table::Builder::Items->new( { itemnumbers => \@itemnumbers } ) |
444 |
); |
296 |
->build_table; |
445 |
$template->param( $op => 1 ) if $op; |
|
|
446 |
|
447 |
if ($op eq "action") { |
448 |
|
449 |
#my @not_deleted_loop = map{{itemnumber=>$_}}@not_deleted; |
450 |
|
451 |
$template->param( |
297 |
$template->param( |
452 |
not_deleted_items => $not_deleted_items, |
298 |
items => $items_table->{items}, |
453 |
deleted_items => $deleted_items, |
299 |
item_header_loop => $items_table->{headers}, |
454 |
delete_records => $del_records, |
|
|
455 |
deleted_records => $deleted_records, |
456 |
not_deleted_loop => \@not_deleted |
457 |
); |
300 |
); |
458 |
} |
301 |
} |
459 |
|
302 |
|
460 |
foreach my $error (@errors) { |
303 |
$template->param( |
461 |
$template->param($error => 1) if $error; |
304 |
op => $nextop, |
462 |
} |
305 |
del => $del, |
463 |
$template->param(src => $src); |
306 |
( $op ? ( $op => 1 ) : () ), |
464 |
$template->param(biblionumber => $biblionumber); |
307 |
src => $src, |
465 |
output_html_with_http_headers $input, $cookie, $template->output; |
308 |
biblionumber => $biblionumber, |
466 |
exit; |
309 |
); |
467 |
|
|
|
468 |
|
469 |
# ---------------- Functions |
470 |
|
471 |
sub BuildItemsData{ |
472 |
my @itemnumbers=@_; |
473 |
# now, build existiing item list |
474 |
my %witness; #---- stores the list of subfields used at least once, with the "meaning" of the code |
475 |
my @big_array; |
476 |
#---- finds where items.itemnumber is stored |
477 |
my ( $itemtagfield, $itemtagsubfield) = &GetMarcFromKohaField( "items.itemnumber" ); |
478 |
my ($branchtagfield, $branchtagsubfield) = &GetMarcFromKohaField( "items.homebranch" ); |
479 |
foreach my $itemnumber (@itemnumbers){ |
480 |
my $itemdata = Koha::Items->find($itemnumber); |
481 |
next unless $itemdata; # Should have been tested earlier, but just in case... |
482 |
$itemdata = $itemdata->unblessed; |
483 |
my $itemmarc=Item2Marc($itemdata); |
484 |
my %this_row; |
485 |
foreach my $field (grep {$_->tag() eq $itemtagfield} $itemmarc->fields()) { |
486 |
# loop through each subfield |
487 |
my $itembranchcode=$field->subfield($branchtagsubfield); |
488 |
if ($itembranchcode && C4::Context->preference("IndependentBranches")) { |
489 |
#verifying rights |
490 |
my $userenv = C4::Context->userenv(); |
491 |
unless (C4::Context->IsSuperLibrarian() or (($userenv->{'branch'} eq $itembranchcode))){ |
492 |
$this_row{'nomod'}=1; |
493 |
} |
494 |
} |
495 |
my $tag=$field->tag(); |
496 |
foreach my $subfield ($field->subfields) { |
497 |
my ($subfcode,$subfvalue)=@$subfield; |
498 |
next if ($tagslib->{$tag}->{$subfcode}->{tab} ne 10 |
499 |
&& $tag ne $itemtagfield |
500 |
&& $subfcode ne $itemtagsubfield); |
501 |
|
502 |
$witness{$subfcode} = $tagslib->{$tag}->{$subfcode}->{lib} if ($tagslib->{$tag}->{$subfcode}->{tab} eq 10); |
503 |
if ($tagslib->{$tag}->{$subfcode}->{tab} eq 10) { |
504 |
$this_row{$subfcode}=GetAuthorisedValueDesc( $tag, |
505 |
$subfcode, $subfvalue, '', $tagslib) |
506 |
|| $subfvalue; |
507 |
} |
508 |
|
509 |
$this_row{itemnumber} = $subfvalue if ($tag eq $itemtagfield && $subfcode eq $itemtagsubfield); |
510 |
} |
511 |
} |
512 |
|
513 |
# grab title, author, and ISBN to identify bib that the item |
514 |
# belongs to in the display |
515 |
my $biblio = Koha::Biblios->find( $itemdata->{biblionumber} ); |
516 |
$this_row{title} = $biblio->title; |
517 |
$this_row{author} = $biblio->author; |
518 |
$this_row{isbn} = $biblio->biblioitem->isbn; |
519 |
$this_row{biblionumber} = $biblio->biblionumber; |
520 |
$this_row{holds} = $biblio->holds->count; |
521 |
$this_row{item_holds} = Koha::Holds->search( { itemnumber => $itemnumber } )->count; |
522 |
$this_row{item} = Koha::Items->find($itemnumber); |
523 |
|
524 |
if (%this_row) { |
525 |
push(@big_array, \%this_row); |
526 |
} |
527 |
} |
528 |
@big_array = sort {$a->{0} cmp $b->{0}} @big_array; |
529 |
|
530 |
# now, construct template ! |
531 |
# First, the existing items for display |
532 |
my @item_value_loop; |
533 |
my @witnesscodessorted=sort keys %witness; |
534 |
for my $row ( @big_array ) { |
535 |
my %row_data; |
536 |
my @item_fields = map +{ field => $_ || '' }, @$row{ @witnesscodessorted }; |
537 |
$row_data{item_value} = [ @item_fields ]; |
538 |
$row_data{itemnumber} = $row->{itemnumber}; |
539 |
#reporting this_row values |
540 |
$row_data{'nomod'} = $row->{'nomod'}; |
541 |
$row_data{bibinfo} = $row->{bibinfo}; |
542 |
$row_data{author} = $row->{author}; |
543 |
$row_data{title} = $row->{title}; |
544 |
$row_data{isbn} = $row->{isbn}; |
545 |
$row_data{biblionumber} = $row->{biblionumber}; |
546 |
$row_data{holds} = $row->{holds}; |
547 |
$row_data{item_holds} = $row->{item_holds}; |
548 |
$row_data{item} = $row->{item}; |
549 |
$row_data{safe_to_delete} = $row->{item}->safe_to_delete; |
550 |
my $is_on_loan = C4::Circulation::IsItemIssued( $row->{itemnumber} ); |
551 |
$row_data{onloan} = $is_on_loan ? 1 : 0; |
552 |
push(@item_value_loop,\%row_data); |
553 |
} |
554 |
my @header_loop=map { { header_value=> $witness{$_}} } @witnesscodessorted; |
555 |
|
556 |
my @cannot_be_deleted = map { |
557 |
$_->{safe_to_delete} == 1 ? () : $_->{item}->barcode |
558 |
} @item_value_loop; |
559 |
return { |
560 |
item_loop => \@item_value_loop, |
561 |
cannot_be_deleted => \@cannot_be_deleted, |
562 |
item_header_loop => \@header_loop |
563 |
}; |
564 |
} |
565 |
|
566 |
#BE WARN : it is not the general case |
567 |
# This function can be OK in the item marc record special case |
568 |
# Where subfield is not repeated |
569 |
# And where we are sure that field should correspond |
570 |
# And $tag>10 |
571 |
sub UpdateMarcWith { |
572 |
my ($marcfrom,$marcto)=@_; |
573 |
my ( $itemtag, $itemtagsubfield) = &GetMarcFromKohaField( "items.itemnumber" ); |
574 |
my $fieldfrom=$marcfrom->field($itemtag); |
575 |
my @fields_to=$marcto->field($itemtag); |
576 |
my $modified = 0; |
577 |
|
578 |
return $modified unless $fieldfrom; |
579 |
|
580 |
foreach my $subfield ( $fieldfrom->subfields() ) { |
581 |
foreach my $field_to_update ( @fields_to ) { |
582 |
if ( $subfield->[1] ) { |
583 |
unless ( $field_to_update->subfield($subfield->[0]) eq $subfield->[1] ) { |
584 |
$modified++; |
585 |
$field_to_update->update( $subfield->[0] => $subfield->[1] ); |
586 |
} |
587 |
} |
588 |
else { |
589 |
$modified++; |
590 |
$field_to_update->delete_subfield( code => $subfield->[0] ); |
591 |
} |
592 |
} |
593 |
} |
594 |
return $modified; |
595 |
} |
596 |
|
597 |
sub apply_regex { |
598 |
my ($params) = @_; |
599 |
my $search = $params->{search}; |
600 |
my $replace = $params->{replace}; |
601 |
my $modifiers = $params->{modifiers} || []; |
602 |
my $value = $params->{value}; |
603 |
|
604 |
my @available_modifiers = qw( i g ); |
605 |
my $retained_modifiers = q||; |
606 |
for my $modifier ( split //, @$modifiers ) { |
607 |
$retained_modifiers .= $modifier |
608 |
if grep { /$modifier/ } @available_modifiers; |
609 |
} |
610 |
if ( $retained_modifiers =~ m/^(ig|gi)$/ ) { |
611 |
$value =~ s/$search/$replace/ig; |
612 |
} |
613 |
elsif ( $retained_modifiers eq 'i' ) { |
614 |
$value =~ s/$search/$replace/i; |
615 |
} |
616 |
elsif ( $retained_modifiers eq 'g' ) { |
617 |
$value =~ s/$search/$replace/g; |
618 |
} |
619 |
else { |
620 |
$value =~ s/$search/$replace/; |
621 |
} |
622 |
|
310 |
|
623 |
return $value; |
311 |
output_html_with_http_headers $input, $cookie, $template->output; |
624 |
} |
|
|
625 |
- |
|
|