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::Item::Transfer::Limits;
41 use Koha::Item::Transfers;
46 use Koha::StockRotationItem;
47 use Koha::StockRotationRotas;
49 use base qw(Koha::Object);
53 Koha::Item - Koha Item object class
65 $params can take an optional 'skip_record_index' parameter.
66 If set, the reindexation process will not happen (index_records not called)
68 NOTE: This is a temporary fix to answer a performance issue when lot of items
69 are added (or modified) at the same time.
70 The correct way to fix this is to make the ES reindexation process async.
71 You should not turn it on if you do not understand what it is doing exactly.
77 my $params = @_ ? shift : {};
79 my $log_action = $params->{log_action} // 1;
81 # We do not want to oblige callers to pass this value
82 # Dev conveniences vs performance?
83 unless ( $self->biblioitemnumber ) {
84 $self->biblioitemnumber( $self->biblio->biblioitem->biblioitemnumber );
87 # See related changes from C4::Items::AddItem
88 unless ( $self->itype ) {
89 $self->itype($self->biblio->biblioitem->itemtype);
92 my $today = dt_from_string;
93 my $plugin_action = 'create';
95 unless ( $self->in_storage ) { #AddItem
96 unless ( $self->permanent_location ) {
97 $self->permanent_location($self->location);
99 unless ( $self->replacementpricedate ) {
100 $self->replacementpricedate($today);
102 unless ( $self->datelastseen ) {
103 $self->datelastseen($today);
106 unless ( $self->dateaccessioned ) {
107 $self->dateaccessioned($today);
110 if ( $self->itemcallnumber
111 or $self->cn_source )
113 my $cn_sort = GetClassSort( $self->cn_source, $self->itemcallnumber, "" );
114 $self->cn_sort($cn_sort);
117 logaction( "CATALOGUING", "ADD", $self->itemnumber, "item" )
118 if $log_action && C4::Context->preference("CataloguingLog");
122 $plugin_action = 'modify';
124 my %updated_columns = $self->_result->get_dirty_columns;
125 return $self->SUPER::store unless %updated_columns;
127 # Retrieve the item for comparison if we need to
129 exists $updated_columns{itemlost}
130 or exists $updated_columns{withdrawn}
131 or exists $updated_columns{damaged}
132 ) ? $self->get_from_storage : undef;
134 # Update *_on fields if needed
135 # FIXME: Why not for AddItem as well?
136 my @fields = qw( itemlost withdrawn damaged );
137 for my $field (@fields) {
139 # If the field is defined but empty or 0, we are
140 # removing/unsetting and thus need to clear out
142 if ( exists $updated_columns{$field}
143 && defined( $self->$field )
146 my $field_on = "${field}_on";
147 $self->$field_on(undef);
149 # If the field has changed otherwise, we much update
151 elsif (exists $updated_columns{$field}
152 && $updated_columns{$field}
153 && !$pre_mod_item->$field )
155 my $field_on = "${field}_on";
157 DateTime::Format::MySQL->format_datetime(
164 if ( exists $updated_columns{itemcallnumber}
165 or exists $updated_columns{cn_source} )
167 my $cn_sort = GetClassSort( $self->cn_source, $self->itemcallnumber, "" );
168 $self->cn_sort($cn_sort);
172 if ( exists $updated_columns{location}
173 and $self->location ne 'CART'
174 and $self->location ne 'PROC'
175 and not exists $updated_columns{permanent_location} )
177 $self->permanent_location( $self->location );
180 # If item was lost and has now been found,
181 # reverse any list item charges if necessary.
182 if ( exists $updated_columns{itemlost}
183 and $updated_columns{itemlost} <= 0
184 and $pre_mod_item->itemlost > 0 )
186 $self->_set_found_trigger($pre_mod_item);
189 logaction( "CATALOGUING", "MODIFY", $self->itemnumber, "item " . Dumper($self->unblessed) )
190 if $log_action && C4::Context->preference("CataloguingLog");
193 unless ( $self->dateaccessioned ) {
194 $self->dateaccessioned($today);
197 my $result = $self->SUPER::store;
198 my $indexer = Koha::SearchEngine::Indexer->new({ index => $Koha::SearchEngine::BIBLIOS_INDEX });
199 $indexer->index_records( $self->biblionumber, "specialUpdate", "biblioserver" )
200 unless $params->{skip_record_index};
201 $self->get_from_storage->_after_item_action_hooks({ action => $plugin_action });
212 my $params = @_ ? shift : {};
214 # FIXME check the item has no current issues
215 # i.e. raise the appropriate exception
217 my $result = $self->SUPER::delete;
219 my $indexer = Koha::SearchEngine::Indexer->new({ index => $Koha::SearchEngine::BIBLIOS_INDEX });
220 $indexer->index_records( $self->biblionumber, "specialUpdate", "biblioserver" )
221 unless $params->{skip_record_index};
223 $self->_after_item_action_hooks({ action => 'delete' });
225 logaction( "CATALOGUING", "DELETE", $self->itemnumber, "item" )
226 if C4::Context->preference("CataloguingLog");
237 my $params = @_ ? shift : {};
239 my $safe_to_delete = $self->safe_to_delete;
240 return $safe_to_delete unless $safe_to_delete eq '1';
242 $self->move_to_deleted;
244 return $self->delete($params);
247 =head3 safe_to_delete
249 returns 1 if the item is safe to delete,
251 "book_on_loan" if the item is checked out,
253 "not_same_branch" if the item is blocked by independent branches,
255 "book_reserved" if the there are holds aganst the item, or
257 "linked_analytics" if the item has linked analytic records.
259 "last_item_for_hold" if the item is the last one on a record on which a biblio-level hold is placed
266 return "book_on_loan" if $self->checkout;
268 return "not_same_branch"
269 if defined C4::Context->userenv
270 and !C4::Context->IsSuperLibrarian()
271 and C4::Context->preference("IndependentBranches")
272 and ( C4::Context->userenv->{branch} ne $self->homebranch );
274 # check it doesn't have a waiting reserve
275 return "book_reserved"
276 if $self->holds->search( { found => [ 'W', 'T' ] } )->count;
278 return "linked_analytics"
279 if C4::Items::GetAnalyticsCount( $self->itemnumber ) > 0;
281 return "last_item_for_hold"
282 if $self->biblio->items->count == 1
283 && $self->biblio->holds->search(
292 =head3 move_to_deleted
294 my $is_moved = $item->move_to_deleted;
296 Move an item to the deleteditems table.
297 This can be done before deleting an item, to make sure the data are not completely deleted.
301 sub move_to_deleted {
303 my $item_infos = $self->unblessed;
304 delete $item_infos->{timestamp}; #This ensures the timestamp date in deleteditems will be set to the current timestamp
305 return Koha::Database->new->schema->resultset('Deleteditem')->create($item_infos);
309 =head3 effective_itemtype
311 Returns the itemtype for the item based on whether item level itemtypes are set or not.
315 sub effective_itemtype {
318 return $self->_result()->effective_itemtype();
328 $self->{_home_branch} ||= Koha::Libraries->find( $self->homebranch() );
330 return $self->{_home_branch};
333 =head3 holding_branch
340 $self->{_holding_branch} ||= Koha::Libraries->find( $self->holdingbranch() );
342 return $self->{_holding_branch};
347 my $biblio = $item->biblio;
349 Return the bibliographic record of this item
355 my $biblio_rs = $self->_result->biblio;
356 return Koha::Biblio->_new_from_dbic( $biblio_rs );
361 my $biblioitem = $item->biblioitem;
363 Return the biblioitem record of this item
369 my $biblioitem_rs = $self->_result->biblioitem;
370 return Koha::Biblioitem->_new_from_dbic( $biblioitem_rs );
375 my $checkout = $item->checkout;
377 Return the checkout for this item
383 my $checkout_rs = $self->_result->issue;
384 return unless $checkout_rs;
385 return Koha::Checkout->_new_from_dbic( $checkout_rs );
390 my $holds = $item->holds();
391 my $holds = $item->holds($params);
392 my $holds = $item->holds({ found => 'W'});
394 Return holds attached to an item, optionally accept a hashref of params to pass to search
399 my ( $self,$params ) = @_;
400 my $holds_rs = $self->_result->reserves->search($params);
401 return Koha::Holds->_new_from_dbic( $holds_rs );
406 my $transfer = $item->get_transfer;
408 Return the transfer if the item is in transit or undef
414 my $transfer_rs = $self->_result->branchtransfers->search({ datearrived => undef })->first;
415 return unless $transfer_rs;
416 return Koha::Item::Transfer->_new_from_dbic( $transfer_rs );
419 =head3 last_returned_by
421 Gets and sets the last borrower to return an item.
423 Accepts and returns Koha::Patron objects
425 $item->last_returned_by( $borrowernumber );
427 $last_returned_by = $item->last_returned_by();
431 sub last_returned_by {
432 my ( $self, $borrower ) = @_;
434 my $items_last_returned_by_rs = Koha::Database->new()->schema()->resultset('ItemsLastBorrower');
437 return $items_last_returned_by_rs->update_or_create(
438 { borrowernumber => $borrower->borrowernumber, itemnumber => $self->id } );
441 unless ( $self->{_last_returned_by} ) {
442 my $result = $items_last_returned_by_rs->single( { itemnumber => $self->id } );
444 $self->{_last_returned_by} = Koha::Patrons->find( $result->get_column('borrowernumber') );
448 return $self->{_last_returned_by};
452 =head3 can_article_request
454 my $bool = $item->can_article_request( $borrower )
456 Returns true if item can be specifically requested
458 $borrower must be a Koha::Patron object
462 sub can_article_request {
463 my ( $self, $borrower ) = @_;
465 my $rule = $self->article_request_type($borrower);
467 return 1 if $rule && $rule ne 'no' && $rule ne 'bib_only';
471 =head3 hidden_in_opac
473 my $bool = $item->hidden_in_opac({ [ rules => $rules ] })
475 Returns true if item fields match the hidding criteria defined in $rules.
476 Returns false otherwise.
478 Takes HASHref that can have the following parameters:
480 $rules : { <field> => [ value_1, ... ], ... }
482 Note: $rules inherits its structure from the parsed YAML from reading
483 the I<OpacHiddenItems> system preference.
488 my ( $self, $params ) = @_;
490 my $rules = $params->{rules} // {};
493 if C4::Context->preference('hidelostitems') and
496 my $hidden_in_opac = 0;
498 foreach my $field ( keys %{$rules} ) {
500 if ( any { $self->$field eq $_ } @{ $rules->{$field} } ) {
506 return $hidden_in_opac;
509 =head3 can_be_transferred
511 $item->can_be_transferred({ to => $to_library, from => $from_library })
512 Checks if an item can be transferred to given library.
514 This feature is controlled by two system preferences:
515 UseBranchTransferLimits to enable / disable the feature
516 BranchTransferLimitsType to use either an itemnumber or ccode as an identifier
517 for setting the limitations
519 Takes HASHref that can have the following parameters:
520 MANDATORY PARAMETERS:
523 $from : Koha::Library # if not given, item holdingbranch
524 # will be used instead
526 Returns 1 if item can be transferred to $to_library, otherwise 0.
528 To find out whether at least one item of a Koha::Biblio can be transferred, please
529 see Koha::Biblio->can_be_transferred() instead of using this method for
530 multiple items of the same biblio.
534 sub can_be_transferred {
535 my ($self, $params) = @_;
537 my $to = $params->{to};
538 my $from = $params->{from};
540 $to = $to->branchcode;
541 $from = defined $from ? $from->branchcode : $self->holdingbranch;
543 return 1 if $from eq $to; # Transfer to current branch is allowed
544 return 1 unless C4::Context->preference('UseBranchTransferLimits');
546 my $limittype = C4::Context->preference('BranchTransferLimitsType');
547 return Koha::Item::Transfer::Limits->search({
550 $limittype => $limittype eq 'itemtype'
551 ? $self->effective_itemtype : $self->ccode
556 =head3 _can_pickup_locations
558 $item->_can_pickup_locations({ to => $to_libraries, from => $from_library })
559 Checks if an item can be transferred to given libraries.
561 This feature is controlled by two system preferences:
562 UseBranchTransferLimits to enable / disable the feature
563 BranchTransferLimitsType to use either an itemnumber or ccode as an identifier
564 for setting the limitations
566 Takes HASHref that can have the following parameters:
567 MANDATORY PARAMETERS:
568 $to : Array of Koha::Libraries
570 $from : Koha::Library # if not given, item holdingbranch
571 # will be used instead
573 Returns arry of Koha::Libraries that item can be transferred to $to_library and
576 If checking only one library please use $item->can_be_transferred.
580 sub _can_pickup_locations {
581 my ($self, $params ) = @_;
583 my $to = $params->{to};
584 my $from = $params->{from};
585 $from = defined $from ? $from->branchcode : $self->holdingbranch;
587 my @pickup_locations;
588 my @destination_codes;
589 foreach my $lib (@$to){
590 next unless $lib->pickup_location;
591 push @destination_codes, $lib->branchcode;
592 push @pickup_locations, $lib;
595 return \@pickup_locations unless C4::Context->preference('UseBranchTransferLimits');
597 my $limittype = C4::Context->preference('BranchTransferLimitsType');
598 my $limiter = $limittype eq 'itemtype' ? $self->effective_itemtype : $self->ccode;
600 my $limits = Koha::Item::Transfer::Limits->search({
602 $limittype => $limiter
604 my @limits = $limits->get_column('toBranch');
605 return \@pickup_locations unless @limits;
607 my @can_transfer = Koha::Libraries->search({
608 pickup_location => 1,
610 -in => \@destination_codes,
614 return \@can_transfer;
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 );
637 if(defined $patron) {
638 return \@libs if $branchitemrule->{holdallowed} == 3 && !$self->home_branch->validate_hold_sibling( {branchcode => $patron->branchcode} );
639 return \@libs if $branchitemrule->{holdallowed} == 1 && $self->home_branch->branchcode ne $patron->branchcode;
642 if ($branchitemrule->{hold_fulfillment_policy} eq 'holdgroup') {
643 @libs = $self->home_branch->get_hold_libraries;
644 push @libs, $self->home_branch unless scalar(@libs) > 0;
645 } elsif ($branchitemrule->{hold_fulfillment_policy} eq 'patrongroup') {
646 my $plib = Koha::Libraries->find({ branchcode => $patron->branchcode});
647 @libs = $plib->get_hold_libraries;
648 push @libs, $self->home_branch unless scalar(@libs) > 0;
649 } elsif ($branchitemrule->{hold_fulfillment_policy} eq 'homebranch') {
650 push @libs, $self->home_branch;
651 } elsif ($branchitemrule->{hold_fulfillment_policy} eq 'holdingbranch') {
652 push @libs, $self->holding_branch;
654 @libs = Koha::Libraries->search({
657 order_by => ['branchname']
661 my $pickup_locations = $self->_can_pickup_locations({
665 return $pickup_locations;
668 =head3 article_request_type
670 my $type = $item->article_request_type( $borrower )
672 returns 'yes', 'no', 'bib_only', or 'item_only'
674 $borrower must be a Koha::Patron object
678 sub article_request_type {
679 my ( $self, $borrower ) = @_;
681 my $branch_control = C4::Context->preference('HomeOrHoldingBranch');
683 $branch_control eq 'homebranch' ? $self->homebranch
684 : $branch_control eq 'holdingbranch' ? $self->holdingbranch
686 my $borrowertype = $borrower->categorycode;
687 my $itemtype = $self->effective_itemtype();
688 my $rule = Koha::CirculationRules->get_effective_rule(
690 rule_name => 'article_requests',
691 categorycode => $borrowertype,
692 itemtype => $itemtype,
693 branchcode => $branchcode
697 return q{} unless $rule;
698 return $rule->rule_value || q{}
707 my $attributes = { order_by => 'priority' };
708 my $dtf = Koha::Database->new->schema->storage->datetime_parser;
710 itemnumber => $self->itemnumber,
713 reservedate => { '<=' => $dtf->format_date(dt_from_string) },
714 waitingdate => { '!=' => undef },
717 my $hold_rs = $self->_result->reserves->search( $params, $attributes );
718 return Koha::Holds->_new_from_dbic($hold_rs);
721 =head3 stockrotationitem
723 my $sritem = Koha::Item->stockrotationitem;
725 Returns the stock rotation item associated with the current item.
729 sub stockrotationitem {
731 my $rs = $self->_result->stockrotationitem;
733 return Koha::StockRotationItem->_new_from_dbic( $rs );
738 my $item = $item->add_to_rota($rota_id);
740 Add this item to the rota identified by $ROTA_ID, which means associating it
741 with the first stage of that rota. Should this item already be associated
742 with a rota, then we will move it to the new rota.
747 my ( $self, $rota_id ) = @_;
748 Koha::StockRotationRotas->find($rota_id)->add_item($self->itemnumber);
752 =head3 has_pending_hold
754 my $is_pending_hold = $item->has_pending_hold();
756 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
760 sub has_pending_hold {
762 my $pending_hold = $self->_result->tmp_holdsqueues;
763 return $pending_hold->count ? 1: 0;
768 my $mss = C4::Biblio::GetMarcSubfieldStructure( '', { unsafe => 1 } );
769 my $field = $item->as_marc_field({ [ mss => $mss ] });
771 This method returns a MARC::Field object representing the Koha::Item object
772 with the current mappings configuration.
777 my ( $self, $params ) = @_;
779 my $mss = $params->{mss} // C4::Biblio::GetMarcSubfieldStructure( '', { unsafe => 1 } );
780 my $item_tag = $mss->{'items.itemnumber'}[0]->{tagfield};
784 my @columns = $self->_result->result_source->columns;
786 foreach my $item_field ( @columns ) {
787 my $mapping = $mss->{ "items.$item_field"}[0];
788 my $tagfield = $mapping->{tagfield};
789 my $tagsubfield = $mapping->{tagsubfield};
790 next if !$tagfield; # TODO: Should we raise an exception instead?
791 # Feels like safe fallback is better
793 push @subfields, $tagsubfield => $self->$item_field
794 if defined $self->$item_field and $item_field ne '';
797 my $unlinked_item_subfields = C4::Items::_parse_unlinked_item_subfields_from_xml($self->more_subfields_xml);
798 push( @subfields, @{$unlinked_item_subfields} )
799 if defined $unlinked_item_subfields and $#$unlinked_item_subfields > -1;
803 $field = MARC::Field->new(
804 "$item_tag", ' ', ' ', @subfields
810 =head3 renewal_branchcode
812 Returns the branchcode to be recorded in statistics renewal of the item
816 sub renewal_branchcode {
818 my ($self, $params ) = @_;
820 my $interface = C4::Context->interface;
822 if ( $interface eq 'opac' ){
823 my $renewal_branchcode = C4::Context->preference('OpacRenewalBranch');
824 if( !defined $renewal_branchcode || $renewal_branchcode eq 'opacrenew' ){
825 $branchcode = 'OPACRenew';
827 elsif ( $renewal_branchcode eq 'itemhomebranch' ) {
828 $branchcode = $self->homebranch;
830 elsif ( $renewal_branchcode eq 'patronhomebranch' ) {
831 $branchcode = $self->checkout->patron->branchcode;
833 elsif ( $renewal_branchcode eq 'checkoutbranch' ) {
834 $branchcode = $self->checkout->branchcode;
840 $branchcode = ( C4::Context->userenv && defined C4::Context->userenv->{branch} )
841 ? C4::Context->userenv->{branch} : $params->{branch};
848 Return the cover images associated with this item.
855 my $cover_image_rs = $self->_result->cover_images;
856 return unless $cover_image_rs;
857 return Koha::CoverImages->_new_from_dbic($cover_image_rs);
860 =head3 _set_found_trigger
862 $self->_set_found_trigger
864 Finds the most recent lost item charge for this item and refunds the patron
865 appropriately, taking into account any payments or writeoffs already applied
868 Internal function, not exported, called only by Koha::Item->store.
872 sub _set_found_trigger {
873 my ( $self, $pre_mod_item ) = @_;
875 ## If item was lost, it has now been found, reverse any list item charges if necessary.
876 my $no_refund_after_days =
877 C4::Context->preference('NoRefundOnLostReturnedItemsAge');
878 if ($no_refund_after_days) {
879 my $today = dt_from_string();
880 my $lost_age_in_days =
881 dt_from_string( $pre_mod_item->itemlost_on )->delta_days($today)
884 return $self unless $lost_age_in_days < $no_refund_after_days;
887 my $lostreturn_policy = Koha::CirculationRules->get_lostreturn_policy(
890 return_branch => C4::Context->userenv
891 ? C4::Context->userenv->{'branch'}
896 if ( $lostreturn_policy ) {
898 # refund charge made for lost book
899 my $lost_charge = Koha::Account::Lines->search(
901 itemnumber => $self->itemnumber,
902 debit_type_code => 'LOST',
903 status => [ undef, { '<>' => 'FOUND' } ]
906 order_by => { -desc => [ 'date', 'accountlines_id' ] },
911 if ( $lost_charge ) {
913 my $patron = $lost_charge->patron;
916 my $account = $patron->account;
917 my $total_to_refund = 0;
920 if ( $lost_charge->amount > $lost_charge->amountoutstanding ) {
922 # some amount has been cancelled. collect the offsets that are not writeoffs
923 # this works because the only way to subtract from this kind of a debt is
924 # using the UI buttons 'Pay' and 'Write off'
925 my $credits_offsets = Koha::Account::Offsets->search(
927 debit_id => $lost_charge->id,
928 credit_id => { '!=' => undef }, # it is not the debit itself
929 type => { '!=' => 'Writeoff' },
930 amount => { '<' => 0 } # credits are negative on the DB
934 $total_to_refund = ( $credits_offsets->count > 0 )
935 ? $credits_offsets->total * -1 # credits are negative on the DB
939 my $credit_total = $lost_charge->amountoutstanding + $total_to_refund;
942 if ( $credit_total > 0 ) {
944 C4::Context->userenv ? C4::Context->userenv->{'branch'} : undef;
945 $credit = $account->add_credit(
947 amount => $credit_total,
948 description => 'Item found ' . $self->itemnumber,
949 type => 'LOST_FOUND',
950 interface => C4::Context->interface,
951 library_id => $branchcode,
952 item_id => $self->itemnumber,
953 issue_id => $lost_charge->issue_id
957 $credit->apply( { debits => [$lost_charge] } );
958 $self->{_refunded} = 1;
961 # Update the account status
962 $lost_charge->status('FOUND');
963 $lost_charge->store();
965 # Reconcile balances if required
966 if ( C4::Context->preference('AccountAutoReconcile') ) {
967 $account->reconcile_balance;
972 # restore fine for lost book
973 if ( $lostreturn_policy eq 'restore' ) {
974 my $lost_overdue = Koha::Account::Lines->search(
976 itemnumber => $self->itemnumber,
977 debit_type_code => 'OVERDUE',
981 order_by => { '-desc' => 'date' },
986 if ( $lost_overdue ) {
988 my $patron = $lost_overdue->patron;
990 my $account = $patron->account;
992 # Update status of fine
993 $lost_overdue->status('FOUND')->store();
995 # Find related forgive credit
996 my $refund = $lost_overdue->credits(
998 credit_type_code => 'FORGIVEN',
999 itemnumber => $self->itemnumber,
1000 status => [ { '!=' => 'VOID' }, undef ]
1002 { order_by => { '-desc' => 'date' }, rows => 1 }
1006 # Revert the forgive credit
1008 $self->{_restored} = 1;
1011 # Reconcile balances if required
1012 if ( C4::Context->preference('AccountAutoReconcile') ) {
1013 $account->reconcile_balance;
1017 } elsif ( $lostreturn_policy eq 'charge' ) {
1018 $self->{_charge} = 1;
1025 =head3 to_api_mapping
1027 This method returns the mapping for representing a Koha::Item object
1032 sub to_api_mapping {
1034 itemnumber => 'item_id',
1035 biblionumber => 'biblio_id',
1036 biblioitemnumber => undef,
1037 barcode => 'external_id',
1038 dateaccessioned => 'acquisition_date',
1039 booksellerid => 'acquisition_source',
1040 homebranch => 'home_library_id',
1041 price => 'purchase_price',
1042 replacementprice => 'replacement_price',
1043 replacementpricedate => 'replacement_price_date',
1044 datelastborrowed => 'last_checkout_date',
1045 datelastseen => 'last_seen_date',
1047 notforloan => 'not_for_loan_status',
1048 damaged => 'damaged_status',
1049 damaged_on => 'damaged_date',
1050 itemlost => 'lost_status',
1051 itemlost_on => 'lost_date',
1052 withdrawn => 'withdrawn',
1053 withdrawn_on => 'withdrawn_date',
1054 itemcallnumber => 'callnumber',
1055 coded_location_qualifier => 'coded_location_qualifier',
1056 issues => 'checkouts_count',
1057 renewals => 'renewals_count',
1058 reserves => 'holds_count',
1059 restricted => 'restricted_status',
1060 itemnotes => 'public_notes',
1061 itemnotes_nonpublic => 'internal_notes',
1062 holdingbranch => 'holding_library_id',
1063 timestamp => 'timestamp',
1064 location => 'location',
1065 permanent_location => 'permanent_location',
1066 onloan => 'checked_out_date',
1067 cn_source => 'call_number_source',
1068 cn_sort => 'call_number_sort',
1069 ccode => 'collection_code',
1070 materials => 'materials_notes',
1072 itype => 'item_type',
1073 more_subfields_xml => 'extended_subfields',
1074 enumchron => 'serial_issue_number',
1075 copynumber => 'copy_number',
1076 stocknumber => 'inventory_number',
1077 new_status => 'new_status'
1083 my $itemtype = $item->itemtype;
1085 Returns Koha object for effective itemtype
1091 return Koha::ItemTypes->find( $self->effective_itemtype );
1094 =head2 Internal methods
1096 =head3 _after_item_action_hooks
1098 Helper method that takes care of calling all plugin hooks
1102 sub _after_item_action_hooks {
1103 my ( $self, $params ) = @_;
1105 my $action = $params->{action};
1107 Koha::Plugins->call(
1108 'after_item_action',
1112 item_id => $self->itemnumber,
1127 Kyle M Hall <kyle@bywatersolutions.com>