3 # Copyright ByWater Solutions 2014
5 # This file is part of Koha.
7 # Koha is free software; you can redistribute it and/or modify it under the
8 # terms of the GNU General Public License as published by the Free Software
9 # Foundation; either version 3 of the License, or (at your option) any later
12 # Koha is distributed in the hope that it will be useful, but WITHOUT ANY
13 # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
14 # A PARTICULAR PURPOSE. See the GNU General Public License for more details.
16 # You should have received a copy of the GNU General Public License along
17 # with Koha; if not, write to the Free Software Foundation, Inc.,
18 # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
23 use List
::MoreUtils
qw(any);
26 use Koha
::DateUtils
qw( dt_from_string );
32 use Koha
::IssuingRules
;
33 use Koha
::Item
::Transfer
::Limits
;
34 use Koha
::Item
::Transfers
;
37 use Koha
::StockRotationItem
;
38 use Koha
::StockRotationRotas
;
40 use base
qw(Koha::Object);
44 Koha::Item - Koha Item object class
52 =head3 effective_itemtype
54 Returns the itemtype for the item based on whether item level itemtypes are set or not.
58 sub effective_itemtype
{
61 return $self->_result()->effective_itemtype();
71 $self->{_home_branch
} ||= Koha
::Libraries
->find( $self->homebranch() );
73 return $self->{_home_branch
};
83 $self->{_holding_branch
} ||= Koha
::Libraries
->find( $self->holdingbranch() );
85 return $self->{_holding_branch
};
90 my $biblio = $item->biblio;
92 Return the bibliographic record of this item
98 my $biblio_rs = $self->_result->biblio;
99 return Koha
::Biblio
->_new_from_dbic( $biblio_rs );
104 my $biblioitem = $item->biblioitem;
106 Return the biblioitem record of this item
112 my $biblioitem_rs = $self->_result->biblioitem;
113 return Koha
::Biblioitem
->_new_from_dbic( $biblioitem_rs );
118 my $checkout = $item->checkout;
120 Return the checkout for this item
126 my $checkout_rs = $self->_result->issue;
127 return unless $checkout_rs;
128 return Koha
::Checkout
->_new_from_dbic( $checkout_rs );
133 my $holds = $item->holds();
134 my $holds = $item->holds($params);
135 my $holds = $item->holds({ found => 'W'});
137 Return holds attached to an item, optionally accept a hashref of params to pass to search
142 my ( $self,$params ) = @_;
143 my $holds_rs = $self->_result->reserves->search($params);
144 return Koha
::Holds
->_new_from_dbic( $holds_rs );
149 my $transfer = $item->get_transfer;
151 Return the transfer if the item is in transit or undef
157 my $transfer_rs = $self->_result->branchtransfers->search({ datearrived
=> undef })->first;
158 return unless $transfer_rs;
159 return Koha
::Item
::Transfer
->_new_from_dbic( $transfer_rs );
162 =head3 last_returned_by
164 Gets and sets the last borrower to return an item.
166 Accepts and returns Koha::Patron objects
168 $item->last_returned_by( $borrowernumber );
170 $last_returned_by = $item->last_returned_by();
174 sub last_returned_by
{
175 my ( $self, $borrower ) = @_;
177 my $items_last_returned_by_rs = Koha
::Database
->new()->schema()->resultset('ItemsLastBorrower');
180 return $items_last_returned_by_rs->update_or_create(
181 { borrowernumber
=> $borrower->borrowernumber, itemnumber
=> $self->id } );
184 unless ( $self->{_last_returned_by
} ) {
185 my $result = $items_last_returned_by_rs->single( { itemnumber
=> $self->id } );
187 $self->{_last_returned_by
} = Koha
::Patrons
->find( $result->get_column('borrowernumber') );
191 return $self->{_last_returned_by
};
195 =head3 can_article_request
197 my $bool = $item->can_article_request( $borrower )
199 Returns true if item can be specifically requested
201 $borrower must be a Koha::Patron object
205 sub can_article_request
{
206 my ( $self, $borrower ) = @_;
208 my $rule = $self->article_request_type($borrower);
210 return 1 if $rule && $rule ne 'no' && $rule ne 'bib_only';
214 =head3 hidden_in_opac
216 my $bool = $item->hidden_in_opac({ [ rules => $rules ] })
218 Returns true if item fields match the hidding criteria defined in $rules.
219 Returns false otherwise.
221 Takes HASHref that can have the following parameters:
223 $rules : { <field> => [ value_1, ... ], ... }
225 Note: $rules inherits its structure from the parsed YAML from reading
226 the I<OpacHiddenItems> system preference.
231 my ( $self, $params ) = @_;
233 my $rules = $params->{rules
} // {};
236 if C4
::Context
->preference('hidelostitems') and
239 my $hidden_in_opac = 0;
241 foreach my $field ( keys %{$rules} ) {
243 if ( any
{ $self->$field eq $_ } @
{ $rules->{$field} } ) {
249 return $hidden_in_opac;
252 =head3 can_be_transferred
254 $item->can_be_transferred({ to => $to_library, from => $from_library })
255 Checks if an item can be transferred to given library.
257 This feature is controlled by two system preferences:
258 UseBranchTransferLimits to enable / disable the feature
259 BranchTransferLimitsType to use either an itemnumber or ccode as an identifier
260 for setting the limitations
262 Takes HASHref that can have the following parameters:
263 MANDATORY PARAMETERS:
266 $from : Koha::Library # if not given, item holdingbranch
267 # will be used instead
269 Returns 1 if item can be transferred to $to_library, otherwise 0.
271 To find out whether at least one item of a Koha::Biblio can be transferred, please
272 see Koha::Biblio->can_be_transferred() instead of using this method for
273 multiple items of the same biblio.
277 sub can_be_transferred
{
278 my ($self, $params) = @_;
280 my $to = $params->{to
};
281 my $from = $params->{from
};
283 $to = $to->branchcode;
284 $from = defined $from ?
$from->branchcode : $self->holdingbranch;
286 return 1 if $from eq $to; # Transfer to current branch is allowed
287 return 1 unless C4
::Context
->preference('UseBranchTransferLimits');
289 my $limittype = C4
::Context
->preference('BranchTransferLimitsType');
290 return Koha
::Item
::Transfer
::Limits
->search({
293 $limittype => $limittype eq 'itemtype'
294 ?
$self->effective_itemtype : $self->ccode
298 =head3 pickup_locations
300 @pickup_locations = $item->pickup_locations( {patron => $patron } )
302 Returns possible pickup locations for this item, according to patron's home library (if patron is defined and holds are allowed only from hold groups)
303 and if item can be transferred to each pickup location.
307 sub pickup_locations
{
308 my ($self, $params) = @_;
310 my $patron = $params->{patron
};
312 my $circ_control_branch =
313 C4
::Reserves
::GetReservesControlBranch
( $self->unblessed(), $patron->unblessed );
315 C4
::Circulation
::GetBranchItemRule
( $circ_control_branch, $self->itype );
318 if(defined $patron) {
319 return @libs if $branchitemrule->{holdallowed
} == 3 && !$self->home_branch->validate_hold_sibling( {branchcode
=> $patron->branchcode} );
320 return @libs if $branchitemrule->{holdallowed
} == 1 && $self->home_branch->branchcode ne $patron->branchcode;
323 if ($branchitemrule->{hold_fulfillment_policy
} eq 'holdgroup') {
324 @libs = $self->home_branch->get_hold_libraries;
325 push @libs, $self->home_branch unless scalar(@libs) > 0;
326 } elsif ($branchitemrule->{hold_fulfillment_policy
} eq 'patrongroup') {
327 my $plib = Koha
::Libraries
->find({ branchcode
=> $patron->branchcode});
328 @libs = $plib->get_hold_libraries;
329 push @libs, $self->home_branch unless scalar(@libs) > 0;
330 } elsif ($branchitemrule->{hold_fulfillment_policy
} eq 'homebranch') {
331 push @libs, $self->home_branch;
332 } elsif ($branchitemrule->{hold_fulfillment_policy
} eq 'holdingbranch') {
333 push @libs, $self->holding_branch;
335 @libs = Koha
::Libraries
->search({
338 order_by
=> ['branchname']
342 my @pickup_locations;
343 foreach my $library (@libs) {
344 if ($library->pickup_location && $self->can_be_transferred({ to
=> $library })) {
345 push @pickup_locations, $library;
349 return wantarray ?
@pickup_locations : \
@pickup_locations;
352 =head3 article_request_type
354 my $type = $item->article_request_type( $borrower )
356 returns 'yes', 'no', 'bib_only', or 'item_only'
358 $borrower must be a Koha::Patron object
362 sub article_request_type
{
363 my ( $self, $borrower ) = @_;
365 my $branch_control = C4
::Context
->preference('HomeOrHoldingBranch');
367 $branch_control eq 'homebranch' ?
$self->homebranch
368 : $branch_control eq 'holdingbranch' ?
$self->holdingbranch
370 my $borrowertype = $borrower->categorycode;
371 my $itemtype = $self->effective_itemtype();
372 my $issuing_rule = Koha
::IssuingRules
->get_effective_issuing_rule({ categorycode
=> $borrowertype, itemtype
=> $itemtype, branchcode
=> $branchcode });
374 return q{} unless $issuing_rule;
375 return $issuing_rule->article_requests || q{}
384 my $attributes = { order_by
=> 'priority' };
385 my $dtf = Koha
::Database
->new->schema->storage->datetime_parser;
387 itemnumber
=> $self->itemnumber,
390 reservedate
=> { '<=' => $dtf->format_date(dt_from_string
) },
391 waitingdate
=> { '!=' => undef },
394 my $hold_rs = $self->_result->reserves->search( $params, $attributes );
395 return Koha
::Holds
->_new_from_dbic($hold_rs);
398 =head3 stockrotationitem
400 my $sritem = Koha::Item->stockrotationitem;
402 Returns the stock rotation item associated with the current item.
406 sub stockrotationitem
{
408 my $rs = $self->_result->stockrotationitem;
410 return Koha
::StockRotationItem
->_new_from_dbic( $rs );
415 my $item = $item->add_to_rota($rota_id);
417 Add this item to the rota identified by $ROTA_ID, which means associating it
418 with the first stage of that rota. Should this item already be associated
419 with a rota, then we will move it to the new rota.
424 my ( $self, $rota_id ) = @_;
425 Koha
::StockRotationRotas
->find($rota_id)->add_item($self->itemnumber);
429 =head3 has_pending_hold
431 my $is_pending_hold = $item->has_pending_hold();
433 This method checks the tmp_holdsqueue to see if this item has been selected for a hold, but not filled yet and returns true or false
437 sub has_pending_hold
{
439 my $pending_hold = $self->_result->tmp_holdsqueues;
440 return $pending_hold->count ?
1: 0;
445 my $mss = C4::Biblio::GetMarcSubfieldStructure( '', { unsafe => 1 } );
446 my $field = $item->as_marc_field({ [ mss => $mss ] });
448 This method returns a MARC::Field object representing the Koha::Item object
449 with the current mappings configuration.
454 my ( $self, $params ) = @_;
456 my $mss = $params->{mss
} // C4
::Biblio
::GetMarcSubfieldStructure
( '', { unsafe
=> 1 } );
457 my $item_tag = $mss->{'items.itemnumber'}[0]->{tagfield
};
461 my @columns = $self->_result->result_source->columns;
463 foreach my $item_field ( @columns ) {
464 my $mapping = $mss->{ "items.$item_field"}[0];
465 my $tagfield = $mapping->{tagfield
};
466 my $tagsubfield = $mapping->{tagsubfield
};
467 next if !$tagfield; # TODO: Should we raise an exception instead?
468 # Feels like safe fallback is better
470 push @subfields, $tagsubfield => $self->$item_field;
473 my $unlinked_item_subfields = C4
::Items
::_parse_unlinked_item_subfields_from_xml
($self->more_subfields_xml);
474 push( @subfields, @
{$unlinked_item_subfields} )
475 if defined $unlinked_item_subfields and $#$unlinked_item_subfields > -1;
479 $field = MARC
::Field
->new(
480 "$item_tag", ' ', ' ', @subfields
486 =head3 to_api_mapping
488 This method returns the mapping for representing a Koha::Item object
495 itemnumber
=> 'item_id',
496 biblionumber
=> 'biblio_id',
497 biblioitemnumber
=> undef,
498 barcode
=> 'external_id',
499 dateaccessioned
=> 'acquisition_date',
500 booksellerid
=> 'acquisition_source',
501 homebranch
=> 'home_library_id',
502 price
=> 'purchase_price',
503 replacementprice
=> 'replacement_price',
504 replacementpricedate
=> 'replacement_price_date',
505 datelastborrowed
=> 'last_checkout_date',
506 datelastseen
=> 'last_seen_date',
508 notforloan
=> 'not_for_loan_status',
509 damaged
=> 'damaged_status',
510 damaged_on
=> 'damaged_date',
511 itemlost
=> 'lost_status',
512 itemlost_on
=> 'lost_date',
513 withdrawn
=> 'withdrawn',
514 withdrawn_on
=> 'withdrawn_date',
515 itemcallnumber
=> 'callnumber',
516 coded_location_qualifier
=> 'coded_location_qualifier',
517 issues
=> 'checkouts_count',
518 renewals
=> 'renewals_count',
519 reserves
=> 'holds_count',
520 restricted
=> 'restricted_status',
521 itemnotes
=> 'public_notes',
522 itemnotes_nonpublic
=> 'internal_notes',
523 holdingbranch
=> 'holding_library_id',
525 timestamp
=> 'timestamp',
526 location
=> 'location',
527 permanent_location
=> 'permanent_location',
528 onloan
=> 'checked_out_date',
529 cn_source
=> 'call_number_source',
530 cn_sort
=> 'call_number_sort',
531 ccode
=> 'collection_code',
532 materials
=> 'materials_notes',
534 itype
=> 'item_type',
535 more_subfields_xml
=> 'extended_subfields',
536 enumchron
=> 'serial_issue_number',
537 copynumber
=> 'copy_number',
538 stocknumber
=> 'inventory_number',
539 new_status
=> 'new_status'
543 =head2 Internal methods
555 Kyle M Hall <kyle@bywatersolutions.com>