File Coverage

File:C4/Items.pm
Coverage:3.9%

linestmtbrancondsubtimecode
1package C4::Items;
2
3# Copyright 2007 LibLime, Inc.
4# Parts Copyright Biblibre 2010
5#
6# This file is part of Koha.
7#
8# Koha is free software; you can redistribute it and/or modify it under the
9# terms of the GNU General Public License as published by the Free Software
10# Foundation; either version 2 of the License, or (at your option) any later
11# version.
12#
13# Koha is distributed in the hope that it will be useful, but WITHOUT ANY
14# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
15# A PARTICULAR PURPOSE. See the GNU General Public License for more details.
16#
17# You should have received a copy of the GNU General Public License along
18# with Koha; if not, write to the Free Software Foundation, Inc.,
19# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20
21
14
14
14
467
63
478
use strict;
22#use warnings; FIXME - Bug 2505
23
24
14
14
14
101
59
1125
use Carp;
25
14
14
14
302
81
259
use C4::Context;
26
14
14
14
386
55
5725
use C4::Koha;
27
14
14
14
604
50
7101
use C4::Biblio;
28
14
14
14
74
23
902
use C4::Dates qw/format_date format_date_in_iso/;
29
14
14
14
3038
122
1338
use C4::Search qw/SimpleSearch/;
30
14
14
14
138
105
683
use MARC::Record;
31
14
14
14
154
78
2810
use C4::ClassSource;
32
14
14
14
145
68
1491
use C4::Log;
33
14
14
14
138
87
1008
use List::MoreUtils qw/any/;
34
14
14
14
137
56
859
use Data::Dumper; # used as part of logging item record changes, not just for
35                  # debugging; so please don't remove this
36
37
14
14
14
105
44
2087
use vars qw($VERSION @ISA @EXPORT);
38
39BEGIN {
40
14
78
    $VERSION = 3.01;
41
42
14
71
        require Exporter;
43
14
198
    @ISA = qw( Exporter );
44
45    # function exports
46
14
194983
    @EXPORT = qw(
47        GetItem
48        AddItemFromMarc
49        AddItem
50        AddItemBatchFromMarc
51        ModItemFromMarc
52                Item2Marc
53        ModItem
54        ModDateLastSeen
55        ModItemTransfer
56        DelItem
57
58        CheckItemPreSave
59
60        GetItemStatus
61        GetItemLocation
62        GetLostItems
63        GetItemsForInventory
64        GetItemsCount
65        GetItemInfosOf
66        GetItemsByBiblioitemnumber
67        GetItemsInfo
68        GetItemsLocationInfo
69        GetHostItemsInfo
70        get_itemnumbers_of
71        get_hostitemnumbers_of
72        GetItemnumberFromBarcode
73        GetBarcodeFromItemnumber
74        GetHiddenItemnumbers
75                DelItemCheck
76                MoveItemFromBiblio
77                GetLatestAcquisitions
78        CartToShelf
79
80        GetAnalyticsCount
81        GetItemHolds
82
83
84        PrepareItemrecordDisplay
85
86    );
87}
88
89 - 128
=head1 NAME

C4::Items - item management functions

=head1 DESCRIPTION

This module contains an API for manipulating item 
records in Koha, and is used by cataloguing, circulation,
acquisitions, and serials management.

A Koha item record is stored in two places: the
items table and embedded in a MARC tag in the XML
version of the associated bib record in C<biblioitems.marcxml>.
This is done to allow the item information to be readily
indexed (e.g., by Zebra), but means that each item
modification transaction must keep the items table
and the MARC XML in sync at all times.

Consequently, all code that creates, modifies, or deletes
item records B<must> use an appropriate function from 
C<C4::Items>.  If no existing function is suitable, it is
better to add one to C<C4::Items> than to use add
one-off SQL statements to add or modify items.

The items table will be considered authoritative.  In other
words, if there is ever a discrepancy between the items
table and the MARC XML, the items table should be considered
accurate.

=head1 HISTORICAL NOTE

Most of the functions in C<C4::Items> were originally in
the C<C4::Biblio> module.

=head1 CORE EXPORTED FUNCTIONS

The following functions are meant for use by users
of C<C4::Items>

=cut
129
130 - 138
=head2 GetItem

  $item = GetItem($itemnumber,$barcode,$serial);

Return item information, for a given itemnumber or barcode.
The return value is a hashref mapping item column
names to values.  If C<$serial> is true, include serial publication data.

=cut
139
140sub GetItem {
141
0
    my ($itemnumber,$barcode, $serial) = @_;
142
0
    my $dbh = C4::Context->dbh;
143
0
        my $data;
144
0
    if ($itemnumber) {
145
0
        my $sth = $dbh->prepare("
146            SELECT * FROM items
147            WHERE itemnumber = ?");
148
0
        $sth->execute($itemnumber);
149
0
        $data = $sth->fetchrow_hashref;
150    } else {
151
0
        my $sth = $dbh->prepare("
152            SELECT * FROM items
153            WHERE barcode = ?"
154            );
155
0
        $sth->execute($barcode);
156
0
        $data = $sth->fetchrow_hashref;
157    }
158
0
    if ( $serial) {
159
0
    my $ssth = $dbh->prepare("SELECT serialseq,publisheddate from serialitems left join serial on serialitems.serialid=serial.serialid where serialitems.itemnumber=?");
160
0
        $ssth->execute($data->{'itemnumber'}) ;
161
0
        ($data->{'serialseq'} , $data->{'publisheddate'}) = $ssth->fetchrow_array();
162    }
163        #if we don't have an items.itype, use biblioitems.itemtype.
164
0
        if( ! $data->{'itype'} ) {
165
0
                my $sth = $dbh->prepare("SELECT itemtype FROM biblioitems WHERE biblionumber = ?");
166
0
                $sth->execute($data->{'biblionumber'});
167
0
                ($data->{'itype'}) = $sth->fetchrow_array;
168        }
169
0
    return $data;
170} # sub GetItem
171
172 - 182
=head2 CartToShelf

  CartToShelf($itemnumber);

Set the current shelving location of the item record
to its stored permanent shelving location.  This is
primarily used to indicate when an item whose current
location is a special processing ('PROC') or shelving cart
('CART') location is back in the stacks.

=cut
183
184sub CartToShelf {
185
0
    my ( $itemnumber ) = @_;
186
187
0
    unless ( $itemnumber ) {
188
0
        croak "FAILED CartToShelf() - no itemnumber supplied";
189    }
190
191
0
    my $item = GetItem($itemnumber);
192
0
    $item->{location} = $item->{permanent_location};
193
0
    ModItem($item, undef, $itemnumber);
194}
195
196 - 204
=head2 AddItemFromMarc

  my ($biblionumber, $biblioitemnumber, $itemnumber) 
      = AddItemFromMarc($source_item_marc, $biblionumber);

Given a MARC::Record object containing an embedded item
record and a biblionumber, create a new item record.

=cut
205
206sub AddItemFromMarc {
207
0
    my ( $source_item_marc, $biblionumber ) = @_;
208
0
    my $dbh = C4::Context->dbh;
209
210    # parse item hash from MARC
211
0
    my $frameworkcode = GetFrameworkCode( $biblionumber );
212
0
        my ($itemtag,$itemsubfield)=GetMarcFromKohaField("items.itemnumber",$frameworkcode);
213
214
0
        my $localitemmarc=MARC::Record->new;
215
0
        $localitemmarc->append_fields($source_item_marc->field($itemtag));
216
0
    my $item = &TransformMarcToKoha( $dbh, $localitemmarc, $frameworkcode ,'items');
217
0
    my $unlinked_item_subfields = _get_unlinked_item_subfields($localitemmarc, $frameworkcode);
218
0
    return AddItem($item, $biblionumber, $dbh, $frameworkcode, $unlinked_item_subfields);
219}
220
221 - 240
=head2 AddItem

  my ($biblionumber, $biblioitemnumber, $itemnumber) 
      = AddItem($item, $biblionumber[, $dbh, $frameworkcode, $unlinked_item_subfields]);

Given a hash containing item column names as keys,
create a new Koha item record.

The first two optional parameters (C<$dbh> and C<$frameworkcode>)
do not need to be supplied for general use; they exist
simply to allow them to be picked up from AddItemFromMarc.

The final optional parameter, C<$unlinked_item_subfields>, contains
an arrayref containing subfields present in the original MARC
representation of the item (e.g., from the item editor) that are
not mapped to C<items> columns directly but should instead
be stored in C<items.more_subfields_xml> and included in 
the biblio items tag for display and indexing.

=cut
241
242sub AddItem {
243
0
    my $item = shift;
244
0
    my $biblionumber = shift;
245
246
0
    my $dbh = @_ ? shift : C4::Context->dbh;
247
0
    my $frameworkcode = @_ ? shift : GetFrameworkCode( $biblionumber );
248
0
    my $unlinked_item_subfields;
249
0
    if (@_) {
250
0
        $unlinked_item_subfields = shift
251    };
252
253    # needs old biblionumber and biblioitemnumber
254
0
    $item->{'biblionumber'} = $biblionumber;
255
0
    my $sth = $dbh->prepare("SELECT biblioitemnumber FROM biblioitems WHERE biblionumber=?");
256
0
    $sth->execute( $item->{'biblionumber'} );
257
0
    ($item->{'biblioitemnumber'}) = $sth->fetchrow;
258
259
0
    _set_defaults_for_add($item);
260
0
    _set_derived_columns_for_add($item);
261
0
    $item->{'more_subfields_xml'} = _get_unlinked_subfields_xml($unlinked_item_subfields);
262    # FIXME - checks here
263
0
    unless ( $item->{itype} ) { # default to biblioitem.itemtype if no itype
264
0
        my $itype_sth = $dbh->prepare("SELECT itemtype FROM biblioitems WHERE biblionumber = ?");
265
0
        $itype_sth->execute( $item->{'biblionumber'} );
266
0
        ( $item->{'itype'} ) = $itype_sth->fetchrow_array;
267    }
268
269
0
        my ( $itemnumber, $error ) = _koha_new_item( $item, $item->{barcode} );
270
0
    $item->{'itemnumber'} = $itemnumber;
271
272
0
    ModZebra( $item->{biblionumber}, "specialUpdate", "biblioserver", undef, undef );
273
274
0
    logaction("CATALOGUING", "ADD", $itemnumber, "item") if C4::Context->preference("CataloguingLog");
275
276
0
    return ($item->{biblionumber}, $item->{biblioitemnumber}, $itemnumber);
277}
278
279 - 322
=head2 AddItemBatchFromMarc

  ($itemnumber_ref, $error_ref) = AddItemBatchFromMarc($record, 
             $biblionumber, $biblioitemnumber, $frameworkcode);

Efficiently create item records from a MARC biblio record with
embedded item fields.  This routine is suitable for batch jobs.

This API assumes that the bib record has already been
saved to the C<biblio> and C<biblioitems> tables.  It does
not expect that C<biblioitems.marc> and C<biblioitems.marcxml>
are populated, but it will do so via a call to ModBibiloMarc.

The goal of this API is to have a similar effect to using AddBiblio
and AddItems in succession, but without inefficient repeated
parsing of the MARC XML bib record.

This function returns an arrayref of new itemsnumbers and an arrayref of item
errors encountered during the processing.  Each entry in the errors
list is a hashref containing the following keys:

=over

=item item_sequence

Sequence number of original item tag in the MARC record.

=item item_barcode

Item barcode, provide to assist in the construction of
useful error messages.

=item error_condition

Code representing the error condition.  Can be 'duplicate_barcode',
'invalid_homebranch', or 'invalid_holdingbranch'.

=item error_information

Additional information appropriate to the error condition.

=back

=cut
323
324sub AddItemBatchFromMarc {
325
0
    my ($record, $biblionumber, $biblioitemnumber, $frameworkcode) = @_;
326
0
    my $error;
327
0
    my @itemnumbers = ();
328
0
    my @errors = ();
329
0
    my $dbh = C4::Context->dbh;
330
331    # loop through the item tags and start creating items
332
0
    my @bad_item_fields = ();
333
0
    my ($itemtag, $itemsubfield) = &GetMarcFromKohaField("items.itemnumber",'');
334
0
    my $item_sequence_num = 0;
335
0
    ITEMFIELD: foreach my $item_field ($record->field($itemtag)) {
336
0
        $item_sequence_num++;
337        # we take the item field and stick it into a new
338        # MARC record -- this is required so far because (FIXME)
339        # TransformMarcToKoha requires a MARC::Record, not a MARC::Field
340        # and there is no TransformMarcFieldToKoha
341
0
        my $temp_item_marc = MARC::Record->new();
342
0
        $temp_item_marc->append_fields($item_field);
343
344        # add biblionumber and biblioitemnumber
345
0
        my $item = TransformMarcToKoha( $dbh, $temp_item_marc, $frameworkcode, 'items' );
346
0
        my $unlinked_item_subfields = _get_unlinked_item_subfields($temp_item_marc, $frameworkcode);
347
0
        $item->{'more_subfields_xml'} = _get_unlinked_subfields_xml($unlinked_item_subfields);
348
0
        $item->{'biblionumber'} = $biblionumber;
349
0
        $item->{'biblioitemnumber'} = $biblioitemnumber;
350
351        # check for duplicate barcode
352
0
        my %item_errors = CheckItemPreSave($item);
353
0
        if (%item_errors) {
354
0
            push @errors, _repack_item_errors($item_sequence_num, $item, \%item_errors);
355
0
            push @bad_item_fields, $item_field;
356
0
            next ITEMFIELD;
357        }
358
359
0
        _set_defaults_for_add($item);
360
0
        _set_derived_columns_for_add($item);
361
0
        my ( $itemnumber, $error ) = _koha_new_item( $item, $item->{barcode} );
362
0
        warn $error if $error;
363
0
        push @itemnumbers, $itemnumber; # FIXME not checking error
364
0
        $item->{'itemnumber'} = $itemnumber;
365
366
0
        logaction("CATALOGUING", "ADD", $itemnumber, "item") if C4::Context->preference("CataloguingLog");
367
368
0
        my $new_item_marc = _marc_from_item_hash($item, $frameworkcode, $unlinked_item_subfields);
369
0
        $item_field->replace_with($new_item_marc->field($itemtag));
370    }
371
372    # remove any MARC item fields for rejected items
373
0
    foreach my $item_field (@bad_item_fields) {
374
0
        $record->delete_field($item_field);
375    }
376
377    # update the MARC biblio
378 # $biblionumber = ModBiblioMarc( $record, $biblionumber, $frameworkcode );
379
380
0
    return (\@itemnumbers, \@errors);
381}
382
383 - 407
=head2 ModItemFromMarc

  ModItemFromMarc($item_marc, $biblionumber, $itemnumber);

This function updates an item record based on a supplied
C<MARC::Record> object containing an embedded item field.
This API is meant for the use of C<additem.pl>; for 
other purposes, C<ModItem> should be used.

This function uses the hash %default_values_for_mod_from_marc,
which contains default values for item fields to
apply when modifying an item.  This is needed beccause
if an item field's value is cleared, TransformMarcToKoha
does not include the column in the
hash that's passed to ModItem, which without
use of this hash makes it impossible to clear
an item field's value.  See bug 2466.

Note that only columns that can be directly
changed from the cataloging and serials
item editors are included in this hash.

Returns item record

=cut
408
409my %default_values_for_mod_from_marc = (
410    barcode => undef,
411    booksellerid => undef,
412    ccode => undef,
413    'items.cn_source' => undef,
414    copynumber => undef,
415    damaged => 0,
416# dateaccessioned => undef,
417    enumchron => undef,
418    holdingbranch => undef,
419    homebranch => undef,
420    itemcallnumber => undef,
421    itemlost => 0,
422    itemnotes => undef,
423    itype => undef,
424    location => undef,
425    permanent_location => undef,
426    materials => undef,
427    notforloan => 0,
428    paidfor => undef,
429    price => undef,
430    replacementprice => undef,
431    replacementpricedate => undef,
432    restricted => undef,
433    stack => undef,
434    stocknumber => undef,
435    uri => undef,
436    wthdrawn => 0,
437);
438
439sub ModItemFromMarc {
440
0
    my $item_marc = shift;
441
0
    my $biblionumber = shift;
442
0
    my $itemnumber = shift;
443
444
0
    my $dbh = C4::Context->dbh;
445
0
    my $frameworkcode = GetFrameworkCode($biblionumber);
446
0
    my ( $itemtag, $itemsubfield ) = GetMarcFromKohaField( "items.itemnumber", $frameworkcode );
447
448
0
    my $localitemmarc = MARC::Record->new;
449
0
    $localitemmarc->append_fields( $item_marc->field($itemtag) );
450
0
    my $item = &TransformMarcToKoha( $dbh, $localitemmarc, $frameworkcode, 'items' );
451
0
    foreach my $item_field ( keys %default_values_for_mod_from_marc ) {
452
0
        $item->{$item_field} = $default_values_for_mod_from_marc{$item_field} unless (exists $item->{$item_field});
453    }
454
0
    my $unlinked_item_subfields = _get_unlinked_item_subfields( $localitemmarc, $frameworkcode );
455
456
0
    ModItem($item, $biblionumber, $itemnumber, $dbh, $frameworkcode, $unlinked_item_subfields);
457
0
    return $item;
458}
459
460 - 483
=head2 ModItem

  ModItem({ column => $newvalue }, $biblionumber, $itemnumber);

Change one or more columns in an item record and update
the MARC representation of the item.

The first argument is a hashref mapping from item column
names to the new values.  The second and third arguments
are the biblionumber and itemnumber, respectively.

The fourth, optional parameter, C<$unlinked_item_subfields>, contains
an arrayref containing subfields present in the original MARC
representation of the item (e.g., from the item editor) that are
not mapped to C<items> columns directly but should instead
be stored in C<items.more_subfields_xml> and included in 
the biblio items tag for display and indexing.

If one of the changed columns is used to calculate
the derived value of a column such as C<items.cn_sort>, 
this routine will perform the necessary calculation
and set the value.

=cut
484
485sub ModItem {
486
0
    my $item = shift;
487
0
    my $biblionumber = shift;
488
0
    my $itemnumber = shift;
489
490    # if $biblionumber is undefined, get it from the current item
491
0
    unless (defined $biblionumber) {
492
0
        $biblionumber = _get_single_item_column('biblionumber', $itemnumber);
493    }
494
495
0
    my $dbh = @_ ? shift : C4::Context->dbh;
496
0
    my $frameworkcode = @_ ? shift : GetFrameworkCode( $biblionumber );
497
498
0
    my $unlinked_item_subfields;
499
0
    if (@_) {
500
0
        $unlinked_item_subfields = shift;
501
0
        $item->{'more_subfields_xml'} = _get_unlinked_subfields_xml($unlinked_item_subfields);
502    };
503
504
0
    $item->{'itemnumber'} = $itemnumber or return undef;
505
506
0
    $item->{onloan} = undef if $item->{itemlost};
507
508
0
    _set_derived_columns_for_mod($item);
509
0
    _do_column_fixes_for_mod($item);
510    # FIXME add checks
511    # duplicate barcode
512    # attempt to change itemnumber
513    # attempt to change biblionumber (if we want
514    # an API to relink an item to a different bib,
515    # it should be a separate function)
516
517    # update items table
518
0
    _koha_modify_item($item);
519
520    # request that bib be reindexed so that searching on current
521    # item status is possible
522
0
    ModZebra( $biblionumber, "specialUpdate", "biblioserver", undef, undef );
523
524
0
    logaction("CATALOGUING", "MODIFY", $itemnumber, Dumper($item)) if C4::Context->preference("CataloguingLog");
525}
526
527 - 534
=head2 ModItemTransfer

  ModItemTransfer($itenumber, $frombranch, $tobranch);

Marks an item as being transferred from one branch
to another.

=cut
535
536sub ModItemTransfer {
537
0
    my ( $itemnumber, $frombranch, $tobranch ) = @_;
538
539
0
    my $dbh = C4::Context->dbh;
540
541    #new entry in branchtransfers....
542
0
    my $sth = $dbh->prepare(
543        "INSERT INTO branchtransfers (itemnumber, frombranch, datesent, tobranch)
544        VALUES (?, ?, NOW(), ?)");
545
0
    $sth->execute($itemnumber, $frombranch, $tobranch);
546
547
0
    ModItem({ holdingbranch => $tobranch }, undef, $itemnumber);
548
0
    ModDateLastSeen($itemnumber);
549
0
    return;
550}
551
552 - 559
=head2 ModDateLastSeen

  ModDateLastSeen($itemnum);

Mark item as seen. Is called when an item is issued, returned or manually marked during inventory/stocktaking.
C<$itemnum> is the item number

=cut
560
561sub ModDateLastSeen {
562
0
    my ($itemnumber) = @_;
563
564
0
    my $today = C4::Dates->new();
565
0
    ModItem({ itemlost => 0, datelastseen => $today->output("iso") }, undef, $itemnumber);
566}
567
568 - 574
=head2 DelItem

  DelItem($dbh, $biblionumber, $itemnumber);

Exported function (core API) for deleting an item record in Koha.

=cut
575
576sub DelItem {
577
0
    my ( $dbh, $biblionumber, $itemnumber ) = @_;
578
579    # FIXME check the item has no current issues
580
581
0
    _koha_delete_item( $dbh, $itemnumber );
582
583    # get the MARC record
584
0
    my $record = GetMarcBiblio($biblionumber);
585
0
    ModZebra( $biblionumber, "specialUpdate", "biblioserver", undef, undef );
586
587    # backup the record
588
0
    my $copy2deleted = $dbh->prepare("UPDATE deleteditems SET marc=? WHERE itemnumber=?");
589
0
    $copy2deleted->execute( $record->as_usmarc(), $itemnumber );
590    # This last update statement makes that the timestamp column in deleteditems is updated too. If you remove these lines, please add a line to update the timestamp separately. See Bugzilla report 7146 and Biblio.pm (DelBiblio).
591
592    #search item field code
593
0
    logaction("CATALOGUING", "DELETE", $itemnumber, "item") if C4::Context->preference("CataloguingLog");
594}
595
596 - 636
=head2 CheckItemPreSave

    my $item_ref = TransformMarcToKoha($marc, 'items');
    # do stuff
    my %errors = CheckItemPreSave($item_ref);
    if (exists $errors{'duplicate_barcode'}) {
        print "item has duplicate barcode: ", $errors{'duplicate_barcode'}, "\n";
    } elsif (exists $errors{'invalid_homebranch'}) {
        print "item has invalid home branch: ", $errors{'invalid_homebranch'}, "\n";
    } elsif (exists $errors{'invalid_holdingbranch'}) {
        print "item has invalid holding branch: ", $errors{'invalid_holdingbranch'}, "\n";
    } else {
        print "item is OK";
    }

Given a hashref containing item fields, determine if it can be
inserted or updated in the database.  Specifically, checks for
database integrity issues, and returns a hash containing any
of the following keys, if applicable.

=over 2

=item duplicate_barcode

Barcode, if it duplicates one already found in the database.

=item invalid_homebranch

Home branch, if not defined in branches table.

=item invalid_holdingbranch

Holding branch, if not defined in branches table.

=back

This function does NOT implement any policy-related checks,
e.g., whether current operator is allowed to save an
item that has a given branch code.

=cut
637
638sub CheckItemPreSave {
639
0
    my $item_ref = shift;
640
0
    require C4::Branch;
641
642
0
    my %errors = ();
643
644    # check for duplicate barcode
645
0
    if (exists $item_ref->{'barcode'} and defined $item_ref->{'barcode'}) {
646
0
        my $existing_itemnumber = GetItemnumberFromBarcode($item_ref->{'barcode'});
647
0
        if ($existing_itemnumber) {
648
0
            if (!exists $item_ref->{'itemnumber'} # new item
649                or $item_ref->{'itemnumber'} != $existing_itemnumber) { # existing item
650
0
                $errors{'duplicate_barcode'} = $item_ref->{'barcode'};
651            }
652        }
653    }
654
655    # check for valid home branch
656
0
    if (exists $item_ref->{'homebranch'} and defined $item_ref->{'homebranch'}) {
657
0
        my $branch_name = C4::Branch::GetBranchName($item_ref->{'homebranch'});
658
0
        unless (defined $branch_name) {
659            # relies on fact that branches.branchname is a non-NULL column,
660            # so GetBranchName returns undef only if branch does not exist
661
0
            $errors{'invalid_homebranch'} = $item_ref->{'homebranch'};
662        }
663    }
664
665    # check for valid holding branch
666
0
    if (exists $item_ref->{'holdingbranch'} and defined $item_ref->{'holdingbranch'}) {
667
0
        my $branch_name = C4::Branch::GetBranchName($item_ref->{'holdingbranch'});
668
0
        unless (defined $branch_name) {
669            # relies on fact that branches.branchname is a non-NULL column,
670            # so GetBranchName returns undef only if branch does not exist
671
0
            $errors{'invalid_holdingbranch'} = $item_ref->{'holdingbranch'};
672        }
673    }
674
675
0
    return %errors;
676
677}
678
679 - 690
=head1 EXPORTED SPECIAL ACCESSOR FUNCTIONS

The following functions provide various ways of 
getting an item record, a set of item records, or
lists of authorized values for certain item fields.

Some of the functions in this group are candidates
for refactoring -- for example, some of the code
in C<GetItemsByBiblioitemnumber> and C<GetItemsInfo>
has copy-and-paste work.

=cut
691
692 - 728
=head2 GetItemStatus

  $itemstatushash = GetItemStatus($fwkcode);

Returns a list of valid values for the
C<items.notforloan> field.

NOTE: does B<not> return an individual item's
status.

Can be MARC dependant.
fwkcode is optional.
But basically could be can be loan or not
Create a status selector with the following code

=head3 in PERL SCRIPT

 my $itemstatushash = getitemstatus;
 my @itemstatusloop;
 foreach my $thisstatus (keys %$itemstatushash) {
     my %row =(value => $thisstatus,
                 statusname => $itemstatushash->{$thisstatus}->{'statusname'},
             );
     push @itemstatusloop, \%row;
 }
 $template->param(statusloop=>\@itemstatusloop);

=head3 in TEMPLATE

 <select name="statusloop">
     <option value="">Default</option>
 <!-- TMPL_LOOP name="statusloop" -->
     <option value="<!-- TMPL_VAR name="value" -->" <!-- TMPL_IF name="selected" -->selected<!-- /TMPL_IF -->><!-- TMPL_VAR name="statusname" --></option>
 <!-- /TMPL_LOOP -->
 </select>

=cut
729
730sub GetItemStatus {
731
732    # returns a reference to a hash of references to status...
733
0
    my ($fwk) = @_;
734
0
    my %itemstatus;
735
0
    my $dbh = C4::Context->dbh;
736
0
    my $sth;
737
0
    $fwk = '' unless ($fwk);
738
0
    my ( $tag, $subfield ) =
739      GetMarcFromKohaField( "items.notforloan", $fwk );
740
0
    if ( $tag and $subfield ) {
741
0
        my $sth =
742          $dbh->prepare(
743            "SELECT authorised_value
744            FROM marc_subfield_structure
745            WHERE tagfield=?
746                AND tagsubfield=?
747                AND frameworkcode=?
748            "
749          );
750
0
        $sth->execute( $tag, $subfield, $fwk );
751
0
        if ( my ($authorisedvaluecat) = $sth->fetchrow ) {
752
0
            my $authvalsth =
753              $dbh->prepare(
754                "SELECT authorised_value,lib
755                FROM authorised_values
756                WHERE category=?
757                ORDER BY lib
758                "
759              );
760
0
            $authvalsth->execute($authorisedvaluecat);
761
0
            while ( my ( $authorisedvalue, $lib ) = $authvalsth->fetchrow ) {
762
0
                $itemstatus{$authorisedvalue} = $lib;
763            }
764
0
            return \%itemstatus;
765
0
            exit 1;
766        }
767        else {
768
769            #No authvalue list
770            # build default
771        }
772    }
773
774    #No authvalue list
775    #build default
776
0
    $itemstatus{"1"} = "Not For Loan";
777
0
    return \%itemstatus;
778}
779
780 - 816
=head2 GetItemLocation

  $itemlochash = GetItemLocation($fwk);

Returns a list of valid values for the
C<items.location> field.

NOTE: does B<not> return an individual item's
location.

where fwk stands for an optional framework code.
Create a location selector with the following code

=head3 in PERL SCRIPT

  my $itemlochash = getitemlocation;
  my @itemlocloop;
  foreach my $thisloc (keys %$itemlochash) {
      my $selected = 1 if $thisbranch eq $branch;
      my %row =(locval => $thisloc,
                  selected => $selected,
                  locname => $itemlochash->{$thisloc},
               );
      push @itemlocloop, \%row;
  }
  $template->param(itemlocationloop => \@itemlocloop);

=head3 in TEMPLATE

  <select name="location">
      <option value="">Default</option>
  <!-- TMPL_LOOP name="itemlocationloop" -->
      <option value="<!-- TMPL_VAR name="locval" -->" <!-- TMPL_IF name="selected" -->selected<!-- /TMPL_IF -->><!-- TMPL_VAR name="locname" --></option>
  <!-- /TMPL_LOOP -->
  </select>

=cut
817
818sub GetItemLocation {
819
820    # returns a reference to a hash of references to location...
821
0
    my ($fwk) = @_;
822
0
    my %itemlocation;
823
0
    my $dbh = C4::Context->dbh;
824
0
    my $sth;
825
0
    $fwk = '' unless ($fwk);
826
0
    my ( $tag, $subfield ) =
827      GetMarcFromKohaField( "items.location", $fwk );
828
0
    if ( $tag and $subfield ) {
829
0
        my $sth =
830          $dbh->prepare(
831            "SELECT authorised_value
832            FROM marc_subfield_structure
833            WHERE tagfield=?
834                AND tagsubfield=?
835                AND frameworkcode=?"
836          );
837
0
        $sth->execute( $tag, $subfield, $fwk );
838
0
        if ( my ($authorisedvaluecat) = $sth->fetchrow ) {
839
0
            my $authvalsth =
840              $dbh->prepare(
841                "SELECT authorised_value,lib
842                FROM authorised_values
843                WHERE category=?
844                ORDER BY lib"
845              );
846
0
            $authvalsth->execute($authorisedvaluecat);
847
0
            while ( my ( $authorisedvalue, $lib ) = $authvalsth->fetchrow ) {
848
0
                $itemlocation{$authorisedvalue} = $lib;
849            }
850
0
            return \%itemlocation;
851
0
            exit 1;
852        }
853        else {
854
855            #No authvalue list
856            # build default
857        }
858    }
859
860    #No authvalue list
861    #build default
862
0
    $itemlocation{"1"} = "Not For Loan";
863
0
    return \%itemlocation;
864}
865
866 - 898
=head2 GetLostItems

  $items = GetLostItems( $where, $orderby );

This function gets a list of lost items.

=over 2

=item input:

C<$where> is a hashref. it containts a field of the items table as key
and the value to match as value. For example:

{ barcode    => 'abc123',
  homebranch => 'CPL',    }

C<$orderby> is a field of the items table by which the resultset
should be orderd.

=item return:

C<$items> is a reference to an array full of hashrefs with columns
from the "items" table as keys.

=item usage in the perl script:

  my $where = { barcode => '0001548' };
  my $items = GetLostItems( $where, "homebranch" );
  $template->param( itemsloop => $items );

=back

=cut
899
900sub GetLostItems {
901    # Getting input args.
902
0
    my $where = shift;
903
0
    my $orderby = shift;
904
0
    my $dbh = C4::Context->dbh;
905
906
0
    my $query = "
907        SELECT *
908        FROM items
909            LEFT JOIN biblio ON (items.biblionumber = biblio.biblionumber)
910            LEFT JOIN biblioitems ON (items.biblionumber = biblioitems.biblionumber)
911            LEFT JOIN authorised_values ON (items.itemlost = authorised_values.authorised_value)
912        WHERE
913         authorised_values.category = 'LOST'
914           AND itemlost IS NOT NULL
915          AND itemlost <> 0
916    ";
917
0
    my @query_parameters;
918
0
    foreach my $key (keys %$where) {
919
0
        $query .= " AND $key LIKE ?";
920
0
        push @query_parameters, "%$where->{$key}%";
921    }
922
0
    my @ordervalues = qw/title author homebranch itype barcode price replacementprice lib datelastseen location/;
923
924
0
    if ( defined $orderby && grep($orderby, @ordervalues)) {
925
0
        $query .= ' ORDER BY '.$orderby;
926    }
927
928
0
    my $sth = $dbh->prepare($query);
929
0
    $sth->execute( @query_parameters );
930
0
    my $items = [];
931
0
    while ( my $row = $sth->fetchrow_hashref ){
932
0
        push @$items, $row;
933    }
934
0
    return $items;
935}
936
937 - 954
=head2 GetItemsForInventory

  $itemlist = GetItemsForInventory($minlocation, $maxlocation, 
                 $location, $itemtype $datelastseen, $branch, 
                 $offset, $size, $statushash);

Retrieve a list of title/authors/barcode/callnumber, for biblio inventory.

The sub returns a reference to a list of hashes, each containing
itemnumber, author, title, barcode, item callnumber, and date last
seen. It is ordered by callnumber then title.

The required minlocation & maxlocation parameters are used to specify a range of item callnumbers
the datelastseen can be used to specify that you want to see items not seen since a past date only.
offset & size can be used to retrieve only a part of the whole listing (defaut behaviour)
$statushash requires a hashref that has the authorized values fieldname (intems.notforloan, etc...) as keys, and an arrayref of statuscodes we are searching for as values.

=cut
955
956sub GetItemsForInventory {
957
0
    my ( $minlocation, $maxlocation,$location, $itemtype, $ignoreissued, $datelastseen, $branchcode, $branch, $offset, $size, $statushash ) = @_;
958
0
    my $dbh = C4::Context->dbh;
959
0
    my ( @bind_params, @where_strings );
960
961
0
    my $query = <<'END_SQL';
962SELECT items.itemnumber, barcode, itemcallnumber, title, author, biblio.biblionumber, datelastseen
963FROM items
964  LEFT JOIN biblio ON items.biblionumber = biblio.biblionumber
965  LEFT JOIN biblioitems on items.biblionumber = biblioitems.biblionumber
966END_SQL
967
0
    if ($statushash){
968
0
        for my $authvfield (keys %$statushash){
969
0
0
            if ( scalar @{$statushash->{$authvfield}} > 0 ){
970
0
0
                my $joinedvals = join ',', @{$statushash->{$authvfield}};
971
0
                push @where_strings, "$authvfield in (" . $joinedvals . ")";
972            }
973        }
974    }
975
976
0
    if ($minlocation) {
977
0
        push @where_strings, 'itemcallnumber >= ?';
978
0
        push @bind_params, $minlocation;
979    }
980
981
0
    if ($maxlocation) {
982
0
        push @where_strings, 'itemcallnumber <= ?';
983
0
        push @bind_params, $maxlocation;
984    }
985
986
0
    if ($datelastseen) {
987
0
        $datelastseen = format_date_in_iso($datelastseen);
988
0
        push @where_strings, '(datelastseen < ? OR datelastseen IS NULL)';
989
0
        push @bind_params, $datelastseen;
990    }
991
992
0
    if ( $location ) {
993
0
        push @where_strings, 'items.location = ?';
994
0
        push @bind_params, $location;
995    }
996
997
0
    if ( $branchcode ) {
998
0
        if($branch eq "homebranch"){
999
0
        push @where_strings, 'items.homebranch = ?';
1000        }else{
1001
0
            push @where_strings, 'items.holdingbranch = ?';
1002        }
1003
0
        push @bind_params, $branchcode;
1004    }
1005
1006
0
    if ( $itemtype ) {
1007
0
        push @where_strings, 'biblioitems.itemtype = ?';
1008
0
        push @bind_params, $itemtype;
1009    }
1010
1011
0
    if ( $ignoreissued) {
1012
0
        $query .= "LEFT JOIN issues ON items.itemnumber = issues.itemnumber ";
1013
0
        push @where_strings, 'issues.date_due IS NULL';
1014    }
1015
1016
0
    if ( @where_strings ) {
1017
0
        $query .= 'WHERE ';
1018
0
        $query .= join ' AND ', @where_strings;
1019    }
1020
0
    $query .= ' ORDER BY items.cn_sort, itemcallnumber, title';
1021
0
    my $sth = $dbh->prepare($query);
1022
0
    $sth->execute( @bind_params );
1023
1024
0
    my @results;
1025
0
    $size--;
1026
0
    while ( my $row = $sth->fetchrow_hashref ) {
1027
0
        $offset-- if ($offset);
1028
0
        $row->{datelastseen}=format_date($row->{datelastseen});
1029
0
        if ( ( !$offset ) && $size ) {
1030
0
            push @results, $row;
1031
0
            $size--;
1032        }
1033    }
1034
0
    return \@results;
1035}
1036
1037 - 1043
=head2 GetItemsCount

  $count = &GetItemsCount( $biblionumber);

This function return count of item with $biblionumber

=cut
1044
1045sub GetItemsCount {
1046
0
    my ( $biblionumber ) = @_;
1047
0
    my $dbh = C4::Context->dbh;
1048
0
    my $query = "SELECT count(*)
1049          FROM items
1050          WHERE biblionumber=?";
1051
0
    my $sth = $dbh->prepare($query);
1052
0
    $sth->execute($biblionumber);
1053
0
    my $count = $sth->fetchrow;
1054
0
    return ($count);
1055}
1056
1057 - 1061
=head2 GetItemInfosOf

  GetItemInfosOf(@itemnumbers);

=cut
1062
1063sub GetItemInfosOf {
1064
0
    my @itemnumbers = @_;
1065
1066
0
    my $query = '
1067        SELECT *
1068        FROM items
1069        WHERE itemnumber IN (' . join( ',', @itemnumbers ) . ')
1070    ';
1071
0
    return get_infos_of( $query, 'itemnumber' );
1072}
1073
1074 - 1081
=head2 GetItemsByBiblioitemnumber

  GetItemsByBiblioitemnumber($biblioitemnumber);

Returns an arrayref of hashrefs suitable for use in a TMPL_LOOP
Called by C<C4::XISBN>

=cut
1082
1083sub GetItemsByBiblioitemnumber {
1084
0
    my ( $bibitem ) = @_;
1085
0
    my $dbh = C4::Context->dbh;
1086
0
    my $sth = $dbh->prepare("SELECT * FROM items WHERE items.biblioitemnumber = ?") || die $dbh->errstr;
1087    # Get all items attached to a biblioitem
1088
0
    my $i = 0;
1089
0
    my @results;
1090
0
    $sth->execute($bibitem) || die $sth->errstr;
1091
0
    while ( my $data = $sth->fetchrow_hashref ) {
1092        # Foreach item, get circulation information
1093
0
        my $sth2 = $dbh->prepare( "SELECT * FROM issues,borrowers
1094                                   WHERE itemnumber = ?
1095                                   AND issues.borrowernumber = borrowers.borrowernumber"
1096        );
1097
0
        $sth2->execute( $data->{'itemnumber'} );
1098
0
        if ( my $data2 = $sth2->fetchrow_hashref ) {
1099            # if item is out, set the due date and who it is out too
1100
0
            $data->{'date_due'} = $data2->{'date_due'};
1101
0
            $data->{'cardnumber'} = $data2->{'cardnumber'};
1102
0
            $data->{'borrowernumber'} = $data2->{'borrowernumber'};
1103        }
1104        else {
1105            # set date_due to blank, so in the template we check itemlost, and wthdrawn
1106
0
            $data->{'date_due'} = '';
1107        } # else
1108        # Find the last 3 people who borrowed this item.
1109
0
        my $query2 = "SELECT * FROM old_issues, borrowers WHERE itemnumber = ?
1110                      AND old_issues.borrowernumber = borrowers.borrowernumber
1111                      ORDER BY returndate desc,timestamp desc LIMIT 3";
1112
0
        $sth2 = $dbh->prepare($query2) || die $dbh->errstr;
1113
0
        $sth2->execute( $data->{'itemnumber'} ) || die $sth2->errstr;
1114
0
        my $i2 = 0;
1115
0
        while ( my $data2 = $sth2->fetchrow_hashref ) {
1116
0
            $data->{"timestamp$i2"} = $data2->{'timestamp'};
1117
0
            $data->{"card$i2"} = $data2->{'cardnumber'};
1118
0
            $data->{"borrower$i2"} = $data2->{'borrowernumber'};
1119
0
            $i2++;
1120        }
1121
0
        push(@results,$data);
1122    }
1123
0
    return (\@results);
1124}
1125
1126 - 1166
=head2 GetItemsInfo

  @results = GetItemsInfo($biblionumber);

Returns information about items with the given biblionumber.

C<GetItemsInfo> returns a list of references-to-hash. Each element
contains a number of keys. Most of them are attributes from the
C<biblio>, C<biblioitems>, C<items>, and C<itemtypes> tables in the
Koha database. Other keys include:

=over 2

=item C<$data-E<gt>{branchname}>

The name (not the code) of the branch to which the book belongs.

=item C<$data-E<gt>{datelastseen}>

This is simply C<items.datelastseen>, except that while the date is
stored in YYYY-MM-DD format in the database, here it is converted to
DD/MM/YYYY format. A NULL date is returned as C<//>.

=item C<$data-E<gt>{datedue}>

=item C<$data-E<gt>{class}>

This is the concatenation of C<biblioitems.classification>, the book's
Dewey code, and C<biblioitems.subclass>.

=item C<$data-E<gt>{ocount}>

I think this is the number of copies of the book available.

=item C<$data-E<gt>{order}>

If this is set, it is set to C<One Order>.

=back

=cut
1167
1168sub GetItemsInfo {
1169
0
    my ( $biblionumber ) = @_;
1170
0
    my $dbh = C4::Context->dbh;
1171    # note biblioitems.* must be avoided to prevent large marc and marcxml fields from killing performance.
1172
0
    my $query = "
1173    SELECT items.*,
1174           biblio.*,
1175           biblioitems.volume,
1176           biblioitems.number,
1177           biblioitems.itemtype,
1178           biblioitems.isbn,
1179           biblioitems.issn,
1180           biblioitems.publicationyear,
1181           biblioitems.publishercode,
1182           biblioitems.volumedate,
1183           biblioitems.volumedesc,
1184           biblioitems.lccn,
1185           biblioitems.url,
1186           items.notforloan as itemnotforloan,
1187           itemtypes.description,
1188           itemtypes.notforloan as notforloan_per_itemtype,
1189           holding.branchurl,
1190           holding.branchname,
1191           holding.opac_info as branch_opac_info
1192     FROM items
1193     LEFT JOIN branches AS holding ON items.holdingbranch = holding.branchcode
1194     LEFT JOIN branches AS home ON items.homebranch=home.branchcode
1195     LEFT JOIN biblio ON biblio.biblionumber = items.biblionumber
1196     LEFT JOIN biblioitems ON biblioitems.biblioitemnumber = items.biblioitemnumber
1197     LEFT JOIN itemtypes ON itemtypes.itemtype = "
1198     . (C4::Context->preference('item-level_itypes') ? 'items.itype' : 'biblioitems.itemtype');
1199
0
    $query .= " WHERE items.biblionumber = ? ORDER BY home.branchname,items.dateaccessioned desc" ;
1200
0
    my $sth = $dbh->prepare($query);
1201
0
    $sth->execute($biblionumber);
1202
0
    my $i = 0;
1203
0
    my @results;
1204
0
    my $serial;
1205
1206
0
    my $isth = $dbh->prepare(
1207        "SELECT issues.*,borrowers.cardnumber,borrowers.surname,borrowers.firstname,borrowers.branchcode as bcode
1208        FROM issues LEFT JOIN borrowers ON issues.borrowernumber=borrowers.borrowernumber
1209        WHERE itemnumber = ?"
1210       );
1211
0
        my $ssth = $dbh->prepare("SELECT serialseq,publisheddate from serialitems left join serial on serialitems.serialid=serial.serialid where serialitems.itemnumber=? ");
1212
0
        while ( my $data = $sth->fetchrow_hashref ) {
1213
0
        my $datedue = '';
1214
0
        $isth->execute( $data->{'itemnumber'} );
1215
0
        if ( my $idata = $isth->fetchrow_hashref ) {
1216
0
            $data->{borrowernumber} = $idata->{borrowernumber};
1217
0
            $data->{cardnumber} = $idata->{cardnumber};
1218
0
            $data->{surname} = $idata->{surname};
1219
0
            $data->{firstname} = $idata->{firstname};
1220
0
            $data->{lastreneweddate} = $idata->{lastreneweddate};
1221
0
            $datedue = $idata->{'date_due'};
1222
0
        if (C4::Context->preference("IndependantBranches")){
1223
0
        my $userenv = C4::Context->userenv;
1224
0
        if ( ($userenv) && ( $userenv->{flags} % 2 != 1 ) ) {
1225
0
            $data->{'NOTSAMEBRANCH'} = 1 if ($idata->{'bcode'} ne $userenv->{branch});
1226        }
1227        }
1228        }
1229
0
                if ( $data->{'serial'}) {
1230
0
                        $ssth->execute($data->{'itemnumber'}) ;
1231
0
                        ($data->{'serialseq'} , $data->{'publisheddate'}) = $ssth->fetchrow_array();
1232
0
                        $serial = 1;
1233        }
1234        #get branch information.....
1235
0
        my $bsth = $dbh->prepare(
1236            "SELECT * FROM branches WHERE branchcode = ?
1237        "
1238        );
1239
0
        $bsth->execute( $data->{'holdingbranch'} );
1240
0
        if ( my $bdata = $bsth->fetchrow_hashref ) {
1241
0
            $data->{'branchname'} = $bdata->{'branchname'};
1242        }
1243
0
        $data->{'datedue'} = $datedue;
1244
1245        # get notforloan complete status if applicable
1246
0
        my $sthnflstatus = $dbh->prepare(
1247            'SELECT authorised_value
1248            FROM marc_subfield_structure
1249            WHERE kohafield="items.notforloan"
1250        '
1251        );
1252
1253
0
        $sthnflstatus->execute;
1254
0
        my ($authorised_valuecode) = $sthnflstatus->fetchrow;
1255
0
        if ($authorised_valuecode) {
1256
0
            $sthnflstatus = $dbh->prepare(
1257                "SELECT lib FROM authorised_values
1258                 WHERE category=?
1259                 AND authorised_value=?"
1260            );
1261
0
            $sthnflstatus->execute( $authorised_valuecode,
1262                $data->{itemnotforloan} );
1263
0
            my ($lib) = $sthnflstatus->fetchrow;
1264
0
            $data->{notforloanvalue} = $lib;
1265        }
1266
1267        # get restricted status and description if applicable
1268
0
        my $restrictedstatus = $dbh->prepare(
1269            'SELECT authorised_value
1270            FROM marc_subfield_structure
1271            WHERE kohafield="items.restricted"
1272        '
1273        );
1274
1275
0
        $restrictedstatus->execute;
1276
0
        ($authorised_valuecode) = $restrictedstatus->fetchrow;
1277
0
        if ($authorised_valuecode) {
1278
0
            $restrictedstatus = $dbh->prepare(
1279                "SELECT lib,lib_opac FROM authorised_values
1280                 WHERE category=?
1281                 AND authorised_value=?"
1282            );
1283
0
            $restrictedstatus->execute( $authorised_valuecode,
1284                $data->{restricted} );
1285
1286
0
            if ( my $rstdata = $restrictedstatus->fetchrow_hashref ) {
1287
0
                $data->{restricted} = $rstdata->{'lib'};
1288
0
                $data->{restrictedopac} = $rstdata->{'lib_opac'};
1289            }
1290        }
1291
1292        # my stack procedures
1293
0
        my $stackstatus = $dbh->prepare(
1294            'SELECT authorised_value
1295             FROM marc_subfield_structure
1296             WHERE kohafield="items.stack"
1297        '
1298        );
1299
0
        $stackstatus->execute;
1300
1301
0
        ($authorised_valuecode) = $stackstatus->fetchrow;
1302
0
        if ($authorised_valuecode) {
1303
0
            $stackstatus = $dbh->prepare(
1304                "SELECT lib
1305                 FROM authorised_values
1306                 WHERE category=?
1307                 AND authorised_value=?
1308            "
1309            );
1310
0
            $stackstatus->execute( $authorised_valuecode, $data->{stack} );
1311
0
            my ($lib) = $stackstatus->fetchrow;
1312
0
            $data->{stack} = $lib;
1313        }
1314        # Find the last 3 people who borrowed this item.
1315
0
        my $sth2 = $dbh->prepare("SELECT * FROM old_issues,borrowers
1316                                    WHERE itemnumber = ?
1317                                    AND old_issues.borrowernumber = borrowers.borrowernumber
1318                                    ORDER BY returndate DESC
1319                                    LIMIT 3");
1320
0
        $sth2->execute($data->{'itemnumber'});
1321
0
        my $ii = 0;
1322
0
        while (my $data2 = $sth2->fetchrow_hashref()) {
1323
0
            $data->{"timestamp$ii"} = $data2->{'timestamp'} if $data2->{'timestamp'};
1324
0
            $data->{"card$ii"} = $data2->{'cardnumber'} if $data2->{'cardnumber'};
1325
0
            $data->{"borrower$ii"} = $data2->{'borrowernumber'} if $data2->{'borrowernumber'};
1326
0
            $ii++;
1327        }
1328
1329
0
        $results[$i] = $data;
1330
0
        $i++;
1331    }
1332
0
        if($serial) {
1333
0
0
                return( sort { ($b->{'publisheddate'} || $b->{'enumchron'}) cmp ($a->{'publisheddate'} || $a->{'enumchron'}) } @results );
1334        } else {
1335
0
     return (@results);
1336        }
1337}
1338
1339 - 1380
=head2 GetItemsLocationInfo

  my @itemlocinfo = GetItemsLocationInfo($biblionumber);

Returns the branch names, shelving location and itemcallnumber for each item attached to the biblio in question

C<GetItemsInfo> returns a list of references-to-hash. Data returned:

=over 2

=item C<$data-E<gt>{homebranch}>

Branch Name of the item's homebranch

=item C<$data-E<gt>{holdingbranch}>

Branch Name of the item's holdingbranch

=item C<$data-E<gt>{location}>

Item's shelving location code

=item C<$data-E<gt>{location_intranet}>

The intranet description for the Shelving Location as set in authorised_values 'LOC'

=item C<$data-E<gt>{location_opac}>

The OPAC description for the Shelving Location as set in authorised_values 'LOC'.  Falls back to intranet description if no OPAC 
description is set.

=item C<$data-E<gt>{itemcallnumber}>

Item's itemcallnumber

=item C<$data-E<gt>{cn_sort}>

Item's call number normalized for sorting

=back
  
=cut
1381
1382sub GetItemsLocationInfo {
1383
0
        my $biblionumber = shift;
1384
0
        my @results;
1385
1386
0
        my $dbh = C4::Context->dbh;
1387
0
        my $query = "SELECT a.branchname as homebranch, b.branchname as holdingbranch,
1388                            location, itemcallnumber, cn_sort
1389                     FROM items, branches as a, branches as b
1390                     WHERE homebranch = a.branchcode AND holdingbranch = b.branchcode
1391                     AND biblionumber = ?
1392                     ORDER BY cn_sort ASC";
1393
0
        my $sth = $dbh->prepare($query);
1394
0
        $sth->execute($biblionumber);
1395
1396
0
        while ( my $data = $sth->fetchrow_hashref ) {
1397
0
             $data->{location_intranet} = GetKohaAuthorisedValueLib('LOC', $data->{location});
1398
0
             $data->{location_opac}= GetKohaAuthorisedValueLib('LOC', $data->{location}, 1);
1399
0
             push @results, $data;
1400        }
1401
0
        return @results;
1402}
1403
1404 - 1409
=head2 GetHostItemsInfo

	$hostiteminfo = GetHostItemsInfo($hostfield);
	Returns the iteminfo for items linked to records via a host field

=cut
1410
1411sub GetHostItemsInfo {
1412
0
        my ($record) = @_;
1413
0
        my @returnitemsInfo;
1414
1415
0
        if (C4::Context->preference('marcflavour') eq 'MARC21' ||
1416        C4::Context->preference('marcflavour') eq 'NORMARC'){
1417
0
            foreach my $hostfield ( $record->field('773') ) {
1418
0
         my $hostbiblionumber = $hostfield->subfield("0");
1419
0
                my $linkeditemnumber = $hostfield->subfield("9");
1420
0
         my @hostitemInfos = GetItemsInfo($hostbiblionumber);
1421
0
                foreach my $hostitemInfo (@hostitemInfos){
1422
0
                 if ($hostitemInfo->{itemnumber} eq $linkeditemnumber){
1423
0
                         push (@returnitemsInfo,$hostitemInfo);
1424
0
                                last;
1425                 }
1426         }
1427            }
1428        } elsif ( C4::Context->preference('marcflavour') eq 'UNIMARC'){
1429
0
            foreach my $hostfield ( $record->field('461') ) {
1430
0
         my $hostbiblionumber = $hostfield->subfield("0");
1431
0
                my $linkeditemnumber = $hostfield->subfield("9");
1432
0
         my @hostitemInfos = GetItemsInfo($hostbiblionumber);
1433
0
                foreach my $hostitemInfo (@hostitemInfos){
1434
0
                 if ($hostitemInfo->{itemnumber} eq $linkeditemnumber){
1435
0
                         push (@returnitemsInfo,$hostitemInfo);
1436
0
                                last;
1437                 }
1438         }
1439            }
1440        }
1441
0
        return @returnitemsInfo;
1442}
1443
1444
1445 - 1450
=head2 GetLastAcquisitions

  my $lastacq = GetLastAcquisitions({'branches' => ('branch1','branch2'), 
                                    'itemtypes' => ('BK','BD')}, 10);

=cut
1451
1452sub GetLastAcquisitions {
1453
0
        my ($data,$max) = @_;
1454
1455
0
        my $itemtype = C4::Context->preference('item-level_itypes') ? 'itype' : 'itemtype';
1456
1457
0
0
        my $number_of_branches = @{$data->{branches}};
1458
0
0
        my $number_of_itemtypes = @{$data->{itemtypes}};
1459
1460
1461
0
        my @where = ('WHERE 1 ');
1462
0
        $number_of_branches and push @where
1463           , 'AND holdingbranch IN ('
1464           , join(',', ('?') x $number_of_branches )
1465           , ')'
1466         ;
1467
1468
0
        $number_of_itemtypes and push @where
1469           , "AND $itemtype IN ("
1470           , join(',', ('?') x $number_of_itemtypes )
1471           , ')'
1472         ;
1473
1474
0
        my $query = "SELECT biblio.biblionumber as biblionumber, title, dateaccessioned
1475                                 FROM items RIGHT JOIN biblio ON (items.biblionumber=biblio.biblionumber)
1476                                    RIGHT JOIN biblioitems ON (items.biblioitemnumber=biblioitems.biblioitemnumber)
1477                                    @where
1478                                    GROUP BY biblio.biblionumber
1479                                    ORDER BY dateaccessioned DESC LIMIT $max";
1480
1481
0
        my $dbh = C4::Context->dbh;
1482
0
        my $sth = $dbh->prepare($query);
1483
1484
0
0
0
    $sth->execute((@{$data->{branches}}, @{$data->{itemtypes}}));
1485
1486
0
        my @results;
1487
0
        while( my $row = $sth->fetchrow_hashref){
1488
0
                push @results, {date => $row->{dateaccessioned}
1489                                                , biblionumber => $row->{biblionumber}
1490                                                , title => $row->{title}};
1491        }
1492
1493
0
        return @results;
1494}
1495
1496 - 1506
=head2 get_itemnumbers_of

  my @itemnumbers_of = get_itemnumbers_of(@biblionumbers);

Given a list of biblionumbers, return the list of corresponding itemnumbers
for each biblionumber.

Return a reference on a hash where keys are biblionumbers and values are
references on array of itemnumbers.

=cut
1507
1508sub get_itemnumbers_of {
1509
0
    my @biblionumbers = @_;
1510
1511
0
    my $dbh = C4::Context->dbh;
1512
1513
0
    my $query = '
1514        SELECT itemnumber,
1515            biblionumber
1516        FROM items
1517        WHERE biblionumber IN (?' . ( ',?' x scalar @biblionumbers - 1 ) . ')
1518    ';
1519
0
    my $sth = $dbh->prepare($query);
1520
0
    $sth->execute(@biblionumbers);
1521
1522
0
    my %itemnumbers_of;
1523
1524
0
    while ( my ( $itemnumber, $biblionumber ) = $sth->fetchrow_array ) {
1525
0
0
        push @{ $itemnumbers_of{$biblionumber} }, $itemnumber;
1526    }
1527
1528
0
    return \%itemnumbers_of;
1529}
1530
1531 - 1540
=head2 get_hostitemnumbers_of

  my @itemnumbers_of = get_hostitemnumbers_of($biblionumber);

Given a biblionumber, return the list of corresponding itemnumbers that are linked to it via host fields

Return a reference on a hash where key is a biblionumber and values are
references on array of itemnumbers.

=cut
1541
1542
1543sub get_hostitemnumbers_of {
1544
0
        my ($biblionumber) = @_;
1545
0
        my $marcrecord = GetMarcBiblio($biblionumber);
1546
0
        my (@returnhostitemnumbers,$tag, $biblio_s, $item_s);
1547
1548
0
        my $marcflavor = C4::Context->preference('marcflavour');
1549
0
        if ($marcflavor eq 'MARC21' || $marcflavor eq 'NORMARC') {
1550
0
        $tag='773';
1551
0
        $biblio_s='0';
1552
0
        $item_s='9';
1553    } elsif ($marcflavor eq 'UNIMARC') {
1554
0
        $tag='461';
1555
0
        $biblio_s='0';
1556
0
        $item_s='9';
1557    }
1558
1559
0
    foreach my $hostfield ( $marcrecord->field($tag) ) {
1560
0
        my $hostbiblionumber = $hostfield->subfield($biblio_s);
1561
0
        my $linkeditemnumber = $hostfield->subfield($item_s);
1562
0
        my @itemnumbers;
1563
0
        if (my $itemnumbers = get_itemnumbers_of($hostbiblionumber)->{$hostbiblionumber})
1564        {
1565
0
            @itemnumbers = @$itemnumbers;
1566        }
1567
0
        foreach my $itemnumber (@itemnumbers){
1568
0
            if ($itemnumber eq $linkeditemnumber){
1569
0
                push (@returnhostitemnumbers,$itemnumber);
1570
0
                last;
1571            }
1572        }
1573    }
1574
0
    return @returnhostitemnumbers;
1575}
1576
1577
1578 - 1582
=head2 GetItemnumberFromBarcode

  $result = GetItemnumberFromBarcode($barcode);

=cut
1583
1584sub GetItemnumberFromBarcode {
1585
0
    my ($barcode) = @_;
1586
0
    my $dbh = C4::Context->dbh;
1587
1588
0
    my $rq =
1589      $dbh->prepare("SELECT itemnumber FROM items WHERE items.barcode=?");
1590
0
    $rq->execute($barcode);
1591
0
    my ($result) = $rq->fetchrow;
1592
0
    return ($result);
1593}
1594
1595 - 1599
=head2 GetBarcodeFromItemnumber

  $result = GetBarcodeFromItemnumber($itemnumber);

=cut
1600
1601sub GetBarcodeFromItemnumber {
1602
0
    my ($itemnumber) = @_;
1603
0
    my $dbh = C4::Context->dbh;
1604
1605
0
    my $rq =
1606      $dbh->prepare("SELECT barcode FROM items WHERE items.itemnumber=?");
1607
0
    $rq->execute($itemnumber);
1608
0
    my ($result) = $rq->fetchrow;
1609
0
    return ($result);
1610}
1611
1612 - 1620
=head2 GetHiddenItemnumbers

=over 4

$result = GetHiddenItemnumbers(@items);

=back

=cut
1621
1622sub GetHiddenItemnumbers {
1623
0
    my (@items) = @_;
1624
0
    my @resultitems;
1625
1626
0
    my $yaml = C4::Context->preference('OpacHiddenItems');
1627
0
    $yaml = "$yaml\n\n"; # YAML is anal on ending \n. Surplus does not hurt
1628
0
    my $hidingrules;
1629
0
    eval {
1630
0
        $hidingrules = YAML::Load($yaml);
1631    };
1632
0
    if ($@) {
1633
0
        warn "Unable to parse OpacHiddenItems syspref : $@";
1634
0
        return ();
1635    }
1636
0
    my $dbh = C4::Context->dbh;
1637
1638    # For each item
1639
0
    foreach my $item (@items) {
1640
1641        # We check each rule
1642
0
        foreach my $field (keys %$hidingrules) {
1643
0
            my $val;
1644
0
            if (exists $item->{$field}) {
1645
0
                $val = $item->{$field};
1646            }
1647            else {
1648
0
                my $query = "SELECT $field from items where itemnumber = ?";
1649
0
                $val = $dbh->selectrow_array($query, undef, $item->{'itemnumber'});
1650            }
1651
0
            $val = '' unless defined $val;
1652
1653            # If the results matches the values in the yaml file
1654
0
0
0
            if (any { $val eq $_ } @{$hidingrules->{$field}}) {
1655
1656                # We add the itemnumber to the list
1657
0
                push @resultitems, $item->{'itemnumber'};
1658
1659                # If at least one rule matched for an item, no need to test the others
1660
0
                last;
1661            }
1662        }
1663    }
1664
0
    return @resultitems;
1665}
1666
1667 - 1691
=head3 get_item_authorised_values

find the types and values for all authorised values assigned to this item.

parameters: itemnumber

returns: a hashref malling the authorised value to the value set for this itemnumber

    $authorised_values = {
             'CCODE'      => undef,
             'DAMAGED'    => '0',
             'LOC'        => '3',
             'LOST'       => '0'
             'NOT_LOAN'   => '0',
             'RESTRICTED' => undef,
             'STACK'      => undef,
             'WITHDRAWN'  => '0',
             'branches'   => 'CPL',
             'cn_source'  => undef,
             'itemtypes'  => 'SER',
           };

Notes: see C4::Biblio::get_biblio_authorised_values for a similar method at the biblio level.

=cut
1692
1693sub get_item_authorised_values {
1694
0
    my $itemnumber = shift;
1695
1696    # assume that these entries in the authorised_value table are item level.
1697
0
    my $query = q(SELECT distinct authorised_value, kohafield
1698                    FROM marc_subfield_structure
1699                    WHERE kohafield like 'item%'
1700                      AND authorised_value != '' );
1701
1702
0
    my $itemlevel_authorised_values = C4::Context->dbh->selectall_hashref( $query, 'authorised_value' );
1703
0
    my $iteminfo = GetItem( $itemnumber );
1704    # warn( Data::Dumper->Dump( [ $itemlevel_authorised_values ], [ 'itemlevel_authorised_values' ] ) );
1705
0
    my $return;
1706
0
    foreach my $this_authorised_value ( keys %$itemlevel_authorised_values ) {
1707
0
        my $field = $itemlevel_authorised_values->{ $this_authorised_value }->{'kohafield'};
1708
0
        $field =~ s/^items\.//;
1709
0
        if ( exists $iteminfo->{ $field } ) {
1710
0
            $return->{ $this_authorised_value } = $iteminfo->{ $field };
1711        }
1712    }
1713    # warn( Data::Dumper->Dump( [ $return ], [ 'return' ] ) );
1714
0
    return $return;
1715}
1716
1717 - 1738
=head3 get_authorised_value_images

find a list of icons that are appropriate for display based on the
authorised values for a biblio.

parameters: listref of authorised values, such as comes from
get_item_authorised_values or
from C4::Biblio::get_biblio_authorised_values

returns: listref of hashrefs for each image. Each hashref looks like this:

      { imageurl => '/intranet-tmpl/prog/img/itemtypeimg/npl/WEB.gif',
        label    => '',
        category => '',
        value    => '', }

Notes: Currently, I put on the full path to the images on the staff
side. This should either be configurable or not done at all. Since I
have to deal with 'intranet' or 'opac' in
get_biblio_authorised_values, perhaps I should be passing it in.

=cut
1739
1740sub get_authorised_value_images {
1741
0
    my $authorised_values = shift;
1742
1743
0
    my @imagelist;
1744
1745
0
    my $authorised_value_list = GetAuthorisedValues();
1746    # warn ( Data::Dumper->Dump( [ $authorised_value_list ], [ 'authorised_value_list' ] ) );
1747
0
    foreach my $this_authorised_value ( @$authorised_value_list ) {
1748
0
        if ( exists $authorised_values->{ $this_authorised_value->{'category'} }
1749             && $authorised_values->{ $this_authorised_value->{'category'} } eq $this_authorised_value->{'authorised_value'} ) {
1750            # warn ( Data::Dumper->Dump( [ $this_authorised_value ], [ 'this_authorised_value' ] ) );
1751
0
            if ( defined $this_authorised_value->{'imageurl'} ) {
1752
0
                push @imagelist, { imageurl => C4::Koha::getitemtypeimagelocation( 'intranet', $this_authorised_value->{'imageurl'} ),
1753                                   label => $this_authorised_value->{'lib'},
1754                                   category => $this_authorised_value->{'category'},
1755                                   value => $this_authorised_value->{'authorised_value'}, };
1756            }
1757        }
1758    }
1759
1760    # warn ( Data::Dumper->Dump( [ \@imagelist ], [ 'imagelist' ] ) );
1761
0
    return \@imagelist;
1762
1763}
1764
1765 - 1773
=head1 LIMITED USE FUNCTIONS

The following functions, while part of the public API,
are not exported.  This is generally because they are
meant to be used by only one script for a specific
purpose, and should not be used in any other context
without careful thought.

=cut
1774
1775 - 1784
=head2 GetMarcItem

  my $item_marc = GetMarcItem($biblionumber, $itemnumber);

Returns MARC::Record of the item passed in parameter.
This function is meant for use only in C<cataloguing/additem.pl>,
where it is needed to support that script's MARC-like
editor.

=cut
1785
1786sub GetMarcItem {
1787
0
    my ( $biblionumber, $itemnumber ) = @_;
1788
1789    # GetMarcItem has been revised so that it does the following:
1790    # 1. Gets the item information from the items table.
1791    # 2. Converts it to a MARC field for storage in the bib record.
1792    #
1793    # The previous behavior was:
1794    # 1. Get the bib record.
1795    # 2. Return the MARC tag corresponding to the item record.
1796    #
1797    # The difference is that one treats the items row as authoritative,
1798    # while the other treats the MARC representation as authoritative
1799    # under certain circumstances.
1800
1801
0
    my $itemrecord = GetItem($itemnumber);
1802
1803    # Tack on 'items.' prefix to column names so that TransformKohaToMarc will work.
1804    # Also, don't emit a subfield if the underlying field is blank.
1805
1806
1807
0
    return Item2Marc($itemrecord,$biblionumber);
1808
1809}
1810sub Item2Marc {
1811
0
        my ($itemrecord,$biblionumber)=@_;
1812
0
    my $mungeditem = {
1813        map {
1814
0
            defined($itemrecord->{$_}) && $itemrecord->{$_} ne '' ? ("items.$_" => $itemrecord->{$_}) : ()
1815
0
        } keys %{ $itemrecord }
1816    };
1817
0
    my $itemmarc = TransformKohaToMarc($mungeditem);
1818
0
    my ( $itemtag, $itemsubfield ) = GetMarcFromKohaField("items.itemnumber",GetFrameworkCode($biblionumber)||'');
1819
1820
0
    my $unlinked_item_subfields = _parse_unlinked_item_subfields_from_xml($mungeditem->{'items.more_subfields_xml'});
1821
0
    if (defined $unlinked_item_subfields and $#$unlinked_item_subfields > -1) {
1822
0
                foreach my $field ($itemmarc->field($itemtag)){
1823
0
            $field->add_subfields(@$unlinked_item_subfields);
1824        }
1825    }
1826
0
        return $itemmarc;
1827}
1828
1829 - 1835
=head1 PRIVATE FUNCTIONS AND VARIABLES

The following functions are not meant to be called
directly, but are documented in order to explain
the inner workings of C<C4::Items>.

=cut
1836
1837 - 1852
=head2 %derived_columns

This hash keeps track of item columns that
are strictly derived from other columns in
the item record and are not meant to be set
independently.

Each key in the hash should be the name of a
column (as named by TransformMarcToKoha).  Each
value should be hashref whose keys are the
columns on which the derived column depends.  The
hashref should also contain a 'BUILDER' key
that is a reference to a sub that calculates
the derived value.

=cut
1853
1854my %derived_columns = (
1855    'items.cn_sort' => {
1856        'itemcallnumber' => 1,
1857        'items.cn_source' => 1,
1858        'BUILDER' => \&_calc_items_cn_sort,
1859    }
1860);
1861
1862 - 1870
=head2 _set_derived_columns_for_add 

  _set_derived_column_for_add($item);

Given an item hash representing a new item to be added,
calculate any derived columns.  Currently the only
such column is C<items.cn_sort>.

=cut
1871
1872sub _set_derived_columns_for_add {
1873
0
    my $item = shift;
1874
1875
0
    foreach my $column (keys %derived_columns) {
1876
0
        my $builder = $derived_columns{$column}->{'BUILDER'};
1877
0
        my $source_values = {};
1878
0
0
        foreach my $source_column (keys %{ $derived_columns{$column} }) {
1879
0
            next if $source_column eq 'BUILDER';
1880
0
            $source_values->{$source_column} = $item->{$source_column};
1881        }
1882
0
        $builder->($item, $source_values);
1883    }
1884}
1885
1886 - 1905
=head2 _set_derived_columns_for_mod 

  _set_derived_column_for_mod($item);

Given an item hash representing a new item to be modified.
calculate any derived columns.  Currently the only
such column is C<items.cn_sort>.

This routine differs from C<_set_derived_columns_for_add>
in that it needs to handle partial item records.  In other
words, the caller of C<ModItem> may have supplied only one
or two columns to be changed, so this function needs to
determine whether any of the columns to be changed affect
any of the derived columns.  Also, if a derived column
depends on more than one column, but the caller is not
changing all of then, this routine retrieves the unchanged
values from the database in order to ensure a correct
calculation.

=cut
1906
1907sub _set_derived_columns_for_mod {
1908
0
    my $item = shift;
1909
1910
0
    foreach my $column (keys %derived_columns) {
1911
0
        my $builder = $derived_columns{$column}->{'BUILDER'};
1912
0
        my $source_values = {};
1913
0
        my %missing_sources = ();
1914
0
        my $must_recalc = 0;
1915
0
0
        foreach my $source_column (keys %{ $derived_columns{$column} }) {
1916
0
            next if $source_column eq 'BUILDER';
1917
0
            if (exists $item->{$source_column}) {
1918
0
                $must_recalc = 1;
1919
0
                $source_values->{$source_column} = $item->{$source_column};
1920            } else {
1921
0
                $missing_sources{$source_column} = 1;
1922            }
1923        }
1924
0
        if ($must_recalc) {
1925
0
            foreach my $source_column (keys %missing_sources) {
1926
0
                $source_values->{$source_column} = _get_single_item_column($source_column, $item->{'itemnumber'});
1927            }
1928
0
            $builder->($item, $source_values);
1929        }
1930    }
1931}
1932
1933 - 1944
=head2 _do_column_fixes_for_mod

  _do_column_fixes_for_mod($item);

Given an item hashref containing one or more
columns to modify, fix up certain values.
Specifically, set to 0 any passed value
of C<notforloan>, C<damaged>, C<itemlost>, or
C<wthdrawn> that is either undefined or
contains the empty string.

=cut
1945
1946sub _do_column_fixes_for_mod {
1947
0
    my $item = shift;
1948
1949
0
    if (exists $item->{'notforloan'} and
1950        (not defined $item->{'notforloan'} or $item->{'notforloan'} eq '')) {
1951
0
        $item->{'notforloan'} = 0;
1952    }
1953
0
    if (exists $item->{'damaged'} and
1954        (not defined $item->{'damaged'} or $item->{'damaged'} eq '')) {
1955
0
        $item->{'damaged'} = 0;
1956    }
1957
0
    if (exists $item->{'itemlost'} and
1958        (not defined $item->{'itemlost'} or $item->{'itemlost'} eq '')) {
1959
0
        $item->{'itemlost'} = 0;
1960    }
1961
0
    if (exists $item->{'wthdrawn'} and
1962        (not defined $item->{'wthdrawn'} or $item->{'wthdrawn'} eq '')) {
1963
0
        $item->{'wthdrawn'} = 0;
1964    }
1965
0
    if (exists $item->{'location'} && !exists $item->{'permanent_location'}) {
1966
0
        $item->{'permanent_location'} = $item->{'location'};
1967    }
1968
0
    if (exists $item->{'timestamp'}) {
1969
0
        delete $item->{'timestamp'};
1970    }
1971}
1972
1973 - 1980
=head2 _get_single_item_column

  _get_single_item_column($column, $itemnumber);

Retrieves the value of a single column from an C<items>
row specified by C<$itemnumber>.

=cut
1981
1982sub _get_single_item_column {
1983
0
    my $column = shift;
1984
0
    my $itemnumber = shift;
1985
1986
0
    my $dbh = C4::Context->dbh;
1987
0
    my $sth = $dbh->prepare("SELECT $column FROM items WHERE itemnumber = ?");
1988
0
    $sth->execute($itemnumber);
1989
0
    my ($value) = $sth->fetchrow();
1990
0
    return $value;
1991}
1992
1993 - 1999
=head2 _calc_items_cn_sort

  _calc_items_cn_sort($item, $source_values);

Helper routine to calculate C<items.cn_sort>.

=cut
2000
2001sub _calc_items_cn_sort {
2002
0
    my $item = shift;
2003
0
    my $source_values = shift;
2004
2005
0
    $item->{'items.cn_sort'} = GetClassSort($source_values->{'items.cn_source'}, $source_values->{'itemcallnumber'}, "");
2006}
2007
2008 - 2041
=head2 _set_defaults_for_add 

  _set_defaults_for_add($item_hash);

Given an item hash representing an item to be added, set
correct default values for columns whose default value
is not handled by the DBMS.  This includes the following
columns:

=over 2

=item * 

C<items.dateaccessioned>

=item *

C<items.notforloan>

=item *

C<items.damaged>

=item *

C<items.itemlost>

=item *

C<items.wthdrawn>

=back

=cut
2042
2043sub _set_defaults_for_add {
2044
0
    my $item = shift;
2045
0
    $item->{dateaccessioned} ||= C4::Dates->new->output('iso');
2046
0
0
    $item->{$_} ||= 0 for (qw( notforloan damaged itemlost wthdrawn));
2047}
2048
2049 - 2055
=head2 _koha_new_item

  my ($itemnumber,$error) = _koha_new_item( $item, $barcode );

Perform the actual insert into the C<items> table.

=cut
2056
2057sub _koha_new_item {
2058
0
    my ( $item, $barcode ) = @_;
2059
0
    my $dbh=C4::Context->dbh;
2060
0
    my $error;
2061
0
    my $query =
2062           "INSERT INTO items SET
2063            biblionumber = ?,
2064            biblioitemnumber = ?,
2065            barcode = ?,
2066            dateaccessioned = ?,
2067            booksellerid = ?,
2068            homebranch = ?,
2069            price = ?,
2070            replacementprice = ?,
2071            replacementpricedate = ?,
2072            datelastborrowed = ?,
2073            datelastseen = ?,
2074            stack = ?,
2075            notforloan = ?,
2076            damaged = ?,
2077            itemlost = ?,
2078            wthdrawn = ?,
2079            itemcallnumber = ?,
2080            restricted = ?,
2081            itemnotes = ?,
2082            holdingbranch = ?,
2083            paidfor = ?,
2084            location = ?,
2085            permanent_location = ?,
2086            onloan = ?,
2087            issues = ?,
2088            renewals = ?,
2089            reserves = ?,
2090            cn_source = ?,
2091            cn_sort = ?,
2092            ccode = ?,
2093            itype = ?,
2094            materials = ?,
2095            uri = ?,
2096            enumchron = ?,
2097            more_subfields_xml = ?,
2098            copynumber = ?,
2099            stocknumber = ?
2100          ";
2101
0
    my $sth = $dbh->prepare($query);
2102
0
    my $today = C4::Dates->today('iso');
2103
0
   $sth->execute(
2104            $item->{'biblionumber'},
2105            $item->{'biblioitemnumber'},
2106            $barcode,
2107            $item->{'dateaccessioned'},
2108            $item->{'booksellerid'},
2109            $item->{'homebranch'},
2110            $item->{'price'},
2111            $item->{'replacementprice'},
2112            $item->{'replacementpricedate'} || $today,
2113            $item->{datelastborrowed},
2114            $item->{datelastseen} || $today,
2115            $item->{stack},
2116            $item->{'notforloan'},
2117            $item->{'damaged'},
2118            $item->{'itemlost'},
2119            $item->{'wthdrawn'},
2120            $item->{'itemcallnumber'},
2121            $item->{'restricted'},
2122            $item->{'itemnotes'},
2123            $item->{'holdingbranch'},
2124            $item->{'paidfor'},
2125            $item->{'location'},
2126            $item->{'permanent_location'},
2127            $item->{'onloan'},
2128            $item->{'issues'},
2129            $item->{'renewals'},
2130            $item->{'reserves'},
2131            $item->{'items.cn_source'},
2132            $item->{'items.cn_sort'},
2133            $item->{'ccode'},
2134            $item->{'itype'},
2135            $item->{'materials'},
2136            $item->{'uri'},
2137            $item->{'enumchron'},
2138            $item->{'more_subfields_xml'},
2139            $item->{'copynumber'},
2140            $item->{'stocknumber'},
2141    );
2142
2143
0
    my $itemnumber;
2144
0
    if ( defined $sth->errstr ) {
2145
0
        $error.="ERROR in _koha_new_item $query".$sth->errstr;
2146    }
2147    else {
2148
0
        $itemnumber = $dbh->{'mysql_insertid'};
2149    }
2150
2151
0
    return ( $itemnumber, $error );
2152}
2153
2154 - 2162
=head2 MoveItemFromBiblio

  MoveItemFromBiblio($itenumber, $frombiblio, $tobiblio);

Moves an item from a biblio to another

Returns undef if the move failed or the biblionumber of the destination record otherwise

=cut
2163
2164sub MoveItemFromBiblio {
2165
0
    my ($itemnumber, $frombiblio, $tobiblio) = @_;
2166
0
    my $dbh = C4::Context->dbh;
2167
0
    my $sth = $dbh->prepare("SELECT biblioitemnumber FROM biblioitems WHERE biblionumber = ?");
2168
0
    $sth->execute( $tobiblio );
2169
0
    my ( $tobiblioitem ) = $sth->fetchrow();
2170
0
    $sth = $dbh->prepare("UPDATE items SET biblioitemnumber = ?, biblionumber = ? WHERE itemnumber = ? AND biblionumber = ?");
2171
0
    my $return = $sth->execute($tobiblioitem, $tobiblio, $itemnumber, $frombiblio);
2172
0
    if ($return == 1) {
2173
0
        ModZebra( $tobiblio, "specialUpdate", "biblioserver", undef, undef );
2174
0
        ModZebra( $frombiblio, "specialUpdate", "biblioserver", undef, undef );
2175            # Checking if the item we want to move is in an order
2176
0
        require C4::Acquisition;
2177
0
        my $order = C4::Acquisition::GetOrderFromItemnumber($itemnumber);
2178
0
            if ($order) {
2179                    # Replacing the biblionumber within the order if necessary
2180
0
                    $order->{'biblionumber'} = $tobiblio;
2181
0
                C4::Acquisition::ModOrder($order);
2182            }
2183
0
        return $tobiblio;
2184        }
2185
0
    return;
2186}
2187
2188 - 2194
=head2 DelItemCheck

   DelItemCheck($dbh, $biblionumber, $itemnumber);

Exported function (core API) for deleting an item record in Koha if there no current issue.

=cut
2195
2196sub DelItemCheck {
2197
0
    my ( $dbh, $biblionumber, $itemnumber ) = @_;
2198
0
    my $error;
2199
2200
0
        my $countanalytics=GetAnalyticsCount($itemnumber);
2201
2202
2203    # check that there is no issue on this item before deletion.
2204
0
    my $sth=$dbh->prepare("select * from issues i where i.itemnumber=?");
2205
0
    $sth->execute($itemnumber);
2206
2207
0
    my $item = GetItem($itemnumber);
2208
0
    my $onloan=$sth->fetchrow;
2209
2210
0
    if ($onloan){
2211
0
        $error = "book_on_loan"
2212    }
2213    elsif ( !(C4::Context->userenv->{flags} & 1) and
2214            C4::Context->preference("IndependantBranches") and
2215           (C4::Context->userenv->{branch} ne
2216             $item->{C4::Context->preference("HomeOrHoldingBranch")||'homebranch'}) )
2217    {
2218
0
        $error = "not_same_branch";
2219    }
2220        else{
2221        # check it doesnt have a waiting reserve
2222
0
        $sth=$dbh->prepare("SELECT * FROM reserves WHERE (found = 'W' or found = 'T') AND itemnumber = ?");
2223
0
        $sth->execute($itemnumber);
2224
0
        my $reserve=$sth->fetchrow;
2225
0
        if ($reserve){
2226
0
            $error = "book_reserved";
2227        } elsif ($countanalytics > 0){
2228
0
                $error = "linked_analytics";
2229        } else {
2230
0
            DelItem($dbh, $biblionumber, $itemnumber);
2231
0
            return 1;
2232        }
2233    }
2234
0
    return $error;
2235}
2236
2237 - 2244
=head2 _koha_modify_item

  my ($itemnumber,$error) =_koha_modify_item( $item );

Perform the actual update of the C<items> row.  Note that this
routine accepts a hashref specifying the columns to update.

=cut
2245
2246sub _koha_modify_item {
2247
0
    my ( $item ) = @_;
2248
0
    my $dbh=C4::Context->dbh;
2249
0
    my $error;
2250
2251
0
    my $query = "UPDATE items SET ";
2252
0
    my @bind;
2253
0
    for my $key ( keys %$item ) {
2254
0
        $query.="$key=?,";
2255
0
        push @bind, $item->{$key};
2256    }
2257
0
    $query =~ s/,$//;
2258
0
    $query .= " WHERE itemnumber=?";
2259
0
    push @bind, $item->{'itemnumber'};
2260
0
    my $sth = C4::Context->dbh->prepare($query);
2261
0
    $sth->execute(@bind);
2262
0
    if ( C4::Context->dbh->errstr ) {
2263
0
        $error.="ERROR in _koha_modify_item $query".$dbh->errstr;
2264
0
        warn $error;
2265    }
2266
0
    return ($item->{'itemnumber'},$error);
2267}
2268
2269 - 2275
=head2 _koha_delete_item

  _koha_delete_item( $dbh, $itemnum );

Internal function to delete an item record from the koha tables

=cut
2276
2277sub _koha_delete_item {
2278
0
    my ( $dbh, $itemnum ) = @_;
2279
2280    # save the deleted item to deleteditems table
2281
0
    my $sth = $dbh->prepare("SELECT * FROM items WHERE itemnumber=?");
2282
0
    $sth->execute($itemnum);
2283
0
    my $data = $sth->fetchrow_hashref();
2284
0
    my $query = "INSERT INTO deleteditems SET ";
2285
0
    my @bind = ();
2286
0
    foreach my $key ( keys %$data ) {
2287
0
        $query .= "$key = ?,";
2288
0
        push( @bind, $data->{$key} );
2289    }
2290
0
    $query =~ s/\,$//;
2291
0
    $sth = $dbh->prepare($query);
2292
0
    $sth->execute(@bind);
2293
2294    # delete from items table
2295
0
    $sth = $dbh->prepare("DELETE FROM items WHERE itemnumber=?");
2296
0
    $sth->execute($itemnum);
2297
0
    return undef;
2298}
2299
2300 - 2313
=head2 _marc_from_item_hash

  my $item_marc = _marc_from_item_hash($item, $frameworkcode[, $unlinked_item_subfields]);

Given an item hash representing a complete item record,
create a C<MARC::Record> object containing an embedded
tag representing that item.

The third, optional parameter C<$unlinked_item_subfields> is
an arrayref of subfields (not mapped to C<items> fields per the
framework) to be added to the MARC representation
of the item.

=cut
2314
2315sub _marc_from_item_hash {
2316
0
    my $item = shift;
2317
0
    my $frameworkcode = shift;
2318
0
    my $unlinked_item_subfields;
2319
0
    if (@_) {
2320
0
        $unlinked_item_subfields = shift;
2321    }
2322
2323    # Tack on 'items.' prefix to column names so lookup from MARC frameworks will work
2324    # Also, don't emit a subfield if the underlying field is blank.
2325
0
0
    my $mungeditem = { map { (defined($item->{$_}) and $item->{$_} ne '') ?
2326                                (/^items\./ ? ($_ => $item->{$_}) : ("items.$_" => $item->{$_}))
2327
0
                                : () } keys %{ $item } };
2328
2329
0
    my $item_marc = MARC::Record->new();
2330
0
0
    foreach my $item_field ( keys %{$mungeditem} ) {
2331
0
        my ( $tag, $subfield ) = GetMarcFromKohaField( $item_field, $frameworkcode );
2332
0
        next unless defined $tag and defined $subfield; # skip if not mapped to MARC field
2333
0
        my @values = split(/\s?\|\s?/, $mungeditem->{$item_field}, -1);
2334
0
        foreach my $value (@values){
2335
0
            if ( my $field = $item_marc->field($tag) ) {
2336
0
                    $field->add_subfields( $subfield => $value );
2337            } else {
2338
0
                my $add_subfields = [];
2339
0
                if (defined $unlinked_item_subfields and ref($unlinked_item_subfields) eq 'ARRAY' and $#$unlinked_item_subfields > -1) {
2340
0
                    $add_subfields = $unlinked_item_subfields;
2341            }
2342
0
            $item_marc->add_fields( $tag, " ", " ", $subfield => $value, @$add_subfields );
2343            }
2344        }
2345    }
2346
2347
0
    return $item_marc;
2348}
2349
2350 - 2355
=head2 _repack_item_errors

Add an error message hash generated by C<CheckItemPreSave>
to a list of errors.

=cut
2356
2357sub _repack_item_errors {
2358
0
    my $item_sequence_num = shift;
2359
0
    my $item_ref = shift;
2360
0
    my $error_ref = shift;
2361
2362
0
    my @repacked_errors = ();
2363
2364
0
0
    foreach my $error_code (sort keys %{ $error_ref }) {
2365
0
        my $repacked_error = {};
2366
0
        $repacked_error->{'item_sequence'} = $item_sequence_num;
2367
0
        $repacked_error->{'item_barcode'} = exists($item_ref->{'barcode'}) ? $item_ref->{'barcode'} : '';
2368
0
        $repacked_error->{'error_code'} = $error_code;
2369
0
        $repacked_error->{'error_information'} = $error_ref->{$error_code};
2370
0
        push @repacked_errors, $repacked_error;
2371    }
2372
2373
0
    return @repacked_errors;
2374}
2375
2376 - 2380
=head2 _get_unlinked_item_subfields

  my $unlinked_item_subfields = _get_unlinked_item_subfields($original_item_marc, $frameworkcode);

=cut
2381
2382sub _get_unlinked_item_subfields {
2383
0
    my $original_item_marc = shift;
2384
0
    my $frameworkcode = shift;
2385
2386
0
    my $marcstructure = GetMarcStructure(1, $frameworkcode);
2387
2388    # assume that this record has only one field, and that that
2389    # field contains only the item information
2390
0
    my $subfields = [];
2391
0
    my @fields = $original_item_marc->fields();
2392
0
    if ($#fields > -1) {
2393
0
        my $field = $fields[0];
2394
0
            my $tag = $field->tag();
2395
0
        foreach my $subfield ($field->subfields()) {
2396
0
            if (defined $subfield->[1] and
2397                $subfield->[1] ne '' and
2398                !$marcstructure->{$tag}->{$subfield->[0]}->{'kohafield'}) {
2399
0
                push @$subfields, $subfield->[0] => $subfield->[1];
2400            }
2401        }
2402    }
2403
0
    return $subfields;
2404}
2405
2406 - 2410
=head2 _get_unlinked_subfields_xml

  my $unlinked_subfields_xml = _get_unlinked_subfields_xml($unlinked_item_subfields);

=cut
2411
2412sub _get_unlinked_subfields_xml {
2413
0
    my $unlinked_item_subfields = shift;
2414
2415
0
    my $xml;
2416
0
    if (defined $unlinked_item_subfields and ref($unlinked_item_subfields) eq 'ARRAY' and $#$unlinked_item_subfields > -1) {
2417
0
        my $marc = MARC::Record->new();
2418        # use of tag 999 is arbitrary, and doesn't need to match the item tag
2419        # used in the framework
2420
0
        $marc->append_fields(MARC::Field->new('999', ' ', ' ', @$unlinked_item_subfields));
2421
0
        $marc->encoding("UTF-8");
2422
0
        $xml = $marc->as_xml("USMARC");
2423    }
2424
2425
0
    return $xml;
2426}
2427
2428 - 2432
=head2 _parse_unlinked_item_subfields_from_xml

  my $unlinked_item_subfields = _parse_unlinked_item_subfields_from_xml($whole_item->{'more_subfields_xml'}):

=cut
2433
2434sub _parse_unlinked_item_subfields_from_xml {
2435
0
    my $xml = shift;
2436
0
    require C4::Charset;
2437
0
    return unless defined $xml and $xml ne "";
2438
0
    my $marc = MARC::Record->new_from_xml(C4::Charset::StripNonXmlChars($xml),'UTF-8');
2439
0
    my $unlinked_subfields = [];
2440
0
    my @fields = $marc->fields();
2441
0
    if ($#fields > -1) {
2442
0
        foreach my $subfield ($fields[0]->subfields()) {
2443
0
            push @$unlinked_subfields, $subfield->[0] => $subfield->[1];
2444        }
2445    }
2446
0
    return $unlinked_subfields;
2447}
2448
2449 - 2455
=head2 GetAnalyticsCount

  $count= &GetAnalyticsCount($itemnumber)

counts Usage of itemnumber in Analytical bibliorecords. 

=cut
2456
2457sub GetAnalyticsCount {
2458
0
    my ($itemnumber) = @_;
2459
0
    if (C4::Context->preference('NoZebra')) {
2460        # Read the index Koha-Auth-Number for this authid and count the lines
2461
0
        my $result = C4::Search::NZanalyse("hi=$itemnumber");
2462
0
        my @tab = split /;/,$result;
2463
0
        return scalar @tab;
2464    } else {
2465        ### ZOOM search here
2466
0
        my $query;
2467
0
        $query= "hi=".$itemnumber;
2468
0
                my ($err,$res,$result) = C4::Search::SimpleSearch($query,0,10);
2469
0
        return ($result);
2470    }
2471}
2472
2473 - 2482
=head2 GetItemHolds

=over 4
$holds = &GetItemHolds($biblionumber, $itemnumber);

=back

This function return the count of holds with $biblionumber and $itemnumber

=cut
2483
2484sub GetItemHolds {
2485
0
    my ($biblionumber, $itemnumber) = @_;
2486
0
    my $holds;
2487
0
    my $dbh = C4::Context->dbh;
2488
0
    my $query = "SELECT count(*)
2489        FROM reserves
2490        WHERE biblionumber=? AND itemnumber=?";
2491
0
    my $sth = $dbh->prepare($query);
2492
0
    $sth->execute($biblionumber, $itemnumber);
2493
0
    $holds = $sth->fetchrow;
2494
0
    return $holds;
2495}
2496 - 2511
=head1  OTHER FUNCTIONS

=head2 _find_value

  ($indicators, $value) = _find_value($tag, $subfield, $record,$encoding);

Find the given $subfield in the given $tag in the given
MARC::Record $record.  If the subfield is found, returns
the (indicators, value) pair; otherwise, (undef, undef) is
returned.

PROPOSITION :
Such a function is used in addbiblio AND additem and serial-edit and maybe could be used in Authorities.
I suggest we export it from this module.

=cut
2512
2513sub _find_value {
2514
0
    my ( $tagfield, $insubfield, $record, $encoding ) = @_;
2515
0
    my @result;
2516
0
    my $indicator;
2517
0
    if ( $tagfield < 10 ) {
2518
0
        if ( $record->field($tagfield) ) {
2519
0
            push @result, $record->field($tagfield)->data();
2520        } else {
2521
0
            push @result, "";
2522        }
2523    } else {
2524
0
        foreach my $field ( $record->field($tagfield) ) {
2525
0
            my @subfields = $field->subfields();
2526
0
            foreach my $subfield (@subfields) {
2527
0
                if ( @$subfield[0] eq $insubfield ) {
2528
0
                    push @result, @$subfield[1];
2529
0
                    $indicator = $field->indicator(1) . $field->indicator(2);
2530                }
2531            }
2532        }
2533    }
2534
0
    return ( $indicator, @result );
2535}
2536
2537
2538 - 2546
=head2 PrepareItemrecordDisplay

  PrepareItemrecordDisplay($itemrecord,$bibnum,$itemumber,$frameworkcode);

Returns a hash with all the fields for Display a given item data in a template

The $frameworkcode returns the item for the given frameworkcode, ONLY if bibnum is not provided

=cut
2547
2548sub PrepareItemrecordDisplay {
2549
2550
0
    my ( $bibnum, $itemnum, $defaultvalues, $frameworkcode ) = @_;
2551
2552
0
    my $dbh = C4::Context->dbh;
2553
0
    $frameworkcode = &GetFrameworkCode($bibnum) if $bibnum;
2554
0
    my ( $itemtagfield, $itemtagsubfield ) = &GetMarcFromKohaField( "items.itemnumber", $frameworkcode );
2555
0
    my $tagslib = &GetMarcStructure( 1, $frameworkcode );
2556
2557    # return nothing if we don't have found an existing framework.
2558
0
    return q{} unless $tagslib;
2559
0
    my $itemrecord;
2560
0
    if ($itemnum) {
2561
0
        $itemrecord = C4::Items::GetMarcItem( $bibnum, $itemnum );
2562    }
2563
0
    my @loop_data;
2564
0
    my $authorised_values_sth = $dbh->prepare( "SELECT authorised_value,lib FROM authorised_values WHERE category=? ORDER BY lib" );
2565
0
0
    foreach my $tag ( sort keys %{$tagslib} ) {
2566
0
        my $previous_tag = '';
2567
0
        if ( $tag ne '' ) {
2568
2569            # loop through each subfield
2570
0
            my $cntsubf;
2571
0
0
            foreach my $subfield ( sort keys %{ $tagslib->{$tag} } ) {
2572
0
                next if ( subfield_is_koha_internal_p($subfield) );
2573
0
                next if ( $tagslib->{$tag}->{$subfield}->{'tab'} ne "10" );
2574
0
                my %subfield_data;
2575
0
                $subfield_data{tag} = $tag;
2576
0
                $subfield_data{subfield} = $subfield;
2577
0
                $subfield_data{countsubfield} = $cntsubf++;
2578
0
                $subfield_data{kohafield} = $tagslib->{$tag}->{$subfield}->{'kohafield'};
2579
0
                $subfield_data{id} = "tag_".$tag."_subfield_".$subfield."_".int(rand(1000000));
2580
2581                # $subfield_data{marc_lib}=$tagslib->{$tag}->{$subfield}->{lib};
2582
0
                $subfield_data{marc_lib} = $tagslib->{$tag}->{$subfield}->{lib};
2583
0
                $subfield_data{mandatory} = $tagslib->{$tag}->{$subfield}->{mandatory};
2584
0
                $subfield_data{repeatable} = $tagslib->{$tag}->{$subfield}->{repeatable};
2585
0
                $subfield_data{hidden} = "display:none"
2586                  if $tagslib->{$tag}->{$subfield}->{hidden};
2587
0
                my ( $x, $defaultvalue );
2588
0
                if ($itemrecord) {
2589
0
                    ( $x, $defaultvalue ) = _find_value( $tag, $subfield, $itemrecord );
2590                }
2591
0
                $defaultvalue = $tagslib->{$tag}->{$subfield}->{defaultvalue} unless $defaultvalue;
2592
0
                if ( !defined $defaultvalue ) {
2593
0
                    $defaultvalue = q||;
2594                }
2595
0
                $defaultvalue =~ s/"/&quot;/g;
2596
2597                # search for itemcallnumber if applicable
2598
0
                if ( $tagslib->{$tag}->{$subfield}->{kohafield} eq 'items.itemcallnumber'
2599                    && C4::Context->preference('itemcallnumber') ) {
2600
0
                    my $CNtag = substr( C4::Context->preference('itemcallnumber'), 0, 3 );
2601
0
                    my $CNsubfield = substr( C4::Context->preference('itemcallnumber'), 3, 1 );
2602
0
                    if ($itemrecord) {
2603
0
                        my $temp = $itemrecord->field($CNtag);
2604
0
                        if ($temp) {
2605
0
                            $defaultvalue = $temp->subfield($CNsubfield);
2606                        }
2607                    }
2608                }
2609
0
                if ( $tagslib->{$tag}->{$subfield}->{kohafield} eq 'items.itemcallnumber'
2610                    && $defaultvalues
2611                    && $defaultvalues->{'callnumber'} ) {
2612
0
                    my $temp;
2613
0
                    if ($itemrecord) {
2614
0
                        $temp = $itemrecord->field($subfield);
2615                    }
2616
0
                    unless ($temp) {
2617
0
                        $defaultvalue = $defaultvalues->{'callnumber'} if $defaultvalues;
2618                    }
2619                }
2620
0
                if ( ( $tagslib->{$tag}->{$subfield}->{kohafield} eq 'items.holdingbranch' || $tagslib->{$tag}->{$subfield}->{kohafield} eq 'items.homebranch' )
2621                    && $defaultvalues
2622                    && $defaultvalues->{'branchcode'} ) {
2623
0
                    my $temp;
2624
0
                    if ($itemrecord) {
2625
0
                        $temp = $itemrecord->field($subfield);
2626                    }
2627
0
                    unless ($temp) {
2628
0
                        $defaultvalue = $defaultvalues->{branchcode} if $defaultvalues;
2629                    }
2630                }
2631
0
                if ( ( $tagslib->{$tag}->{$subfield}->{kohafield} eq 'items.location' )
2632                    && $defaultvalues
2633                    && $defaultvalues->{'location'} ) {
2634
0
                    my $temp = $itemrecord->field($subfield) if ($itemrecord);
2635
0
                    unless ($temp) {
2636
0
                        $defaultvalue = $defaultvalues->{location} if $defaultvalues;
2637                    }
2638                }
2639
0
                if ( $tagslib->{$tag}->{$subfield}->{authorised_value} ) {
2640
0
                    my @authorised_values;
2641
0
                    my %authorised_lib;
2642
2643                    # builds list, depending on authorised value...
2644                    #---- branch
2645
0
                    if ( $tagslib->{$tag}->{$subfield}->{'authorised_value'} eq "branches" ) {
2646
0
                        if ( ( C4::Context->preference("IndependantBranches") )
2647                            && ( C4::Context->userenv->{flags} % 2 != 1 ) ) {
2648
0
                            my $sth = $dbh->prepare( "SELECT branchcode,branchname FROM branches WHERE branchcode = ? ORDER BY branchname" );
2649
0
                            $sth->execute( C4::Context->userenv->{branch} );
2650
0
                            push @authorised_values, ""
2651                              unless ( $tagslib->{$tag}->{$subfield}->{mandatory} );
2652
0
                            while ( my ( $branchcode, $branchname ) = $sth->fetchrow_array ) {
2653
0
                                push @authorised_values, $branchcode;
2654
0
                                $authorised_lib{$branchcode} = $branchname;
2655                            }
2656                        } else {
2657
0
                            my $sth = $dbh->prepare( "SELECT branchcode,branchname FROM branches ORDER BY branchname" );
2658
0
                            $sth->execute;
2659
0
                            push @authorised_values, ""
2660                              unless ( $tagslib->{$tag}->{$subfield}->{mandatory} );
2661
0
                            while ( my ( $branchcode, $branchname ) = $sth->fetchrow_array ) {
2662
0
                                push @authorised_values, $branchcode;
2663
0
                                $authorised_lib{$branchcode} = $branchname;
2664                            }
2665                        }
2666
2667                        #----- itemtypes
2668                    } elsif ( $tagslib->{$tag}->{$subfield}->{authorised_value} eq "itemtypes" ) {
2669
0
                        my $sth = $dbh->prepare( "SELECT itemtype,description FROM itemtypes ORDER BY description" );
2670
0
                        $sth->execute;
2671
0
                        push @authorised_values, ""
2672                          unless ( $tagslib->{$tag}->{$subfield}->{mandatory} );
2673
0
                        while ( my ( $itemtype, $description ) = $sth->fetchrow_array ) {
2674
0
                            push @authorised_values, $itemtype;
2675
0
                            $authorised_lib{$itemtype} = $description;
2676                        }
2677                        #---- class_sources
2678                    } elsif ( $tagslib->{$tag}->{$subfield}->{authorised_value} eq "cn_source" ) {
2679
0
                        push @authorised_values, "" unless ( $tagslib->{$tag}->{$subfield}->{mandatory} );
2680
2681
0
                        my $class_sources = GetClassSources();
2682
0
                        my $default_source = C4::Context->preference("DefaultClassificationSource");
2683
2684
0
                        foreach my $class_source (sort keys %$class_sources) {
2685
0
                            next unless $class_sources->{$class_source}->{'used'} or
2686                                        ($class_source eq $default_source);
2687
0
                            push @authorised_values, $class_source;
2688
0
                            $authorised_lib{$class_source} = $class_sources->{$class_source}->{'description'};
2689                        }
2690
2691                        #---- "true" authorised value
2692                    } else {
2693
0
                        $authorised_values_sth->execute( $tagslib->{$tag}->{$subfield}->{authorised_value} );
2694
0
                        push @authorised_values, ""
2695                          unless ( $tagslib->{$tag}->{$subfield}->{mandatory} );
2696
0
                        while ( my ( $value, $lib ) = $authorised_values_sth->fetchrow_array ) {
2697
0
                            push @authorised_values, $value;
2698
0
                            $authorised_lib{$value} = $lib;
2699                        }
2700                    }
2701
0
                    $subfield_data{marc_value} = CGI::scrolling_list(
2702                        -name => 'field_value',
2703                        -values => \@authorised_values,
2704                        -default => "$defaultvalue",
2705                        -labels => \%authorised_lib,
2706                        -size => 1,
2707                        -tabindex => '',
2708                        -multiple => 0,
2709                    );
2710                } elsif ( $tagslib->{$tag}->{$subfield}->{value_builder} ) {
2711                        # opening plugin
2712
0
                        my $plugin = C4::Context->intranetdir . "/cataloguing/value_builder/" . $tagslib->{$tag}->{$subfield}->{'value_builder'};
2713
0
                        if (do $plugin) {
2714
0
                            my $temp;
2715
0
                            my $extended_param = plugin_parameters( $dbh, $temp, $tagslib, $subfield_data{id}, undef );
2716
0
                            my ( $function_name, $javascript ) = plugin_javascript( $dbh, $temp, $tagslib, $subfield_data{id}, undef );
2717
0
                            $subfield_data{random} = int(rand(1000000)); # why do we need 2 different randoms?
2718
0
                            $subfield_data{marc_value} = qq[<input tabindex="1" id="$subfield_data{id}" name="field_value" class="input_marceditor" size="67" maxlength="255"
2719                                onfocus="Focus$function_name($subfield_data{random}, '$subfield_data{id}');"
2720                                 onblur=" Blur$function_name($subfield_data{random}, '$subfield_data{id}');" />
2721                                <a href="#" class="buttonDot" onclick="Clic$function_name('$subfield_data{id}'); return false;" title="Tag Editor">...</a>
2722                                $javascript];
2723                        } else {
2724
0
                            warn "Plugin Failed: $plugin";
2725
0
                            $subfield_data{marc_value} = qq(<input tabindex="1" id="$subfield_data{id}" name="field_value" class="input_marceditor" size="67" maxlength="255" />); # supply default input form
2726                        }
2727                }
2728                elsif ( $tag eq '' ) { # it's an hidden field
2729
0
                    $subfield_data{marc_value} = qq(<input type="hidden" tabindex="1" id="$subfield_data{id}" name="field_value" class="input_marceditor" size="67" maxlength="255" value="$defaultvalue" />);
2730                }
2731                elsif ( $tagslib->{$tag}->{$subfield}->{'hidden'} ) { # FIXME: shouldn't input type be "hidden" ?
2732
0
                    $subfield_data{marc_value} = qq(<input type="text" tabindex="1" id="$subfield_data{id}" name="field_value" class="input_marceditor" size="67" maxlength="255" value="$defaultvalue" />);
2733                }
2734                elsif ( length($defaultvalue) > 100
2735                            or (C4::Context->preference("marcflavour") eq "UNIMARC" and
2736                                  300 <= $tag && $tag < 400 && $subfield eq 'a' )
2737                            or (C4::Context->preference("marcflavour") eq "MARC21" and
2738                                  500 <= $tag && $tag < 600 )
2739                          ) {
2740                    # oversize field (textarea)
2741
0
                    $subfield_data{marc_value} = qq(<textarea tabindex="1" id="$subfield_data{id}" name="field_value" class="input_marceditor" size="67" maxlength="255">$defaultvalue</textarea>\n");
2742                } else {
2743
0
                    $subfield_data{marc_value} = "<input type=\"text\" name=\"field_value\" value=\"$defaultvalue\" size=\"50\" maxlength=\"255\" />";
2744                }
2745
0
                push( @loop_data, \%subfield_data );
2746            }
2747        }
2748    }
2749
0
    my $itemnumber;
2750
0
    if ( $itemrecord && $itemrecord->field($itemtagfield) ) {
2751
0
        $itemnumber = $itemrecord->subfield( $itemtagfield, $itemtagsubfield );
2752    }
2753    return {
2754
0
        'itemtagfield' => $itemtagfield,
2755        'itemtagsubfield' => $itemtagsubfield,
2756        'itemnumber' => $itemnumber,
2757        'iteminformation' => \@loop_data
2758    };
2759}
2760
27611;