View | Details | Raw Unified | Return to bug 19316
Collapse All | Expand All

(-)a/Koha/Items.pm (-143 / +144 lines)
Lines 18-24 package Koha::Items; Link Here
18
# along with Koha; if not, see <http://www.gnu.org/licenses>.
18
# along with Koha; if not, see <http://www.gnu.org/licenses>.
19
19
20
use Modern::Perl;
20
use Modern::Perl;
21
use Array::Utils qw( array_minus );
21
use Array::Utils    qw( array_minus );
22
use List::MoreUtils qw( uniq );
22
use List::MoreUtils qw( uniq );
23
use Try::Tiny;
23
use Try::Tiny;
24
24
Lines 69-89 sub filter_by_for_hold { Link Here
69
            rule_value   => 'not_allowed',
69
            rule_value   => 'not_allowed',
70
        }
70
        }
71
    )->get_column('itemtype');
71
    )->get_column('itemtype');
72
    push @hold_not_allowed_itypes, Koha::ItemTypes->search({ notforloan => 1 })->get_column('itemtype');
72
    push @hold_not_allowed_itypes, Koha::ItemTypes->search( { notforloan => 1 } )->get_column('itemtype');
73
73
74
    my $params = {
74
    my $params = {
75
        itemlost   => 0,
75
        itemlost   => 0,
76
        withdrawn  => 0,
76
        withdrawn  => 0,
77
        notforloan => { '<=' => 0 },    # items with negative or zero notforloan value are holdable
77
        notforloan => { '<=' => 0 },    # items with negative or zero notforloan value are holdable
78
        ( C4::Context->preference('AllowHoldsOnDamagedItems')? (): ( damaged => 0 ) ),
78
        ( C4::Context->preference('AllowHoldsOnDamagedItems') ? () : ( damaged => 0 ) ),
79
        ( C4::Context->only_my_library() ? ( homebranch => C4::Context::mybranch() ) : () ),
79
        ( C4::Context->only_my_library()                      ? ( homebranch => C4::Context::mybranch() ) : () ),
80
    };
80
    };
81
81
82
    if ( C4::Context->preference("item-level_itypes") ) {
82
    if ( C4::Context->preference("item-level_itypes") ) {
83
        return $self->search(
83
        return $self->search(
84
            {
84
            {
85
                %$params,
85
                %$params,
86
                itype        => { -not_in => \@hold_not_allowed_itypes },
86
                itype => { -not_in => \@hold_not_allowed_itypes },
87
            }
87
            }
88
        );
88
        );
89
    } else {
89
    } else {
Lines 116-122 are honoured. Link Here
116
=cut
116
=cut
117
117
118
sub filter_by_visible_in_opac {
118
sub filter_by_visible_in_opac {
119
    my ($self, $params) = @_;
119
    my ( $self, $params ) = @_;
120
120
121
    my $patron = $params->{patron};
121
    my $patron = $params->{patron};
122
122
Lines 128-141 sub filter_by_visible_in_opac { Link Here
128
128
129
        my $rules_params;
129
        my $rules_params;
130
        foreach my $field ( keys %$rules ) {
130
        foreach my $field ( keys %$rules ) {
131
            $rules_params->{'me.'.$field} =
131
            $rules_params->{ 'me.' . $field } =
132
              [ { '-not_in' => $rules->{$field} }, undef ];
132
                [ { '-not_in' => $rules->{$field} }, undef ];
133
        }
133
        }
134
134
135
        $result = $result->search( $rules_params );
135
        $result = $result->search($rules_params);
136
    }
136
    }
137
137
138
    if (C4::Context->preference('hidelostitems')) {
138
    if ( C4::Context->preference('hidelostitems') ) {
139
        $result = $result->filter_out_lost;
139
        $result = $result->filter_out_lost;
140
    }
140
    }
141
141
Lines 155-164 sub filter_out_lost { Link Here
155
155
156
    my $params = { itemlost => 0 };
156
    my $params = { itemlost => 0 };
157
157
158
    return $self->search( $params );
158
    return $self->search($params);
159
}
159
}
160
160
161
162
=head3 move_to_biblio
161
=head3 move_to_biblio
163
162
164
 $items->move_to_biblio($to_biblio);
163
 $items->move_to_biblio($to_biblio);
Lines 175-181 sub move_to_biblio { Link Here
175
        $biblionumbers->{ $item->biblionumber } = 1;
174
        $biblionumbers->{ $item->biblionumber } = 1;
176
        $item->move_to_biblio( $to_biblio, { skip_record_index => 1 } );
175
        $item->move_to_biblio( $to_biblio, { skip_record_index => 1 } );
177
    }
176
    }
178
    my $indexer = Koha::SearchEngine::Indexer->new({ index => $Koha::SearchEngine::BIBLIOS_INDEX });
177
    my $indexer = Koha::SearchEngine::Indexer->new( { index => $Koha::SearchEngine::BIBLIOS_INDEX } );
179
    for my $biblionumber ( keys %{$biblionumbers} ) {
178
    for my $biblionumber ( keys %{$biblionumbers} ) {
180
        $indexer->index_records( $biblionumber, "specialUpdate", "biblioserver" );
179
        $indexer->index_records( $biblionumber, "specialUpdate", "biblioserver" );
181
    }
180
    }
Lines 251-398 sub batch_update { Link Here
251
    my $sort2                             = $params->{sort2};
250
    my $sort2                             = $params->{sort2};
252
    my $callback                          = $params->{callback};
251
    my $callback                          = $params->{callback};
253
252
254
    my (@modified_itemnumbers, $modified_fields);
253
    my ( @modified_itemnumbers, $modified_fields );
255
    my $i;
254
    my $i;
256
    my $schema = Koha::Database->new->schema;
255
    my $schema = Koha::Database->new->schema;
257
    while ( my $item = $self->next ) {
256
    while ( my $item = $self->next ) {
258
257
259
        try {$schema->txn_do(sub {
258
        try {
260
            my $modified_holds_priority = 0;
259
            $schema->txn_do(
261
            my $item_returned           = 0;
260
                sub {
262
            my $sort_fields_modified    = 0;
261
                    my $modified_holds_priority = 0;
263
            if ( defined $exclude_from_local_holds_priority ) {
262
                    my $item_returned           = 0;
264
                if(!defined $item->exclude_from_local_holds_priority || $item->exclude_from_local_holds_priority != $exclude_from_local_holds_priority) {
263
                    my $sort_fields_modified    = 0;
265
                    $item->exclude_from_local_holds_priority($exclude_from_local_holds_priority)->store;
264
                    if ( defined $exclude_from_local_holds_priority ) {
266
                    $modified_holds_priority = 1;
265
                        if ( !defined $item->exclude_from_local_holds_priority
267
                }
266
                            || $item->exclude_from_local_holds_priority != $exclude_from_local_holds_priority )
268
            }
267
                        {
269
            if ( defined $sort1 || defined $sort2 ) {
268
                            $item->exclude_from_local_holds_priority($exclude_from_local_holds_priority)->store;
270
                $item->sort1($sort1)->store if $sort1 ne "";
269
                            $modified_holds_priority = 1;
271
                $sort_fields_modified++     if $sort1 ne "";
270
                        }
272
                $item->sort2($sort2)->store if $sort2 ne "";
271
                    }
273
                $sort_fields_modified++     if $sort2 ne "";
272
                    if ( defined $sort1 || defined $sort2 ) {
274
            }
273
                        $item->sort1($sort1)->store if $sort1 ne "";
274
                        $sort_fields_modified++     if $sort1 ne "";
275
                        $item->sort2($sort2)->store if $sort2 ne "";
276
                        $sort_fields_modified++     if $sort2 ne "";
277
                    }
275
278
276
            my $modified = 0;
279
                    my $modified   = 0;
277
            my $new_values = {%$new_values};    # Don't modify the original
280
                    my $new_values = {%$new_values};    # Don't modify the original
278
281
279
            my $old_values = $item->unblessed;
282
                    my $old_values = $item->unblessed;
280
            if ( $item->more_subfields_xml ) {
283
                    if ( $item->more_subfields_xml ) {
281
                $old_values = {
284
                        $old_values = {
282
                    %$old_values,
285
                            %$old_values,
283
                    %{$item->additional_attributes->to_hashref},
286
                            %{ $item->additional_attributes->to_hashref },
284
                };
287
                        };
285
            }
288
                    }
289
290
                    for my $attr ( keys %$regex_mod ) {
291
                        my $old_value = $old_values->{$attr};
286
292
287
            for my $attr ( keys %$regex_mod ) {
293
                        next unless $old_value;
288
                my $old_value = $old_values->{$attr};
289
294
290
                next unless $old_value;
295
                        my $value = apply_regex(
296
                            {
297
                                %{ $regex_mod->{$attr} },
298
                                value => $old_value,
299
                            }
300
                        );
291
301
292
                my $value = apply_regex(
302
                        $new_values->{$attr} = $value;
293
                    {
294
                        %{ $regex_mod->{$attr} },
295
                        value => $old_value,
296
                    }
303
                    }
297
                );
298
304
299
                $new_values->{$attr} = $value;
305
                    for my $attribute ( keys %$new_values ) {
300
            }
306
                        next if $attribute eq 'more_subfields_xml';    # Already counted before
301
307
302
            for my $attribute ( keys %$new_values ) {
308
                        my $old = $old_values->{$attribute};
303
                next if $attribute eq 'more_subfields_xml'; # Already counted before
309
                        my $new = $new_values->{$attribute};
310
                        $modified++
311
                            if ( defined $old xor defined $new )
312
                            || ( defined $old && defined $new && $new ne $old );
313
                    }
304
314
305
                my $old = $old_values->{$attribute};
315
                    {    # Dealing with more_subfields_xml
306
                my $new = $new_values->{$attribute};
307
                $modified++
308
                  if ( defined $old xor defined $new )
309
                  || ( defined $old && defined $new && $new ne $old );
310
            }
311
316
312
            { # Dealing with more_subfields_xml
317
                        my $frameworkcode = $item->biblio->frameworkcode;
313
318
                        my $tagslib       = C4::Biblio::GetMarcStructure( 1, $frameworkcode, { unsafe => 1 } );
314
                my $frameworkcode = $item->biblio->frameworkcode;
319
                        my ( $itemtag, $itemsubfield ) = C4::Biblio::GetMarcFromKohaField("items.itemnumber");
315
                my $tagslib = C4::Biblio::GetMarcStructure( 1, $frameworkcode, { unsafe => 1 });
316
                my ( $itemtag, $itemsubfield ) = C4::Biblio::GetMarcFromKohaField( "items.itemnumber" );
317
318
                my @more_subfield_tags = map {
319
                    (
320
                             ref($_)
321
                          && %$_
322
                          && !$_->{kohafield}    # Get subfields that are not mapped
323
                      )
324
                      ? $_->{tagsubfield}
325
                      : ()
326
                } values %{ $tagslib->{$itemtag} };
327
328
                my $more_subfields_xml = Koha::Item::Attributes->new(
329
                    {
330
                        map {
331
                            exists $new_values->{$_} ? ( $_ => $new_values->{$_} )
332
                              : exists $old_values->{$_}
333
                              ? ( $_ => $old_values->{$_} )
334
                              : ()
335
                        } @more_subfield_tags
336
                    }
337
                )->to_marcxml($frameworkcode);
338
320
339
                $new_values->{more_subfields_xml} = $more_subfields_xml;
321
                        my @more_subfield_tags = map {
322
                            (
323
                                       ref($_)
324
                                    && %$_
325
                                    && !$_->{kohafield}    # Get subfields that are not mapped
326
                                )
327
                                ? $_->{tagsubfield}
328
                                : ()
329
                        } values %{ $tagslib->{$itemtag} };
340
330
341
                delete $new_values->{$_} for @more_subfield_tags; # Clean the hash
331
                        my $more_subfields_xml = Koha::Item::Attributes->new(
332
                            {
333
                                map {
334
                                          exists $new_values->{$_} ? ( $_ => $new_values->{$_} )
335
                                        : exists $old_values->{$_} ? ( $_ => $old_values->{$_} )
336
                                        : ()
337
                                } @more_subfield_tags
338
                            }
339
                        )->to_marcxml($frameworkcode);
342
340
343
            }
341
                        $new_values->{more_subfields_xml} = $more_subfields_xml;
344
342
345
            if ( $modified ) {
343
                        delete $new_values->{$_} for @more_subfield_tags;    # Clean the hash
346
                my $itemlost_pre = $item->itemlost;
347
                $item->set($new_values)->store({skip_record_index => 1});
348
344
349
                C4::Circulation::LostItem(
345
                    }
350
                    $item->itemnumber, 'batchmod', undef,
346
351
                    { skip_record_index => 1 }
347
                    if ($modified) {
352
                ) if $item->itemlost
348
                        my $itemlost_pre = $item->itemlost;
353
                      and not $itemlost_pre;
349
                        $item->set($new_values)->store( { skip_record_index => 1 } );
354
            }
350
355
            if ( $mark_items_returned ){
351
                        C4::Circulation::LostItem(
356
                my $issue = $item->checkout;
352
                            $item->itemnumber, 'batchmod', undef,
357
                if( $issue ){
353
                            { skip_record_index => 1 }
358
                        $item_returned = 1;
354
                            )
359
                        C4::Circulation::MarkIssueReturned(
355
                            if $item->itemlost
360
                        $issue->borrowernumber,
356
                            and not $itemlost_pre;
361
                        $item->itemnumber,
357
                    }
362
                        undef,
358
                    if ($mark_items_returned) {
363
                        $issue->patron->privacy,
359
                        my $issue = $item->checkout;
364
                        {
360
                        if ($issue) {
365
                            skip_record_index => 1,
361
                            $item_returned = 1;
366
                            skip_holds_queue  => 1,
362
                            C4::Circulation::MarkIssueReturned(
363
                                $issue->borrowernumber,
364
                                $item->itemnumber,
365
                                undef,
366
                                $issue->patron->privacy,
367
                                {
368
                                    skip_record_index => 1,
369
                                    skip_holds_queue  => 1,
370
                                }
371
                            );
367
                        }
372
                        }
368
                    );
373
                    }
369
                }
370
            }
371
374
372
            push @modified_itemnumbers, $item->itemnumber
375
                    push @modified_itemnumbers, $item->itemnumber
373
                if $modified || $modified_holds_priority || $item_returned || $sort_fields_modified;
376
                        if $modified || $modified_holds_priority || $item_returned || $sort_fields_modified;
374
            $modified_fields += $modified + $modified_holds_priority + $item_returned + $sort_fields_modified;
377
                    $modified_fields += $modified + $modified_holds_priority + $item_returned + $sort_fields_modified;
375
        })}
378
                }
376
        catch {
379
            )
380
        } catch {
377
            warn $_
381
            warn $_
378
        };
382
        };
379
383
380
        if ( $callback ) {
384
        if ($callback) {
381
            $callback->(++$i);
385
            $callback->( ++$i );
382
        }
386
        }
383
    }
387
    }
384
388
385
    if (@modified_itemnumbers) {
389
    if (@modified_itemnumbers) {
386
        my @biblionumbers = uniq(
390
        my @biblionumbers =
387
            Koha::Items->search( { itemnumber => \@modified_itemnumbers } )
391
            uniq( Koha::Items->search( { itemnumber => \@modified_itemnumbers } )->get_column('biblionumber') );
388
                       ->get_column('biblionumber'));
389
392
390
        if ( @biblionumbers ) {
393
        if (@biblionumbers) {
391
            my $indexer = Koha::SearchEngine::Indexer->new(
394
            my $indexer = Koha::SearchEngine::Indexer->new( { index => $Koha::SearchEngine::BIBLIOS_INDEX } );
392
                { index => $Koha::SearchEngine::BIBLIOS_INDEX } );
393
395
394
            $indexer->index_records( \@biblionumbers, 'specialUpdate',
396
            $indexer->index_records(
395
                "biblioserver", undef );
397
                \@biblionumbers, 'specialUpdate',
398
                "biblioserver",  undef
399
            );
396
        }
400
        }
397
    }
401
    }
398
402
Lines 400-431 sub batch_update { Link Here
400
}
404
}
401
405
402
sub apply_regex {
406
sub apply_regex {
407
403
    # FIXME Should be moved outside of Koha::Items
408
    # FIXME Should be moved outside of Koha::Items
404
    # FIXME This is nearly identical to Koha::SimpleMARC::_modify_values
409
    # FIXME This is nearly identical to Koha::SimpleMARC::_modify_values
405
    my ($params) = @_;
410
    my ($params)  = @_;
406
    my $search   = $params->{search};
411
    my $search    = $params->{search};
407
    my $replace  = $params->{replace};
412
    my $replace   = $params->{replace};
408
    my $modifiers = $params->{modifiers} || q{};
413
    my $modifiers = $params->{modifiers} || q{};
409
    my $value = $params->{value};
414
    my $value     = $params->{value};
410
415
411
    $replace =~ s/"/\\"/g;                    # Protection from embedded code
416
    $replace =~ s/"/\\"/g;              # Protection from embedded code
412
    $replace = '"' . $replace . '"'; # Put in a string for /ee
417
    $replace = '"' . $replace . '"';    # Put in a string for /ee
413
    my @available_modifiers = qw( i g );
418
    my @available_modifiers = qw( i g );
414
    my $retained_modifiers  = q||;
419
    my $retained_modifiers  = q||;
415
    for my $modifier ( split //, $modifiers ) {
420
    for my $modifier ( split //, $modifiers ) {
416
        $retained_modifiers .= $modifier
421
        $retained_modifiers .= $modifier
417
          if grep { /$modifier/ } @available_modifiers;
422
            if grep { /$modifier/ } @available_modifiers;
418
    }
423
    }
419
    if ( $retained_modifiers =~ m/^(ig|gi)$/ ) {
424
    if ( $retained_modifiers =~ m/^(ig|gi)$/ ) {
420
        $value =~ s/$search/$replace/igee;
425
        $value =~ s/$search/$replace/igee;
421
    }
426
    } elsif ( $retained_modifiers eq 'i' ) {
422
    elsif ( $retained_modifiers eq 'i' ) {
423
        $value =~ s/$search/$replace/iee;
427
        $value =~ s/$search/$replace/iee;
424
    }
428
    } elsif ( $retained_modifiers eq 'g' ) {
425
    elsif ( $retained_modifiers eq 'g' ) {
426
        $value =~ s/$search/$replace/gee;
429
        $value =~ s/$search/$replace/gee;
427
    }
430
    } else {
428
    else {
429
        $value =~ s/$search/$replace/ee;
431
        $value =~ s/$search/$replace/ee;
430
    }
432
    }
431
433
Lines 441-453 Search and sort items in a specific order, depending if serials are present or n Link Here
441
=cut
443
=cut
442
444
443
sub search_ordered {
445
sub search_ordered {
444
    my ($self, $params, $attributes) = @_;
446
    my ( $self, $params, $attributes ) = @_;
445
447
446
    $self = $self->search($params, $attributes);
448
    $self = $self->search( $params, $attributes );
447
449
448
    my @biblionumbers = uniq $self->get_column('biblionumber');
450
    my @biblionumbers = uniq $self->get_column('biblionumber');
449
451
450
    if ( scalar ( @biblionumbers ) == 1
452
    if ( scalar(@biblionumbers) == 1
451
        && Koha::Biblios->find( $biblionumbers[0] )->serial )
453
        && Koha::Biblios->find( $biblionumbers[0] )->serial )
452
    {
454
    {
453
        return $self->search(
455
        return $self->search(
Lines 465-471 sub search_ordered { Link Here
465
                    'homebranch.branchname',
467
                    'homebranch.branchname',
466
                    'me.enumchron',
468
                    'me.enumchron',
467
                    \"LPAD( me.copynumber, 8, '0' )",
469
                    \"LPAD( me.copynumber, 8, '0' )",
468
                    {-desc => 'me.dateaccessioned'}
470
                    { -desc => 'me.dateaccessioned' }
469
                ],
471
                ],
470
                join => ['homebranch']
472
                join => ['homebranch']
471
            }
473
            }
472
- 

Return to bug 19316