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 received, transfer will be returned
463 if it exists, otherwise the oldest unsatisfied transfer will be returned.
465 This allows for transfers to queue, which is the case for stock rotation and
466 rotating collections where a manual transfer may need to take precedence but
467 we still expect the item to end up at a final location eventually.
473 my $transfer_rs = $self->_result->branchtransfers->search(
475 datearrived => undef,
476 datecancelled => undef
480 [ { -desc => 'datesent' }, { -asc => 'daterequested' } ],
484 return unless $transfer_rs;
485 return Koha::Item::Transfer->_new_from_dbic($transfer_rs);
488 =head3 last_returned_by
490 Gets and sets the last borrower to return an item.
492 Accepts and returns Koha::Patron objects
494 $item->last_returned_by( $borrowernumber );
496 $last_returned_by = $item->last_returned_by();
500 sub last_returned_by {
501 my ( $self, $borrower ) = @_;
503 my $items_last_returned_by_rs = Koha::Database->new()->schema()->resultset('ItemsLastBorrower');
506 return $items_last_returned_by_rs->update_or_create(
507 { borrowernumber => $borrower->borrowernumber, itemnumber => $self->id } );
510 unless ( $self->{_last_returned_by} ) {
511 my $result = $items_last_returned_by_rs->single( { itemnumber => $self->id } );
513 $self->{_last_returned_by} = Koha::Patrons->find( $result->get_column('borrowernumber') );
517 return $self->{_last_returned_by};
521 =head3 can_article_request
523 my $bool = $item->can_article_request( $borrower )
525 Returns true if item can be specifically requested
527 $borrower must be a Koha::Patron object
531 sub can_article_request {
532 my ( $self, $borrower ) = @_;
534 my $rule = $self->article_request_type($borrower);
536 return 1 if $rule && $rule ne 'no' && $rule ne 'bib_only';
540 =head3 hidden_in_opac
542 my $bool = $item->hidden_in_opac({ [ rules => $rules ] })
544 Returns true if item fields match the hidding criteria defined in $rules.
545 Returns false otherwise.
547 Takes HASHref that can have the following parameters:
549 $rules : { <field> => [ value_1, ... ], ... }
551 Note: $rules inherits its structure from the parsed YAML from reading
552 the I<OpacHiddenItems> system preference.
557 my ( $self, $params ) = @_;
559 my $rules = $params->{rules} // {};
562 if C4::Context->preference('hidelostitems') and
565 my $hidden_in_opac = 0;
567 foreach my $field ( keys %{$rules} ) {
569 if ( any { $self->$field eq $_ } @{ $rules->{$field} } ) {
575 return $hidden_in_opac;
578 =head3 can_be_transferred
580 $item->can_be_transferred({ to => $to_library, from => $from_library })
581 Checks if an item can be transferred to given library.
583 This feature is controlled by two system preferences:
584 UseBranchTransferLimits to enable / disable the feature
585 BranchTransferLimitsType to use either an itemnumber or ccode as an identifier
586 for setting the limitations
588 Takes HASHref that can have the following parameters:
589 MANDATORY PARAMETERS:
592 $from : Koha::Library # if not given, item holdingbranch
593 # will be used instead
595 Returns 1 if item can be transferred to $to_library, otherwise 0.
597 To find out whether at least one item of a Koha::Biblio can be transferred, please
598 see Koha::Biblio->can_be_transferred() instead of using this method for
599 multiple items of the same biblio.
603 sub can_be_transferred {
604 my ($self, $params) = @_;
606 my $to = $params->{to};
607 my $from = $params->{from};
609 $to = $to->branchcode;
610 $from = defined $from ? $from->branchcode : $self->holdingbranch;
612 return 1 if $from eq $to; # Transfer to current branch is allowed
613 return 1 unless C4::Context->preference('UseBranchTransferLimits');
615 my $limittype = C4::Context->preference('BranchTransferLimitsType');
616 return Koha::Item::Transfer::Limits->search({
619 $limittype => $limittype eq 'itemtype'
620 ? $self->effective_itemtype : $self->ccode
625 =head3 pickup_locations
627 $pickup_locations = $item->pickup_locations( {patron => $patron } )
629 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)
630 and if item can be transferred to each pickup location.
634 sub pickup_locations {
635 my ($self, $params) = @_;
637 my $patron = $params->{patron};
639 my $circ_control_branch =
640 C4::Reserves::GetReservesControlBranch( $self->unblessed(), $patron->unblessed );
642 C4::Circulation::GetBranchItemRule( $circ_control_branch, $self->itype );
644 if(defined $patron) {
645 return Koha::Libraries->new()->empty if $branchitemrule->{holdallowed} == 3 && !$self->home_branch->validate_hold_sibling( {branchcode => $patron->branchcode} );
646 return Koha::Libraries->new()->empty if $branchitemrule->{holdallowed} == 1 && $self->home_branch->branchcode ne $patron->branchcode;
649 my $pickup_libraries = Koha::Libraries->search();
650 if ($branchitemrule->{hold_fulfillment_policy} eq 'holdgroup') {
651 $pickup_libraries = $self->home_branch->get_hold_libraries;
652 } elsif ($branchitemrule->{hold_fulfillment_policy} eq 'patrongroup') {
653 my $plib = Koha::Libraries->find({ branchcode => $patron->branchcode});
654 $pickup_libraries = $plib->get_hold_libraries;
655 } elsif ($branchitemrule->{hold_fulfillment_policy} eq 'homebranch') {
656 $pickup_libraries = Koha::Libraries->search({ branchcode => $self->homebranch });
657 } elsif ($branchitemrule->{hold_fulfillment_policy} eq 'holdingbranch') {
658 $pickup_libraries = Koha::Libraries->search({ branchcode => $self->holdingbranch });
661 return $pickup_libraries->search(
666 order_by => ['branchname']
668 ) unless C4::Context->preference('UseBranchTransferLimits');
670 my $limittype = C4::Context->preference('BranchTransferLimitsType');
671 my ($ccode, $itype) = (undef, undef);
672 if( $limittype eq 'ccode' ){
673 $ccode = $self->ccode;
675 $itype = $self->itype;
677 my $limits = Koha::Item::Transfer::Limits->search(
679 fromBranch => $self->holdingbranch,
683 { columns => ['toBranch'] }
686 return $pickup_libraries->search(
688 pickup_location => 1,
690 '-not_in' => $limits->_resultset->as_query
694 order_by => ['branchname']
699 =head3 article_request_type
701 my $type = $item->article_request_type( $borrower )
703 returns 'yes', 'no', 'bib_only', or 'item_only'
705 $borrower must be a Koha::Patron object
709 sub article_request_type {
710 my ( $self, $borrower ) = @_;
712 my $branch_control = C4::Context->preference('HomeOrHoldingBranch');
714 $branch_control eq 'homebranch' ? $self->homebranch
715 : $branch_control eq 'holdingbranch' ? $self->holdingbranch
717 my $borrowertype = $borrower->categorycode;
718 my $itemtype = $self->effective_itemtype();
719 my $rule = Koha::CirculationRules->get_effective_rule(
721 rule_name => 'article_requests',
722 categorycode => $borrowertype,
723 itemtype => $itemtype,
724 branchcode => $branchcode
728 return q{} unless $rule;
729 return $rule->rule_value || q{}
738 my $attributes = { order_by => 'priority' };
739 my $dtf = Koha::Database->new->schema->storage->datetime_parser;
741 itemnumber => $self->itemnumber,
744 reservedate => { '<=' => $dtf->format_date(dt_from_string) },
745 waitingdate => { '!=' => undef },
748 my $hold_rs = $self->_result->reserves->search( $params, $attributes );
749 return Koha::Holds->_new_from_dbic($hold_rs);
752 =head3 stockrotationitem
754 my $sritem = Koha::Item->stockrotationitem;
756 Returns the stock rotation item associated with the current item.
760 sub stockrotationitem {
762 my $rs = $self->_result->stockrotationitem;
764 return Koha::StockRotationItem->_new_from_dbic( $rs );
769 my $item = $item->add_to_rota($rota_id);
771 Add this item to the rota identified by $ROTA_ID, which means associating it
772 with the first stage of that rota. Should this item already be associated
773 with a rota, then we will move it to the new rota.
778 my ( $self, $rota_id ) = @_;
779 Koha::StockRotationRotas->find($rota_id)->add_item($self->itemnumber);
783 =head3 has_pending_hold
785 my $is_pending_hold = $item->has_pending_hold();
787 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
791 sub has_pending_hold {
793 my $pending_hold = $self->_result->tmp_holdsqueues;
794 return $pending_hold->count ? 1: 0;
799 my $mss = C4::Biblio::GetMarcSubfieldStructure( '', { unsafe => 1 } );
800 my $field = $item->as_marc_field({ [ mss => $mss ] });
802 This method returns a MARC::Field object representing the Koha::Item object
803 with the current mappings configuration.
808 my ( $self, $params ) = @_;
810 my $mss = $params->{mss} // C4::Biblio::GetMarcSubfieldStructure( '', { unsafe => 1 } );
811 my $item_tag = $mss->{'items.itemnumber'}[0]->{tagfield};
815 my @columns = $self->_result->result_source->columns;
817 foreach my $item_field ( @columns ) {
818 my $mapping = $mss->{ "items.$item_field"}[0];
819 my $tagfield = $mapping->{tagfield};
820 my $tagsubfield = $mapping->{tagsubfield};
821 next if !$tagfield; # TODO: Should we raise an exception instead?
822 # Feels like safe fallback is better
824 push @subfields, $tagsubfield => $self->$item_field
825 if defined $self->$item_field and $item_field ne '';
828 my $unlinked_item_subfields = C4::Items::_parse_unlinked_item_subfields_from_xml($self->more_subfields_xml);
829 push( @subfields, @{$unlinked_item_subfields} )
830 if defined $unlinked_item_subfields and $#$unlinked_item_subfields > -1;
834 $field = MARC::Field->new(
835 "$item_tag", ' ', ' ', @subfields
841 =head3 renewal_branchcode
843 Returns the branchcode to be recorded in statistics renewal of the item
847 sub renewal_branchcode {
849 my ($self, $params ) = @_;
851 my $interface = C4::Context->interface;
853 if ( $interface eq 'opac' ){
854 my $renewal_branchcode = C4::Context->preference('OpacRenewalBranch');
855 if( !defined $renewal_branchcode || $renewal_branchcode eq 'opacrenew' ){
856 $branchcode = 'OPACRenew';
858 elsif ( $renewal_branchcode eq 'itemhomebranch' ) {
859 $branchcode = $self->homebranch;
861 elsif ( $renewal_branchcode eq 'patronhomebranch' ) {
862 $branchcode = $self->checkout->patron->branchcode;
864 elsif ( $renewal_branchcode eq 'checkoutbranch' ) {
865 $branchcode = $self->checkout->branchcode;
871 $branchcode = ( C4::Context->userenv && defined C4::Context->userenv->{branch} )
872 ? C4::Context->userenv->{branch} : $params->{branch};
879 Return the cover images associated with this item.
886 my $cover_image_rs = $self->_result->cover_images;
887 return unless $cover_image_rs;
888 return Koha::CoverImages->_new_from_dbic($cover_image_rs);
891 =head3 _set_found_trigger
893 $self->_set_found_trigger
895 Finds the most recent lost item charge for this item and refunds the patron
896 appropriately, taking into account any payments or writeoffs already applied
899 Internal function, not exported, called only by Koha::Item->store.
903 sub _set_found_trigger {
904 my ( $self, $pre_mod_item ) = @_;
906 ## If item was lost, it has now been found, reverse any list item charges if necessary.
907 my $no_refund_after_days =
908 C4::Context->preference('NoRefundOnLostReturnedItemsAge');
909 if ($no_refund_after_days) {
910 my $today = dt_from_string();
911 my $lost_age_in_days =
912 dt_from_string( $pre_mod_item->itemlost_on )->delta_days($today)
915 return $self unless $lost_age_in_days < $no_refund_after_days;
918 my $lostreturn_policy = Koha::CirculationRules->get_lostreturn_policy(
921 return_branch => C4::Context->userenv
922 ? C4::Context->userenv->{'branch'}
927 if ( $lostreturn_policy ) {
929 # refund charge made for lost book
930 my $lost_charge = Koha::Account::Lines->search(
932 itemnumber => $self->itemnumber,
933 debit_type_code => 'LOST',
934 status => [ undef, { '<>' => 'FOUND' } ]
937 order_by => { -desc => [ 'date', 'accountlines_id' ] },
942 if ( $lost_charge ) {
944 my $patron = $lost_charge->patron;
947 my $account = $patron->account;
948 my $total_to_refund = 0;
951 if ( $lost_charge->amount > $lost_charge->amountoutstanding ) {
953 # some amount has been cancelled. collect the offsets that are not writeoffs
954 # this works because the only way to subtract from this kind of a debt is
955 # using the UI buttons 'Pay' and 'Write off'
956 my $credits_offsets = Koha::Account::Offsets->search(
958 debit_id => $lost_charge->id,
959 credit_id => { '!=' => undef }, # it is not the debit itself
960 type => { '!=' => 'Writeoff' },
961 amount => { '<' => 0 } # credits are negative on the DB
965 $total_to_refund = ( $credits_offsets->count > 0 )
966 ? $credits_offsets->total * -1 # credits are negative on the DB
970 my $credit_total = $lost_charge->amountoutstanding + $total_to_refund;
973 if ( $credit_total > 0 ) {
975 C4::Context->userenv ? C4::Context->userenv->{'branch'} : undef;
976 $credit = $account->add_credit(
978 amount => $credit_total,
979 description => 'Item found ' . $self->itemnumber,
980 type => 'LOST_FOUND',
981 interface => C4::Context->interface,
982 library_id => $branchcode,
983 item_id => $self->itemnumber,
984 issue_id => $lost_charge->issue_id
988 $credit->apply( { debits => [$lost_charge] } );
989 $self->{_refunded} = 1;
992 # Update the account status
993 $lost_charge->status('FOUND');
994 $lost_charge->store();
996 # Reconcile balances if required
997 if ( C4::Context->preference('AccountAutoReconcile') ) {
998 $account->reconcile_balance;
1003 # restore fine for lost book
1004 if ( $lostreturn_policy eq 'restore' ) {
1005 my $lost_overdue = Koha::Account::Lines->search(
1007 itemnumber => $self->itemnumber,
1008 debit_type_code => 'OVERDUE',
1012 order_by => { '-desc' => 'date' },
1017 if ( $lost_overdue ) {
1019 my $patron = $lost_overdue->patron;
1021 my $account = $patron->account;
1023 # Update status of fine
1024 $lost_overdue->status('FOUND')->store();
1026 # Find related forgive credit
1027 my $refund = $lost_overdue->credits(
1029 credit_type_code => 'FORGIVEN',
1030 itemnumber => $self->itemnumber,
1031 status => [ { '!=' => 'VOID' }, undef ]
1033 { order_by => { '-desc' => 'date' }, rows => 1 }
1037 # Revert the forgive credit
1039 $self->{_restored} = 1;
1042 # Reconcile balances if required
1043 if ( C4::Context->preference('AccountAutoReconcile') ) {
1044 $account->reconcile_balance;
1048 } elsif ( $lostreturn_policy eq 'charge' ) {
1049 $self->{_charge} = 1;
1056 =head3 to_api_mapping
1058 This method returns the mapping for representing a Koha::Item object
1063 sub to_api_mapping {
1065 itemnumber => 'item_id',
1066 biblionumber => 'biblio_id',
1067 biblioitemnumber => undef,
1068 barcode => 'external_id',
1069 dateaccessioned => 'acquisition_date',
1070 booksellerid => 'acquisition_source',
1071 homebranch => 'home_library_id',
1072 price => 'purchase_price',
1073 replacementprice => 'replacement_price',
1074 replacementpricedate => 'replacement_price_date',
1075 datelastborrowed => 'last_checkout_date',
1076 datelastseen => 'last_seen_date',
1078 notforloan => 'not_for_loan_status',
1079 damaged => 'damaged_status',
1080 damaged_on => 'damaged_date',
1081 itemlost => 'lost_status',
1082 itemlost_on => 'lost_date',
1083 withdrawn => 'withdrawn',
1084 withdrawn_on => 'withdrawn_date',
1085 itemcallnumber => 'callnumber',
1086 coded_location_qualifier => 'coded_location_qualifier',
1087 issues => 'checkouts_count',
1088 renewals => 'renewals_count',
1089 reserves => 'holds_count',
1090 restricted => 'restricted_status',
1091 itemnotes => 'public_notes',
1092 itemnotes_nonpublic => 'internal_notes',
1093 holdingbranch => 'holding_library_id',
1094 timestamp => 'timestamp',
1095 location => 'location',
1096 permanent_location => 'permanent_location',
1097 onloan => 'checked_out_date',
1098 cn_source => 'call_number_source',
1099 cn_sort => 'call_number_sort',
1100 ccode => 'collection_code',
1101 materials => 'materials_notes',
1103 itype => 'item_type',
1104 more_subfields_xml => 'extended_subfields',
1105 enumchron => 'serial_issue_number',
1106 copynumber => 'copy_number',
1107 stocknumber => 'inventory_number',
1108 new_status => 'new_status'
1114 my $itemtype = $item->itemtype;
1116 Returns Koha object for effective itemtype
1122 return Koha::ItemTypes->find( $self->effective_itemtype );
1125 =head2 Internal methods
1127 =head3 _after_item_action_hooks
1129 Helper method that takes care of calling all plugin hooks
1133 sub _after_item_action_hooks {
1134 my ( $self, $params ) = @_;
1136 my $action = $params->{action};
1138 Koha::Plugins->call(
1139 'after_item_action',
1143 item_id => $self->itemnumber,
1158 Kyle M Hall <kyle@bywatersolutions.com>