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, force => 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 'force' 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 );
437 # FIXME: Add override functionality to allow for queing transfers
439 Koha::Exceptions::Item::Transfer::Limit->throw()
440 unless ( $params->{force} || $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 LIFO (Last In First Out) order using this method.
463 FIXME: Add Tests for LIFO functionality
469 my $transfer_rs = $self->_result->branchtransfers->search(
470 { datearrived => undef },
472 order_by => [ { -asc => 'datesent' }, { -asc => 'daterequested' } ],
476 return unless $transfer_rs;
477 return Koha::Item::Transfer->_new_from_dbic($transfer_rs);
480 =head3 last_returned_by
482 Gets and sets the last borrower to return an item.
484 Accepts and returns Koha::Patron objects
486 $item->last_returned_by( $borrowernumber );
488 $last_returned_by = $item->last_returned_by();
492 sub last_returned_by {
493 my ( $self, $borrower ) = @_;
495 my $items_last_returned_by_rs = Koha::Database->new()->schema()->resultset('ItemsLastBorrower');
498 return $items_last_returned_by_rs->update_or_create(
499 { borrowernumber => $borrower->borrowernumber, itemnumber => $self->id } );
502 unless ( $self->{_last_returned_by} ) {
503 my $result = $items_last_returned_by_rs->single( { itemnumber => $self->id } );
505 $self->{_last_returned_by} = Koha::Patrons->find( $result->get_column('borrowernumber') );
509 return $self->{_last_returned_by};
513 =head3 can_article_request
515 my $bool = $item->can_article_request( $borrower )
517 Returns true if item can be specifically requested
519 $borrower must be a Koha::Patron object
523 sub can_article_request {
524 my ( $self, $borrower ) = @_;
526 my $rule = $self->article_request_type($borrower);
528 return 1 if $rule && $rule ne 'no' && $rule ne 'bib_only';
532 =head3 hidden_in_opac
534 my $bool = $item->hidden_in_opac({ [ rules => $rules ] })
536 Returns true if item fields match the hidding criteria defined in $rules.
537 Returns false otherwise.
539 Takes HASHref that can have the following parameters:
541 $rules : { <field> => [ value_1, ... ], ... }
543 Note: $rules inherits its structure from the parsed YAML from reading
544 the I<OpacHiddenItems> system preference.
549 my ( $self, $params ) = @_;
551 my $rules = $params->{rules} // {};
554 if C4::Context->preference('hidelostitems') and
557 my $hidden_in_opac = 0;
559 foreach my $field ( keys %{$rules} ) {
561 if ( any { $self->$field eq $_ } @{ $rules->{$field} } ) {
567 return $hidden_in_opac;
570 =head3 can_be_transferred
572 $item->can_be_transferred({ to => $to_library, from => $from_library })
573 Checks if an item can be transferred to given library.
575 This feature is controlled by two system preferences:
576 UseBranchTransferLimits to enable / disable the feature
577 BranchTransferLimitsType to use either an itemnumber or ccode as an identifier
578 for setting the limitations
580 Takes HASHref that can have the following parameters:
581 MANDATORY PARAMETERS:
584 $from : Koha::Library # if not given, item holdingbranch
585 # will be used instead
587 Returns 1 if item can be transferred to $to_library, otherwise 0.
589 To find out whether at least one item of a Koha::Biblio can be transferred, please
590 see Koha::Biblio->can_be_transferred() instead of using this method for
591 multiple items of the same biblio.
595 sub can_be_transferred {
596 my ($self, $params) = @_;
598 my $to = $params->{to};
599 my $from = $params->{from};
601 $to = $to->branchcode;
602 $from = defined $from ? $from->branchcode : $self->holdingbranch;
604 return 1 if $from eq $to; # Transfer to current branch is allowed
605 return 1 unless C4::Context->preference('UseBranchTransferLimits');
607 my $limittype = C4::Context->preference('BranchTransferLimitsType');
608 return Koha::Item::Transfer::Limits->search({
611 $limittype => $limittype eq 'itemtype'
612 ? $self->effective_itemtype : $self->ccode
617 =head3 pickup_locations
619 $pickup_locations = $item->pickup_locations( {patron => $patron } )
621 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)
622 and if item can be transferred to each pickup location.
626 sub pickup_locations {
627 my ($self, $params) = @_;
629 my $patron = $params->{patron};
631 my $circ_control_branch =
632 C4::Reserves::GetReservesControlBranch( $self->unblessed(), $patron->unblessed );
634 C4::Circulation::GetBranchItemRule( $circ_control_branch, $self->itype );
636 if(defined $patron) {
637 return Koha::Libraries->new()->empty if $branchitemrule->{holdallowed} == 3 && !$self->home_branch->validate_hold_sibling( {branchcode => $patron->branchcode} );
638 return Koha::Libraries->new()->empty if $branchitemrule->{holdallowed} == 1 && $self->home_branch->branchcode ne $patron->branchcode;
641 my $pickup_libraries = Koha::Libraries->search();
642 if ($branchitemrule->{hold_fulfillment_policy} eq 'holdgroup') {
643 $pickup_libraries = $self->home_branch->get_hold_libraries;
644 } elsif ($branchitemrule->{hold_fulfillment_policy} eq 'patrongroup') {
645 my $plib = Koha::Libraries->find({ branchcode => $patron->branchcode});
646 $pickup_libraries = $plib->get_hold_libraries;
647 } elsif ($branchitemrule->{hold_fulfillment_policy} eq 'homebranch') {
648 $pickup_libraries = Koha::Libraries->search({ branchcode => $self->homebranch });
649 } elsif ($branchitemrule->{hold_fulfillment_policy} eq 'holdingbranch') {
650 $pickup_libraries = Koha::Libraries->search({ branchcode => $self->holdingbranch });
653 return $pickup_libraries->search(
658 order_by => ['branchname']
660 ) unless C4::Context->preference('UseBranchTransferLimits');
662 my $limittype = C4::Context->preference('BranchTransferLimitsType');
663 my ($ccode, $itype) = (undef, undef);
664 if( $limittype eq 'ccode' ){
665 $ccode = $self->ccode;
667 $itype = $self->itype;
669 my $limits = Koha::Item::Transfer::Limits->search(
671 fromBranch => $self->holdingbranch,
675 { columns => ['toBranch'] }
678 return $pickup_libraries->search(
680 pickup_location => 1,
682 '-not_in' => $limits->_resultset->as_query
686 order_by => ['branchname']
691 =head3 article_request_type
693 my $type = $item->article_request_type( $borrower )
695 returns 'yes', 'no', 'bib_only', or 'item_only'
697 $borrower must be a Koha::Patron object
701 sub article_request_type {
702 my ( $self, $borrower ) = @_;
704 my $branch_control = C4::Context->preference('HomeOrHoldingBranch');
706 $branch_control eq 'homebranch' ? $self->homebranch
707 : $branch_control eq 'holdingbranch' ? $self->holdingbranch
709 my $borrowertype = $borrower->categorycode;
710 my $itemtype = $self->effective_itemtype();
711 my $rule = Koha::CirculationRules->get_effective_rule(
713 rule_name => 'article_requests',
714 categorycode => $borrowertype,
715 itemtype => $itemtype,
716 branchcode => $branchcode
720 return q{} unless $rule;
721 return $rule->rule_value || q{}
730 my $attributes = { order_by => 'priority' };
731 my $dtf = Koha::Database->new->schema->storage->datetime_parser;
733 itemnumber => $self->itemnumber,
736 reservedate => { '<=' => $dtf->format_date(dt_from_string) },
737 waitingdate => { '!=' => undef },
740 my $hold_rs = $self->_result->reserves->search( $params, $attributes );
741 return Koha::Holds->_new_from_dbic($hold_rs);
744 =head3 stockrotationitem
746 my $sritem = Koha::Item->stockrotationitem;
748 Returns the stock rotation item associated with the current item.
752 sub stockrotationitem {
754 my $rs = $self->_result->stockrotationitem;
756 return Koha::StockRotationItem->_new_from_dbic( $rs );
761 my $item = $item->add_to_rota($rota_id);
763 Add this item to the rota identified by $ROTA_ID, which means associating it
764 with the first stage of that rota. Should this item already be associated
765 with a rota, then we will move it to the new rota.
770 my ( $self, $rota_id ) = @_;
771 Koha::StockRotationRotas->find($rota_id)->add_item($self->itemnumber);
775 =head3 has_pending_hold
777 my $is_pending_hold = $item->has_pending_hold();
779 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
783 sub has_pending_hold {
785 my $pending_hold = $self->_result->tmp_holdsqueues;
786 return $pending_hold->count ? 1: 0;
791 my $mss = C4::Biblio::GetMarcSubfieldStructure( '', { unsafe => 1 } );
792 my $field = $item->as_marc_field({ [ mss => $mss ] });
794 This method returns a MARC::Field object representing the Koha::Item object
795 with the current mappings configuration.
800 my ( $self, $params ) = @_;
802 my $mss = $params->{mss} // C4::Biblio::GetMarcSubfieldStructure( '', { unsafe => 1 } );
803 my $item_tag = $mss->{'items.itemnumber'}[0]->{tagfield};
807 my @columns = $self->_result->result_source->columns;
809 foreach my $item_field ( @columns ) {
810 my $mapping = $mss->{ "items.$item_field"}[0];
811 my $tagfield = $mapping->{tagfield};
812 my $tagsubfield = $mapping->{tagsubfield};
813 next if !$tagfield; # TODO: Should we raise an exception instead?
814 # Feels like safe fallback is better
816 push @subfields, $tagsubfield => $self->$item_field
817 if defined $self->$item_field and $item_field ne '';
820 my $unlinked_item_subfields = C4::Items::_parse_unlinked_item_subfields_from_xml($self->more_subfields_xml);
821 push( @subfields, @{$unlinked_item_subfields} )
822 if defined $unlinked_item_subfields and $#$unlinked_item_subfields > -1;
826 $field = MARC::Field->new(
827 "$item_tag", ' ', ' ', @subfields
833 =head3 renewal_branchcode
835 Returns the branchcode to be recorded in statistics renewal of the item
839 sub renewal_branchcode {
841 my ($self, $params ) = @_;
843 my $interface = C4::Context->interface;
845 if ( $interface eq 'opac' ){
846 my $renewal_branchcode = C4::Context->preference('OpacRenewalBranch');
847 if( !defined $renewal_branchcode || $renewal_branchcode eq 'opacrenew' ){
848 $branchcode = 'OPACRenew';
850 elsif ( $renewal_branchcode eq 'itemhomebranch' ) {
851 $branchcode = $self->homebranch;
853 elsif ( $renewal_branchcode eq 'patronhomebranch' ) {
854 $branchcode = $self->checkout->patron->branchcode;
856 elsif ( $renewal_branchcode eq 'checkoutbranch' ) {
857 $branchcode = $self->checkout->branchcode;
863 $branchcode = ( C4::Context->userenv && defined C4::Context->userenv->{branch} )
864 ? C4::Context->userenv->{branch} : $params->{branch};
871 Return the cover images associated with this item.
878 my $cover_image_rs = $self->_result->cover_images;
879 return unless $cover_image_rs;
880 return Koha::CoverImages->_new_from_dbic($cover_image_rs);
883 =head3 _set_found_trigger
885 $self->_set_found_trigger
887 Finds the most recent lost item charge for this item and refunds the patron
888 appropriately, taking into account any payments or writeoffs already applied
891 Internal function, not exported, called only by Koha::Item->store.
895 sub _set_found_trigger {
896 my ( $self, $pre_mod_item ) = @_;
898 ## If item was lost, it has now been found, reverse any list item charges if necessary.
899 my $no_refund_after_days =
900 C4::Context->preference('NoRefundOnLostReturnedItemsAge');
901 if ($no_refund_after_days) {
902 my $today = dt_from_string();
903 my $lost_age_in_days =
904 dt_from_string( $pre_mod_item->itemlost_on )->delta_days($today)
907 return $self unless $lost_age_in_days < $no_refund_after_days;
910 my $lostreturn_policy = Koha::CirculationRules->get_lostreturn_policy(
913 return_branch => C4::Context->userenv
914 ? C4::Context->userenv->{'branch'}
919 if ( $lostreturn_policy ) {
921 # refund charge made for lost book
922 my $lost_charge = Koha::Account::Lines->search(
924 itemnumber => $self->itemnumber,
925 debit_type_code => 'LOST',
926 status => [ undef, { '<>' => 'FOUND' } ]
929 order_by => { -desc => [ 'date', 'accountlines_id' ] },
934 if ( $lost_charge ) {
936 my $patron = $lost_charge->patron;
939 my $account = $patron->account;
940 my $total_to_refund = 0;
943 if ( $lost_charge->amount > $lost_charge->amountoutstanding ) {
945 # some amount has been cancelled. collect the offsets that are not writeoffs
946 # this works because the only way to subtract from this kind of a debt is
947 # using the UI buttons 'Pay' and 'Write off'
948 my $credits_offsets = Koha::Account::Offsets->search(
950 debit_id => $lost_charge->id,
951 credit_id => { '!=' => undef }, # it is not the debit itself
952 type => { '!=' => 'Writeoff' },
953 amount => { '<' => 0 } # credits are negative on the DB
957 $total_to_refund = ( $credits_offsets->count > 0 )
958 ? $credits_offsets->total * -1 # credits are negative on the DB
962 my $credit_total = $lost_charge->amountoutstanding + $total_to_refund;
965 if ( $credit_total > 0 ) {
967 C4::Context->userenv ? C4::Context->userenv->{'branch'} : undef;
968 $credit = $account->add_credit(
970 amount => $credit_total,
971 description => 'Item found ' . $self->itemnumber,
972 type => 'LOST_FOUND',
973 interface => C4::Context->interface,
974 library_id => $branchcode,
975 item_id => $self->itemnumber,
976 issue_id => $lost_charge->issue_id
980 $credit->apply( { debits => [$lost_charge] } );
981 $self->{_refunded} = 1;
984 # Update the account status
985 $lost_charge->status('FOUND');
986 $lost_charge->store();
988 # Reconcile balances if required
989 if ( C4::Context->preference('AccountAutoReconcile') ) {
990 $account->reconcile_balance;
995 # restore fine for lost book
996 if ( $lostreturn_policy eq 'restore' ) {
997 my $lost_overdue = Koha::Account::Lines->search(
999 itemnumber => $self->itemnumber,
1000 debit_type_code => 'OVERDUE',
1004 order_by => { '-desc' => 'date' },
1009 if ( $lost_overdue ) {
1011 my $patron = $lost_overdue->patron;
1013 my $account = $patron->account;
1015 # Update status of fine
1016 $lost_overdue->status('FOUND')->store();
1018 # Find related forgive credit
1019 my $refund = $lost_overdue->credits(
1021 credit_type_code => 'FORGIVEN',
1022 itemnumber => $self->itemnumber,
1023 status => [ { '!=' => 'VOID' }, undef ]
1025 { order_by => { '-desc' => 'date' }, rows => 1 }
1029 # Revert the forgive credit
1031 $self->{_restored} = 1;
1034 # Reconcile balances if required
1035 if ( C4::Context->preference('AccountAutoReconcile') ) {
1036 $account->reconcile_balance;
1040 } elsif ( $lostreturn_policy eq 'charge' ) {
1041 $self->{_charge} = 1;
1048 =head3 to_api_mapping
1050 This method returns the mapping for representing a Koha::Item object
1055 sub to_api_mapping {
1057 itemnumber => 'item_id',
1058 biblionumber => 'biblio_id',
1059 biblioitemnumber => undef,
1060 barcode => 'external_id',
1061 dateaccessioned => 'acquisition_date',
1062 booksellerid => 'acquisition_source',
1063 homebranch => 'home_library_id',
1064 price => 'purchase_price',
1065 replacementprice => 'replacement_price',
1066 replacementpricedate => 'replacement_price_date',
1067 datelastborrowed => 'last_checkout_date',
1068 datelastseen => 'last_seen_date',
1070 notforloan => 'not_for_loan_status',
1071 damaged => 'damaged_status',
1072 damaged_on => 'damaged_date',
1073 itemlost => 'lost_status',
1074 itemlost_on => 'lost_date',
1075 withdrawn => 'withdrawn',
1076 withdrawn_on => 'withdrawn_date',
1077 itemcallnumber => 'callnumber',
1078 coded_location_qualifier => 'coded_location_qualifier',
1079 issues => 'checkouts_count',
1080 renewals => 'renewals_count',
1081 reserves => 'holds_count',
1082 restricted => 'restricted_status',
1083 itemnotes => 'public_notes',
1084 itemnotes_nonpublic => 'internal_notes',
1085 holdingbranch => 'holding_library_id',
1086 timestamp => 'timestamp',
1087 location => 'location',
1088 permanent_location => 'permanent_location',
1089 onloan => 'checked_out_date',
1090 cn_source => 'call_number_source',
1091 cn_sort => 'call_number_sort',
1092 ccode => 'collection_code',
1093 materials => 'materials_notes',
1095 itype => 'item_type',
1096 more_subfields_xml => 'extended_subfields',
1097 enumchron => 'serial_issue_number',
1098 copynumber => 'copy_number',
1099 stocknumber => 'inventory_number',
1100 new_status => 'new_status'
1106 my $itemtype = $item->itemtype;
1108 Returns Koha object for effective itemtype
1114 return Koha::ItemTypes->find( $self->effective_itemtype );
1117 =head2 Internal methods
1119 =head3 _after_item_action_hooks
1121 Helper method that takes care of calling all plugin hooks
1125 sub _after_item_action_hooks {
1126 my ( $self, $params ) = @_;
1128 my $action = $params->{action};
1130 Koha::Plugins->call(
1131 'after_item_action',
1135 item_id => $self->itemnumber,
1150 Kyle M Hall <kyle@bywatersolutions.com>