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 | BEGIN@33 | C4::Items::
1 | 1 | 1 | 756µs | 773µs | BEGIN@21 | C4::Items::
1 | 1 | 1 | 14µs | 85µs | BEGIN@32 | C4::Items::
1 | 1 | 1 | 11µs | 11µs | BEGIN@39 | C4::Items::
1 | 1 | 1 | 11µs | 30µs | BEGIN@34 | C4::Items::
1 | 1 | 1 | 10µs | 20µs | BEGIN@29 | C4::Items::
1 | 1 | 1 | 10µs | 12µs | BEGIN@25 | C4::Items::
1 | 1 | 1 | 9µs | 46µs | BEGIN@24 | C4::Items::
1 | 1 | 1 | 9µs | 34µs | BEGIN@28 | C4::Items::
1 | 1 | 1 | 8µs | 259µs | BEGIN@26 | C4::Items::
1 | 1 | 1 | 8µs | 292µs | BEGIN@27 | C4::Items::
1 | 1 | 1 | 8µs | 65µs | BEGIN@31 | C4::Items::
1 | 1 | 1 | 8µs | 46µs | BEGIN@37 | C4::Items::
1 | 1 | 1 | 8µs | 107µs | BEGIN@30 | C4::Items::
0 | 0 | 0 | 0s | 0s | AddItem | C4::Items::
0 | 0 | 0 | 0s | 0s | AddItemBatchFromMarc | C4::Items::
0 | 0 | 0 | 0s | 0s | AddItemFromMarc | C4::Items::
0 | 0 | 0 | 0s | 0s | CartToShelf | C4::Items::
0 | 0 | 0 | 0s | 0s | CheckItemPreSave | C4::Items::
0 | 0 | 0 | 0s | 0s | DelItem | C4::Items::
0 | 0 | 0 | 0s | 0s | DelItemCheck | C4::Items::
0 | 0 | 0 | 0s | 0s | GetAnalyticsCount | C4::Items::
0 | 0 | 0 | 0s | 0s | GetBarcodeFromItemnumber | C4::Items::
0 | 0 | 0 | 0s | 0s | GetHiddenItemnumbers | C4::Items::
0 | 0 | 0 | 0s | 0s | GetHostItemsInfo | C4::Items::
0 | 0 | 0 | 0s | 0s | GetItem | C4::Items::
0 | 0 | 0 | 0s | 0s | GetItemHolds | C4::Items::
0 | 0 | 0 | 0s | 0s | GetItemInfosOf | C4::Items::
0 | 0 | 0 | 0s | 0s | GetItemLocation | C4::Items::
0 | 0 | 0 | 0s | 0s | GetItemStatus | C4::Items::
0 | 0 | 0 | 0s | 0s | GetItemnumberFromBarcode | C4::Items::
0 | 0 | 0 | 0s | 0s | GetItemnumbersForBiblio | C4::Items::
0 | 0 | 0 | 0s | 0s | GetItemsByBiblioitemnumber | C4::Items::
0 | 0 | 0 | 0s | 0s | GetItemsCount | C4::Items::
0 | 0 | 0 | 0s | 0s | GetItemsForInventory | C4::Items::
0 | 0 | 0 | 0s | 0s | GetItemsInfo | C4::Items::
0 | 0 | 0 | 0s | 0s | GetItemsLocationInfo | C4::Items::
0 | 0 | 0 | 0s | 0s | GetLastAcquisitions | C4::Items::
0 | 0 | 0 | 0s | 0s | GetLostItems | C4::Items::
0 | 0 | 0 | 0s | 0s | GetMarcItem | C4::Items::
0 | 0 | 0 | 0s | 0s | Item2Marc | C4::Items::
0 | 0 | 0 | 0s | 0s | ModDateLastSeen | C4::Items::
0 | 0 | 0 | 0s | 0s | ModItem | C4::Items::
0 | 0 | 0 | 0s | 0s | ModItemFromMarc | C4::Items::
0 | 0 | 0 | 0s | 0s | ModItemTransfer | C4::Items::
0 | 0 | 0 | 0s | 0s | MoveItemFromBiblio | C4::Items::
0 | 0 | 0 | 0s | 0s | PrepareItemrecordDisplay | C4::Items::
0 | 0 | 0 | 0s | 0s | SearchItems | C4::Items::
0 | 0 | 0 | 0s | 0s | ShelfToCart | C4::Items::
0 | 0 | 0 | 0s | 0s | __ANON__[:1679] | C4::Items::
0 | 0 | 0 | 0s | 0s | _calc_items_cn_sort | C4::Items::
0 | 0 | 0 | 0s | 0s | _do_column_fixes_for_mod | C4::Items::
0 | 0 | 0 | 0s | 0s | _find_value | C4::Items::
0 | 0 | 0 | 0s | 0s | _get_items_columns | C4::Items::
0 | 0 | 0 | 0s | 0s | _get_single_item_column | C4::Items::
0 | 0 | 0 | 0s | 0s | _get_unlinked_item_subfields | C4::Items::
0 | 0 | 0 | 0s | 0s | _get_unlinked_subfields_xml | C4::Items::
0 | 0 | 0 | 0s | 0s | _koha_delete_item | C4::Items::
0 | 0 | 0 | 0s | 0s | _koha_modify_item | C4::Items::
0 | 0 | 0 | 0s | 0s | _koha_new_item | C4::Items::
0 | 0 | 0 | 0s | 0s | _marc_from_item_hash | C4::Items::
0 | 0 | 0 | 0s | 0s | _parse_unlinked_item_subfields_from_xml | C4::Items::
0 | 0 | 0 | 0s | 0s | _repack_item_errors | C4::Items::
0 | 0 | 0 | 0s | 0s | _set_defaults_for_add | C4::Items::
0 | 0 | 0 | 0s | 0s | _set_derived_columns_for_add | C4::Items::
0 | 0 | 0 | 0s | 0s | _set_derived_columns_for_mod | C4::Items::
0 | 0 | 0 | 0s | 0s | get_authorised_value_images | C4::Items::
0 | 0 | 0 | 0s | 0s | get_hostitemnumbers_of | C4::Items::
0 | 0 | 0 | 0s | 0s | get_item_authorised_values | C4::Items::
0 | 0 | 0 | 0s | 0s | get_itemnumbers_of | C4::Items::
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; |