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
8 # under the terms of the GNU General Public License as published by
9 # the Free Software Foundation; either version 3 of the License, or
10 # (at your option) any later version.
12 # Koha is distributed in the hope that it will be useful, but
13 # WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 # GNU General Public License for more details.
17 # You should have received a copy of the GNU General Public License
18 # along with Koha; if not, see <http://www.gnu.org/licenses>.
23 use List::MoreUtils qw(any);
28 use Koha::DateUtils qw( dt_from_string );
33 use C4::ClassSource; # FIXME We would like to avoid that
34 use C4::Log qw( logaction );
37 use Koha::CirculationRules;
38 use Koha::CoverImages;
39 use Koha::SearchEngine::Indexer;
40 use Koha::Exceptions::Item::Transfer;
41 use Koha::Item::Transfer::Limits;
42 use Koha::Item::Transfers;
47 use Koha::StockRotationItem;
48 use Koha::StockRotationRotas;
50 use base qw(Koha::Object);
54 Koha::Item - Koha Item object class
66 $params can take an optional 'skip_record_index' parameter.
67 If set, the reindexation process will not happen (index_records not called)
69 NOTE: This is a temporary fix to answer a performance issue when lot of items
70 are added (or modified) at the same time.
71 The correct way to fix this is to make the ES reindexation process async.
72 You should not turn it on if you do not understand what it is doing exactly.
78 my $params = @_ ? shift : {};
80 my $log_action = $params->{log_action} // 1;
82 # We do not want to oblige callers to pass this value
83 # Dev conveniences vs performance?
84 unless ( $self->biblioitemnumber ) {
85 $self->biblioitemnumber( $self->biblio->biblioitem->biblioitemnumber );
88 # See related changes from C4::Items::AddItem
89 unless ( $self->itype ) {
90 $self->itype($self->biblio->biblioitem->itemtype);
93 my $today = dt_from_string;
94 my $action = 'create';
96 unless ( $self->in_storage ) { #AddItem
97 unless ( $self->permanent_location ) {
98 $self->permanent_location($self->location);
100 unless ( $self->replacementpricedate ) {
101 $self->replacementpricedate($today);
103 unless ( $self->datelastseen ) {
104 $self->datelastseen($today);
107 unless ( $self->dateaccessioned ) {
108 $self->dateaccessioned($today);
111 if ( $self->itemcallnumber
112 or $self->cn_source )
114 my $cn_sort = GetClassSort( $self->cn_source, $self->itemcallnumber, "" );
115 $self->cn_sort($cn_sort);
122 my %updated_columns = $self->_result->get_dirty_columns;
123 return $self->SUPER::store unless %updated_columns;
125 # Retrieve the item for comparison if we need to
127 exists $updated_columns{itemlost}
128 or exists $updated_columns{withdrawn}
129 or exists $updated_columns{damaged}
130 ) ? $self->get_from_storage : undef;
132 # Update *_on fields if needed
133 # FIXME: Why not for AddItem as well?
134 my @fields = qw( itemlost withdrawn damaged );
135 for my $field (@fields) {
137 # If the field is defined but empty or 0, we are
138 # removing/unsetting and thus need to clear out
140 if ( exists $updated_columns{$field}
141 && defined( $self->$field )
144 my $field_on = "${field}_on";
145 $self->$field_on(undef);
147 # If the field has changed otherwise, we much update
149 elsif (exists $updated_columns{$field}
150 && $updated_columns{$field}
151 && !$pre_mod_item->$field )
153 my $field_on = "${field}_on";
155 DateTime::Format::MySQL->format_datetime(
162 if ( exists $updated_columns{itemcallnumber}
163 or exists $updated_columns{cn_source} )
165 my $cn_sort = GetClassSort( $self->cn_source, $self->itemcallnumber, "" );
166 $self->cn_sort($cn_sort);
170 if ( exists $updated_columns{location}
171 and $self->location ne 'CART'
172 and $self->location ne 'PROC'
173 and not exists $updated_columns{permanent_location} )
175 $self->permanent_location( $self->location );
178 # If item was lost and has now been found,
179 # reverse any list item charges if necessary.
180 if ( exists $updated_columns{itemlost}
181 and $updated_columns{itemlost} <= 0
182 and $pre_mod_item->itemlost > 0 )
184 $self->_set_found_trigger($pre_mod_item);
189 unless ( $self->dateaccessioned ) {
190 $self->dateaccessioned($today);
193 my $result = $self->SUPER::store;
194 if ( $log_action && C4::Context->preference("CataloguingLog") ) {
196 ? logaction( "CATALOGUING", "ADD", $self->itemnumber, "item" )
197 : logaction( "CATALOGUING", "MODIFY", $self->itemnumber, "item " . Dumper( $self->unblessed ) );
199 my $indexer = Koha::SearchEngine::Indexer->new({ index => $Koha::SearchEngine::BIBLIOS_INDEX });
200 $indexer->index_records( $self->biblionumber, "specialUpdate", "biblioserver" )
201 unless $params->{skip_record_index};
202 $self->get_from_storage->_after_item_action_hooks({ action => $action });
213 my $params = @_ ? shift : {};
215 # FIXME check the item has no current issues
216 # i.e. raise the appropriate exception
218 my $result = $self->SUPER::delete;
220 my $indexer = Koha::SearchEngine::Indexer->new({ index => $Koha::SearchEngine::BIBLIOS_INDEX });
221 $indexer->index_records( $self->biblionumber, "specialUpdate", "biblioserver" )
222 unless $params->{skip_record_index};
224 $self->_after_item_action_hooks({ action => 'delete' });
226 logaction( "CATALOGUING", "DELETE", $self->itemnumber, "item" )
227 if C4::Context->preference("CataloguingLog");
238 my $params = @_ ? shift : {};
240 my $safe_to_delete = $self->safe_to_delete;
241 return $safe_to_delete unless $safe_to_delete eq '1';
243 $self->move_to_deleted;
245 return $self->delete($params);
248 =head3 safe_to_delete
250 returns 1 if the item is safe to delete,
252 "book_on_loan" if the item is checked out,
254 "not_same_branch" if the item is blocked by independent branches,
256 "book_reserved" if the there are holds aganst the item, or
258 "linked_analytics" if the item has linked analytic records.
260 "last_item_for_hold" if the item is the last one on a record on which a biblio-level hold is placed
267 return "book_on_loan" if $self->checkout;
269 return "not_same_branch"
270 if defined C4::Context->userenv
271 and !C4::Context->IsSuperLibrarian()
272 and C4::Context->preference("IndependentBranches")
273 and ( C4::Context->userenv->{branch} ne $self->homebranch );
275 # check it doesn't have a waiting reserve
276 return "book_reserved"
277 if $self->holds->search( { found => [ 'W', 'T' ] } )->count;
279 return "linked_analytics"
280 if C4::Items::GetAnalyticsCount( $self->itemnumber ) > 0;
282 return "last_item_for_hold"
283 if $self->biblio->items->count == 1
284 && $self->biblio->holds->search(
293 =head3 move_to_deleted
295 my $is_moved = $item->move_to_deleted;
297 Move an item to the deleteditems table.
298 This can be done before deleting an item, to make sure the data are not completely deleted.
302 sub move_to_deleted {
304 my $item_infos = $self->unblessed;
305 delete $item_infos->{timestamp}; #This ensures the timestamp date in deleteditems will be set to the current timestamp
306 return Koha::Database->new->schema->resultset('Deleteditem')->create($item_infos);
310 =head3 effective_itemtype
312 Returns the itemtype for the item based on whether item level itemtypes are set or not.
316 sub effective_itemtype {
319 return $self->_result()->effective_itemtype();
329 $self->{_home_branch} ||= Koha::Libraries->find( $self->homebranch() );
331 return $self->{_home_branch};
334 =head3 holding_branch
341 $self->{_holding_branch} ||= Koha::Libraries->find( $self->holdingbranch() );
343 return $self->{_holding_branch};
348 my $biblio = $item->biblio;
350 Return the bibliographic record of this item
356 my $biblio_rs = $self->_result->biblio;
357 return Koha::Biblio->_new_from_dbic( $biblio_rs );
362 my $biblioitem = $item->biblioitem;
364 Return the biblioitem record of this item
370 my $biblioitem_rs = $self->_result->biblioitem;
371 return Koha::Biblioitem->_new_from_dbic( $biblioitem_rs );
376 my $checkout = $item->checkout;
378 Return the checkout for this item
384 my $checkout_rs = $self->_result->issue;
385 return unless $checkout_rs;
386 return Koha::Checkout->_new_from_dbic( $checkout_rs );
391 my $holds = $item->holds();
392 my $holds = $item->holds($params);
393 my $holds = $item->holds({ found => 'W'});
395 Return holds attached to an item, optionally accept a hashref of params to pass to search
400 my ( $self,$params ) = @_;
401 my $holds_rs = $self->_result->reserves->search($params);
402 return Koha::Holds->_new_from_dbic( $holds_rs );
405 =head3 request_transfer
407 my $transfer = $item->request_transfer(
408 { to => $to_library, reason => $reason, ignore_limits => 0 } );
410 Add a transfer request for this item to the given branch for the given reason.
412 An exception will be thrown if the BranchTransferLimits would prevent the requested
413 transfer, unless 'ignore_limits' is passed to override the limits.
415 Note: At this time, only one active transfer (i.e pending arrival date) may exist
416 at a time for any given item. An exception will be thrown should you attempt to
417 add a request when a transfer has already been queued, whether it is in transit
418 or just at the request stage.
422 sub request_transfer {
423 my ( $self, $params ) = @_;
425 # check for mandatory params
426 my @mandatory = ( 'to', 'reason' );
427 for my $param (@mandatory) {
428 unless ( defined( $params->{$param} ) ) {
429 Koha::Exceptions::MissingParameter->throw(
430 error => "The $param parameter is mandatory" );
435 Koha::Exceptions::Item::Transfer::Found->throw( transfer => $request )
436 if ( $request = $self->get_transfer );
438 Koha::Exceptions::Item::Transfer::Limit->throw()
439 unless ( $params->{ignore_limits}
440 || $self->can_be_transferred( { to => $params->{to} } ) );
442 my $transfer = Koha::Item::Transfer->new(
444 itemnumber => $self->itemnumber,
445 daterequested => dt_from_string,
446 frombranch => $self->holdingbranch,
447 tobranch => $params->{to}->branchcode,
448 reason => $params->{reason},
449 comments => $params->{comment}
457 my $transfer = $item->get_transfer;
459 Return the active transfer request or undef
461 Note: Transfers are retrieved in a Modified FIFO (First In First Out) order
462 whereby the most recently sent, but not recieved, transfer will be returned
463 if it exists, otherwise the oldest unsatisfied transfer will be returned.
465 FIXME: Add Tests for FIFO functionality
471 my $transfer_rs = $self->_result->branchtransfers->search(
472 { datearrived => undef },
474 order_by => [ { -desc => 'datesent' }, { -asc => 'daterequested' } ],
478 return unless $transfer_rs;
479 return Koha::Item::Transfer->_new_from_dbic($transfer_rs);
482 =head3 last_returned_by
484 Gets and sets the last borrower to return an item.
486 Accepts and returns Koha::Patron objects
488 $item->last_returned_by( $borrowernumber );
490 $last_returned_by = $item->last_returned_by();
494 sub last_returned_by {
495 my ( $self, $borrower ) = @_;
497 my $items_last_returned_by_rs = Koha::Database->new()->schema()->resultset('ItemsLastBorrower');
500 return $items_last_returned_by_rs->update_or_create(
501 { borrowernumber => $borrower->borrowernumber, itemnumber => $self->id } );
504 unless ( $self->{_last_returned_by} ) {
505 my $result = $items_last_returned_by_rs->single( { itemnumber => $self->id } );
507 $self->{_last_returned_by} = Koha::Patrons->find( $result->get_column('borrowernumber') );
511 return $self->{_last_returned_by};
515 =head3 can_article_request
517 my $bool = $item->can_article_request( $borrower )
519 Returns true if item can be specifically requested
521 $borrower must be a Koha::Patron object
525 sub can_article_request {
526 my ( $self, $borrower ) = @_;
528 my $rule = $self->article_request_type($borrower);
530 return 1 if $rule && $rule ne 'no' && $rule ne 'bib_only';
534 =head3 hidden_in_opac
536 my $bool = $item->hidden_in_opac({ [ rules => $rules ] })
538 Returns true if item fields match the hidding criteria defined in $rules.
539 Returns false otherwise.
541 Takes HASHref that can have the following parameters:
543 $rules : { <field> => [ value_1, ... ], ... }
545 Note: $rules inherits its structure from the parsed YAML from reading
546 the I<OpacHiddenItems> system preference.
551 my ( $self, $params ) = @_;
553 my $rules = $params->{rules} // {};
556 if C4::Context->preference('hidelostitems') and
559 my $hidden_in_opac = 0;
561 foreach my $field ( keys %{$rules} ) {
563 if ( any { $self->$field eq $_ } @{ $rules->{$field} } ) {
569 return $hidden_in_opac;
572 =head3 can_be_transferred
574 $item->can_be_transferred({ to => $to_library, from => $from_library })
575 Checks if an item can be transferred to given library.
577 This feature is controlled by two system preferences:
578 UseBranchTransferLimits to enable / disable the feature
579 BranchTransferLimitsType to use either an itemnumber or ccode as an identifier
580 for setting the limitations
582 Takes HASHref that can have the following parameters:
583 MANDATORY PARAMETERS:
586 $from : Koha::Library # if not given, item holdingbranch
587 # will be used instead
589 Returns 1 if item can be transferred to $to_library, otherwise 0.
591 To find out whether at least one item of a Koha::Biblio can be transferred, please
592 see Koha::Biblio->can_be_transferred() instead of using this method for
593 multiple items of the same biblio.
597 sub can_be_transferred {
598 my ($self, $params) = @_;
600 my $to = $params->{to};
601 my $from = $params->{from};
603 $to = $to->branchcode;
604 $from = defined $from ? $from->branchcode : $self->holdingbranch;
606 return 1 if $from eq $to; # Transfer to current branch is allowed
607 return 1 unless C4::Context->preference('UseBranchTransferLimits');
609 my $limittype = C4::Context->preference('BranchTransferLimitsType');
610 return Koha::Item::Transfer::Limits->search({
613 $limittype => $limittype eq 'itemtype'
614 ? $self->effective_itemtype : $self->ccode
619 =head3 pickup_locations
621 $pickup_locations = $item->pickup_locations( {patron => $patron } )
623 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)
624 and if item can be transferred to each pickup location.
628 sub pickup_locations {
629 my ($self, $params) = @_;
631 my $patron = $params->{patron};
633 my $circ_control_branch =
634 C4::Reserves::GetReservesControlBranch( $self->unblessed(), $patron->unblessed );
636 C4::Circulation::GetBranchItemRule( $circ_control_branch, $self->itype );
638 if(defined $patron) {
639 return Koha::Libraries->new()->empty if $branchitemrule->{holdallowed} == 3 && !$self->home_branch->validate_hold_sibling( {branchcode => $patron->branchcode} );
640 return Koha::Libraries->new()->empty if $branchitemrule->{holdallowed} == 1 && $self->home_branch->branchcode ne $patron->branchcode;
643 my $pickup_libraries = Koha::Libraries->search();
644 if ($branchitemrule->{hold_fulfillment_policy} eq 'holdgroup') {
645 $pickup_libraries = $self->home_branch->get_hold_libraries;
646 } elsif ($branchitemrule->{hold_fulfillment_policy} eq 'patrongroup') {
647 my $plib = Koha::Libraries->find({ branchcode => $patron->branchcode});
648 $pickup_libraries = $plib->get_hold_libraries;
649 } elsif ($branchitemrule->{hold_fulfillment_policy} eq 'homebranch') {
650 $pickup_libraries = Koha::Libraries->search({ branchcode => $self->homebranch });
651 } elsif ($branchitemrule->{hold_fulfillment_policy} eq 'holdingbranch') {
652 $pickup_libraries = Koha::Libraries->search({ branchcode => $self->holdingbranch });
655 return $pickup_libraries->search(
660 order_by => ['branchname']
662 ) unless C4::Context->preference('UseBranchTransferLimits');
664 my $limittype = C4::Context->preference('BranchTransferLimitsType');
665 my ($ccode, $itype) = (undef, undef);
666 if( $limittype eq 'ccode' ){
667 $ccode = $self->ccode;
669 $itype = $self->itype;
671 my $limits = Koha::Item::Transfer::Limits->search(
673 fromBranch => $self->holdingbranch,
677 { columns => ['toBranch'] }
680 return $pickup_libraries->search(
682 pickup_location => 1,
684 '-not_in' => $limits->_resultset->as_query
688 order_by => ['branchname']
693 =head3 article_request_type
695 my $type = $item->article_request_type( $borrower )
697 returns 'yes', 'no', 'bib_only', or 'item_only'
699 $borrower must be a Koha::Patron object
703 sub article_request_type {
704 my ( $self, $borrower ) = @_;
706 my $branch_control = C4::Context->preference('HomeOrHoldingBranch');
708 $branch_control eq 'homebranch' ? $self->homebranch
709 : $branch_control eq 'holdingbranch' ? $self->holdingbranch
711 my $borrowertype = $borrower->categorycode;
712 my $itemtype = $self->effective_itemtype();
713 my $rule = Koha::CirculationRules->get_effective_rule(
715 rule_name => 'article_requests',
716 categorycode => $borrowertype,
717 itemtype => $itemtype,
718 branchcode => $branchcode
722 return q{} unless $rule;
723 return $rule->rule_value || q{}
732 my $attributes = { order_by => 'priority' };
733 my $dtf = Koha::Database->new->schema->storage->datetime_parser;
735 itemnumber => $self->itemnumber,
738 reservedate => { '<=' => $dtf->format_date(dt_from_string) },
739 waitingdate => { '!=' => undef },
742 my $hold_rs = $self->_result->reserves->search( $params, $attributes );
743 return Koha::Holds->_new_from_dbic($hold_rs);
746 =head3 stockrotationitem
748 my $sritem = Koha::Item->stockrotationitem;
750 Returns the stock rotation item associated with the current item.
754 sub stockrotationitem {
756 my $rs = $self->_result->stockrotationitem;
758 return Koha::StockRotationItem->_new_from_dbic( $rs );
763 my $item = $item->add_to_rota($rota_id);
765 Add this item to the rota identified by $ROTA_ID, which means associating it
766 with the first stage of that rota. Should this item already be associated
767 with a rota, then we will move it to the new rota.
772 my ( $self, $rota_id ) = @_;
773 Koha::StockRotationRotas->find($rota_id)->add_item($self->itemnumber);
777 =head3 has_pending_hold
779 my $is_pending_hold = $item->has_pending_hold();
781 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
785 sub has_pending_hold {
787 my $pending_hold = $self->_result->tmp_holdsqueues;
788 return $pending_hold->count ? 1: 0;
793 my $mss = C4::Biblio::GetMarcSubfieldStructure( '', { unsafe => 1 } );
794 my $field = $item->as_marc_field({ [ mss => $mss ] });
796 This method returns a MARC::Field object representing the Koha::Item object
797 with the current mappings configuration.
802 my ( $self, $params ) = @_;
804 my $mss = $params->{mss} // C4::Biblio::GetMarcSubfieldStructure( '', { unsafe => 1 } );
805 my $item_tag = $mss->{'items.itemnumber'}[0]->{tagfield};
809 my @columns = $self->_result->result_source->columns;
811 foreach my $item_field ( @columns ) {
812 my $mapping = $mss->{ "items.$item_field"}[0];
813 my $tagfield = $mapping->{tagfield};
814 my $tagsubfield = $mapping->{tagsubfield};
815 next if !$tagfield; # TODO: Should we raise an exception instead?
816 # Feels like safe fallback is better
818 push @subfields, $tagsubfield => $self->$item_field
819 if defined $self->$item_field and $item_field ne '';
822 my $unlinked_item_subfields = C4::Items::_parse_unlinked_item_subfields_from_xml($self->more_subfields_xml);
823 push( @subfields, @{$unlinked_item_subfields} )
824 if defined $unlinked_item_subfields and $#$unlinked_item_subfields > -1;
828 $field = MARC::Field->new(
829 "$item_tag", ' ', ' ', @subfields
835 =head3 renewal_branchcode
837 Returns the branchcode to be recorded in statistics renewal of the item
841 sub renewal_branchcode {
843 my ($self, $params ) = @_;
845 my $interface = C4::Context->interface;
847 if ( $interface eq 'opac' ){
848 my $renewal_branchcode = C4::Context->preference('OpacRenewalBranch');
849 if( !defined $renewal_branchcode || $renewal_branchcode eq 'opacrenew' ){
850 $branchcode = 'OPACRenew';
852 elsif ( $renewal_branchcode eq 'itemhomebranch' ) {
853 $branchcode = $self->homebranch;
855 elsif ( $renewal_branchcode eq 'patronhomebranch' ) {
856 $branchcode = $self->checkout->patron->branchcode;
858 elsif ( $renewal_branchcode eq 'checkoutbranch' ) {
859 $branchcode = $self->checkout->branchcode;
865 $branchcode = ( C4::Context->userenv && defined C4::Context->userenv->{branch} )
866 ? C4::Context->userenv->{branch} : $params->{branch};
873 Return the cover images associated with this item.
880 my $cover_image_rs = $self->_result->cover_images;
881 return unless $cover_image_rs;
882 return Koha::CoverImages->_new_from_dbic($cover_image_rs);
885 =head3 _set_found_trigger
887 $self->_set_found_trigger
889 Finds the most recent lost item charge for this item and refunds the patron
890 appropriately, taking into account any payments or writeoffs already applied
893 Internal function, not exported, called only by Koha::Item->store.
897 sub _set_found_trigger {
898 my ( $self, $pre_mod_item ) = @_;
900 ## If item was lost, it has now been found, reverse any list item charges if necessary.
901 my $no_refund_after_days =
902 C4::Context->preference('NoRefundOnLostReturnedItemsAge');
903 if ($no_refund_after_days) {
904 my $today = dt_from_string();
905 my $lost_age_in_days =
906 dt_from_string( $pre_mod_item->itemlost_on )->delta_days($today)
909 return $self unless $lost_age_in_days < $no_refund_after_days;
912 my $lostreturn_policy = Koha::CirculationRules->get_lostreturn_policy(
915 return_branch => C4::Context->userenv
916 ? C4::Context->userenv->{'branch'}
921 if ( $lostreturn_policy ) {
923 # refund charge made for lost book
924 my $lost_charge = Koha::Account::Lines->search(
926 itemnumber => $self->itemnumber,
927 debit_type_code => 'LOST',
928 status => [ undef, { '<>' => 'FOUND' } ]
931 order_by => { -desc => [ 'date', 'accountlines_id' ] },
936 if ( $lost_charge ) {
938 my $patron = $lost_charge->patron;
941 my $account = $patron->account;
942 my $total_to_refund = 0;
945 if ( $lost_charge->amount > $lost_charge->amountoutstanding ) {
947 # some amount has been cancelled. collect the offsets that are not writeoffs
948 # this works because the only way to subtract from this kind of a debt is
949 # using the UI buttons 'Pay' and 'Write off'
950 my $credits_offsets = Koha::Account::Offsets->search(
952 debit_id => $lost_charge->id,
953 credit_id => { '!=' => undef }, # it is not the debit itself
954 type => { '!=' => 'Writeoff' },
955 amount => { '<' => 0 } # credits are negative on the DB
959 $total_to_refund = ( $credits_offsets->count > 0 )
960 ? $credits_offsets->total * -1 # credits are negative on the DB
964 my $credit_total = $lost_charge->amountoutstanding + $total_to_refund;
967 if ( $credit_total > 0 ) {
969 C4::Context->userenv ? C4::Context->userenv->{'branch'} : undef;
970 $credit = $account->add_credit(
972 amount => $credit_total,
973 description => 'Item found ' . $self->itemnumber,
974 type => 'LOST_FOUND',
975 interface => C4::Context->interface,
976 library_id => $branchcode,
977 item_id => $self->itemnumber,
978 issue_id => $lost_charge->issue_id
982 $credit->apply( { debits => [$lost_charge] } );
983 $self->{_refunded} = 1;
986 # Update the account status
987 $lost_charge->status('FOUND');
988 $lost_charge->store();
990 # Reconcile balances if required
991 if ( C4::Context->preference('AccountAutoReconcile') ) {
992 $account->reconcile_balance;
997 # restore fine for lost book
998 if ( $lostreturn_policy eq 'restore' ) {
999 my $lost_overdue = Koha::Account::Lines->search(
1001 itemnumber => $self->itemnumber,
1002 debit_type_code => 'OVERDUE',
1006 order_by => { '-desc' => 'date' },
1011 if ( $lost_overdue ) {
1013 my $patron = $lost_overdue->patron;
1015 my $account = $patron->account;
1017 # Update status of fine
1018 $lost_overdue->status('FOUND')->store();
1020 # Find related forgive credit
1021 my $refund = $lost_overdue->credits(
1023 credit_type_code => 'FORGIVEN',
1024 itemnumber => $self->itemnumber,
1025 status => [ { '!=' => 'VOID' }, undef ]
1027 { order_by => { '-desc' => 'date' }, rows => 1 }
1031 # Revert the forgive credit
1033 $self->{_restored} = 1;
1036 # Reconcile balances if required
1037 if ( C4::Context->preference('AccountAutoReconcile') ) {
1038 $account->reconcile_balance;
1042 } elsif ( $lostreturn_policy eq 'charge' ) {
1043 $self->{_charge} = 1;
1050 =head3 to_api_mapping
1052 This method returns the mapping for representing a Koha::Item object
1057 sub to_api_mapping {
1059 itemnumber => 'item_id',
1060 biblionumber => 'biblio_id',
1061 biblioitemnumber => undef,
1062 barcode => 'external_id',
1063 dateaccessioned => 'acquisition_date',
1064 booksellerid => 'acquisition_source',
1065 homebranch => 'home_library_id',
1066 price => 'purchase_price',
1067 replacementprice => 'replacement_price',
1068 replacementpricedate => 'replacement_price_date',
1069 datelastborrowed => 'last_checkout_date',
1070 datelastseen => 'last_seen_date',
1072 notforloan => 'not_for_loan_status',
1073 damaged => 'damaged_status',
1074 damaged_on => 'damaged_date',
1075 itemlost => 'lost_status',
1076 itemlost_on => 'lost_date',
1077 withdrawn => 'withdrawn',
1078 withdrawn_on => 'withdrawn_date',
1079 itemcallnumber => 'callnumber',
1080 coded_location_qualifier => 'coded_location_qualifier',
1081 issues => 'checkouts_count',
1082 renewals => 'renewals_count',
1083 reserves => 'holds_count',
1084 restricted => 'restricted_status',
1085 itemnotes => 'public_notes',
1086 itemnotes_nonpublic => 'internal_notes',
1087 holdingbranch => 'holding_library_id',
1088 timestamp => 'timestamp',
1089 location => 'location',
1090 permanent_location => 'permanent_location',
1091 onloan => 'checked_out_date',
1092 cn_source => 'call_number_source',
1093 cn_sort => 'call_number_sort',
1094 ccode => 'collection_code',
1095 materials => 'materials_notes',
1097 itype => 'item_type',
1098 more_subfields_xml => 'extended_subfields',
1099 enumchron => 'serial_issue_number',
1100 copynumber => 'copy_number',
1101 stocknumber => 'inventory_number',
1102 new_status => 'new_status'
1108 my $itemtype = $item->itemtype;
1110 Returns Koha object for effective itemtype
1116 return Koha::ItemTypes->find( $self->effective_itemtype );
1119 =head2 Internal methods
1121 =head3 _after_item_action_hooks
1123 Helper method that takes care of calling all plugin hooks
1127 sub _after_item_action_hooks {
1128 my ( $self, $params ) = @_;
1130 my $action = $params->{action};
1132 Koha::Plugins->call(
1133 'after_item_action',
1137 item_id => $self->itemnumber,
1152 Kyle M Hall <kyle@bywatersolutions.com>