| Filename | /mnt/catalyst/koha/C4/Items.pm |
| Statements | Executed 33 statements in 8.51ms |
| Calls | P | F | Exclusive Time |
Inclusive Time |
Subroutine |
|---|---|---|---|---|---|
| 1 | 1 | 1 | 1.35ms | 16.8ms | C4::Items::BEGIN@33 |
| 1 | 1 | 1 | 756µs | 773µs | C4::Items::BEGIN@21 |
| 1 | 1 | 1 | 14µs | 85µs | C4::Items::BEGIN@32 |
| 1 | 1 | 1 | 11µs | 11µs | C4::Items::BEGIN@39 |
| 1 | 1 | 1 | 11µs | 30µs | C4::Items::BEGIN@34 |
| 1 | 1 | 1 | 10µs | 20µs | C4::Items::BEGIN@29 |
| 1 | 1 | 1 | 10µs | 12µs | C4::Items::BEGIN@25 |
| 1 | 1 | 1 | 9µs | 46µs | C4::Items::BEGIN@24 |
| 1 | 1 | 1 | 9µs | 34µs | C4::Items::BEGIN@28 |
| 1 | 1 | 1 | 8µs | 259µs | C4::Items::BEGIN@26 |
| 1 | 1 | 1 | 8µs | 292µs | C4::Items::BEGIN@27 |
| 1 | 1 | 1 | 8µs | 65µs | C4::Items::BEGIN@31 |
| 1 | 1 | 1 | 8µs | 46µs | C4::Items::BEGIN@37 |
| 1 | 1 | 1 | 8µs | 107µs | C4::Items::BEGIN@30 |
| 0 | 0 | 0 | 0s | 0s | C4::Items::AddItem |
| 0 | 0 | 0 | 0s | 0s | C4::Items::AddItemBatchFromMarc |
| 0 | 0 | 0 | 0s | 0s | C4::Items::AddItemFromMarc |
| 0 | 0 | 0 | 0s | 0s | C4::Items::CartToShelf |
| 0 | 0 | 0 | 0s | 0s | C4::Items::CheckItemPreSave |
| 0 | 0 | 0 | 0s | 0s | C4::Items::DelItem |
| 0 | 0 | 0 | 0s | 0s | C4::Items::DelItemCheck |
| 0 | 0 | 0 | 0s | 0s | C4::Items::GetAnalyticsCount |
| 0 | 0 | 0 | 0s | 0s | C4::Items::GetBarcodeFromItemnumber |
| 0 | 0 | 0 | 0s | 0s | C4::Items::GetHiddenItemnumbers |
| 0 | 0 | 0 | 0s | 0s | C4::Items::GetHostItemsInfo |
| 0 | 0 | 0 | 0s | 0s | C4::Items::GetItem |
| 0 | 0 | 0 | 0s | 0s | C4::Items::GetItemHolds |
| 0 | 0 | 0 | 0s | 0s | C4::Items::GetItemInfosOf |
| 0 | 0 | 0 | 0s | 0s | C4::Items::GetItemLocation |
| 0 | 0 | 0 | 0s | 0s | C4::Items::GetItemStatus |
| 0 | 0 | 0 | 0s | 0s | C4::Items::GetItemnumberFromBarcode |
| 0 | 0 | 0 | 0s | 0s | C4::Items::GetItemnumbersForBiblio |
| 0 | 0 | 0 | 0s | 0s | C4::Items::GetItemsByBiblioitemnumber |
| 0 | 0 | 0 | 0s | 0s | C4::Items::GetItemsCount |
| 0 | 0 | 0 | 0s | 0s | C4::Items::GetItemsForInventory |
| 0 | 0 | 0 | 0s | 0s | C4::Items::GetItemsInfo |
| 0 | 0 | 0 | 0s | 0s | C4::Items::GetItemsLocationInfo |
| 0 | 0 | 0 | 0s | 0s | C4::Items::GetLastAcquisitions |
| 0 | 0 | 0 | 0s | 0s | C4::Items::GetLostItems |
| 0 | 0 | 0 | 0s | 0s | C4::Items::GetMarcItem |
| 0 | 0 | 0 | 0s | 0s | C4::Items::Item2Marc |
| 0 | 0 | 0 | 0s | 0s | C4::Items::ModDateLastSeen |
| 0 | 0 | 0 | 0s | 0s | C4::Items::ModItem |
| 0 | 0 | 0 | 0s | 0s | C4::Items::ModItemFromMarc |
| 0 | 0 | 0 | 0s | 0s | C4::Items::ModItemTransfer |
| 0 | 0 | 0 | 0s | 0s | C4::Items::MoveItemFromBiblio |
| 0 | 0 | 0 | 0s | 0s | C4::Items::PrepareItemrecordDisplay |
| 0 | 0 | 0 | 0s | 0s | C4::Items::SearchItems |
| 0 | 0 | 0 | 0s | 0s | C4::Items::ShelfToCart |
| 0 | 0 | 0 | 0s | 0s | C4::Items::__ANON__[:1679] |
| 0 | 0 | 0 | 0s | 0s | C4::Items::_calc_items_cn_sort |
| 0 | 0 | 0 | 0s | 0s | C4::Items::_do_column_fixes_for_mod |
| 0 | 0 | 0 | 0s | 0s | C4::Items::_find_value |
| 0 | 0 | 0 | 0s | 0s | C4::Items::_get_items_columns |
| 0 | 0 | 0 | 0s | 0s | C4::Items::_get_single_item_column |
| 0 | 0 | 0 | 0s | 0s | C4::Items::_get_unlinked_item_subfields |
| 0 | 0 | 0 | 0s | 0s | C4::Items::_get_unlinked_subfields_xml |
| 0 | 0 | 0 | 0s | 0s | C4::Items::_koha_delete_item |
| 0 | 0 | 0 | 0s | 0s | C4::Items::_koha_modify_item |
| 0 | 0 | 0 | 0s | 0s | C4::Items::_koha_new_item |
| 0 | 0 | 0 | 0s | 0s | C4::Items::_marc_from_item_hash |
| 0 | 0 | 0 | 0s | 0s | C4::Items::_parse_unlinked_item_subfields_from_xml |
| 0 | 0 | 0 | 0s | 0s | C4::Items::_repack_item_errors |
| 0 | 0 | 0 | 0s | 0s | C4::Items::_set_defaults_for_add |
| 0 | 0 | 0 | 0s | 0s | C4::Items::_set_derived_columns_for_add |
| 0 | 0 | 0 | 0s | 0s | C4::Items::_set_derived_columns_for_mod |
| 0 | 0 | 0 | 0s | 0s | C4::Items::get_authorised_value_images |
| 0 | 0 | 0 | 0s | 0s | C4::Items::get_hostitemnumbers_of |
| 0 | 0 | 0 | 0s | 0s | C4::Items::get_item_authorised_values |
| 0 | 0 | 0 | 0s | 0s | C4::Items::get_itemnumbers_of |
| Line | State ments |
Time on line |
Calls | Time in subs |
Code |
|---|---|---|---|---|---|
| 1 | package 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 | 2 | 32µs | 2 | 791µs | # spent 773µs (756+18) within C4::Items::BEGIN@21 which was called:
# once (756µs+18µs) by C4::Reserves::BEGIN@29 at line 21 # spent 773µs making 1 call to C4::Items::BEGIN@21
# spent 18µs making 1 call to strict::import |
| 22 | #use warnings; FIXME - Bug 2505 | ||||
| 23 | |||||
| 24 | 2 | 24µs | 2 | 84µs | # spent 46µs (9+37) within C4::Items::BEGIN@24 which was called:
# once (9µs+37µs) by C4::Reserves::BEGIN@29 at line 24 # spent 46µs making 1 call to C4::Items::BEGIN@24
# spent 37µs making 1 call to Exporter::import |
| 25 | 2 | 21µs | 2 | 15µs | # spent 12µs (10+2) within C4::Items::BEGIN@25 which was called:
# once (10µs+2µs) by C4::Reserves::BEGIN@29 at line 25 # spent 12µs making 1 call to C4::Items::BEGIN@25
# spent 2µs making 1 call to C4::Context::import |
| 26 | 2 | 25µs | 2 | 510µs | # spent 259µs (8+251) within C4::Items::BEGIN@26 which was called:
# once (8µs+251µs) by C4::Reserves::BEGIN@29 at line 26 # spent 259µs making 1 call to C4::Items::BEGIN@26
# spent 251µs making 1 call to Exporter::import |
| 27 | 2 | 32µs | 2 | 575µs | # spent 292µs (8+284) within C4::Items::BEGIN@27 which was called:
# once (8µs+284µs) by C4::Reserves::BEGIN@29 at line 27 # spent 292µs making 1 call to C4::Items::BEGIN@27
# spent 284µs making 1 call to Exporter::import |
| 28 | 2 | 22µs | 2 | 58µs | # spent 34µs (9+25) within C4::Items::BEGIN@28 which was called:
# once (9µs+25µs) by C4::Reserves::BEGIN@29 at line 28 # spent 34µs making 1 call to C4::Items::BEGIN@28
# spent 25µs making 1 call to Exporter::import |
| 29 | 2 | 22µs | 2 | 30µs | # spent 20µs (10+10) within C4::Items::BEGIN@29 which was called:
# once (10µs+10µs) by C4::Reserves::BEGIN@29 at line 29 # spent 20µs making 1 call to C4::Items::BEGIN@29
# spent 10µs making 1 call to Exporter::import |
| 30 | 2 | 23µs | 2 | 206µs | # spent 107µs (8+99) within C4::Items::BEGIN@30 which was called:
# once (8µs+99µs) by C4::Reserves::BEGIN@29 at line 30 # spent 107µs making 1 call to C4::Items::BEGIN@30
# spent 99µs making 1 call to Exporter::import |
| 31 | 2 | 24µs | 2 | 122µs | # spent 65µs (8+57) within C4::Items::BEGIN@31 which was called:
# once (8µs+57µs) by C4::Reserves::BEGIN@29 at line 31 # spent 65µs making 1 call to C4::Items::BEGIN@31
# spent 57µs making 1 call to Exporter::import |
| 32 | 2 | 30µs | 2 | 155µs | # spent 85µs (14+71) within C4::Items::BEGIN@32 which was called:
# once (14µs+71µs) by C4::Reserves::BEGIN@29 at line 32 # spent 85µs making 1 call to C4::Items::BEGIN@32
# spent 71µs making 1 call to Exporter::import |
| 33 | 2 | 724µs | 2 | 16.8ms | # spent 16.8ms (1.35+15.5) within C4::Items::BEGIN@33 which was called:
# once (1.35ms+15.5ms) by C4::Reserves::BEGIN@29 at line 33 # spent 16.8ms making 1 call to C4::Items::BEGIN@33
# spent 33µs making 1 call to Exporter::import |
| 34 | 2 | 30µs | 2 | 50µs | # spent 30µs (11+20) within C4::Items::BEGIN@34 which was called:
# once (11µs+20µs) by C4::Reserves::BEGIN@29 at line 34 # spent 30µs making 1 call to C4::Items::BEGIN@34
# spent 20µs making 1 call to Exporter::import |
| 35 | # debugging; so please don't remove this | ||||
| 36 | |||||
| 37 | 2 | 74µs | 2 | 84µs | # spent 46µs (8+38) within C4::Items::BEGIN@37 which was called:
# once (8µs+38µs) by C4::Reserves::BEGIN@29 at line 37 # spent 46µs making 1 call to C4::Items::BEGIN@37
# spent 38µs making 1 call to vars::import |
| 38 | |||||
| 39 | # spent 11µs within C4::Items::BEGIN@39 which was called:
# once (11µs+0s) by C4::Reserves::BEGIN@29 at line 91 | ||||
| 40 | 1 | 900ns | $VERSION = 3.07.00.049; | ||
| 41 | |||||
| 42 | 1 | 500ns | require Exporter; | ||
| 43 | 1 | 4µs | @ISA = qw( Exporter ); | ||
| 44 | |||||
| 45 | # function exports | ||||
| 46 | 1 | 5µs | @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 | GetItemnumbersForBiblio | ||||
| 71 | get_itemnumbers_of | ||||
| 72 | get_hostitemnumbers_of | ||||
| 73 | GetItemnumberFromBarcode | ||||
| 74 | GetBarcodeFromItemnumber | ||||
| 75 | GetHiddenItemnumbers | ||||
| 76 | DelItemCheck | ||||
| 77 | MoveItemFromBiblio | ||||
| 78 | GetLatestAcquisitions | ||||
| 79 | |||||
| 80 | CartToShelf | ||||
| 81 | ShelfToCart | ||||
| 82 | |||||
| 83 | GetAnalyticsCount | ||||
| 84 | GetItemHolds | ||||
| 85 | |||||
| 86 | SearchItems | ||||
| 87 | |||||
| 88 | PrepareItemrecordDisplay | ||||
| 89 | |||||
| 90 | ); | ||||
| 91 | 1 | 7.39ms | 1 | 11µs | } # spent 11µs making 1 call to C4::Items::BEGIN@39 |
| 92 | |||||
| 93 | =head1 NAME | ||||
| 94 | |||||
| 95 | C4::Items - item management functions | ||||
| 96 | |||||
| 97 | =head1 DESCRIPTION | ||||
| 98 | |||||
| 99 | This module contains an API for manipulating item | ||||
| 100 | records in Koha, and is used by cataloguing, circulation, | ||||
| 101 | acquisitions, and serials management. | ||||
| 102 | |||||
| 103 | A Koha item record is stored in two places: the | ||||
| 104 | items table and embedded in a MARC tag in the XML | ||||
| 105 | version of the associated bib record in C<biblioitems.marcxml>. | ||||
| 106 | This is done to allow the item information to be readily | ||||
| 107 | indexed (e.g., by Zebra), but means that each item | ||||
| 108 | modification transaction must keep the items table | ||||
| 109 | and the MARC XML in sync at all times. | ||||
| 110 | |||||
| 111 | Consequently, all code that creates, modifies, or deletes | ||||
| 112 | item records B<must> use an appropriate function from | ||||
| 113 | C<C4::Items>. If no existing function is suitable, it is | ||||
| 114 | better to add one to C<C4::Items> than to use add | ||||
| 115 | one-off SQL statements to add or modify items. | ||||
| 116 | |||||
| 117 | The items table will be considered authoritative. In other | ||||
| 118 | words, if there is ever a discrepancy between the items | ||||
| 119 | table and the MARC XML, the items table should be considered | ||||
| 120 | accurate. | ||||
| 121 | |||||
| 122 | =head1 HISTORICAL NOTE | ||||
| 123 | |||||
| 124 | Most of the functions in C<C4::Items> were originally in | ||||
| 125 | the C<C4::Biblio> module. | ||||
| 126 | |||||
| 127 | =head1 CORE EXPORTED FUNCTIONS | ||||
| 128 | |||||
| 129 | The following functions are meant for use by users | ||||
| 130 | of C<C4::Items> | ||||
| 131 | |||||
| 132 | =cut | ||||
| 133 | |||||
| 134 | =head2 GetItem | ||||
| 135 | |||||
| 136 | $item = GetItem($itemnumber,$barcode,$serial); | ||||
| 137 | |||||
| 138 | Return item information, for a given itemnumber or barcode. | ||||
| 139 | The return value is a hashref mapping item column | ||||
| 140 | names to values. If C<$serial> is true, include serial publication data. | ||||
| 141 | |||||
| 142 | =cut | ||||
| 143 | |||||
| 144 | sub GetItem { | ||||
| 145 | my ($itemnumber,$barcode, $serial) = @_; | ||||
| 146 | my $dbh = C4::Context->dbh; | ||||
| 147 | my $data; | ||||
| 148 | |||||
| 149 | if ($itemnumber) { | ||||
| 150 | my $sth = $dbh->prepare(" | ||||
| 151 | SELECT * FROM items | ||||
| 152 | WHERE itemnumber = ?"); | ||||
| 153 | $sth->execute($itemnumber); | ||||
| 154 | $data = $sth->fetchrow_hashref; | ||||
| 155 | } else { | ||||
| 156 | my $sth = $dbh->prepare(" | ||||
| 157 | SELECT * FROM items | ||||
| 158 | WHERE barcode = ?" | ||||
| 159 | ); | ||||
| 160 | $sth->execute($barcode); | ||||
| 161 | $data = $sth->fetchrow_hashref; | ||||
| 162 | } | ||||
| 163 | |||||
| 164 | return unless ( $data ); | ||||
| 165 | |||||
| 166 | if ( $serial) { | ||||
| 167 | my $ssth = $dbh->prepare("SELECT serialseq,publisheddate from serialitems left join serial on serialitems.serialid=serial.serialid where serialitems.itemnumber=?"); | ||||
| 168 | $ssth->execute($data->{'itemnumber'}) ; | ||||
| 169 | ($data->{'serialseq'} , $data->{'publisheddate'}) = $ssth->fetchrow_array(); | ||||
| 170 | } | ||||
| 171 | #if we don't have an items.itype, use biblioitems.itemtype. | ||||
| 172 | if( ! $data->{'itype'} ) { | ||||
| 173 | my $sth = $dbh->prepare("SELECT itemtype FROM biblioitems WHERE biblionumber = ?"); | ||||
| 174 | $sth->execute($data->{'biblionumber'}); | ||||
| 175 | ($data->{'itype'}) = $sth->fetchrow_array; | ||||
| 176 | } | ||||
| 177 | return $data; | ||||
| 178 | } # sub GetItem | ||||
| 179 | |||||
| 180 | =head2 CartToShelf | ||||
| 181 | |||||
| 182 | CartToShelf($itemnumber); | ||||
| 183 | |||||
| 184 | Set the current shelving location of the item record | ||||
| 185 | to its stored permanent shelving location. This is | ||||
| 186 | primarily used to indicate when an item whose current | ||||
| 187 | location is a special processing ('PROC') or shelving cart | ||||
| 188 | ('CART') location is back in the stacks. | ||||
| 189 | |||||
| 190 | =cut | ||||
| 191 | |||||
| 192 | sub CartToShelf { | ||||
| 193 | my ( $itemnumber ) = @_; | ||||
| 194 | |||||
| 195 | unless ( $itemnumber ) { | ||||
| 196 | croak "FAILED CartToShelf() - no itemnumber supplied"; | ||||
| 197 | } | ||||
| 198 | |||||
| 199 | my $item = GetItem($itemnumber); | ||||
| 200 | if ( $item->{location} eq 'CART' ) { | ||||
| 201 | $item->{location} = $item->{permanent_location}; | ||||
| 202 | ModItem($item, undef, $itemnumber); | ||||
| 203 | } | ||||
| 204 | } | ||||
| 205 | |||||
| 206 | =head2 ShelfToCart | ||||
| 207 | |||||
| 208 | ShelfToCart($itemnumber); | ||||
| 209 | |||||
| 210 | Set the current shelving location of the item | ||||
| 211 | to shelving cart ('CART'). | ||||
| 212 | |||||
| 213 | =cut | ||||
| 214 | |||||
| 215 | sub ShelfToCart { | ||||
| 216 | my ( $itemnumber ) = @_; | ||||
| 217 | |||||
| 218 | unless ( $itemnumber ) { | ||||
| 219 | croak "FAILED ShelfToCart() - no itemnumber supplied"; | ||||
| 220 | } | ||||
| 221 | |||||
| 222 | my $item = GetItem($itemnumber); | ||||
| 223 | $item->{'location'} = 'CART'; | ||||
| 224 | ModItem($item, undef, $itemnumber); | ||||
| 225 | } | ||||
| 226 | |||||
| 227 | =head2 AddItemFromMarc | ||||
| 228 | |||||
| 229 | my ($biblionumber, $biblioitemnumber, $itemnumber) | ||||
| 230 | = AddItemFromMarc($source_item_marc, $biblionumber); | ||||
| 231 | |||||
| 232 | Given a MARC::Record object containing an embedded item | ||||
| 233 | record and a biblionumber, create a new item record. | ||||
| 234 | |||||
| 235 | =cut | ||||
| 236 | |||||
| 237 | sub AddItemFromMarc { | ||||
| 238 | my ( $source_item_marc, $biblionumber ) = @_; | ||||
| 239 | my $dbh = C4::Context->dbh; | ||||
| 240 | |||||
| 241 | # parse item hash from MARC | ||||
| 242 | my $frameworkcode = GetFrameworkCode( $biblionumber ); | ||||
| 243 | my ($itemtag,$itemsubfield)=GetMarcFromKohaField("items.itemnumber",$frameworkcode); | ||||
| 244 | |||||
| 245 | my $localitemmarc=MARC::Record->new; | ||||
| 246 | $localitemmarc->append_fields($source_item_marc->field($itemtag)); | ||||
| 247 | my $item = &TransformMarcToKoha( $dbh, $localitemmarc, $frameworkcode ,'items'); | ||||
| 248 | my $unlinked_item_subfields = _get_unlinked_item_subfields($localitemmarc, $frameworkcode); | ||||
| 249 | return AddItem($item, $biblionumber, $dbh, $frameworkcode, $unlinked_item_subfields); | ||||
| 250 | } | ||||
| 251 | |||||
| 252 | =head2 AddItem | ||||
| 253 | |||||
| 254 | my ($biblionumber, $biblioitemnumber, $itemnumber) | ||||
| 255 | = AddItem($item, $biblionumber[, $dbh, $frameworkcode, $unlinked_item_subfields]); | ||||
| 256 | |||||
| 257 | Given a hash containing item column names as keys, | ||||
| 258 | create a new Koha item record. | ||||
| 259 | |||||
| 260 | The first two optional parameters (C<$dbh> and C<$frameworkcode>) | ||||
| 261 | do not need to be supplied for general use; they exist | ||||
| 262 | simply to allow them to be picked up from AddItemFromMarc. | ||||
| 263 | |||||
| 264 | The final optional parameter, C<$unlinked_item_subfields>, contains | ||||
| 265 | an arrayref containing subfields present in the original MARC | ||||
| 266 | representation of the item (e.g., from the item editor) that are | ||||
| 267 | not mapped to C<items> columns directly but should instead | ||||
| 268 | be stored in C<items.more_subfields_xml> and included in | ||||
| 269 | the biblio items tag for display and indexing. | ||||
| 270 | |||||
| 271 | =cut | ||||
| 272 | |||||
| 273 | sub AddItem { | ||||
| 274 | my $item = shift; | ||||
| 275 | my $biblionumber = shift; | ||||
| 276 | |||||
| 277 | my $dbh = @_ ? shift : C4::Context->dbh; | ||||
| 278 | my $frameworkcode = @_ ? shift : GetFrameworkCode( $biblionumber ); | ||||
| 279 | my $unlinked_item_subfields; | ||||
| 280 | if (@_) { | ||||
| 281 | $unlinked_item_subfields = shift | ||||
| 282 | }; | ||||
| 283 | |||||
| 284 | # needs old biblionumber and biblioitemnumber | ||||
| 285 | $item->{'biblionumber'} = $biblionumber; | ||||
| 286 | my $sth = $dbh->prepare("SELECT biblioitemnumber FROM biblioitems WHERE biblionumber=?"); | ||||
| 287 | $sth->execute( $item->{'biblionumber'} ); | ||||
| 288 | ($item->{'biblioitemnumber'}) = $sth->fetchrow; | ||||
| 289 | |||||
| 290 | _set_defaults_for_add($item); | ||||
| 291 | _set_derived_columns_for_add($item); | ||||
| 292 | $item->{'more_subfields_xml'} = _get_unlinked_subfields_xml($unlinked_item_subfields); | ||||
| 293 | # FIXME - checks here | ||||
| 294 | unless ( $item->{itype} ) { # default to biblioitem.itemtype if no itype | ||||
| 295 | my $itype_sth = $dbh->prepare("SELECT itemtype FROM biblioitems WHERE biblionumber = ?"); | ||||
| 296 | $itype_sth->execute( $item->{'biblionumber'} ); | ||||
| 297 | ( $item->{'itype'} ) = $itype_sth->fetchrow_array; | ||||
| 298 | } | ||||
| 299 | |||||
| 300 | my ( $itemnumber, $error ) = _koha_new_item( $item, $item->{barcode} ); | ||||
| 301 | $item->{'itemnumber'} = $itemnumber; | ||||
| 302 | |||||
| 303 | ModZebra( $item->{biblionumber}, "specialUpdate", "biblioserver" ); | ||||
| 304 | |||||
| 305 | logaction("CATALOGUING", "ADD", $itemnumber, "item") if C4::Context->preference("CataloguingLog"); | ||||
| 306 | |||||
| 307 | return ($item->{biblionumber}, $item->{biblioitemnumber}, $itemnumber); | ||||
| 308 | } | ||||
| 309 | |||||
| 310 | =head2 AddItemBatchFromMarc | ||||
| 311 | |||||
| 312 | ($itemnumber_ref, $error_ref) = AddItemBatchFromMarc($record, | ||||
| 313 | $biblionumber, $biblioitemnumber, $frameworkcode); | ||||
| 314 | |||||
| 315 | Efficiently create item records from a MARC biblio record with | ||||
| 316 | embedded item fields. This routine is suitable for batch jobs. | ||||
| 317 | |||||
| 318 | This API assumes that the bib record has already been | ||||
| 319 | saved to the C<biblio> and C<biblioitems> tables. It does | ||||
| 320 | not expect that C<biblioitems.marc> and C<biblioitems.marcxml> | ||||
| 321 | are populated, but it will do so via a call to ModBibiloMarc. | ||||
| 322 | |||||
| 323 | The goal of this API is to have a similar effect to using AddBiblio | ||||
| 324 | and AddItems in succession, but without inefficient repeated | ||||
| 325 | parsing of the MARC XML bib record. | ||||
| 326 | |||||
| 327 | This function returns an arrayref of new itemsnumbers and an arrayref of item | ||||
| 328 | errors encountered during the processing. Each entry in the errors | ||||
| 329 | list is a hashref containing the following keys: | ||||
| 330 | |||||
| 331 | =over | ||||
| 332 | |||||
| 333 | =item item_sequence | ||||
| 334 | |||||
| 335 | Sequence number of original item tag in the MARC record. | ||||
| 336 | |||||
| 337 | =item item_barcode | ||||
| 338 | |||||
| 339 | Item barcode, provide to assist in the construction of | ||||
| 340 | useful error messages. | ||||
| 341 | |||||
| 342 | =item error_code | ||||
| 343 | |||||
| 344 | Code representing the error condition. Can be 'duplicate_barcode', | ||||
| 345 | 'invalid_homebranch', or 'invalid_holdingbranch'. | ||||
| 346 | |||||
| 347 | =item error_information | ||||
| 348 | |||||
| 349 | Additional information appropriate to the error condition. | ||||
| 350 | |||||
| 351 | =back | ||||
| 352 | |||||
| 353 | =cut | ||||
| 354 | |||||
| 355 | sub AddItemBatchFromMarc { | ||||
| 356 | my ($record, $biblionumber, $biblioitemnumber, $frameworkcode) = @_; | ||||
| 357 | my $error; | ||||
| 358 | my @itemnumbers = (); | ||||
| 359 | my @errors = (); | ||||
| 360 | my $dbh = C4::Context->dbh; | ||||
| 361 | |||||
| 362 | # We modify the record, so lets work on a clone so we don't change the | ||||
| 363 | # original. | ||||
| 364 | $record = $record->clone(); | ||||
| 365 | # loop through the item tags and start creating items | ||||
| 366 | my @bad_item_fields = (); | ||||
| 367 | my ($itemtag, $itemsubfield) = &GetMarcFromKohaField("items.itemnumber",''); | ||||
| 368 | my $item_sequence_num = 0; | ||||
| 369 | ITEMFIELD: foreach my $item_field ($record->field($itemtag)) { | ||||
| 370 | $item_sequence_num++; | ||||
| 371 | # we take the item field and stick it into a new | ||||
| 372 | # MARC record -- this is required so far because (FIXME) | ||||
| 373 | # TransformMarcToKoha requires a MARC::Record, not a MARC::Field | ||||
| 374 | # and there is no TransformMarcFieldToKoha | ||||
| 375 | my $temp_item_marc = MARC::Record->new(); | ||||
| 376 | $temp_item_marc->append_fields($item_field); | ||||
| 377 | |||||
| 378 | # add biblionumber and biblioitemnumber | ||||
| 379 | my $item = TransformMarcToKoha( $dbh, $temp_item_marc, $frameworkcode, 'items' ); | ||||
| 380 | my $unlinked_item_subfields = _get_unlinked_item_subfields($temp_item_marc, $frameworkcode); | ||||
| 381 | $item->{'more_subfields_xml'} = _get_unlinked_subfields_xml($unlinked_item_subfields); | ||||
| 382 | $item->{'biblionumber'} = $biblionumber; | ||||
| 383 | $item->{'biblioitemnumber'} = $biblioitemnumber; | ||||
| 384 | |||||
| 385 | # check for duplicate barcode | ||||
| 386 | my %item_errors = CheckItemPreSave($item); | ||||
| 387 | if (%item_errors) { | ||||
| 388 | push @errors, _repack_item_errors($item_sequence_num, $item, \%item_errors); | ||||
| 389 | push @bad_item_fields, $item_field; | ||||
| 390 | next ITEMFIELD; | ||||
| 391 | } | ||||
| 392 | |||||
| 393 | _set_defaults_for_add($item); | ||||
| 394 | _set_derived_columns_for_add($item); | ||||
| 395 | my ( $itemnumber, $error ) = _koha_new_item( $item, $item->{barcode} ); | ||||
| 396 | warn $error if $error; | ||||
| 397 | push @itemnumbers, $itemnumber; # FIXME not checking error | ||||
| 398 | $item->{'itemnumber'} = $itemnumber; | ||||
| 399 | |||||
| 400 | logaction("CATALOGUING", "ADD", $itemnumber, "item") if C4::Context->preference("CataloguingLog"); | ||||
| 401 | |||||
| 402 | my $new_item_marc = _marc_from_item_hash($item, $frameworkcode, $unlinked_item_subfields); | ||||
| 403 | $item_field->replace_with($new_item_marc->field($itemtag)); | ||||
| 404 | } | ||||
| 405 | |||||
| 406 | # remove any MARC item fields for rejected items | ||||
| 407 | foreach my $item_field (@bad_item_fields) { | ||||
| 408 | $record->delete_field($item_field); | ||||
| 409 | } | ||||
| 410 | |||||
| 411 | # update the MARC biblio | ||||
| 412 | # $biblionumber = ModBiblioMarc( $record, $biblionumber, $frameworkcode ); | ||||
| 413 | |||||
| 414 | return (\@itemnumbers, \@errors); | ||||
| 415 | } | ||||
| 416 | |||||
| 417 | =head2 ModItemFromMarc | ||||
| 418 | |||||
| 419 | ModItemFromMarc($item_marc, $biblionumber, $itemnumber); | ||||
| 420 | |||||
| 421 | This function updates an item record based on a supplied | ||||
| 422 | C<MARC::Record> object containing an embedded item field. | ||||
| 423 | This API is meant for the use of C<additem.pl>; for | ||||
| 424 | other purposes, C<ModItem> should be used. | ||||
| 425 | |||||
| 426 | This function uses the hash %default_values_for_mod_from_marc, | ||||
| 427 | which contains default values for item fields to | ||||
| 428 | apply when modifying an item. This is needed beccause | ||||
| 429 | if an item field's value is cleared, TransformMarcToKoha | ||||
| 430 | does not include the column in the | ||||
| 431 | hash that's passed to ModItem, which without | ||||
| 432 | use of this hash makes it impossible to clear | ||||
| 433 | an item field's value. See bug 2466. | ||||
| 434 | |||||
| 435 | Note that only columns that can be directly | ||||
| 436 | changed from the cataloging and serials | ||||
| 437 | item editors are included in this hash. | ||||
| 438 | |||||
| 439 | Returns item record | ||||
| 440 | |||||
| 441 | =cut | ||||
| 442 | |||||
| 443 | 1 | 9µs | my %default_values_for_mod_from_marc = ( | ||
| 444 | barcode => undef, | ||||
| 445 | booksellerid => undef, | ||||
| 446 | ccode => undef, | ||||
| 447 | 'items.cn_source' => undef, | ||||
| 448 | coded_location_qualifier => undef, | ||||
| 449 | copynumber => undef, | ||||
| 450 | damaged => 0, | ||||
| 451 | # dateaccessioned => undef, | ||||
| 452 | enumchron => undef, | ||||
| 453 | holdingbranch => undef, | ||||
| 454 | homebranch => undef, | ||||
| 455 | itemcallnumber => undef, | ||||
| 456 | itemlost => 0, | ||||
| 457 | itemnotes => undef, | ||||
| 458 | itype => undef, | ||||
| 459 | location => undef, | ||||
| 460 | permanent_location => undef, | ||||
| 461 | materials => undef, | ||||
| 462 | notforloan => 0, | ||||
| 463 | paidfor => undef, | ||||
| 464 | price => undef, | ||||
| 465 | replacementprice => undef, | ||||
| 466 | replacementpricedate => undef, | ||||
| 467 | restricted => undef, | ||||
| 468 | stack => undef, | ||||
| 469 | stocknumber => undef, | ||||
| 470 | uri => undef, | ||||
| 471 | withdrawn => 0, | ||||
| 472 | ); | ||||
| 473 | |||||
| 474 | sub ModItemFromMarc { | ||||
| 475 | my $item_marc = shift; | ||||
| 476 | my $biblionumber = shift; | ||||
| 477 | my $itemnumber = shift; | ||||
| 478 | |||||
| 479 | my $dbh = C4::Context->dbh; | ||||
| 480 | my $frameworkcode = GetFrameworkCode($biblionumber); | ||||
| 481 | my ( $itemtag, $itemsubfield ) = GetMarcFromKohaField( "items.itemnumber", $frameworkcode ); | ||||
| 482 | |||||
| 483 | my $localitemmarc = MARC::Record->new; | ||||
| 484 | $localitemmarc->append_fields( $item_marc->field($itemtag) ); | ||||
| 485 | my $item = &TransformMarcToKoha( $dbh, $localitemmarc, $frameworkcode, 'items' ); | ||||
| 486 | foreach my $item_field ( keys %default_values_for_mod_from_marc ) { | ||||
| 487 | $item->{$item_field} = $default_values_for_mod_from_marc{$item_field} unless (exists $item->{$item_field}); | ||||
| 488 | } | ||||
| 489 | my $unlinked_item_subfields = _get_unlinked_item_subfields( $localitemmarc, $frameworkcode ); | ||||
| 490 | |||||
| 491 | ModItem($item, $biblionumber, $itemnumber, $dbh, $frameworkcode, $unlinked_item_subfields); | ||||
| 492 | return $item; | ||||
| 493 | } | ||||
| 494 | |||||
| 495 | =head2 ModItem | ||||
| 496 | |||||
| 497 | ModItem({ column => $newvalue }, $biblionumber, $itemnumber); | ||||
| 498 | |||||
| 499 | Change one or more columns in an item record and update | ||||
| 500 | the MARC representation of the item. | ||||
| 501 | |||||
| 502 | The first argument is a hashref mapping from item column | ||||
| 503 | names to the new values. The second and third arguments | ||||
| 504 | are the biblionumber and itemnumber, respectively. | ||||
| 505 | |||||
| 506 | The fourth, optional parameter, C<$unlinked_item_subfields>, contains | ||||
| 507 | an arrayref containing subfields present in the original MARC | ||||
| 508 | representation of the item (e.g., from the item editor) that are | ||||
| 509 | not mapped to C<items> columns directly but should instead | ||||
| 510 | be stored in C<items.more_subfields_xml> and included in | ||||
| 511 | the biblio items tag for display and indexing. | ||||
| 512 | |||||
| 513 | If one of the changed columns is used to calculate | ||||
| 514 | the derived value of a column such as C<items.cn_sort>, | ||||
| 515 | this routine will perform the necessary calculation | ||||
| 516 | and set the value. | ||||
| 517 | |||||
| 518 | =cut | ||||
| 519 | |||||
| 520 | sub ModItem { | ||||
| 521 | my $item = shift; | ||||
| 522 | my $biblionumber = shift; | ||||
| 523 | my $itemnumber = shift; | ||||
| 524 | |||||
| 525 | # if $biblionumber is undefined, get it from the current item | ||||
| 526 | unless (defined $biblionumber) { | ||||
| 527 | $biblionumber = _get_single_item_column('biblionumber', $itemnumber); | ||||
| 528 | } | ||||
| 529 | |||||
| 530 | my $dbh = @_ ? shift : C4::Context->dbh; | ||||
| 531 | my $frameworkcode = @_ ? shift : GetFrameworkCode( $biblionumber ); | ||||
| 532 | |||||
| 533 | my $unlinked_item_subfields; | ||||
| 534 | if (@_) { | ||||
| 535 | $unlinked_item_subfields = shift; | ||||
| 536 | $item->{'more_subfields_xml'} = _get_unlinked_subfields_xml($unlinked_item_subfields); | ||||
| 537 | }; | ||||
| 538 | |||||
| 539 | $item->{'itemnumber'} = $itemnumber or return; | ||||
| 540 | |||||
| 541 | $item->{onloan} = undef if $item->{itemlost}; | ||||
| 542 | |||||
| 543 | _set_derived_columns_for_mod($item); | ||||
| 544 | _do_column_fixes_for_mod($item); | ||||
| 545 | # FIXME add checks | ||||
| 546 | # duplicate barcode | ||||
| 547 | # attempt to change itemnumber | ||||
| 548 | # attempt to change biblionumber (if we want | ||||
| 549 | # an API to relink an item to a different bib, | ||||
| 550 | # it should be a separate function) | ||||
| 551 | |||||
| 552 | # update items table | ||||
| 553 | _koha_modify_item($item); | ||||
| 554 | |||||
| 555 | # request that bib be reindexed so that searching on current | ||||
| 556 | # item status is possible | ||||
| 557 | ModZebra( $biblionumber, "specialUpdate", "biblioserver" ); | ||||
| 558 | |||||
| 559 | logaction("CATALOGUING", "MODIFY", $itemnumber, "item ".Dumper($item)) if C4::Context->preference("CataloguingLog"); | ||||
| 560 | } | ||||
| 561 | |||||
| 562 | =head2 ModItemTransfer | ||||
| 563 | |||||
| 564 | ModItemTransfer($itenumber, $frombranch, $tobranch); | ||||
| 565 | |||||
| 566 | Marks an item as being transferred from one branch | ||||
| 567 | to another. | ||||
| 568 | |||||
| 569 | =cut | ||||
| 570 | |||||
| 571 | sub ModItemTransfer { | ||||
| 572 | my ( $itemnumber, $frombranch, $tobranch ) = @_; | ||||
| 573 | |||||
| 574 | my $dbh = C4::Context->dbh; | ||||
| 575 | |||||
| 576 | # Remove the 'shelving cart' location status if it is being used. | ||||
| 577 | CartToShelf( $itemnumber ) if ( C4::Context->preference("ReturnToShelvingCart") ); | ||||
| 578 | |||||
| 579 | #new entry in branchtransfers.... | ||||
| 580 | my $sth = $dbh->prepare( | ||||
| 581 | "INSERT INTO branchtransfers (itemnumber, frombranch, datesent, tobranch) | ||||
| 582 | VALUES (?, ?, NOW(), ?)"); | ||||
| 583 | $sth->execute($itemnumber, $frombranch, $tobranch); | ||||
| 584 | |||||
| 585 | ModItem({ holdingbranch => $tobranch }, undef, $itemnumber); | ||||
| 586 | ModDateLastSeen($itemnumber); | ||||
| 587 | return; | ||||
| 588 | } | ||||
| 589 | |||||
| 590 | =head2 ModDateLastSeen | ||||
| 591 | |||||
| 592 | ModDateLastSeen($itemnum); | ||||
| 593 | |||||
| 594 | Mark item as seen. Is called when an item is issued, returned or manually marked during inventory/stocktaking. | ||||
| 595 | C<$itemnum> is the item number | ||||
| 596 | |||||
| 597 | =cut | ||||
| 598 | |||||
| 599 | sub ModDateLastSeen { | ||||
| 600 | my ($itemnumber) = @_; | ||||
| 601 | |||||
| 602 | my $today = C4::Dates->new(); | ||||
| 603 | ModItem({ itemlost => 0, datelastseen => $today->output("iso") }, undef, $itemnumber); | ||||
| 604 | } | ||||
| 605 | |||||
| 606 | =head2 DelItem | ||||
| 607 | |||||
| 608 | DelItem($dbh, $biblionumber, $itemnumber); | ||||
| 609 | |||||
| 610 | Exported function (core API) for deleting an item record in Koha. | ||||
| 611 | |||||
| 612 | =cut | ||||
| 613 | |||||
| 614 | sub DelItem { | ||||
| 615 | my ( $dbh, $biblionumber, $itemnumber ) = @_; | ||||
| 616 | |||||
| 617 | # FIXME check the item has no current issues | ||||
| 618 | |||||
| 619 | _koha_delete_item( $dbh, $itemnumber ); | ||||
| 620 | |||||
| 621 | # get the MARC record | ||||
| 622 | my $record = GetMarcBiblio($biblionumber); | ||||
| 623 | ModZebra( $biblionumber, "specialUpdate", "biblioserver" ); | ||||
| 624 | |||||
| 625 | #search item field code | ||||
| 626 | logaction("CATALOGUING", "DELETE", $itemnumber, "item") if C4::Context->preference("CataloguingLog"); | ||||
| 627 | } | ||||
| 628 | |||||
| 629 | =head2 CheckItemPreSave | ||||
| 630 | |||||
| 631 | my $item_ref = TransformMarcToKoha($marc, 'items'); | ||||
| 632 | # do stuff | ||||
| 633 | my %errors = CheckItemPreSave($item_ref); | ||||
| 634 | if (exists $errors{'duplicate_barcode'}) { | ||||
| 635 | print "item has duplicate barcode: ", $errors{'duplicate_barcode'}, "\n"; | ||||
| 636 | } elsif (exists $errors{'invalid_homebranch'}) { | ||||
| 637 | print "item has invalid home branch: ", $errors{'invalid_homebranch'}, "\n"; | ||||
| 638 | } elsif (exists $errors{'invalid_holdingbranch'}) { | ||||
| 639 | print "item has invalid holding branch: ", $errors{'invalid_holdingbranch'}, "\n"; | ||||
| 640 | } else { | ||||
| 641 | print "item is OK"; | ||||
| 642 | } | ||||
| 643 | |||||
| 644 | Given a hashref containing item fields, determine if it can be | ||||
| 645 | inserted or updated in the database. Specifically, checks for | ||||
| 646 | database integrity issues, and returns a hash containing any | ||||
| 647 | of the following keys, if applicable. | ||||
| 648 | |||||
| 649 | =over 2 | ||||
| 650 | |||||
| 651 | =item duplicate_barcode | ||||
| 652 | |||||
| 653 | Barcode, if it duplicates one already found in the database. | ||||
| 654 | |||||
| 655 | =item invalid_homebranch | ||||
| 656 | |||||
| 657 | Home branch, if not defined in branches table. | ||||
| 658 | |||||
| 659 | =item invalid_holdingbranch | ||||
| 660 | |||||
| 661 | Holding branch, if not defined in branches table. | ||||
| 662 | |||||
| 663 | =back | ||||
| 664 | |||||
| 665 | This function does NOT implement any policy-related checks, | ||||
| 666 | e.g., whether current operator is allowed to save an | ||||
| 667 | item that has a given branch code. | ||||
| 668 | |||||
| 669 | =cut | ||||
| 670 | |||||
| 671 | sub CheckItemPreSave { | ||||
| 672 | my $item_ref = shift; | ||||
| 673 | require C4::Branch; | ||||
| 674 | |||||
| 675 | my %errors = (); | ||||
| 676 | |||||
| 677 | # check for duplicate barcode | ||||
| 678 | if (exists $item_ref->{'barcode'} and defined $item_ref->{'barcode'}) { | ||||
| 679 | my $existing_itemnumber = GetItemnumberFromBarcode($item_ref->{'barcode'}); | ||||
| 680 | if ($existing_itemnumber) { | ||||
| 681 | if (!exists $item_ref->{'itemnumber'} # new item | ||||
| 682 | or $item_ref->{'itemnumber'} != $existing_itemnumber) { # existing item | ||||
| 683 | $errors{'duplicate_barcode'} = $item_ref->{'barcode'}; | ||||
| 684 | } | ||||
| 685 | } | ||||
| 686 | } | ||||
| 687 | |||||
| 688 | # check for valid home branch | ||||
| 689 | if (exists $item_ref->{'homebranch'} and defined $item_ref->{'homebranch'}) { | ||||
| 690 | my $branch_name = C4::Branch::GetBranchName($item_ref->{'homebranch'}); | ||||
| 691 | unless (defined $branch_name) { | ||||
| 692 | # relies on fact that branches.branchname is a non-NULL column, | ||||
| 693 | # so GetBranchName returns undef only if branch does not exist | ||||
| 694 | $errors{'invalid_homebranch'} = $item_ref->{'homebranch'}; | ||||
| 695 | } | ||||
| 696 | } | ||||
| 697 | |||||
| 698 | # check for valid holding branch | ||||
| 699 | if (exists $item_ref->{'holdingbranch'} and defined $item_ref->{'holdingbranch'}) { | ||||
| 700 | my $branch_name = C4::Branch::GetBranchName($item_ref->{'holdingbranch'}); | ||||
| 701 | unless (defined $branch_name) { | ||||
| 702 | # relies on fact that branches.branchname is a non-NULL column, | ||||
| 703 | # so GetBranchName returns undef only if branch does not exist | ||||
| 704 | $errors{'invalid_holdingbranch'} = $item_ref->{'holdingbranch'}; | ||||
| 705 | } | ||||
| 706 | } | ||||
| 707 | |||||
| 708 | return %errors; | ||||
| 709 | |||||
| 710 | } | ||||
| 711 | |||||
| 712 | =head1 EXPORTED SPECIAL ACCESSOR FUNCTIONS | ||||
| 713 | |||||
| 714 | The following functions provide various ways of | ||||
| 715 | getting an item record, a set of item records, or | ||||
| 716 | lists of authorized values for certain item fields. | ||||
| 717 | |||||
| 718 | Some of the functions in this group are candidates | ||||
| 719 | for refactoring -- for example, some of the code | ||||
| 720 | in C<GetItemsByBiblioitemnumber> and C<GetItemsInfo> | ||||
| 721 | has copy-and-paste work. | ||||
| 722 | |||||
| 723 | =cut | ||||
| 724 | |||||
| 725 | =head2 GetItemStatus | ||||
| 726 | |||||
| 727 | $itemstatushash = GetItemStatus($fwkcode); | ||||
| 728 | |||||
| 729 | Returns a list of valid values for the | ||||
| 730 | C<items.notforloan> field. | ||||
| 731 | |||||
| 732 | NOTE: does B<not> return an individual item's | ||||
| 733 | status. | ||||
| 734 | |||||
| 735 | Can be MARC dependant. | ||||
| 736 | fwkcode is optional. | ||||
| 737 | But basically could be can be loan or not | ||||
| 738 | Create a status selector with the following code | ||||
| 739 | |||||
| 740 | =head3 in PERL SCRIPT | ||||
| 741 | |||||
| 742 | my $itemstatushash = getitemstatus; | ||||
| 743 | my @itemstatusloop; | ||||
| 744 | foreach my $thisstatus (keys %$itemstatushash) { | ||||
| 745 | my %row =(value => $thisstatus, | ||||
| 746 | statusname => $itemstatushash->{$thisstatus}->{'statusname'}, | ||||
| 747 | ); | ||||
| 748 | push @itemstatusloop, \%row; | ||||
| 749 | } | ||||
| 750 | $template->param(statusloop=>\@itemstatusloop); | ||||
| 751 | |||||
| 752 | =head3 in TEMPLATE | ||||
| 753 | |||||
| 754 | <select name="statusloop" id="statusloop"> | ||||
| 755 | <option value="">Default</option> | ||||
| 756 | [% FOREACH statusloo IN statusloop %] | ||||
| 757 | [% IF ( statusloo.selected ) %] | ||||
| 758 | <option value="[% statusloo.value %]" selected="selected">[% statusloo.statusname %]</option> | ||||
| 759 | [% ELSE %] | ||||
| 760 | <option value="[% statusloo.value %]">[% statusloo.statusname %]</option> | ||||
| 761 | [% END %] | ||||
| 762 | [% END %] | ||||
| 763 | </select> | ||||
| 764 | |||||
| 765 | =cut | ||||
| 766 | |||||
| 767 | sub GetItemStatus { | ||||
| 768 | |||||
| 769 | # returns a reference to a hash of references to status... | ||||
| 770 | my ($fwk) = @_; | ||||
| 771 | my %itemstatus; | ||||
| 772 | my $dbh = C4::Context->dbh; | ||||
| 773 | my $sth; | ||||
| 774 | $fwk = '' unless ($fwk); | ||||
| 775 | my ( $tag, $subfield ) = | ||||
| 776 | GetMarcFromKohaField( "items.notforloan", $fwk ); | ||||
| 777 | if ( $tag and $subfield ) { | ||||
| 778 | my $sth = | ||||
| 779 | $dbh->prepare( | ||||
| 780 | "SELECT authorised_value | ||||
| 781 | FROM marc_subfield_structure | ||||
| 782 | WHERE tagfield=? | ||||
| 783 | AND tagsubfield=? | ||||
| 784 | AND frameworkcode=? | ||||
| 785 | " | ||||
| 786 | ); | ||||
| 787 | $sth->execute( $tag, $subfield, $fwk ); | ||||
| 788 | if ( my ($authorisedvaluecat) = $sth->fetchrow ) { | ||||
| 789 | my $authvalsth = | ||||
| 790 | $dbh->prepare( | ||||
| 791 | "SELECT authorised_value,lib | ||||
| 792 | FROM authorised_values | ||||
| 793 | WHERE category=? | ||||
| 794 | ORDER BY lib | ||||
| 795 | " | ||||
| 796 | ); | ||||
| 797 | $authvalsth->execute($authorisedvaluecat); | ||||
| 798 | while ( my ( $authorisedvalue, $lib ) = $authvalsth->fetchrow ) { | ||||
| 799 | $itemstatus{$authorisedvalue} = $lib; | ||||
| 800 | } | ||||
| 801 | return \%itemstatus; | ||||
| 802 | exit 1; | ||||
| 803 | } | ||||
| 804 | else { | ||||
| 805 | |||||
| 806 | #No authvalue list | ||||
| 807 | # build default | ||||
| 808 | } | ||||
| 809 | } | ||||
| 810 | |||||
| 811 | #No authvalue list | ||||
| 812 | #build default | ||||
| 813 | $itemstatus{"1"} = "Not For Loan"; | ||||
| 814 | return \%itemstatus; | ||||
| 815 | } | ||||
| 816 | |||||
| 817 | =head2 GetItemLocation | ||||
| 818 | |||||
| 819 | $itemlochash = GetItemLocation($fwk); | ||||
| 820 | |||||
| 821 | Returns a list of valid values for the | ||||
| 822 | C<items.location> field. | ||||
| 823 | |||||
| 824 | NOTE: does B<not> return an individual item's | ||||
| 825 | location. | ||||
| 826 | |||||
| 827 | where fwk stands for an optional framework code. | ||||
| 828 | Create a location selector with the following code | ||||
| 829 | |||||
| 830 | =head3 in PERL SCRIPT | ||||
| 831 | |||||
| 832 | my $itemlochash = getitemlocation; | ||||
| 833 | my @itemlocloop; | ||||
| 834 | foreach my $thisloc (keys %$itemlochash) { | ||||
| 835 | my $selected = 1 if $thisbranch eq $branch; | ||||
| 836 | my %row =(locval => $thisloc, | ||||
| 837 | selected => $selected, | ||||
| 838 | locname => $itemlochash->{$thisloc}, | ||||
| 839 | ); | ||||
| 840 | push @itemlocloop, \%row; | ||||
| 841 | } | ||||
| 842 | $template->param(itemlocationloop => \@itemlocloop); | ||||
| 843 | |||||
| 844 | =head3 in TEMPLATE | ||||
| 845 | |||||
| 846 | <select name="location"> | ||||
| 847 | <option value="">Default</option> | ||||
| 848 | <!-- TMPL_LOOP name="itemlocationloop" --> | ||||
| 849 | <option value="<!-- TMPL_VAR name="locval" -->" <!-- TMPL_IF name="selected" -->selected<!-- /TMPL_IF -->><!-- TMPL_VAR name="locname" --></option> | ||||
| 850 | <!-- /TMPL_LOOP --> | ||||
| 851 | </select> | ||||
| 852 | |||||
| 853 | =cut | ||||
| 854 | |||||
| 855 | sub GetItemLocation { | ||||
| 856 | |||||
| 857 | # returns a reference to a hash of references to location... | ||||
| 858 | my ($fwk) = @_; | ||||
| 859 | my %itemlocation; | ||||
| 860 | my $dbh = C4::Context->dbh; | ||||
| 861 | my $sth; | ||||
| 862 | $fwk = '' unless ($fwk); | ||||
| 863 | my ( $tag, $subfield ) = | ||||
| 864 | GetMarcFromKohaField( "items.location", $fwk ); | ||||
| 865 | if ( $tag and $subfield ) { | ||||
| 866 | my $sth = | ||||
| 867 | $dbh->prepare( | ||||
| 868 | "SELECT authorised_value | ||||
| 869 | FROM marc_subfield_structure | ||||
| 870 | WHERE tagfield=? | ||||
| 871 | AND tagsubfield=? | ||||
| 872 | AND frameworkcode=?" | ||||
| 873 | ); | ||||
| 874 | $sth->execute( $tag, $subfield, $fwk ); | ||||
| 875 | if ( my ($authorisedvaluecat) = $sth->fetchrow ) { | ||||
| 876 | my $authvalsth = | ||||
| 877 | $dbh->prepare( | ||||
| 878 | "SELECT authorised_value,lib | ||||
| 879 | FROM authorised_values | ||||
| 880 | WHERE category=? | ||||
| 881 | ORDER BY lib" | ||||
| 882 | ); | ||||
| 883 | $authvalsth->execute($authorisedvaluecat); | ||||
| 884 | while ( my ( $authorisedvalue, $lib ) = $authvalsth->fetchrow ) { | ||||
| 885 | $itemlocation{$authorisedvalue} = $lib; | ||||
| 886 | } | ||||
| 887 | return \%itemlocation; | ||||
| 888 | exit 1; | ||||
| 889 | } | ||||
| 890 | else { | ||||
| 891 | |||||
| 892 | #No authvalue list | ||||
| 893 | # build default | ||||
| 894 | } | ||||
| 895 | } | ||||
| 896 | |||||
| 897 | #No authvalue list | ||||
| 898 | #build default | ||||
| 899 | $itemlocation{"1"} = "Not For Loan"; | ||||
| 900 | return \%itemlocation; | ||||
| 901 | } | ||||
| 902 | |||||
| 903 | =head2 GetLostItems | ||||
| 904 | |||||
| 905 | $items = GetLostItems( $where, $orderby ); | ||||
| 906 | |||||
| 907 | This function gets a list of lost items. | ||||
| 908 | |||||
| 909 | =over 2 | ||||
| 910 | |||||
| 911 | =item input: | ||||
| 912 | |||||
| 913 | C<$where> is a hashref. it containts a field of the items table as key | ||||
| 914 | and the value to match as value. For example: | ||||
| 915 | |||||
| 916 | { barcode => 'abc123', | ||||
| 917 | homebranch => 'CPL', } | ||||
| 918 | |||||
| 919 | C<$orderby> is a field of the items table by which the resultset | ||||
| 920 | should be orderd. | ||||
| 921 | |||||
| 922 | =item return: | ||||
| 923 | |||||
| 924 | C<$items> is a reference to an array full of hashrefs with columns | ||||
| 925 | from the "items" table as keys. | ||||
| 926 | |||||
| 927 | =item usage in the perl script: | ||||
| 928 | |||||
| 929 | my $where = { barcode => '0001548' }; | ||||
| 930 | my $items = GetLostItems( $where, "homebranch" ); | ||||
| 931 | $template->param( itemsloop => $items ); | ||||
| 932 | |||||
| 933 | =back | ||||
| 934 | |||||
| 935 | =cut | ||||
| 936 | |||||
| 937 | sub GetLostItems { | ||||
| 938 | # Getting input args. | ||||
| 939 | my $where = shift; | ||||
| 940 | my $orderby = shift; | ||||
| 941 | my $dbh = C4::Context->dbh; | ||||
| 942 | |||||
| 943 | my $query = " | ||||
| 944 | SELECT title, author, lib, itemlost, authorised_value, barcode, datelastseen, price, replacementprice, homebranch, | ||||
| 945 | itype, itemtype, holdingbranch, location, itemnotes, items.biblionumber as biblionumber | ||||
| 946 | FROM items | ||||
| 947 | LEFT JOIN biblio ON (items.biblionumber = biblio.biblionumber) | ||||
| 948 | LEFT JOIN biblioitems ON (items.biblionumber = biblioitems.biblionumber) | ||||
| 949 | LEFT JOIN authorised_values ON (items.itemlost = authorised_values.authorised_value) | ||||
| 950 | WHERE | ||||
| 951 | authorised_values.category = 'LOST' | ||||
| 952 | AND itemlost IS NOT NULL | ||||
| 953 | AND itemlost <> 0 | ||||
| 954 | "; | ||||
| 955 | my @query_parameters; | ||||
| 956 | foreach my $key (keys %$where) { | ||||
| 957 | $query .= " AND $key LIKE ?"; | ||||
| 958 | push @query_parameters, "%$where->{$key}%"; | ||||
| 959 | } | ||||
| 960 | my @ordervalues = qw/title author homebranch itype barcode price replacementprice lib datelastseen location/; | ||||
| 961 | |||||
| 962 | if ( defined $orderby && grep($orderby, @ordervalues)) { | ||||
| 963 | $query .= ' ORDER BY '.$orderby; | ||||
| 964 | } | ||||
| 965 | |||||
| 966 | my $sth = $dbh->prepare($query); | ||||
| 967 | $sth->execute( @query_parameters ); | ||||
| 968 | my $items = []; | ||||
| 969 | while ( my $row = $sth->fetchrow_hashref ){ | ||||
| 970 | push @$items, $row; | ||||
| 971 | } | ||||
| 972 | return $items; | ||||
| 973 | } | ||||
| 974 | |||||
| 975 | =head2 GetItemsForInventory | ||||
| 976 | |||||
| 977 | ($itemlist, $iTotalRecords) = GetItemsForInventory($minlocation, $maxlocation, $location, $itemtype, $ignoreissued, $datelastseen, $branchcode, $offset, $size, $statushash); | ||||
| 978 | |||||
| 979 | Retrieve a list of title/authors/barcode/callnumber, for biblio inventory. | ||||
| 980 | |||||
| 981 | The sub returns a reference to a list of hashes, each containing | ||||
| 982 | itemnumber, author, title, barcode, item callnumber, and date last | ||||
| 983 | seen. It is ordered by callnumber then title. | ||||
| 984 | |||||
| 985 | The required minlocation & maxlocation parameters are used to specify a range of item callnumbers | ||||
| 986 | the datelastseen can be used to specify that you want to see items not seen since a past date only. | ||||
| 987 | offset & size can be used to retrieve only a part of the whole listing (defaut behaviour) | ||||
| 988 | $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. | ||||
| 989 | |||||
| 990 | $iTotalRecords is the number of rows that would have been returned without the $offset, $size limit clause | ||||
| 991 | |||||
| 992 | =cut | ||||
| 993 | |||||
| 994 | sub GetItemsForInventory { | ||||
| 995 | my ( $minlocation, $maxlocation,$location, $itemtype, $ignoreissued, $datelastseen, $branchcode, $branch, $offset, $size, $statushash ) = @_; | ||||
| 996 | my $dbh = C4::Context->dbh; | ||||
| 997 | my ( @bind_params, @where_strings ); | ||||
| 998 | |||||
| 999 | my $select_columns = q{ | ||||
| 1000 | SELECT items.itemnumber, barcode, itemcallnumber, title, author, biblio.biblionumber, biblio.frameworkcode, datelastseen, homebranch, location, notforloan, damaged, itemlost, stocknumber | ||||
| 1001 | }; | ||||
| 1002 | my $select_count = q{SELECT COUNT(*)}; | ||||
| 1003 | my $query = q{ | ||||
| 1004 | FROM items | ||||
| 1005 | LEFT JOIN biblio ON items.biblionumber = biblio.biblionumber | ||||
| 1006 | LEFT JOIN biblioitems on items.biblionumber = biblioitems.biblionumber | ||||
| 1007 | }; | ||||
| 1008 | if ($statushash){ | ||||
| 1009 | for my $authvfield (keys %$statushash){ | ||||
| 1010 | if ( scalar @{$statushash->{$authvfield}} > 0 ){ | ||||
| 1011 | my $joinedvals = join ',', @{$statushash->{$authvfield}}; | ||||
| 1012 | push @where_strings, "$authvfield in (" . $joinedvals . ")"; | ||||
| 1013 | } | ||||
| 1014 | } | ||||
| 1015 | } | ||||
| 1016 | |||||
| 1017 | if ($minlocation) { | ||||
| 1018 | push @where_strings, 'itemcallnumber >= ?'; | ||||
| 1019 | push @bind_params, $minlocation; | ||||
| 1020 | } | ||||
| 1021 | |||||
| 1022 | if ($maxlocation) { | ||||
| 1023 | push @where_strings, 'itemcallnumber <= ?'; | ||||
| 1024 | push @bind_params, $maxlocation; | ||||
| 1025 | } | ||||
| 1026 | |||||
| 1027 | if ($datelastseen) { | ||||
| 1028 | $datelastseen = format_date_in_iso($datelastseen); | ||||
| 1029 | push @where_strings, '(datelastseen < ? OR datelastseen IS NULL)'; | ||||
| 1030 | push @bind_params, $datelastseen; | ||||
| 1031 | } | ||||
| 1032 | |||||
| 1033 | if ( $location ) { | ||||
| 1034 | push @where_strings, 'items.location = ?'; | ||||
| 1035 | push @bind_params, $location; | ||||
| 1036 | } | ||||
| 1037 | |||||
| 1038 | if ( $branchcode ) { | ||||
| 1039 | if($branch eq "homebranch"){ | ||||
| 1040 | push @where_strings, 'items.homebranch = ?'; | ||||
| 1041 | }else{ | ||||
| 1042 | push @where_strings, 'items.holdingbranch = ?'; | ||||
| 1043 | } | ||||
| 1044 | push @bind_params, $branchcode; | ||||
| 1045 | } | ||||
| 1046 | |||||
| 1047 | if ( $itemtype ) { | ||||
| 1048 | push @where_strings, 'biblioitems.itemtype = ?'; | ||||
| 1049 | push @bind_params, $itemtype; | ||||
| 1050 | } | ||||
| 1051 | |||||
| 1052 | if ( $ignoreissued) { | ||||
| 1053 | $query .= "LEFT JOIN issues ON items.itemnumber = issues.itemnumber "; | ||||
| 1054 | push @where_strings, 'issues.date_due IS NULL'; | ||||
| 1055 | } | ||||
| 1056 | |||||
| 1057 | if ( @where_strings ) { | ||||
| 1058 | $query .= 'WHERE '; | ||||
| 1059 | $query .= join ' AND ', @where_strings; | ||||
| 1060 | } | ||||
| 1061 | $query .= ' ORDER BY items.cn_sort, itemcallnumber, title'; | ||||
| 1062 | my $count_query = $select_count . $query; | ||||
| 1063 | $query .= " LIMIT $offset, $size" if ($offset and $size); | ||||
| 1064 | $query = $select_columns . $query; | ||||
| 1065 | my $sth = $dbh->prepare($query); | ||||
| 1066 | $sth->execute( @bind_params ); | ||||
| 1067 | |||||
| 1068 | my @results = (); | ||||
| 1069 | my $tmpresults = $sth->fetchall_arrayref({}); | ||||
| 1070 | $sth = $dbh->prepare( $count_query ); | ||||
| 1071 | $sth->execute( @bind_params ); | ||||
| 1072 | my ($iTotalRecords) = $sth->fetchrow_array(); | ||||
| 1073 | |||||
| 1074 | foreach my $row (@$tmpresults) { | ||||
| 1075 | |||||
| 1076 | # Auth values | ||||
| 1077 | foreach (keys %$row) { | ||||
| 1078 | # If the koha field is mapped to a marc field | ||||
| 1079 | my ($f, $sf) = GetMarcFromKohaField("items.$_", $row->{'frameworkcode'}); | ||||
| 1080 | if ($f and $sf) { | ||||
| 1081 | # We replace the code with it's description | ||||
| 1082 | my $authvals = C4::Koha::GetKohaAuthorisedValuesFromField($f, $sf, $row->{'frameworkcode'}); | ||||
| 1083 | $row->{$_} = $authvals->{$row->{$_}} if defined $authvals->{$row->{$_}}; | ||||
| 1084 | } | ||||
| 1085 | } | ||||
| 1086 | push @results, $row; | ||||
| 1087 | } | ||||
| 1088 | |||||
| 1089 | return (\@results, $iTotalRecords); | ||||
| 1090 | } | ||||
| 1091 | |||||
| 1092 | =head2 GetItemsCount | ||||
| 1093 | |||||
| 1094 | $count = &GetItemsCount( $biblionumber); | ||||
| 1095 | |||||
| 1096 | This function return count of item with $biblionumber | ||||
| 1097 | |||||
| 1098 | =cut | ||||
| 1099 | |||||
| 1100 | sub GetItemsCount { | ||||
| 1101 | my ( $biblionumber ) = @_; | ||||
| 1102 | my $dbh = C4::Context->dbh; | ||||
| 1103 | my $query = "SELECT count(*) | ||||
| 1104 | FROM items | ||||
| 1105 | WHERE biblionumber=?"; | ||||
| 1106 | my $sth = $dbh->prepare($query); | ||||
| 1107 | $sth->execute($biblionumber); | ||||
| 1108 | my $count = $sth->fetchrow; | ||||
| 1109 | return ($count); | ||||
| 1110 | } | ||||
| 1111 | |||||
| 1112 | =head2 GetItemInfosOf | ||||
| 1113 | |||||
| 1114 | GetItemInfosOf(@itemnumbers); | ||||
| 1115 | |||||
| 1116 | =cut | ||||
| 1117 | |||||
| 1118 | sub GetItemInfosOf { | ||||
| 1119 | my @itemnumbers = @_; | ||||
| 1120 | |||||
| 1121 | my $query = ' | ||||
| 1122 | SELECT * | ||||
| 1123 | FROM items | ||||
| 1124 | WHERE itemnumber IN (' . join( ',', @itemnumbers ) . ') | ||||
| 1125 | '; | ||||
| 1126 | return get_infos_of( $query, 'itemnumber' ); | ||||
| 1127 | } | ||||
| 1128 | |||||
| 1129 | =head2 GetItemsByBiblioitemnumber | ||||
| 1130 | |||||
| 1131 | GetItemsByBiblioitemnumber($biblioitemnumber); | ||||
| 1132 | |||||
| 1133 | Returns an arrayref of hashrefs suitable for use in a TMPL_LOOP | ||||
| 1134 | Called by C<C4::XISBN> | ||||
| 1135 | |||||
| 1136 | =cut | ||||
| 1137 | |||||
| 1138 | sub GetItemsByBiblioitemnumber { | ||||
| 1139 | my ( $bibitem ) = @_; | ||||
| 1140 | my $dbh = C4::Context->dbh; | ||||
| 1141 | my $sth = $dbh->prepare("SELECT * FROM items WHERE items.biblioitemnumber = ?") || die $dbh->errstr; | ||||
| 1142 | # Get all items attached to a biblioitem | ||||
| 1143 | my $i = 0; | ||||
| 1144 | my @results; | ||||
| 1145 | $sth->execute($bibitem) || die $sth->errstr; | ||||
| 1146 | while ( my $data = $sth->fetchrow_hashref ) { | ||||
| 1147 | # Foreach item, get circulation information | ||||
| 1148 | my $sth2 = $dbh->prepare( "SELECT * FROM issues,borrowers | ||||
| 1149 | WHERE itemnumber = ? | ||||
| 1150 | AND issues.borrowernumber = borrowers.borrowernumber" | ||||
| 1151 | ); | ||||
| 1152 | $sth2->execute( $data->{'itemnumber'} ); | ||||
| 1153 | if ( my $data2 = $sth2->fetchrow_hashref ) { | ||||
| 1154 | # if item is out, set the due date and who it is out too | ||||
| 1155 | $data->{'date_due'} = $data2->{'date_due'}; | ||||
| 1156 | $data->{'cardnumber'} = $data2->{'cardnumber'}; | ||||
| 1157 | $data->{'borrowernumber'} = $data2->{'borrowernumber'}; | ||||
| 1158 | } | ||||
| 1159 | else { | ||||
| 1160 | # set date_due to blank, so in the template we check itemlost, and withdrawn | ||||
| 1161 | $data->{'date_due'} = ''; | ||||
| 1162 | } # else | ||||
| 1163 | # Find the last 3 people who borrowed this item. | ||||
| 1164 | my $query2 = "SELECT * FROM old_issues, borrowers WHERE itemnumber = ? | ||||
| 1165 | AND old_issues.borrowernumber = borrowers.borrowernumber | ||||
| 1166 | ORDER BY returndate desc,timestamp desc LIMIT 3"; | ||||
| 1167 | $sth2 = $dbh->prepare($query2) || die $dbh->errstr; | ||||
| 1168 | $sth2->execute( $data->{'itemnumber'} ) || die $sth2->errstr; | ||||
| 1169 | my $i2 = 0; | ||||
| 1170 | while ( my $data2 = $sth2->fetchrow_hashref ) { | ||||
| 1171 | $data->{"timestamp$i2"} = $data2->{'timestamp'}; | ||||
| 1172 | $data->{"card$i2"} = $data2->{'cardnumber'}; | ||||
| 1173 | $data->{"borrower$i2"} = $data2->{'borrowernumber'}; | ||||
| 1174 | $i2++; | ||||
| 1175 | } | ||||
| 1176 | push(@results,$data); | ||||
| 1177 | } | ||||
| 1178 | return (\@results); | ||||
| 1179 | } | ||||
| 1180 | |||||
| 1181 | =head2 GetItemsInfo | ||||
| 1182 | |||||
| 1183 | @results = GetItemsInfo($biblionumber); | ||||
| 1184 | |||||
| 1185 | Returns information about items with the given biblionumber. | ||||
| 1186 | |||||
| 1187 | C<GetItemsInfo> returns a list of references-to-hash. Each element | ||||
| 1188 | contains a number of keys. Most of them are attributes from the | ||||
| 1189 | C<biblio>, C<biblioitems>, C<items>, and C<itemtypes> tables in the | ||||
| 1190 | Koha database. Other keys include: | ||||
| 1191 | |||||
| 1192 | =over 2 | ||||
| 1193 | |||||
| 1194 | =item C<$data-E<gt>{branchname}> | ||||
| 1195 | |||||
| 1196 | The name (not the code) of the branch to which the book belongs. | ||||
| 1197 | |||||
| 1198 | =item C<$data-E<gt>{datelastseen}> | ||||
| 1199 | |||||
| 1200 | This is simply C<items.datelastseen>, except that while the date is | ||||
| 1201 | stored in YYYY-MM-DD format in the database, here it is converted to | ||||
| 1202 | DD/MM/YYYY format. A NULL date is returned as C<//>. | ||||
| 1203 | |||||
| 1204 | =item C<$data-E<gt>{datedue}> | ||||
| 1205 | |||||
| 1206 | =item C<$data-E<gt>{class}> | ||||
| 1207 | |||||
| 1208 | This is the concatenation of C<biblioitems.classification>, the book's | ||||
| 1209 | Dewey code, and C<biblioitems.subclass>. | ||||
| 1210 | |||||
| 1211 | =item C<$data-E<gt>{ocount}> | ||||
| 1212 | |||||
| 1213 | I think this is the number of copies of the book available. | ||||
| 1214 | |||||
| 1215 | =item C<$data-E<gt>{order}> | ||||
| 1216 | |||||
| 1217 | If this is set, it is set to C<One Order>. | ||||
| 1218 | |||||
| 1219 | =back | ||||
| 1220 | |||||
| 1221 | =cut | ||||
| 1222 | |||||
| 1223 | sub GetItemsInfo { | ||||
| 1224 | my ( $biblionumber ) = @_; | ||||
| 1225 | my $dbh = C4::Context->dbh; | ||||
| 1226 | # note biblioitems.* must be avoided to prevent large marc and marcxml fields from killing performance. | ||||
| 1227 | my $query = " | ||||
| 1228 | SELECT items.*, | ||||
| 1229 | biblio.*, | ||||
| 1230 | biblioitems.volume, | ||||
| 1231 | biblioitems.number, | ||||
| 1232 | biblioitems.itemtype, | ||||
| 1233 | biblioitems.isbn, | ||||
| 1234 | biblioitems.issn, | ||||
| 1235 | biblioitems.publicationyear, | ||||
| 1236 | biblioitems.publishercode, | ||||
| 1237 | biblioitems.volumedate, | ||||
| 1238 | biblioitems.volumedesc, | ||||
| 1239 | biblioitems.lccn, | ||||
| 1240 | biblioitems.url, | ||||
| 1241 | items.notforloan as itemnotforloan, | ||||
| 1242 | itemtypes.description, | ||||
| 1243 | itemtypes.notforloan as notforloan_per_itemtype, | ||||
| 1244 | holding.branchurl, | ||||
| 1245 | holding.branchname, | ||||
| 1246 | holding.opac_info as branch_opac_info | ||||
| 1247 | FROM items | ||||
| 1248 | LEFT JOIN branches AS holding ON items.holdingbranch = holding.branchcode | ||||
| 1249 | LEFT JOIN branches AS home ON items.homebranch=home.branchcode | ||||
| 1250 | LEFT JOIN biblio ON biblio.biblionumber = items.biblionumber | ||||
| 1251 | LEFT JOIN biblioitems ON biblioitems.biblioitemnumber = items.biblioitemnumber | ||||
| 1252 | LEFT JOIN itemtypes ON itemtypes.itemtype = " | ||||
| 1253 | . (C4::Context->preference('item-level_itypes') ? 'items.itype' : 'biblioitems.itemtype'); | ||||
| 1254 | $query .= " WHERE items.biblionumber = ? ORDER BY home.branchname, items.enumchron, LPAD( items.copynumber, 8, '0' ), items.dateaccessioned DESC" ; | ||||
| 1255 | my $sth = $dbh->prepare($query); | ||||
| 1256 | $sth->execute($biblionumber); | ||||
| 1257 | my $i = 0; | ||||
| 1258 | my @results; | ||||
| 1259 | my $serial; | ||||
| 1260 | |||||
| 1261 | my $isth = $dbh->prepare( | ||||
| 1262 | "SELECT issues.*,borrowers.cardnumber,borrowers.surname,borrowers.firstname,borrowers.branchcode as bcode | ||||
| 1263 | FROM issues LEFT JOIN borrowers ON issues.borrowernumber=borrowers.borrowernumber | ||||
| 1264 | WHERE itemnumber = ?" | ||||
| 1265 | ); | ||||
| 1266 | my $ssth = $dbh->prepare("SELECT serialseq,publisheddate from serialitems left join serial on serialitems.serialid=serial.serialid where serialitems.itemnumber=? "); | ||||
| 1267 | while ( my $data = $sth->fetchrow_hashref ) { | ||||
| 1268 | my $datedue = ''; | ||||
| 1269 | $isth->execute( $data->{'itemnumber'} ); | ||||
| 1270 | if ( my $idata = $isth->fetchrow_hashref ) { | ||||
| 1271 | $data->{borrowernumber} = $idata->{borrowernumber}; | ||||
| 1272 | $data->{cardnumber} = $idata->{cardnumber}; | ||||
| 1273 | $data->{surname} = $idata->{surname}; | ||||
| 1274 | $data->{firstname} = $idata->{firstname}; | ||||
| 1275 | $data->{lastreneweddate} = $idata->{lastreneweddate}; | ||||
| 1276 | $datedue = $idata->{'date_due'}; | ||||
| 1277 | if (C4::Context->preference("IndependentBranches")){ | ||||
| 1278 | my $userenv = C4::Context->userenv; | ||||
| 1279 | unless ( C4::Context->IsSuperLibrarian() ) { | ||||
| 1280 | $data->{'NOTSAMEBRANCH'} = 1 if ($idata->{'bcode'} ne $userenv->{branch}); | ||||
| 1281 | } | ||||
| 1282 | } | ||||
| 1283 | } | ||||
| 1284 | if ( $data->{'serial'}) { | ||||
| 1285 | $ssth->execute($data->{'itemnumber'}) ; | ||||
| 1286 | ($data->{'serialseq'} , $data->{'publisheddate'}) = $ssth->fetchrow_array(); | ||||
| 1287 | $serial = 1; | ||||
| 1288 | } | ||||
| 1289 | #get branch information..... | ||||
| 1290 | my $bsth = $dbh->prepare( | ||||
| 1291 | "SELECT * FROM branches WHERE branchcode = ? | ||||
| 1292 | " | ||||
| 1293 | ); | ||||
| 1294 | $bsth->execute( $data->{'holdingbranch'} ); | ||||
| 1295 | if ( my $bdata = $bsth->fetchrow_hashref ) { | ||||
| 1296 | $data->{'branchname'} = $bdata->{'branchname'}; | ||||
| 1297 | } | ||||
| 1298 | $data->{'datedue'} = $datedue; | ||||
| 1299 | |||||
| 1300 | # get notforloan complete status if applicable | ||||
| 1301 | if ( my $code = C4::Koha::GetAuthValCode( 'items.notforloan', $data->{frameworkcode} ) ) { | ||||
| 1302 | $data->{notforloanvalue} = C4::Koha::GetKohaAuthorisedValueLib( $code, $data->{itemnotforloan} ); | ||||
| 1303 | $data->{notforloanvalueopac} = C4::Koha::GetKohaAuthorisedValueLib( $code, $data->{itemnotforloan}, 1 ); | ||||
| 1304 | } | ||||
| 1305 | |||||
| 1306 | # get restricted status and description if applicable | ||||
| 1307 | if ( my $code = C4::Koha::GetAuthValCode( 'items.restricted', $data->{frameworkcode} ) ) { | ||||
| 1308 | $data->{restrictedopac} = C4::Koha::GetKohaAuthorisedValueLib( $code, $data->{restricted}, 1 ); | ||||
| 1309 | $data->{restricted} = C4::Koha::GetKohaAuthorisedValueLib( $code, $data->{restricted} ); | ||||
| 1310 | } | ||||
| 1311 | |||||
| 1312 | # my stack procedures | ||||
| 1313 | if ( my $code = C4::Koha::GetAuthValCode( 'items.stack', $data->{frameworkcode} ) ) { | ||||
| 1314 | $data->{stack} = C4::Koha::GetKohaAuthorisedValueLib( $code, $data->{stack} ); | ||||
| 1315 | } | ||||
| 1316 | # Find the last 3 people who borrowed this item. | ||||
| 1317 | my $sth2 = $dbh->prepare("SELECT * FROM old_issues,borrowers | ||||
| 1318 | WHERE itemnumber = ? | ||||
| 1319 | AND old_issues.borrowernumber = borrowers.borrowernumber | ||||
| 1320 | ORDER BY returndate DESC | ||||
| 1321 | LIMIT 3"); | ||||
| 1322 | $sth2->execute($data->{'itemnumber'}); | ||||
| 1323 | my $ii = 0; | ||||
| 1324 | while (my $data2 = $sth2->fetchrow_hashref()) { | ||||
| 1325 | $data->{"timestamp$ii"} = $data2->{'timestamp'} if $data2->{'timestamp'}; | ||||
| 1326 | $data->{"card$ii"} = $data2->{'cardnumber'} if $data2->{'cardnumber'}; | ||||
| 1327 | $data->{"borrower$ii"} = $data2->{'borrowernumber'} if $data2->{'borrowernumber'}; | ||||
| 1328 | $ii++; | ||||
| 1329 | } | ||||
| 1330 | |||||
| 1331 | $results[$i] = $data; | ||||
| 1332 | $i++; | ||||
| 1333 | } | ||||
| 1334 | if($serial) { | ||||
| 1335 | return( sort { ($b->{'publisheddate'} || $b->{'enumchron'}) cmp ($a->{'publisheddate'} || $a->{'enumchron'}) } @results ); | ||||
| 1336 | } else { | ||||
| 1337 | return (@results); | ||||
| 1338 | } | ||||
| 1339 | } | ||||
| 1340 | |||||
| 1341 | =head2 GetItemsLocationInfo | ||||
| 1342 | |||||
| 1343 | my @itemlocinfo = GetItemsLocationInfo($biblionumber); | ||||
| 1344 | |||||
| 1345 | Returns the branch names, shelving location and itemcallnumber for each item attached to the biblio in question | ||||
| 1346 | |||||
| 1347 | C<GetItemsInfo> returns a list of references-to-hash. Data returned: | ||||
| 1348 | |||||
| 1349 | =over 2 | ||||
| 1350 | |||||
| 1351 | =item C<$data-E<gt>{homebranch}> | ||||
| 1352 | |||||
| 1353 | Branch Name of the item's homebranch | ||||
| 1354 | |||||
| 1355 | =item C<$data-E<gt>{holdingbranch}> | ||||
| 1356 | |||||
| 1357 | Branch Name of the item's holdingbranch | ||||
| 1358 | |||||
| 1359 | =item C<$data-E<gt>{location}> | ||||
| 1360 | |||||
| 1361 | Item's shelving location code | ||||
| 1362 | |||||
| 1363 | =item C<$data-E<gt>{location_intranet}> | ||||
| 1364 | |||||
| 1365 | The intranet description for the Shelving Location as set in authorised_values 'LOC' | ||||
| 1366 | |||||
| 1367 | =item C<$data-E<gt>{location_opac}> | ||||
| 1368 | |||||
| 1369 | The OPAC description for the Shelving Location as set in authorised_values 'LOC'. Falls back to intranet description if no OPAC | ||||
| 1370 | description is set. | ||||
| 1371 | |||||
| 1372 | =item C<$data-E<gt>{itemcallnumber}> | ||||
| 1373 | |||||
| 1374 | Item's itemcallnumber | ||||
| 1375 | |||||
| 1376 | =item C<$data-E<gt>{cn_sort}> | ||||
| 1377 | |||||
| 1378 | Item's call number normalized for sorting | ||||
| 1379 | |||||
| 1380 | =back | ||||
| 1381 | |||||
| 1382 | =cut | ||||
| 1383 | |||||
| 1384 | sub GetItemsLocationInfo { | ||||
| 1385 | my $biblionumber = shift; | ||||
| 1386 | my @results; | ||||
| 1387 | |||||
| 1388 | my $dbh = C4::Context->dbh; | ||||
| 1389 | my $query = "SELECT a.branchname as homebranch, b.branchname as holdingbranch, | ||||
| 1390 | location, itemcallnumber, cn_sort | ||||
| 1391 | FROM items, branches as a, branches as b | ||||
| 1392 | WHERE homebranch = a.branchcode AND holdingbranch = b.branchcode | ||||
| 1393 | AND biblionumber = ? | ||||
| 1394 | ORDER BY cn_sort ASC"; | ||||
| 1395 | my $sth = $dbh->prepare($query); | ||||
| 1396 | $sth->execute($biblionumber); | ||||
| 1397 | |||||
| 1398 | while ( my $data = $sth->fetchrow_hashref ) { | ||||
| 1399 | $data->{location_intranet} = GetKohaAuthorisedValueLib('LOC', $data->{location}); | ||||
| 1400 | $data->{location_opac}= GetKohaAuthorisedValueLib('LOC', $data->{location}, 1); | ||||
| 1401 | push @results, $data; | ||||
| 1402 | } | ||||
| 1403 | return @results; | ||||
| 1404 | } | ||||
| 1405 | |||||
| 1406 | =head2 GetHostItemsInfo | ||||
| 1407 | |||||
| 1408 | $hostiteminfo = GetHostItemsInfo($hostfield); | ||||
| 1409 | Returns the iteminfo for items linked to records via a host field | ||||
| 1410 | |||||
| 1411 | =cut | ||||
| 1412 | |||||
| 1413 | sub GetHostItemsInfo { | ||||
| 1414 | my ($record) = @_; | ||||
| 1415 | my @returnitemsInfo; | ||||
| 1416 | unless ($record){ | ||||
| 1417 | return; | ||||
| 1418 | } | ||||
| 1419 | if (C4::Context->preference('marcflavour') eq 'MARC21' || | ||||
| 1420 | C4::Context->preference('marcflavour') eq 'NORMARC'){ | ||||
| 1421 | foreach my $hostfield ( $record->field('773') ) { | ||||
| 1422 | my $hostbiblionumber = $hostfield->subfield("0"); | ||||
| 1423 | my $linkeditemnumber = $hostfield->subfield("9"); | ||||
| 1424 | my @hostitemInfos = GetItemsInfo($hostbiblionumber); | ||||
| 1425 | foreach my $hostitemInfo (@hostitemInfos){ | ||||
| 1426 | if ($hostitemInfo->{itemnumber} eq $linkeditemnumber){ | ||||
| 1427 | push (@returnitemsInfo,$hostitemInfo); | ||||
| 1428 | last; | ||||
| 1429 | } | ||||
| 1430 | } | ||||
| 1431 | } | ||||
| 1432 | } elsif ( C4::Context->preference('marcflavour') eq 'UNIMARC'){ | ||||
| 1433 | foreach my $hostfield ( $record->field('461') ) { | ||||
| 1434 | my $hostbiblionumber = $hostfield->subfield("0"); | ||||
| 1435 | my $linkeditemnumber = $hostfield->subfield("9"); | ||||
| 1436 | my @hostitemInfos = GetItemsInfo($hostbiblionumber); | ||||
| 1437 | foreach my $hostitemInfo (@hostitemInfos){ | ||||
| 1438 | if ($hostitemInfo->{itemnumber} eq $linkeditemnumber){ | ||||
| 1439 | push (@returnitemsInfo,$hostitemInfo); | ||||
| 1440 | last; | ||||
| 1441 | } | ||||
| 1442 | } | ||||
| 1443 | } | ||||
| 1444 | } | ||||
| 1445 | return @returnitemsInfo; | ||||
| 1446 | } | ||||
| 1447 | |||||
| 1448 | |||||
| 1449 | =head2 GetLastAcquisitions | ||||
| 1450 | |||||
| 1451 | my $lastacq = GetLastAcquisitions({'branches' => ('branch1','branch2'), | ||||
| 1452 | 'itemtypes' => ('BK','BD')}, 10); | ||||
| 1453 | |||||
| 1454 | =cut | ||||
| 1455 | |||||
| 1456 | sub GetLastAcquisitions { | ||||
| 1457 | my ($data,$max) = @_; | ||||
| 1458 | |||||
| 1459 | my $itemtype = C4::Context->preference('item-level_itypes') ? 'itype' : 'itemtype'; | ||||
| 1460 | |||||
| 1461 | my $number_of_branches = @{$data->{branches}}; | ||||
| 1462 | my $number_of_itemtypes = @{$data->{itemtypes}}; | ||||
| 1463 | |||||
| 1464 | |||||
| 1465 | my @where = ('WHERE 1 '); | ||||
| 1466 | $number_of_branches and push @where | ||||
| 1467 | , 'AND holdingbranch IN (' | ||||
| 1468 | , join(',', ('?') x $number_of_branches ) | ||||
| 1469 | , ')' | ||||
| 1470 | ; | ||||
| 1471 | |||||
| 1472 | $number_of_itemtypes and push @where | ||||
| 1473 | , "AND $itemtype IN (" | ||||
| 1474 | , join(',', ('?') x $number_of_itemtypes ) | ||||
| 1475 | , ')' | ||||
| 1476 | ; | ||||
| 1477 | |||||
| 1478 | my $query = "SELECT biblio.biblionumber as biblionumber, title, dateaccessioned | ||||
| 1479 | FROM items RIGHT JOIN biblio ON (items.biblionumber=biblio.biblionumber) | ||||
| 1480 | RIGHT JOIN biblioitems ON (items.biblioitemnumber=biblioitems.biblioitemnumber) | ||||
| 1481 | @where | ||||
| 1482 | GROUP BY biblio.biblionumber | ||||
| 1483 | ORDER BY dateaccessioned DESC LIMIT $max"; | ||||
| 1484 | |||||
| 1485 | my $dbh = C4::Context->dbh; | ||||
| 1486 | my $sth = $dbh->prepare($query); | ||||
| 1487 | |||||
| 1488 | $sth->execute((@{$data->{branches}}, @{$data->{itemtypes}})); | ||||
| 1489 | |||||
| 1490 | my @results; | ||||
| 1491 | while( my $row = $sth->fetchrow_hashref){ | ||||
| 1492 | push @results, {date => $row->{dateaccessioned} | ||||
| 1493 | , biblionumber => $row->{biblionumber} | ||||
| 1494 | , title => $row->{title}}; | ||||
| 1495 | } | ||||
| 1496 | |||||
| 1497 | return @results; | ||||
| 1498 | } | ||||
| 1499 | |||||
| 1500 | =head2 GetItemnumbersForBiblio | ||||
| 1501 | |||||
| 1502 | my $itemnumbers = GetItemnumbersForBiblio($biblionumber); | ||||
| 1503 | |||||
| 1504 | Given a single biblionumber, return an arrayref of all the corresponding itemnumbers | ||||
| 1505 | |||||
| 1506 | =cut | ||||
| 1507 | |||||
| 1508 | sub GetItemnumbersForBiblio { | ||||
| 1509 | my $biblionumber = shift; | ||||
| 1510 | my @items; | ||||
| 1511 | my $dbh = C4::Context->dbh; | ||||
| 1512 | my $sth = $dbh->prepare("SELECT itemnumber FROM items WHERE biblionumber = ?"); | ||||
| 1513 | $sth->execute($biblionumber); | ||||
| 1514 | while (my $result = $sth->fetchrow_hashref) { | ||||
| 1515 | push @items, $result->{'itemnumber'}; | ||||
| 1516 | } | ||||
| 1517 | return \@items; | ||||
| 1518 | } | ||||
| 1519 | |||||
| 1520 | =head2 get_itemnumbers_of | ||||
| 1521 | |||||
| 1522 | my @itemnumbers_of = get_itemnumbers_of(@biblionumbers); | ||||
| 1523 | |||||
| 1524 | Given a list of biblionumbers, return the list of corresponding itemnumbers | ||||
| 1525 | for each biblionumber. | ||||
| 1526 | |||||
| 1527 | Return a reference on a hash where keys are biblionumbers and values are | ||||
| 1528 | references on array of itemnumbers. | ||||
| 1529 | |||||
| 1530 | =cut | ||||
| 1531 | |||||
| 1532 | sub get_itemnumbers_of { | ||||
| 1533 | my @biblionumbers = @_; | ||||
| 1534 | |||||
| 1535 | my $dbh = C4::Context->dbh; | ||||
| 1536 | |||||
| 1537 | my $query = ' | ||||
| 1538 | SELECT itemnumber, | ||||
| 1539 | biblionumber | ||||
| 1540 | FROM items | ||||
| 1541 | WHERE biblionumber IN (?' . ( ',?' x scalar @biblionumbers - 1 ) . ') | ||||
| 1542 | '; | ||||
| 1543 | my $sth = $dbh->prepare($query); | ||||
| 1544 | $sth->execute(@biblionumbers); | ||||
| 1545 | |||||
| 1546 | my %itemnumbers_of; | ||||
| 1547 | |||||
| 1548 | while ( my ( $itemnumber, $biblionumber ) = $sth->fetchrow_array ) { | ||||
| 1549 | push @{ $itemnumbers_of{$biblionumber} }, $itemnumber; | ||||
| 1550 | } | ||||
| 1551 | |||||
| 1552 | return \%itemnumbers_of; | ||||
| 1553 | } | ||||
| 1554 | |||||
| 1555 | =head2 get_hostitemnumbers_of | ||||
| 1556 | |||||
| 1557 | my @itemnumbers_of = get_hostitemnumbers_of($biblionumber); | ||||
| 1558 | |||||
| 1559 | Given a biblionumber, return the list of corresponding itemnumbers that are linked to it via host fields | ||||
| 1560 | |||||
| 1561 | Return a reference on a hash where key is a biblionumber and values are | ||||
| 1562 | references on array of itemnumbers. | ||||
| 1563 | |||||
| 1564 | =cut | ||||
| 1565 | |||||
| 1566 | |||||
| 1567 | sub get_hostitemnumbers_of { | ||||
| 1568 | my ($biblionumber) = @_; | ||||
| 1569 | my $marcrecord = GetMarcBiblio($biblionumber); | ||||
| 1570 | my (@returnhostitemnumbers,$tag, $biblio_s, $item_s); | ||||
| 1571 | |||||
| 1572 | my $marcflavor = C4::Context->preference('marcflavour'); | ||||
| 1573 | if ($marcflavor eq 'MARC21' || $marcflavor eq 'NORMARC') { | ||||
| 1574 | $tag='773'; | ||||
| 1575 | $biblio_s='0'; | ||||
| 1576 | $item_s='9'; | ||||
| 1577 | } elsif ($marcflavor eq 'UNIMARC') { | ||||
| 1578 | $tag='461'; | ||||
| 1579 | $biblio_s='0'; | ||||
| 1580 | $item_s='9'; | ||||
| 1581 | } | ||||
| 1582 | |||||
| 1583 | foreach my $hostfield ( $marcrecord->field($tag) ) { | ||||
| 1584 | my $hostbiblionumber = $hostfield->subfield($biblio_s); | ||||
| 1585 | my $linkeditemnumber = $hostfield->subfield($item_s); | ||||
| 1586 | my @itemnumbers; | ||||
| 1587 | if (my $itemnumbers = get_itemnumbers_of($hostbiblionumber)->{$hostbiblionumber}) | ||||
| 1588 | { | ||||
| 1589 | @itemnumbers = @$itemnumbers; | ||||
| 1590 | } | ||||
| 1591 | foreach my $itemnumber (@itemnumbers){ | ||||
| 1592 | if ($itemnumber eq $linkeditemnumber){ | ||||
| 1593 | push (@returnhostitemnumbers,$itemnumber); | ||||
| 1594 | last; | ||||
| 1595 | } | ||||
| 1596 | } | ||||
| 1597 | } | ||||
| 1598 | return @returnhostitemnumbers; | ||||
| 1599 | } | ||||
| 1600 | |||||
| 1601 | |||||
| 1602 | =head2 GetItemnumberFromBarcode | ||||
| 1603 | |||||
| 1604 | $result = GetItemnumberFromBarcode($barcode); | ||||
| 1605 | |||||
| 1606 | =cut | ||||
| 1607 | |||||
| 1608 | sub GetItemnumberFromBarcode { | ||||
| 1609 | my ($barcode) = @_; | ||||
| 1610 | my $dbh = C4::Context->dbh; | ||||
| 1611 | |||||
| 1612 | my $rq = | ||||
| 1613 | $dbh->prepare("SELECT itemnumber FROM items WHERE items.barcode=?"); | ||||
| 1614 | $rq->execute($barcode); | ||||
| 1615 | my ($result) = $rq->fetchrow; | ||||
| 1616 | return ($result); | ||||
| 1617 | } | ||||
| 1618 | |||||
| 1619 | =head2 GetBarcodeFromItemnumber | ||||
| 1620 | |||||
| 1621 | $result = GetBarcodeFromItemnumber($itemnumber); | ||||
| 1622 | |||||
| 1623 | =cut | ||||
| 1624 | |||||
| 1625 | sub GetBarcodeFromItemnumber { | ||||
| 1626 | my ($itemnumber) = @_; | ||||
| 1627 | my $dbh = C4::Context->dbh; | ||||
| 1628 | |||||
| 1629 | my $rq = | ||||
| 1630 | $dbh->prepare("SELECT barcode FROM items WHERE items.itemnumber=?"); | ||||
| 1631 | $rq->execute($itemnumber); | ||||
| 1632 | my ($result) = $rq->fetchrow; | ||||
| 1633 | return ($result); | ||||
| 1634 | } | ||||
| 1635 | |||||
| 1636 | =head2 GetHiddenItemnumbers | ||||
| 1637 | |||||
| 1638 | my @itemnumbers_to_hide = GetHiddenItemnumbers(@items); | ||||
| 1639 | |||||
| 1640 | Given a list of items it checks which should be hidden from the OPAC given | ||||
| 1641 | the current configuration. Returns a list of itemnumbers corresponding to | ||||
| 1642 | those that should be hidden. | ||||
| 1643 | |||||
| 1644 | =cut | ||||
| 1645 | |||||
| 1646 | sub GetHiddenItemnumbers { | ||||
| 1647 | my (@items) = @_; | ||||
| 1648 | my @resultitems; | ||||
| 1649 | |||||
| 1650 | my $yaml = C4::Context->preference('OpacHiddenItems'); | ||||
| 1651 | return () if (! $yaml =~ /\S/ ); | ||||
| 1652 | $yaml = "$yaml\n\n"; # YAML is anal on ending \n. Surplus does not hurt | ||||
| 1653 | my $hidingrules; | ||||
| 1654 | eval { | ||||
| 1655 | $hidingrules = YAML::Load($yaml); | ||||
| 1656 | }; | ||||
| 1657 | if ($@) { | ||||
| 1658 | warn "Unable to parse OpacHiddenItems syspref : $@"; | ||||
| 1659 | return (); | ||||
| 1660 | } | ||||
| 1661 | my $dbh = C4::Context->dbh; | ||||
| 1662 | |||||
| 1663 | # For each item | ||||
| 1664 | foreach my $item (@items) { | ||||
| 1665 | |||||
| 1666 | # We check each rule | ||||
| 1667 | foreach my $field (keys %$hidingrules) { | ||||
| 1668 | my $val; | ||||
| 1669 | if (exists $item->{$field}) { | ||||
| 1670 | $val = $item->{$field}; | ||||
| 1671 | } | ||||
| 1672 | else { | ||||
| 1673 | my $query = "SELECT $field from items where itemnumber = ?"; | ||||
| 1674 | $val = $dbh->selectrow_array($query, undef, $item->{'itemnumber'}); | ||||
| 1675 | } | ||||
| 1676 | $val = '' unless defined $val; | ||||
| 1677 | |||||
| 1678 | # If the results matches the values in the yaml file | ||||
| 1679 | if (any { $val eq $_ } @{$hidingrules->{$field}}) { | ||||
| 1680 | |||||
| 1681 | # We add the itemnumber to the list | ||||
| 1682 | push @resultitems, $item->{'itemnumber'}; | ||||
| 1683 | |||||
| 1684 | # If at least one rule matched for an item, no need to test the others | ||||
| 1685 | last; | ||||
| 1686 | } | ||||
| 1687 | } | ||||
| 1688 | } | ||||
| 1689 | return @resultitems; | ||||
| 1690 | } | ||||
| 1691 | |||||
| 1692 | =head3 get_item_authorised_values | ||||
| 1693 | |||||
| 1694 | find the types and values for all authorised values assigned to this item. | ||||
| 1695 | |||||
| 1696 | parameters: itemnumber | ||||
| 1697 | |||||
| 1698 | returns: a hashref malling the authorised value to the value set for this itemnumber | ||||
| 1699 | |||||
| 1700 | $authorised_values = { | ||||
| 1701 | 'CCODE' => undef, | ||||
| 1702 | 'DAMAGED' => '0', | ||||
| 1703 | 'LOC' => '3', | ||||
| 1704 | 'LOST' => '0' | ||||
| 1705 | 'NOT_LOAN' => '0', | ||||
| 1706 | 'RESTRICTED' => undef, | ||||
| 1707 | 'STACK' => undef, | ||||
| 1708 | 'WITHDRAWN' => '0', | ||||
| 1709 | 'branches' => 'CPL', | ||||
| 1710 | 'cn_source' => undef, | ||||
| 1711 | 'itemtypes' => 'SER', | ||||
| 1712 | }; | ||||
| 1713 | |||||
| 1714 | Notes: see C4::Biblio::get_biblio_authorised_values for a similar method at the biblio level. | ||||
| 1715 | |||||
| 1716 | =cut | ||||
| 1717 | |||||
| 1718 | sub get_item_authorised_values { | ||||
| 1719 | my $itemnumber = shift; | ||||
| 1720 | |||||
| 1721 | # assume that these entries in the authorised_value table are item level. | ||||
| 1722 | my $query = q(SELECT distinct authorised_value, kohafield | ||||
| 1723 | FROM marc_subfield_structure | ||||
| 1724 | WHERE kohafield like 'item%' | ||||
| 1725 | AND authorised_value != '' ); | ||||
| 1726 | |||||
| 1727 | my $itemlevel_authorised_values = C4::Context->dbh->selectall_hashref( $query, 'authorised_value' ); | ||||
| 1728 | my $iteminfo = GetItem( $itemnumber ); | ||||
| 1729 | # warn( Data::Dumper->Dump( [ $itemlevel_authorised_values ], [ 'itemlevel_authorised_values' ] ) ); | ||||
| 1730 | my $return; | ||||
| 1731 | foreach my $this_authorised_value ( keys %$itemlevel_authorised_values ) { | ||||
| 1732 | my $field = $itemlevel_authorised_values->{ $this_authorised_value }->{'kohafield'}; | ||||
| 1733 | $field =~ s/^items\.//; | ||||
| 1734 | if ( exists $iteminfo->{ $field } ) { | ||||
| 1735 | $return->{ $this_authorised_value } = $iteminfo->{ $field }; | ||||
| 1736 | } | ||||
| 1737 | } | ||||
| 1738 | # warn( Data::Dumper->Dump( [ $return ], [ 'return' ] ) ); | ||||
| 1739 | return $return; | ||||
| 1740 | } | ||||
| 1741 | |||||
| 1742 | =head3 get_authorised_value_images | ||||
| 1743 | |||||
| 1744 | find a list of icons that are appropriate for display based on the | ||||
| 1745 | authorised values for a biblio. | ||||
| 1746 | |||||
| 1747 | parameters: listref of authorised values, such as comes from | ||||
| 1748 | get_item_authorised_values or | ||||
| 1749 | from C4::Biblio::get_biblio_authorised_values | ||||
| 1750 | |||||
| 1751 | returns: listref of hashrefs for each image. Each hashref looks like this: | ||||
| 1752 | |||||
| 1753 | { imageurl => '/intranet-tmpl/prog/img/itemtypeimg/npl/WEB.gif', | ||||
| 1754 | label => '', | ||||
| 1755 | category => '', | ||||
| 1756 | value => '', } | ||||
| 1757 | |||||
| 1758 | Notes: Currently, I put on the full path to the images on the staff | ||||
| 1759 | side. This should either be configurable or not done at all. Since I | ||||
| 1760 | have to deal with 'intranet' or 'opac' in | ||||
| 1761 | get_biblio_authorised_values, perhaps I should be passing it in. | ||||
| 1762 | |||||
| 1763 | =cut | ||||
| 1764 | |||||
| 1765 | sub get_authorised_value_images { | ||||
| 1766 | my $authorised_values = shift; | ||||
| 1767 | |||||
| 1768 | my @imagelist; | ||||
| 1769 | |||||
| 1770 | my $authorised_value_list = GetAuthorisedValues(); | ||||
| 1771 | # warn ( Data::Dumper->Dump( [ $authorised_value_list ], [ 'authorised_value_list' ] ) ); | ||||
| 1772 | foreach my $this_authorised_value ( @$authorised_value_list ) { | ||||
| 1773 | if ( exists $authorised_values->{ $this_authorised_value->{'category'} } | ||||
| 1774 | && $authorised_values->{ $this_authorised_value->{'category'} } eq $this_authorised_value->{'authorised_value'} ) { | ||||
| 1775 | # warn ( Data::Dumper->Dump( [ $this_authorised_value ], [ 'this_authorised_value' ] ) ); | ||||
| 1776 | if ( defined $this_authorised_value->{'imageurl'} ) { | ||||
| 1777 | push @imagelist, { imageurl => C4::Koha::getitemtypeimagelocation( 'intranet', $this_authorised_value->{'imageurl'} ), | ||||
| 1778 | label => $this_authorised_value->{'lib'}, | ||||
| 1779 | category => $this_authorised_value->{'category'}, | ||||
| 1780 | value => $this_authorised_value->{'authorised_value'}, }; | ||||
| 1781 | } | ||||
| 1782 | } | ||||
| 1783 | } | ||||
| 1784 | |||||
| 1785 | # warn ( Data::Dumper->Dump( [ \@imagelist ], [ 'imagelist' ] ) ); | ||||
| 1786 | return \@imagelist; | ||||
| 1787 | |||||
| 1788 | } | ||||
| 1789 | |||||
| 1790 | =head1 LIMITED USE FUNCTIONS | ||||
| 1791 | |||||
| 1792 | The following functions, while part of the public API, | ||||
| 1793 | are not exported. This is generally because they are | ||||
| 1794 | meant to be used by only one script for a specific | ||||
| 1795 | purpose, and should not be used in any other context | ||||
| 1796 | without careful thought. | ||||
| 1797 | |||||
| 1798 | =cut | ||||
| 1799 | |||||
| 1800 | =head2 GetMarcItem | ||||
| 1801 | |||||
| 1802 | my $item_marc = GetMarcItem($biblionumber, $itemnumber); | ||||
| 1803 | |||||
| 1804 | Returns MARC::Record of the item passed in parameter. | ||||
| 1805 | This function is meant for use only in C<cataloguing/additem.pl>, | ||||
| 1806 | where it is needed to support that script's MARC-like | ||||
| 1807 | editor. | ||||
| 1808 | |||||
| 1809 | =cut | ||||
| 1810 | |||||
| 1811 | sub GetMarcItem { | ||||
| 1812 | my ( $biblionumber, $itemnumber ) = @_; | ||||
| 1813 | |||||
| 1814 | # GetMarcItem has been revised so that it does the following: | ||||
| 1815 | # 1. Gets the item information from the items table. | ||||
| 1816 | # 2. Converts it to a MARC field for storage in the bib record. | ||||
| 1817 | # | ||||
| 1818 | # The previous behavior was: | ||||
| 1819 | # 1. Get the bib record. | ||||
| 1820 | # 2. Return the MARC tag corresponding to the item record. | ||||
| 1821 | # | ||||
| 1822 | # The difference is that one treats the items row as authoritative, | ||||
| 1823 | # while the other treats the MARC representation as authoritative | ||||
| 1824 | # under certain circumstances. | ||||
| 1825 | |||||
| 1826 | my $itemrecord = GetItem($itemnumber); | ||||
| 1827 | |||||
| 1828 | # Tack on 'items.' prefix to column names so that TransformKohaToMarc will work. | ||||
| 1829 | # Also, don't emit a subfield if the underlying field is blank. | ||||
| 1830 | |||||
| 1831 | |||||
| 1832 | return Item2Marc($itemrecord,$biblionumber); | ||||
| 1833 | |||||
| 1834 | } | ||||
| 1835 | sub Item2Marc { | ||||
| 1836 | my ($itemrecord,$biblionumber)=@_; | ||||
| 1837 | my $mungeditem = { | ||||
| 1838 | map { | ||||
| 1839 | defined($itemrecord->{$_}) && $itemrecord->{$_} ne '' ? ("items.$_" => $itemrecord->{$_}) : () | ||||
| 1840 | } keys %{ $itemrecord } | ||||
| 1841 | }; | ||||
| 1842 | my $itemmarc = TransformKohaToMarc($mungeditem); | ||||
| 1843 | my ( $itemtag, $itemsubfield ) = GetMarcFromKohaField("items.itemnumber",GetFrameworkCode($biblionumber)||''); | ||||
| 1844 | |||||
| 1845 | my $unlinked_item_subfields = _parse_unlinked_item_subfields_from_xml($mungeditem->{'items.more_subfields_xml'}); | ||||
| 1846 | if (defined $unlinked_item_subfields and $#$unlinked_item_subfields > -1) { | ||||
| 1847 | foreach my $field ($itemmarc->field($itemtag)){ | ||||
| 1848 | $field->add_subfields(@$unlinked_item_subfields); | ||||
| 1849 | } | ||||
| 1850 | } | ||||
| 1851 | return $itemmarc; | ||||
| 1852 | } | ||||
| 1853 | |||||
| 1854 | =head1 PRIVATE FUNCTIONS AND VARIABLES | ||||
| 1855 | |||||
| 1856 | The following functions are not meant to be called | ||||
| 1857 | directly, but are documented in order to explain | ||||
| 1858 | the inner workings of C<C4::Items>. | ||||
| 1859 | |||||
| 1860 | =cut | ||||
| 1861 | |||||
| 1862 | =head2 %derived_columns | ||||
| 1863 | |||||
| 1864 | This hash keeps track of item columns that | ||||
| 1865 | are strictly derived from other columns in | ||||
| 1866 | the item record and are not meant to be set | ||||
| 1867 | independently. | ||||
| 1868 | |||||
| 1869 | Each key in the hash should be the name of a | ||||
| 1870 | column (as named by TransformMarcToKoha). Each | ||||
| 1871 | value should be hashref whose keys are the | ||||
| 1872 | columns on which the derived column depends. The | ||||
| 1873 | hashref should also contain a 'BUILDER' key | ||||
| 1874 | that is a reference to a sub that calculates | ||||
| 1875 | the derived value. | ||||
| 1876 | |||||
| 1877 | =cut | ||||
| 1878 | |||||
| 1879 | 1 | 2µs | my %derived_columns = ( | ||
| 1880 | 'items.cn_sort' => { | ||||
| 1881 | 'itemcallnumber' => 1, | ||||
| 1882 | 'items.cn_source' => 1, | ||||
| 1883 | 'BUILDER' => \&_calc_items_cn_sort, | ||||
| 1884 | } | ||||
| 1885 | ); | ||||
| 1886 | |||||
| 1887 | =head2 _set_derived_columns_for_add | ||||
| 1888 | |||||
| 1889 | _set_derived_column_for_add($item); | ||||
| 1890 | |||||
| 1891 | Given an item hash representing a new item to be added, | ||||
| 1892 | calculate any derived columns. Currently the only | ||||
| 1893 | such column is C<items.cn_sort>. | ||||
| 1894 | |||||
| 1895 | =cut | ||||
| 1896 | |||||
| 1897 | sub _set_derived_columns_for_add { | ||||
| 1898 | my $item = shift; | ||||
| 1899 | |||||
| 1900 | foreach my $column (keys %derived_columns) { | ||||
| 1901 | my $builder = $derived_columns{$column}->{'BUILDER'}; | ||||
| 1902 | my $source_values = {}; | ||||
| 1903 | foreach my $source_column (keys %{ $derived_columns{$column} }) { | ||||
| 1904 | next if $source_column eq 'BUILDER'; | ||||
| 1905 | $source_values->{$source_column} = $item->{$source_column}; | ||||
| 1906 | } | ||||
| 1907 | $builder->($item, $source_values); | ||||
| 1908 | } | ||||
| 1909 | } | ||||
| 1910 | |||||
| 1911 | =head2 _set_derived_columns_for_mod | ||||
| 1912 | |||||
| 1913 | _set_derived_column_for_mod($item); | ||||
| 1914 | |||||
| 1915 | Given an item hash representing a new item to be modified. | ||||
| 1916 | calculate any derived columns. Currently the only | ||||
| 1917 | such column is C<items.cn_sort>. | ||||
| 1918 | |||||
| 1919 | This routine differs from C<_set_derived_columns_for_add> | ||||
| 1920 | in that it needs to handle partial item records. In other | ||||
| 1921 | words, the caller of C<ModItem> may have supplied only one | ||||
| 1922 | or two columns to be changed, so this function needs to | ||||
| 1923 | determine whether any of the columns to be changed affect | ||||
| 1924 | any of the derived columns. Also, if a derived column | ||||
| 1925 | depends on more than one column, but the caller is not | ||||
| 1926 | changing all of then, this routine retrieves the unchanged | ||||
| 1927 | values from the database in order to ensure a correct | ||||
| 1928 | calculation. | ||||
| 1929 | |||||
| 1930 | =cut | ||||
| 1931 | |||||
| 1932 | sub _set_derived_columns_for_mod { | ||||
| 1933 | my $item = shift; | ||||
| 1934 | |||||
| 1935 | foreach my $column (keys %derived_columns) { | ||||
| 1936 | my $builder = $derived_columns{$column}->{'BUILDER'}; | ||||
| 1937 | my $source_values = {}; | ||||
| 1938 | my %missing_sources = (); | ||||
| 1939 | my $must_recalc = 0; | ||||
| 1940 | foreach my $source_column (keys %{ $derived_columns{$column} }) { | ||||
| 1941 | next if $source_column eq 'BUILDER'; | ||||
| 1942 | if (exists $item->{$source_column}) { | ||||
| 1943 | $must_recalc = 1; | ||||
| 1944 | $source_values->{$source_column} = $item->{$source_column}; | ||||
| 1945 | } else { | ||||
| 1946 | $missing_sources{$source_column} = 1; | ||||
| 1947 | } | ||||
| 1948 | } | ||||
| 1949 | if ($must_recalc) { | ||||
| 1950 | foreach my $source_column (keys %missing_sources) { | ||||
| 1951 | $source_values->{$source_column} = _get_single_item_column($source_column, $item->{'itemnumber'}); | ||||
| 1952 | } | ||||
| 1953 | $builder->($item, $source_values); | ||||
| 1954 | } | ||||
| 1955 | } | ||||
| 1956 | } | ||||
| 1957 | |||||
| 1958 | =head2 _do_column_fixes_for_mod | ||||
| 1959 | |||||
| 1960 | _do_column_fixes_for_mod($item); | ||||
| 1961 | |||||
| 1962 | Given an item hashref containing one or more | ||||
| 1963 | columns to modify, fix up certain values. | ||||
| 1964 | Specifically, set to 0 any passed value | ||||
| 1965 | of C<notforloan>, C<damaged>, C<itemlost>, or | ||||
| 1966 | C<withdrawn> that is either undefined or | ||||
| 1967 | contains the empty string. | ||||
| 1968 | |||||
| 1969 | =cut | ||||
| 1970 | |||||
| 1971 | sub _do_column_fixes_for_mod { | ||||
| 1972 | my $item = shift; | ||||
| 1973 | |||||
| 1974 | if (exists $item->{'notforloan'} and | ||||
| 1975 | (not defined $item->{'notforloan'} or $item->{'notforloan'} eq '')) { | ||||
| 1976 | $item->{'notforloan'} = 0; | ||||
| 1977 | } | ||||
| 1978 | if (exists $item->{'damaged'} and | ||||
| 1979 | (not defined $item->{'damaged'} or $item->{'damaged'} eq '')) { | ||||
| 1980 | $item->{'damaged'} = 0; | ||||
| 1981 | } | ||||
| 1982 | if (exists $item->{'itemlost'} and | ||||
| 1983 | (not defined $item->{'itemlost'} or $item->{'itemlost'} eq '')) { | ||||
| 1984 | $item->{'itemlost'} = 0; | ||||
| 1985 | } | ||||
| 1986 | if (exists $item->{'withdrawn'} and | ||||
| 1987 | (not defined $item->{'withdrawn'} or $item->{'withdrawn'} eq '')) { | ||||
| 1988 | $item->{'withdrawn'} = 0; | ||||
| 1989 | } | ||||
| 1990 | if (exists $item->{'location'} && !exists $item->{'permanent_location'}) { | ||||
| 1991 | $item->{'permanent_location'} = $item->{'location'}; | ||||
| 1992 | } | ||||
| 1993 | if (exists $item->{'timestamp'}) { | ||||
| 1994 | delete $item->{'timestamp'}; | ||||
| 1995 | } | ||||
| 1996 | } | ||||
| 1997 | |||||
| 1998 | =head2 _get_single_item_column | ||||
| 1999 | |||||
| 2000 | _get_single_item_column($column, $itemnumber); | ||||
| 2001 | |||||
| 2002 | Retrieves the value of a single column from an C<items> | ||||
| 2003 | row specified by C<$itemnumber>. | ||||
| 2004 | |||||
| 2005 | =cut | ||||
| 2006 | |||||
| 2007 | sub _get_single_item_column { | ||||
| 2008 | my $column = shift; | ||||
| 2009 | my $itemnumber = shift; | ||||
| 2010 | |||||
| 2011 | my $dbh = C4::Context->dbh; | ||||
| 2012 | my $sth = $dbh->prepare("SELECT $column FROM items WHERE itemnumber = ?"); | ||||
| 2013 | $sth->execute($itemnumber); | ||||
| 2014 | my ($value) = $sth->fetchrow(); | ||||
| 2015 | return $value; | ||||
| 2016 | } | ||||
| 2017 | |||||
| 2018 | =head2 _calc_items_cn_sort | ||||
| 2019 | |||||
| 2020 | _calc_items_cn_sort($item, $source_values); | ||||
| 2021 | |||||
| 2022 | Helper routine to calculate C<items.cn_sort>. | ||||
| 2023 | |||||
| 2024 | =cut | ||||
| 2025 | |||||
| 2026 | sub _calc_items_cn_sort { | ||||
| 2027 | my $item = shift; | ||||
| 2028 | my $source_values = shift; | ||||
| 2029 | |||||
| 2030 | $item->{'items.cn_sort'} = GetClassSort($source_values->{'items.cn_source'}, $source_values->{'itemcallnumber'}, ""); | ||||
| 2031 | } | ||||
| 2032 | |||||
| 2033 | =head2 _set_defaults_for_add | ||||
| 2034 | |||||
| 2035 | _set_defaults_for_add($item_hash); | ||||
| 2036 | |||||
| 2037 | Given an item hash representing an item to be added, set | ||||
| 2038 | correct default values for columns whose default value | ||||
| 2039 | is not handled by the DBMS. This includes the following | ||||
| 2040 | columns: | ||||
| 2041 | |||||
| 2042 | =over 2 | ||||
| 2043 | |||||
| 2044 | =item * | ||||
| 2045 | |||||
| 2046 | C<items.dateaccessioned> | ||||
| 2047 | |||||
| 2048 | =item * | ||||
| 2049 | |||||
| 2050 | C<items.notforloan> | ||||
| 2051 | |||||
| 2052 | =item * | ||||
| 2053 | |||||
| 2054 | C<items.damaged> | ||||
| 2055 | |||||
| 2056 | =item * | ||||
| 2057 | |||||
| 2058 | C<items.itemlost> | ||||
| 2059 | |||||
| 2060 | =item * | ||||
| 2061 | |||||
| 2062 | C<items.withdrawn> | ||||
| 2063 | |||||
| 2064 | =back | ||||
| 2065 | |||||
| 2066 | =cut | ||||
| 2067 | |||||
| 2068 | sub _set_defaults_for_add { | ||||
| 2069 | my $item = shift; | ||||
| 2070 | $item->{dateaccessioned} ||= C4::Dates->new->output('iso'); | ||||
| 2071 | $item->{$_} ||= 0 for (qw( notforloan damaged itemlost withdrawn)); | ||||
| 2072 | } | ||||
| 2073 | |||||
| 2074 | =head2 _koha_new_item | ||||
| 2075 | |||||
| 2076 | my ($itemnumber,$error) = _koha_new_item( $item, $barcode ); | ||||
| 2077 | |||||
| 2078 | Perform the actual insert into the C<items> table. | ||||
| 2079 | |||||
| 2080 | =cut | ||||
| 2081 | |||||
| 2082 | sub _koha_new_item { | ||||
| 2083 | my ( $item, $barcode ) = @_; | ||||
| 2084 | my $dbh=C4::Context->dbh; | ||||
| 2085 | my $error; | ||||
| 2086 | my $query = | ||||
| 2087 | "INSERT INTO items SET | ||||
| 2088 | biblionumber = ?, | ||||
| 2089 | biblioitemnumber = ?, | ||||
| 2090 | barcode = ?, | ||||
| 2091 | dateaccessioned = ?, | ||||
| 2092 | booksellerid = ?, | ||||
| 2093 | homebranch = ?, | ||||
| 2094 | price = ?, | ||||
| 2095 | replacementprice = ?, | ||||
| 2096 | replacementpricedate = ?, | ||||
| 2097 | datelastborrowed = ?, | ||||
| 2098 | datelastseen = ?, | ||||
| 2099 | stack = ?, | ||||
| 2100 | notforloan = ?, | ||||
| 2101 | damaged = ?, | ||||
| 2102 | itemlost = ?, | ||||
| 2103 | withdrawn = ?, | ||||
| 2104 | itemcallnumber = ?, | ||||
| 2105 | coded_location_qualifier = ?, | ||||
| 2106 | restricted = ?, | ||||
| 2107 | itemnotes = ?, | ||||
| 2108 | holdingbranch = ?, | ||||
| 2109 | paidfor = ?, | ||||
| 2110 | location = ?, | ||||
| 2111 | permanent_location = ?, | ||||
| 2112 | onloan = ?, | ||||
| 2113 | issues = ?, | ||||
| 2114 | renewals = ?, | ||||
| 2115 | reserves = ?, | ||||
| 2116 | cn_source = ?, | ||||
| 2117 | cn_sort = ?, | ||||
| 2118 | ccode = ?, | ||||
| 2119 | itype = ?, | ||||
| 2120 | materials = ?, | ||||
| 2121 | uri = ?, | ||||
| 2122 | enumchron = ?, | ||||
| 2123 | more_subfields_xml = ?, | ||||
| 2124 | copynumber = ?, | ||||
| 2125 | stocknumber = ? | ||||
| 2126 | "; | ||||
| 2127 | my $sth = $dbh->prepare($query); | ||||
| 2128 | my $today = C4::Dates->today('iso'); | ||||
| 2129 | $sth->execute( | ||||
| 2130 | $item->{'biblionumber'}, | ||||
| 2131 | $item->{'biblioitemnumber'}, | ||||
| 2132 | $barcode, | ||||
| 2133 | $item->{'dateaccessioned'}, | ||||
| 2134 | $item->{'booksellerid'}, | ||||
| 2135 | $item->{'homebranch'}, | ||||
| 2136 | $item->{'price'}, | ||||
| 2137 | $item->{'replacementprice'}, | ||||
| 2138 | $item->{'replacementpricedate'} || $today, | ||||
| 2139 | $item->{datelastborrowed}, | ||||
| 2140 | $item->{datelastseen} || $today, | ||||
| 2141 | $item->{stack}, | ||||
| 2142 | $item->{'notforloan'}, | ||||
| 2143 | $item->{'damaged'}, | ||||
| 2144 | $item->{'itemlost'}, | ||||
| 2145 | $item->{'withdrawn'}, | ||||
| 2146 | $item->{'itemcallnumber'}, | ||||
| 2147 | $item->{'coded_location_qualifier'}, | ||||
| 2148 | $item->{'restricted'}, | ||||
| 2149 | $item->{'itemnotes'}, | ||||
| 2150 | $item->{'holdingbranch'}, | ||||
| 2151 | $item->{'paidfor'}, | ||||
| 2152 | $item->{'location'}, | ||||
| 2153 | $item->{'permanent_location'}, | ||||
| 2154 | $item->{'onloan'}, | ||||
| 2155 | $item->{'issues'}, | ||||
| 2156 | $item->{'renewals'}, | ||||
| 2157 | $item->{'reserves'}, | ||||
| 2158 | $item->{'items.cn_source'}, | ||||
| 2159 | $item->{'items.cn_sort'}, | ||||
| 2160 | $item->{'ccode'}, | ||||
| 2161 | $item->{'itype'}, | ||||
| 2162 | $item->{'materials'}, | ||||
| 2163 | $item->{'uri'}, | ||||
| 2164 | $item->{'enumchron'}, | ||||
| 2165 | $item->{'more_subfields_xml'}, | ||||
| 2166 | $item->{'copynumber'}, | ||||
| 2167 | $item->{'stocknumber'}, | ||||
| 2168 | ); | ||||
| 2169 | |||||
| 2170 | my $itemnumber; | ||||
| 2171 | if ( defined $sth->errstr ) { | ||||
| 2172 | $error.="ERROR in _koha_new_item $query".$sth->errstr; | ||||
| 2173 | } | ||||
| 2174 | else { | ||||
| 2175 | $itemnumber = $dbh->{'mysql_insertid'}; | ||||
| 2176 | } | ||||
| 2177 | |||||
| 2178 | return ( $itemnumber, $error ); | ||||
| 2179 | } | ||||
| 2180 | |||||
| 2181 | =head2 MoveItemFromBiblio | ||||
| 2182 | |||||
| 2183 | MoveItemFromBiblio($itenumber, $frombiblio, $tobiblio); | ||||
| 2184 | |||||
| 2185 | Moves an item from a biblio to another | ||||
| 2186 | |||||
| 2187 | Returns undef if the move failed or the biblionumber of the destination record otherwise | ||||
| 2188 | |||||
| 2189 | =cut | ||||
| 2190 | |||||
| 2191 | sub MoveItemFromBiblio { | ||||
| 2192 | my ($itemnumber, $frombiblio, $tobiblio) = @_; | ||||
| 2193 | my $dbh = C4::Context->dbh; | ||||
| 2194 | my $sth = $dbh->prepare("SELECT biblioitemnumber FROM biblioitems WHERE biblionumber = ?"); | ||||
| 2195 | $sth->execute( $tobiblio ); | ||||
| 2196 | my ( $tobiblioitem ) = $sth->fetchrow(); | ||||
| 2197 | $sth = $dbh->prepare("UPDATE items SET biblioitemnumber = ?, biblionumber = ? WHERE itemnumber = ? AND biblionumber = ?"); | ||||
| 2198 | my $return = $sth->execute($tobiblioitem, $tobiblio, $itemnumber, $frombiblio); | ||||
| 2199 | if ($return == 1) { | ||||
| 2200 | ModZebra( $tobiblio, "specialUpdate", "biblioserver" ); | ||||
| 2201 | ModZebra( $frombiblio, "specialUpdate", "biblioserver" ); | ||||
| 2202 | # Checking if the item we want to move is in an order | ||||
| 2203 | require C4::Acquisition; | ||||
| 2204 | my $order = C4::Acquisition::GetOrderFromItemnumber($itemnumber); | ||||
| 2205 | if ($order) { | ||||
| 2206 | # Replacing the biblionumber within the order if necessary | ||||
| 2207 | $order->{'biblionumber'} = $tobiblio; | ||||
| 2208 | C4::Acquisition::ModOrder($order); | ||||
| 2209 | } | ||||
| 2210 | return $tobiblio; | ||||
| 2211 | } | ||||
| 2212 | return; | ||||
| 2213 | } | ||||
| 2214 | |||||
| 2215 | =head2 DelItemCheck | ||||
| 2216 | |||||
| 2217 | DelItemCheck($dbh, $biblionumber, $itemnumber); | ||||
| 2218 | |||||
| 2219 | Exported function (core API) for deleting an item record in Koha if there no current issue. | ||||
| 2220 | |||||
| 2221 | =cut | ||||
| 2222 | |||||
| 2223 | sub DelItemCheck { | ||||
| 2224 | my ( $dbh, $biblionumber, $itemnumber ) = @_; | ||||
| 2225 | my $error; | ||||
| 2226 | |||||
| 2227 | my $countanalytics=GetAnalyticsCount($itemnumber); | ||||
| 2228 | |||||
| 2229 | |||||
| 2230 | # check that there is no issue on this item before deletion. | ||||
| 2231 | my $sth = $dbh->prepare(q{ | ||||
| 2232 | SELECT COUNT(*) FROM issues | ||||
| 2233 | WHERE itemnumber = ? | ||||
| 2234 | }); | ||||
| 2235 | $sth->execute($itemnumber); | ||||
| 2236 | my ($onloan) = $sth->fetchrow; | ||||
| 2237 | |||||
| 2238 | my $item = GetItem($itemnumber); | ||||
| 2239 | |||||
| 2240 | if ($onloan){ | ||||
| 2241 | $error = "book_on_loan" | ||||
| 2242 | } | ||||
| 2243 | elsif ( !C4::Context->IsSuperLibrarian() | ||||
| 2244 | and C4::Context->preference("IndependentBranches") | ||||
| 2245 | and ( C4::Context->userenv->{branch} ne $item->{'homebranch'} ) ) | ||||
| 2246 | { | ||||
| 2247 | $error = "not_same_branch"; | ||||
| 2248 | } | ||||
| 2249 | else{ | ||||
| 2250 | # check it doesnt have a waiting reserve | ||||
| 2251 | $sth = $dbh->prepare(q{ | ||||
| 2252 | SELECT COUNT(*) FROM reserves | ||||
| 2253 | WHERE (found = 'W' OR found = 'T') | ||||
| 2254 | AND itemnumber = ? | ||||
| 2255 | }); | ||||
| 2256 | $sth->execute($itemnumber); | ||||
| 2257 | my ($reserve) = $sth->fetchrow; | ||||
| 2258 | if ($reserve){ | ||||
| 2259 | $error = "book_reserved"; | ||||
| 2260 | } elsif ($countanalytics > 0){ | ||||
| 2261 | $error = "linked_analytics"; | ||||
| 2262 | } else { | ||||
| 2263 | DelItem($dbh, $biblionumber, $itemnumber); | ||||
| 2264 | return 1; | ||||
| 2265 | } | ||||
| 2266 | } | ||||
| 2267 | return $error; | ||||
| 2268 | } | ||||
| 2269 | |||||
| 2270 | =head2 _koha_modify_item | ||||
| 2271 | |||||
| 2272 | my ($itemnumber,$error) =_koha_modify_item( $item ); | ||||
| 2273 | |||||
| 2274 | Perform the actual update of the C<items> row. Note that this | ||||
| 2275 | routine accepts a hashref specifying the columns to update. | ||||
| 2276 | |||||
| 2277 | =cut | ||||
| 2278 | |||||
| 2279 | sub _koha_modify_item { | ||||
| 2280 | my ( $item ) = @_; | ||||
| 2281 | my $dbh=C4::Context->dbh; | ||||
| 2282 | my $error; | ||||
| 2283 | |||||
| 2284 | my $query = "UPDATE items SET "; | ||||
| 2285 | my @bind; | ||||
| 2286 | for my $key ( keys %$item ) { | ||||
| 2287 | next if ( $key eq 'itemnumber' ); | ||||
| 2288 | $query.="$key=?,"; | ||||
| 2289 | push @bind, $item->{$key}; | ||||
| 2290 | } | ||||
| 2291 | $query =~ s/,$//; | ||||
| 2292 | $query .= " WHERE itemnumber=?"; | ||||
| 2293 | push @bind, $item->{'itemnumber'}; | ||||
| 2294 | my $sth = $dbh->prepare($query); | ||||
| 2295 | $sth->execute(@bind); | ||||
| 2296 | if ( $sth->err ) { | ||||
| 2297 | $error.="ERROR in _koha_modify_item $query: ".$sth->errstr; | ||||
| 2298 | warn $error; | ||||
| 2299 | } | ||||
| 2300 | return ($item->{'itemnumber'},$error); | ||||
| 2301 | } | ||||
| 2302 | |||||
| 2303 | =head2 _koha_delete_item | ||||
| 2304 | |||||
| 2305 | _koha_delete_item( $dbh, $itemnum ); | ||||
| 2306 | |||||
| 2307 | Internal function to delete an item record from the koha tables | ||||
| 2308 | |||||
| 2309 | =cut | ||||
| 2310 | |||||
| 2311 | sub _koha_delete_item { | ||||
| 2312 | my ( $dbh, $itemnum ) = @_; | ||||
| 2313 | |||||
| 2314 | # save the deleted item to deleteditems table | ||||
| 2315 | my $sth = $dbh->prepare("SELECT * FROM items WHERE itemnumber=?"); | ||||
| 2316 | $sth->execute($itemnum); | ||||
| 2317 | my $data = $sth->fetchrow_hashref(); | ||||
| 2318 | my $query = "INSERT INTO deleteditems SET "; | ||||
| 2319 | my @bind = (); | ||||
| 2320 | foreach my $key ( keys %$data ) { | ||||
| 2321 | next if ( $key eq 'timestamp' ); # timestamp will be set by db | ||||
| 2322 | $query .= "$key = ?,"; | ||||
| 2323 | push( @bind, $data->{$key} ); | ||||
| 2324 | } | ||||
| 2325 | $query =~ s/\,$//; | ||||
| 2326 | $sth = $dbh->prepare($query); | ||||
| 2327 | $sth->execute(@bind); | ||||
| 2328 | |||||
| 2329 | # delete from items table | ||||
| 2330 | $sth = $dbh->prepare("DELETE FROM items WHERE itemnumber=?"); | ||||
| 2331 | $sth->execute($itemnum); | ||||
| 2332 | return; | ||||
| 2333 | } | ||||
| 2334 | |||||
| 2335 | =head2 _marc_from_item_hash | ||||
| 2336 | |||||
| 2337 | my $item_marc = _marc_from_item_hash($item, $frameworkcode[, $unlinked_item_subfields]); | ||||
| 2338 | |||||
| 2339 | Given an item hash representing a complete item record, | ||||
| 2340 | create a C<MARC::Record> object containing an embedded | ||||
| 2341 | tag representing that item. | ||||
| 2342 | |||||
| 2343 | The third, optional parameter C<$unlinked_item_subfields> is | ||||
| 2344 | an arrayref of subfields (not mapped to C<items> fields per the | ||||
| 2345 | framework) to be added to the MARC representation | ||||
| 2346 | of the item. | ||||
| 2347 | |||||
| 2348 | =cut | ||||
| 2349 | |||||
| 2350 | sub _marc_from_item_hash { | ||||
| 2351 | my $item = shift; | ||||
| 2352 | my $frameworkcode = shift; | ||||
| 2353 | my $unlinked_item_subfields; | ||||
| 2354 | if (@_) { | ||||
| 2355 | $unlinked_item_subfields = shift; | ||||
| 2356 | } | ||||
| 2357 | |||||
| 2358 | # Tack on 'items.' prefix to column names so lookup from MARC frameworks will work | ||||
| 2359 | # Also, don't emit a subfield if the underlying field is blank. | ||||
| 2360 | my $mungeditem = { map { (defined($item->{$_}) and $item->{$_} ne '') ? | ||||
| 2361 | (/^items\./ ? ($_ => $item->{$_}) : ("items.$_" => $item->{$_})) | ||||
| 2362 | : () } keys %{ $item } }; | ||||
| 2363 | |||||
| 2364 | my $item_marc = MARC::Record->new(); | ||||
| 2365 | foreach my $item_field ( keys %{$mungeditem} ) { | ||||
| 2366 | my ( $tag, $subfield ) = GetMarcFromKohaField( $item_field, $frameworkcode ); | ||||
| 2367 | next unless defined $tag and defined $subfield; # skip if not mapped to MARC field | ||||
| 2368 | my @values = split(/\s?\|\s?/, $mungeditem->{$item_field}, -1); | ||||
| 2369 | foreach my $value (@values){ | ||||
| 2370 | if ( my $field = $item_marc->field($tag) ) { | ||||
| 2371 | $field->add_subfields( $subfield => $value ); | ||||
| 2372 | } else { | ||||
| 2373 | my $add_subfields = []; | ||||
| 2374 | if (defined $unlinked_item_subfields and ref($unlinked_item_subfields) eq 'ARRAY' and $#$unlinked_item_subfields > -1) { | ||||
| 2375 | $add_subfields = $unlinked_item_subfields; | ||||
| 2376 | } | ||||
| 2377 | $item_marc->add_fields( $tag, " ", " ", $subfield => $value, @$add_subfields ); | ||||
| 2378 | } | ||||
| 2379 | } | ||||
| 2380 | } | ||||
| 2381 | |||||
| 2382 | return $item_marc; | ||||
| 2383 | } | ||||
| 2384 | |||||
| 2385 | =head2 _repack_item_errors | ||||
| 2386 | |||||
| 2387 | Add an error message hash generated by C<CheckItemPreSave> | ||||
| 2388 | to a list of errors. | ||||
| 2389 | |||||
| 2390 | =cut | ||||
| 2391 | |||||
| 2392 | sub _repack_item_errors { | ||||
| 2393 | my $item_sequence_num = shift; | ||||
| 2394 | my $item_ref = shift; | ||||
| 2395 | my $error_ref = shift; | ||||
| 2396 | |||||
| 2397 | my @repacked_errors = (); | ||||
| 2398 | |||||
| 2399 | foreach my $error_code (sort keys %{ $error_ref }) { | ||||
| 2400 | my $repacked_error = {}; | ||||
| 2401 | $repacked_error->{'item_sequence'} = $item_sequence_num; | ||||
| 2402 | $repacked_error->{'item_barcode'} = exists($item_ref->{'barcode'}) ? $item_ref->{'barcode'} : ''; | ||||
| 2403 | $repacked_error->{'error_code'} = $error_code; | ||||
| 2404 | $repacked_error->{'error_information'} = $error_ref->{$error_code}; | ||||
| 2405 | push @repacked_errors, $repacked_error; | ||||
| 2406 | } | ||||
| 2407 | |||||
| 2408 | return @repacked_errors; | ||||
| 2409 | } | ||||
| 2410 | |||||
| 2411 | =head2 _get_unlinked_item_subfields | ||||
| 2412 | |||||
| 2413 | my $unlinked_item_subfields = _get_unlinked_item_subfields($original_item_marc, $frameworkcode); | ||||
| 2414 | |||||
| 2415 | =cut | ||||
| 2416 | |||||
| 2417 | sub _get_unlinked_item_subfields { | ||||
| 2418 | my $original_item_marc = shift; | ||||
| 2419 | my $frameworkcode = shift; | ||||
| 2420 | |||||
| 2421 | my $marcstructure = GetMarcStructure(1, $frameworkcode); | ||||
| 2422 | |||||
| 2423 | # assume that this record has only one field, and that that | ||||
| 2424 | # field contains only the item information | ||||
| 2425 | my $subfields = []; | ||||
| 2426 | my @fields = $original_item_marc->fields(); | ||||
| 2427 | if ($#fields > -1) { | ||||
| 2428 | my $field = $fields[0]; | ||||
| 2429 | my $tag = $field->tag(); | ||||
| 2430 | foreach my $subfield ($field->subfields()) { | ||||
| 2431 | if (defined $subfield->[1] and | ||||
| 2432 | $subfield->[1] ne '' and | ||||
| 2433 | !$marcstructure->{$tag}->{$subfield->[0]}->{'kohafield'}) { | ||||
| 2434 | push @$subfields, $subfield->[0] => $subfield->[1]; | ||||
| 2435 | } | ||||
| 2436 | } | ||||
| 2437 | } | ||||
| 2438 | return $subfields; | ||||
| 2439 | } | ||||
| 2440 | |||||
| 2441 | =head2 _get_unlinked_subfields_xml | ||||
| 2442 | |||||
| 2443 | my $unlinked_subfields_xml = _get_unlinked_subfields_xml($unlinked_item_subfields); | ||||
| 2444 | |||||
| 2445 | =cut | ||||
| 2446 | |||||
| 2447 | sub _get_unlinked_subfields_xml { | ||||
| 2448 | my $unlinked_item_subfields = shift; | ||||
| 2449 | |||||
| 2450 | my $xml; | ||||
| 2451 | if (defined $unlinked_item_subfields and ref($unlinked_item_subfields) eq 'ARRAY' and $#$unlinked_item_subfields > -1) { | ||||
| 2452 | my $marc = MARC::Record->new(); | ||||
| 2453 | # use of tag 999 is arbitrary, and doesn't need to match the item tag | ||||
| 2454 | # used in the framework | ||||
| 2455 | $marc->append_fields(MARC::Field->new('999', ' ', ' ', @$unlinked_item_subfields)); | ||||
| 2456 | $marc->encoding("UTF-8"); | ||||
| 2457 | $xml = $marc->as_xml("USMARC"); | ||||
| 2458 | } | ||||
| 2459 | |||||
| 2460 | return $xml; | ||||
| 2461 | } | ||||
| 2462 | |||||
| 2463 | =head2 _parse_unlinked_item_subfields_from_xml | ||||
| 2464 | |||||
| 2465 | my $unlinked_item_subfields = _parse_unlinked_item_subfields_from_xml($whole_item->{'more_subfields_xml'}): | ||||
| 2466 | |||||
| 2467 | =cut | ||||
| 2468 | |||||
| 2469 | sub _parse_unlinked_item_subfields_from_xml { | ||||
| 2470 | my $xml = shift; | ||||
| 2471 | require C4::Charset; | ||||
| 2472 | return unless defined $xml and $xml ne ""; | ||||
| 2473 | my $marc = MARC::Record->new_from_xml(C4::Charset::StripNonXmlChars($xml),'UTF-8'); | ||||
| 2474 | my $unlinked_subfields = []; | ||||
| 2475 | my @fields = $marc->fields(); | ||||
| 2476 | if ($#fields > -1) { | ||||
| 2477 | foreach my $subfield ($fields[0]->subfields()) { | ||||
| 2478 | push @$unlinked_subfields, $subfield->[0] => $subfield->[1]; | ||||
| 2479 | } | ||||
| 2480 | } | ||||
| 2481 | return $unlinked_subfields; | ||||
| 2482 | } | ||||
| 2483 | |||||
| 2484 | =head2 GetAnalyticsCount | ||||
| 2485 | |||||
| 2486 | $count= &GetAnalyticsCount($itemnumber) | ||||
| 2487 | |||||
| 2488 | counts Usage of itemnumber in Analytical bibliorecords. | ||||
| 2489 | |||||
| 2490 | =cut | ||||
| 2491 | |||||
| 2492 | sub GetAnalyticsCount { | ||||
| 2493 | my ($itemnumber) = @_; | ||||
| 2494 | require C4::Search; | ||||
| 2495 | |||||
| 2496 | ### ZOOM search here | ||||
| 2497 | my $query; | ||||
| 2498 | $query= "hi=".$itemnumber; | ||||
| 2499 | my ($err,$res,$result) = C4::Search::SimpleSearch($query,0,10); | ||||
| 2500 | return ($result); | ||||
| 2501 | } | ||||
| 2502 | |||||
| 2503 | =head2 GetItemHolds | ||||
| 2504 | |||||
| 2505 | =over 4 | ||||
| 2506 | $holds = &GetItemHolds($biblionumber, $itemnumber); | ||||
| 2507 | |||||
| 2508 | =back | ||||
| 2509 | |||||
| 2510 | This function return the count of holds with $biblionumber and $itemnumber | ||||
| 2511 | |||||
| 2512 | =cut | ||||
| 2513 | |||||
| 2514 | sub GetItemHolds { | ||||
| 2515 | my ($biblionumber, $itemnumber) = @_; | ||||
| 2516 | my $holds; | ||||
| 2517 | my $dbh = C4::Context->dbh; | ||||
| 2518 | my $query = "SELECT count(*) | ||||
| 2519 | FROM reserves | ||||
| 2520 | WHERE biblionumber=? AND itemnumber=?"; | ||||
| 2521 | my $sth = $dbh->prepare($query); | ||||
| 2522 | $sth->execute($biblionumber, $itemnumber); | ||||
| 2523 | $holds = $sth->fetchrow; | ||||
| 2524 | return $holds; | ||||
| 2525 | } | ||||
| 2526 | |||||
| 2527 | # Return the list of the column names of items table | ||||
| 2528 | sub _get_items_columns { | ||||
| 2529 | my $dbh = C4::Context->dbh; | ||||
| 2530 | my $sth = $dbh->column_info(undef, undef, 'items', '%'); | ||||
| 2531 | $sth->execute; | ||||
| 2532 | my $results = $sth->fetchall_hashref('COLUMN_NAME'); | ||||
| 2533 | return keys %$results; | ||||
| 2534 | } | ||||
| 2535 | |||||
| 2536 | =head2 SearchItems | ||||
| 2537 | |||||
| 2538 | my $items = SearchItems($field, $value); | ||||
| 2539 | |||||
| 2540 | SearchItems will search for items on a specific given field. | ||||
| 2541 | For instance you can search all items with a specific stocknumber like this: | ||||
| 2542 | |||||
| 2543 | my $items = SearchItems('stocknumber', $stocknumber); | ||||
| 2544 | |||||
| 2545 | =cut | ||||
| 2546 | |||||
| 2547 | sub SearchItems { | ||||
| 2548 | my ($field, $value) = @_; | ||||
| 2549 | |||||
| 2550 | my $dbh = C4::Context->dbh; | ||||
| 2551 | my @columns = _get_items_columns; | ||||
| 2552 | my $results = []; | ||||
| 2553 | if(0 < grep /^$field$/, @columns) { | ||||
| 2554 | my $query = "SELECT $field FROM items WHERE $field = ?"; | ||||
| 2555 | my $sth = $dbh->prepare( $query ); | ||||
| 2556 | $sth->execute( $value ); | ||||
| 2557 | $results = $sth->fetchall_arrayref({}); | ||||
| 2558 | } | ||||
| 2559 | return $results; | ||||
| 2560 | } | ||||
| 2561 | |||||
| 2562 | |||||
| 2563 | =head1 OTHER FUNCTIONS | ||||
| 2564 | |||||
| 2565 | =head2 _find_value | ||||
| 2566 | |||||
| 2567 | ($indicators, $value) = _find_value($tag, $subfield, $record,$encoding); | ||||
| 2568 | |||||
| 2569 | Find the given $subfield in the given $tag in the given | ||||
| 2570 | MARC::Record $record. If the subfield is found, returns | ||||
| 2571 | the (indicators, value) pair; otherwise, (undef, undef) is | ||||
| 2572 | returned. | ||||
| 2573 | |||||
| 2574 | PROPOSITION : | ||||
| 2575 | Such a function is used in addbiblio AND additem and serial-edit and maybe could be used in Authorities. | ||||
| 2576 | I suggest we export it from this module. | ||||
| 2577 | |||||
| 2578 | =cut | ||||
| 2579 | |||||
| 2580 | sub _find_value { | ||||
| 2581 | my ( $tagfield, $insubfield, $record, $encoding ) = @_; | ||||
| 2582 | my @result; | ||||
| 2583 | my $indicator; | ||||
| 2584 | if ( $tagfield < 10 ) { | ||||
| 2585 | if ( $record->field($tagfield) ) { | ||||
| 2586 | push @result, $record->field($tagfield)->data(); | ||||
| 2587 | } else { | ||||
| 2588 | push @result, ""; | ||||
| 2589 | } | ||||
| 2590 | } else { | ||||
| 2591 | foreach my $field ( $record->field($tagfield) ) { | ||||
| 2592 | my @subfields = $field->subfields(); | ||||
| 2593 | foreach my $subfield (@subfields) { | ||||
| 2594 | if ( @$subfield[0] eq $insubfield ) { | ||||
| 2595 | push @result, @$subfield[1]; | ||||
| 2596 | $indicator = $field->indicator(1) . $field->indicator(2); | ||||
| 2597 | } | ||||
| 2598 | } | ||||
| 2599 | } | ||||
| 2600 | } | ||||
| 2601 | return ( $indicator, @result ); | ||||
| 2602 | } | ||||
| 2603 | |||||
| 2604 | |||||
| 2605 | =head2 PrepareItemrecordDisplay | ||||
| 2606 | |||||
| 2607 | PrepareItemrecordDisplay($itemrecord,$bibnum,$itemumber,$frameworkcode); | ||||
| 2608 | |||||
| 2609 | Returns a hash with all the fields for Display a given item data in a template | ||||
| 2610 | |||||
| 2611 | The $frameworkcode returns the item for the given frameworkcode, ONLY if bibnum is not provided | ||||
| 2612 | |||||
| 2613 | =cut | ||||
| 2614 | |||||
| 2615 | sub PrepareItemrecordDisplay { | ||||
| 2616 | |||||
| 2617 | my ( $bibnum, $itemnum, $defaultvalues, $frameworkcode ) = @_; | ||||
| 2618 | |||||
| 2619 | my $dbh = C4::Context->dbh; | ||||
| 2620 | $frameworkcode = &GetFrameworkCode($bibnum) if $bibnum; | ||||
| 2621 | my ( $itemtagfield, $itemtagsubfield ) = &GetMarcFromKohaField( "items.itemnumber", $frameworkcode ); | ||||
| 2622 | my $tagslib = &GetMarcStructure( 1, $frameworkcode ); | ||||
| 2623 | |||||
| 2624 | # return nothing if we don't have found an existing framework. | ||||
| 2625 | return q{} unless $tagslib; | ||||
| 2626 | my $itemrecord; | ||||
| 2627 | if ($itemnum) { | ||||
| 2628 | $itemrecord = C4::Items::GetMarcItem( $bibnum, $itemnum ); | ||||
| 2629 | } | ||||
| 2630 | my @loop_data; | ||||
| 2631 | |||||
| 2632 | my $branch_limit = C4::Context->userenv ? C4::Context->userenv->{"branch"} : ""; | ||||
| 2633 | my $query = qq{ | ||||
| 2634 | SELECT authorised_value,lib FROM authorised_values | ||||
| 2635 | }; | ||||
| 2636 | $query .= qq{ | ||||
| 2637 | LEFT JOIN authorised_values_branches ON ( id = av_id ) | ||||
| 2638 | } if $branch_limit; | ||||
| 2639 | $query .= qq{ | ||||
| 2640 | WHERE category = ? | ||||
| 2641 | }; | ||||
| 2642 | $query .= qq{ AND ( branchcode = ? OR branchcode IS NULL )} if $branch_limit; | ||||
| 2643 | $query .= qq{ ORDER BY lib}; | ||||
| 2644 | my $authorised_values_sth = $dbh->prepare( $query ); | ||||
| 2645 | foreach my $tag ( sort keys %{$tagslib} ) { | ||||
| 2646 | my $previous_tag = ''; | ||||
| 2647 | if ( $tag ne '' ) { | ||||
| 2648 | |||||
| 2649 | # loop through each subfield | ||||
| 2650 | my $cntsubf; | ||||
| 2651 | foreach my $subfield ( sort keys %{ $tagslib->{$tag} } ) { | ||||
| 2652 | next if ( subfield_is_koha_internal_p($subfield) ); | ||||
| 2653 | next if ( $tagslib->{$tag}->{$subfield}->{'tab'} ne "10" ); | ||||
| 2654 | my %subfield_data; | ||||
| 2655 | $subfield_data{tag} = $tag; | ||||
| 2656 | $subfield_data{subfield} = $subfield; | ||||
| 2657 | $subfield_data{countsubfield} = $cntsubf++; | ||||
| 2658 | $subfield_data{kohafield} = $tagslib->{$tag}->{$subfield}->{'kohafield'}; | ||||
| 2659 | $subfield_data{id} = "tag_".$tag."_subfield_".$subfield."_".int(rand(1000000)); | ||||
| 2660 | |||||
| 2661 | # $subfield_data{marc_lib}=$tagslib->{$tag}->{$subfield}->{lib}; | ||||
| 2662 | $subfield_data{marc_lib} = $tagslib->{$tag}->{$subfield}->{lib}; | ||||
| 2663 | $subfield_data{mandatory} = $tagslib->{$tag}->{$subfield}->{mandatory}; | ||||
| 2664 | $subfield_data{repeatable} = $tagslib->{$tag}->{$subfield}->{repeatable}; | ||||
| 2665 | $subfield_data{hidden} = "display:none" | ||||
| 2666 | if ( ( $tagslib->{$tag}->{$subfield}->{hidden} > 4 ) | ||||
| 2667 | || ( $tagslib->{$tag}->{$subfield}->{hidden} < -4 ) ); | ||||
| 2668 | my ( $x, $defaultvalue ); | ||||
| 2669 | if ($itemrecord) { | ||||
| 2670 | ( $x, $defaultvalue ) = _find_value( $tag, $subfield, $itemrecord ); | ||||
| 2671 | } | ||||
| 2672 | $defaultvalue = $tagslib->{$tag}->{$subfield}->{defaultvalue} unless $defaultvalue; | ||||
| 2673 | if ( !defined $defaultvalue ) { | ||||
| 2674 | $defaultvalue = q||; | ||||
| 2675 | } else { | ||||
| 2676 | $defaultvalue =~ s/"/"/g; | ||||
| 2677 | } | ||||
| 2678 | |||||
| 2679 | # search for itemcallnumber if applicable | ||||
| 2680 | if ( $tagslib->{$tag}->{$subfield}->{kohafield} eq 'items.itemcallnumber' | ||||
| 2681 | && C4::Context->preference('itemcallnumber') ) { | ||||
| 2682 | my $CNtag = substr( C4::Context->preference('itemcallnumber'), 0, 3 ); | ||||
| 2683 | my $CNsubfield = substr( C4::Context->preference('itemcallnumber'), 3, 1 ); | ||||
| 2684 | if ( $itemrecord and my $field = $itemrecord->field($CNtag) ) { | ||||
| 2685 | $defaultvalue = $field->subfield($CNsubfield); | ||||
| 2686 | } | ||||
| 2687 | } | ||||
| 2688 | if ( $tagslib->{$tag}->{$subfield}->{kohafield} eq 'items.itemcallnumber' | ||||
| 2689 | && $defaultvalues | ||||
| 2690 | && $defaultvalues->{'callnumber'} ) { | ||||
| 2691 | if( $itemrecord and $defaultvalues and not $itemrecord->field($subfield) ){ | ||||
| 2692 | # if the item record exists, only use default value if the item has no callnumber | ||||
| 2693 | $defaultvalue = $defaultvalues->{callnumber}; | ||||
| 2694 | } elsif ( !$itemrecord and $defaultvalues ) { | ||||
| 2695 | # if the item record *doesn't* exists, always use the default value | ||||
| 2696 | $defaultvalue = $defaultvalues->{callnumber}; | ||||
| 2697 | } | ||||
| 2698 | } | ||||
| 2699 | if ( ( $tagslib->{$tag}->{$subfield}->{kohafield} eq 'items.holdingbranch' || $tagslib->{$tag}->{$subfield}->{kohafield} eq 'items.homebranch' ) | ||||
| 2700 | && $defaultvalues | ||||
| 2701 | && $defaultvalues->{'branchcode'} ) { | ||||
| 2702 | if ( $itemrecord and $defaultvalues and not $itemrecord->field($subfield) ) { | ||||
| 2703 | $defaultvalue = $defaultvalues->{branchcode}; | ||||
| 2704 | } | ||||
| 2705 | } | ||||
| 2706 | if ( ( $tagslib->{$tag}->{$subfield}->{kohafield} eq 'items.location' ) | ||||
| 2707 | && $defaultvalues | ||||
| 2708 | && $defaultvalues->{'location'} ) { | ||||
| 2709 | |||||
| 2710 | if ( $itemrecord and $defaultvalues and not $itemrecord->field($subfield) ) { | ||||
| 2711 | # if the item record exists, only use default value if the item has no locationr | ||||
| 2712 | $defaultvalue = $defaultvalues->{location}; | ||||
| 2713 | } elsif ( !$itemrecord and $defaultvalues ) { | ||||
| 2714 | # if the item record *doesn't* exists, always use the default value | ||||
| 2715 | $defaultvalue = $defaultvalues->{location}; | ||||
| 2716 | } | ||||
| 2717 | } | ||||
| 2718 | if ( $tagslib->{$tag}->{$subfield}->{authorised_value} ) { | ||||
| 2719 | my @authorised_values; | ||||
| 2720 | my %authorised_lib; | ||||
| 2721 | |||||
| 2722 | # builds list, depending on authorised value... | ||||
| 2723 | #---- branch | ||||
| 2724 | if ( $tagslib->{$tag}->{$subfield}->{'authorised_value'} eq "branches" ) { | ||||
| 2725 | if ( ( C4::Context->preference("IndependentBranches") ) | ||||
| 2726 | && !C4::Context->IsSuperLibrarian() ) { | ||||
| 2727 | my $sth = $dbh->prepare( "SELECT branchcode,branchname FROM branches WHERE branchcode = ? ORDER BY branchname" ); | ||||
| 2728 | $sth->execute( C4::Context->userenv->{branch} ); | ||||
| 2729 | push @authorised_values, "" | ||||
| 2730 | unless ( $tagslib->{$tag}->{$subfield}->{mandatory} ); | ||||
| 2731 | while ( my ( $branchcode, $branchname ) = $sth->fetchrow_array ) { | ||||
| 2732 | push @authorised_values, $branchcode; | ||||
| 2733 | $authorised_lib{$branchcode} = $branchname; | ||||
| 2734 | } | ||||
| 2735 | } else { | ||||
| 2736 | my $sth = $dbh->prepare( "SELECT branchcode,branchname FROM branches ORDER BY branchname" ); | ||||
| 2737 | $sth->execute; | ||||
| 2738 | push @authorised_values, "" | ||||
| 2739 | unless ( $tagslib->{$tag}->{$subfield}->{mandatory} ); | ||||
| 2740 | while ( my ( $branchcode, $branchname ) = $sth->fetchrow_array ) { | ||||
| 2741 | push @authorised_values, $branchcode; | ||||
| 2742 | $authorised_lib{$branchcode} = $branchname; | ||||
| 2743 | } | ||||
| 2744 | } | ||||
| 2745 | |||||
| 2746 | $defaultvalue = C4::Context->userenv ? C4::Context->userenv->{branch} : undef; | ||||
| 2747 | if ( $defaultvalues and $defaultvalues->{branchcode} ) { | ||||
| 2748 | $defaultvalue = $defaultvalues->{branchcode}; | ||||
| 2749 | } | ||||
| 2750 | |||||
| 2751 | #----- itemtypes | ||||
| 2752 | } elsif ( $tagslib->{$tag}->{$subfield}->{authorised_value} eq "itemtypes" ) { | ||||
| 2753 | my $sth = $dbh->prepare( "SELECT itemtype,description FROM itemtypes ORDER BY description" ); | ||||
| 2754 | $sth->execute; | ||||
| 2755 | push @authorised_values, "" | ||||
| 2756 | unless ( $tagslib->{$tag}->{$subfield}->{mandatory} ); | ||||
| 2757 | while ( my ( $itemtype, $description ) = $sth->fetchrow_array ) { | ||||
| 2758 | push @authorised_values, $itemtype; | ||||
| 2759 | $authorised_lib{$itemtype} = $description; | ||||
| 2760 | } | ||||
| 2761 | #---- class_sources | ||||
| 2762 | } elsif ( $tagslib->{$tag}->{$subfield}->{authorised_value} eq "cn_source" ) { | ||||
| 2763 | push @authorised_values, "" unless ( $tagslib->{$tag}->{$subfield}->{mandatory} ); | ||||
| 2764 | |||||
| 2765 | my $class_sources = GetClassSources(); | ||||
| 2766 | my $default_source = C4::Context->preference("DefaultClassificationSource"); | ||||
| 2767 | |||||
| 2768 | foreach my $class_source (sort keys %$class_sources) { | ||||
| 2769 | next unless $class_sources->{$class_source}->{'used'} or | ||||
| 2770 | ($class_source eq $default_source); | ||||
| 2771 | push @authorised_values, $class_source; | ||||
| 2772 | $authorised_lib{$class_source} = $class_sources->{$class_source}->{'description'}; | ||||
| 2773 | } | ||||
| 2774 | |||||
| 2775 | $defaultvalue = $default_source; | ||||
| 2776 | |||||
| 2777 | #---- "true" authorised value | ||||
| 2778 | } else { | ||||
| 2779 | $authorised_values_sth->execute( | ||||
| 2780 | $tagslib->{$tag}->{$subfield}->{authorised_value}, | ||||
| 2781 | $branch_limit ? $branch_limit : () | ||||
| 2782 | ); | ||||
| 2783 | push @authorised_values, "" | ||||
| 2784 | unless ( $tagslib->{$tag}->{$subfield}->{mandatory} ); | ||||
| 2785 | while ( my ( $value, $lib ) = $authorised_values_sth->fetchrow_array ) { | ||||
| 2786 | push @authorised_values, $value; | ||||
| 2787 | $authorised_lib{$value} = $lib; | ||||
| 2788 | } | ||||
| 2789 | } | ||||
| 2790 | $subfield_data{marc_value} = CGI::scrolling_list( | ||||
| 2791 | -name => 'field_value', | ||||
| 2792 | -values => \@authorised_values, | ||||
| 2793 | -default => "$defaultvalue", | ||||
| 2794 | -labels => \%authorised_lib, | ||||
| 2795 | -size => 1, | ||||
| 2796 | -tabindex => '', | ||||
| 2797 | -multiple => 0, | ||||
| 2798 | ); | ||||
| 2799 | } elsif ( $tagslib->{$tag}->{$subfield}->{value_builder} ) { | ||||
| 2800 | # opening plugin | ||||
| 2801 | my $plugin = C4::Context->intranetdir . "/cataloguing/value_builder/" . $tagslib->{$tag}->{$subfield}->{'value_builder'}; | ||||
| 2802 | if (do $plugin) { | ||||
| 2803 | my $extended_param = plugin_parameters( $dbh, undef, $tagslib, $subfield_data{id}, undef ); | ||||
| 2804 | my ( $function_name, $javascript ) = plugin_javascript( $dbh, undef, $tagslib, $subfield_data{id}, undef ); | ||||
| 2805 | $subfield_data{random} = int(rand(1000000)); # why do we need 2 different randoms? | ||||
| 2806 | $subfield_data{marc_value} = qq[<input type="text" tabindex="1" id="$subfield_data{id}" name="field_value" class="input_marceditor" size="50" maxlength="255" | ||||
| 2807 | onfocus="Focus$function_name($subfield_data{random}, '$subfield_data{id}');" | ||||
| 2808 | onblur=" Blur$function_name($subfield_data{random}, '$subfield_data{id}');" /> | ||||
| 2809 | <a href="#" class="buttonDot" onclick="Clic$function_name('$subfield_data{id}'); return false;" title="Tag Editor">...</a> | ||||
| 2810 | $javascript]; | ||||
| 2811 | } else { | ||||
| 2812 | warn "Plugin Failed: $plugin"; | ||||
| 2813 | $subfield_data{marc_value} = qq(<input type="text" tabindex="1" id="$subfield_data{id}" name="field_value" class="input_marceditor" size="50" maxlength="255" />); # supply default input form | ||||
| 2814 | } | ||||
| 2815 | } | ||||
| 2816 | elsif ( $tag eq '' ) { # it's an hidden field | ||||
| 2817 | $subfield_data{marc_value} = qq(<input type="hidden" tabindex="1" id="$subfield_data{id}" name="field_value" class="input_marceditor" size="50" maxlength="255" value="$defaultvalue" />); | ||||
| 2818 | } | ||||
| 2819 | elsif ( $tagslib->{$tag}->{$subfield}->{'hidden'} ) { # FIXME: shouldn't input type be "hidden" ? | ||||
| 2820 | $subfield_data{marc_value} = qq(<input type="text" tabindex="1" id="$subfield_data{id}" name="field_value" class="input_marceditor" size="50" maxlength="255" value="$defaultvalue" />); | ||||
| 2821 | } | ||||
| 2822 | elsif ( length($defaultvalue) > 100 | ||||
| 2823 | or (C4::Context->preference("marcflavour") eq "UNIMARC" and | ||||
| 2824 | 300 <= $tag && $tag < 400 && $subfield eq 'a' ) | ||||
| 2825 | or (C4::Context->preference("marcflavour") eq "MARC21" and | ||||
| 2826 | 500 <= $tag && $tag < 600 ) | ||||
| 2827 | ) { | ||||
| 2828 | # oversize field (textarea) | ||||
| 2829 | $subfield_data{marc_value} = qq(<textarea tabindex="1" id="$subfield_data{id}" name="field_value" class="input_marceditor" size="50" maxlength="255">$defaultvalue</textarea>\n"); | ||||
| 2830 | } else { | ||||
| 2831 | $subfield_data{marc_value} = "<input type=\"text\" name=\"field_value\" value=\"$defaultvalue\" size=\"50\" maxlength=\"255\" />"; | ||||
| 2832 | } | ||||
| 2833 | push( @loop_data, \%subfield_data ); | ||||
| 2834 | } | ||||
| 2835 | } | ||||
| 2836 | } | ||||
| 2837 | my $itemnumber; | ||||
| 2838 | if ( $itemrecord && $itemrecord->field($itemtagfield) ) { | ||||
| 2839 | $itemnumber = $itemrecord->subfield( $itemtagfield, $itemtagsubfield ); | ||||
| 2840 | } | ||||
| 2841 | return { | ||||
| 2842 | 'itemtagfield' => $itemtagfield, | ||||
| 2843 | 'itemtagsubfield' => $itemtagsubfield, | ||||
| 2844 | 'itemnumber' => $itemnumber, | ||||
| 2845 | 'iteminformation' => \@loop_data | ||||
| 2846 | }; | ||||
| 2847 | } | ||||
| 2848 | |||||
| 2849 | 1 | 6µs | 1; |