use Koha::Libraries;
use Koha::Account::Lines;
use Koha::Holds;
-use Koha::RefundLostItemFeeRules;
use Koha::Account::Lines;
use Koha::Account::Offsets;
use Koha::Config::SysPrefs;
use Carp;
use List::MoreUtils qw( uniq any );
use Scalar::Util qw( looks_like_number );
+use Try::Tiny;
use Date::Calc qw(
Today
Today_and_Now
=head2 transferbook
- ($dotransfer, $messages, $iteminformation) = &transferbook($newbranch,
- $barcode, $ignore_reserves, $trigger);
+ ($dotransfer, $messages, $iteminformation) = &transferbook({
+ from_branch => $frombranch
+ to_branch => $tobranch,
+ barcode => $barcode,
+ ignore_reserves => $ignore_reserves,
+ trigger => $trigger
+ });
Transfers an item to a new branch. If the item is currently on loan, it is automatically returned before the actual transfer.
-C<$newbranch> is the code for the branch to which the item should be transferred.
+C<$fbr> is the code for the branch initiating the transfer.
+C<$tbr> is the code for the branch to which the item should be transferred.
C<$barcode> is the barcode of the item to be transferred.
=cut
sub transferbook {
- my ( $tbr, $barcode, $ignoreRs, $trigger ) = @_;
+ my $params = shift;
+ my $tbr = $params->{to_branch};
+ my $fbr = $params->{from_branch};
+ my $ignoreRs = $params->{ignore_reserves};
+ my $barcode = $params->{barcode};
+ my $trigger = $params->{trigger};
my $messages;
my $dotransfer = 1;
my $item = Koha::Items->find( { barcode => $barcode } );
+ Koha::Exceptions::MissingParameter->throw(
+ "Missing mandatory parameter: from_branch")
+ unless $fbr;
+
+ Koha::Exceptions::MissingParameter->throw(
+ "Missing mandatory parameter: to_branch")
+ unless $tbr;
+
# bad barcode..
unless ( $item ) {
$messages->{'BadBarcode'} = $barcode;
my $itemnumber = $item->itemnumber;
# get branches of book...
my $hbr = $item->homebranch;
- my $fbr = $item->holdingbranch;
# if using Branch Transfer Limits
if ( C4::Context->preference("UseBranchTransferLimits") == 1 ) {
my $switch_onsite_checkout = $params->{switch_onsite_checkout} || 0;
my $cat_borrower = $borrower->{'categorycode'};
my $dbh = C4::Context->dbh;
- my $branch;
- # Get which branchcode we need
- $branch = _GetCircControlBranch($item_object->unblessed,$borrower);
+ # Get which branchcode we need
+ my $branch = _GetCircControlBranch($item_object->unblessed,$borrower);
my $type = $item_object->effective_itemtype;
+ my ($type_object, $parent_type, $parent_maxissueqty_rule);
+ $type_object = Koha::ItemTypes->find( $type );
+ $parent_type = $type_object->parent_type if $type_object;
+ my $child_types = Koha::ItemTypes->search({ parent_type => $type });
+ # Find any children if we are a parent_type;
+
# given branch, patron category, and item type, determine
# applicable issuing rule
+
+ $parent_maxissueqty_rule = Koha::CirculationRules->get_effective_rule(
+ {
+ categorycode => $cat_borrower,
+ itemtype => $parent_type,
+ branchcode => $branch,
+ rule_name => 'maxissueqty',
+ }
+ ) if $parent_type;
+ # If the parent rule is for default type we discount it
+ $parent_maxissueqty_rule = undef if $parent_maxissueqty_rule && !defined $parent_maxissueqty_rule->itemtype;
+
my $maxissueqty_rule = Koha::CirculationRules->get_effective_rule(
{
categorycode => $cat_borrower,
rule_name => 'maxissueqty',
}
);
+
my $maxonsiteissueqty_rule = Koha::CirculationRules->get_effective_rule(
{
categorycode => $cat_borrower,
);
+ my $patron = Koha::Patrons->find($borrower->{borrowernumber});
# if a rule is found and has a loan limit set, count
# how many loans the patron already has that meet that
# rule
- if (defined($maxissueqty_rule) and $maxissueqty_rule->rule_value ne '') {
- my @bind_params;
- my $count_query = q|
- SELECT COUNT(*) AS total, COALESCE(SUM(onsite_checkout), 0) AS onsite_checkouts
- FROM issues
- JOIN items USING (itemnumber)
- |;
+ if (defined($maxissueqty_rule) and $maxissueqty_rule->rule_value ne "") {
- my $rule_itemtype = $maxissueqty_rule->itemtype;
- unless ($rule_itemtype) {
- # matching rule has the default item type, so count only
- # those existing loans that don't fall under a more
- # specific rule
- if (C4::Context->preference('item-level_itypes')) {
- $count_query .= " WHERE items.itype NOT IN (
- SELECT itemtype FROM circulation_rules
- WHERE branchcode = ?
- AND (categorycode = ? OR categorycode = ?)
- AND itemtype IS NOT NULL
- AND rule_name = 'maxissueqty'
- ) ";
+ my $checkouts;
+ if ( $maxissueqty_rule->branchcode ) {
+ if ( C4::Context->preference('CircControl') eq 'PickupLibrary' ) {
+ $checkouts = $patron->checkouts->search(
+ { 'me.branchcode' => $maxissueqty_rule->branchcode } );
+ } elsif (C4::Context->preference('CircControl') eq 'PatronLibrary') {
+ $checkouts = $patron->checkouts; # if branch is the patron's home branch, then count all loans by patron
} else {
- $count_query .= " JOIN biblioitems USING (biblionumber)
- WHERE biblioitems.itemtype NOT IN (
- SELECT itemtype FROM circulation_rules
- WHERE branchcode = ?
- AND (categorycode = ? OR categorycode = ?)
- AND itemtype IS NOT NULL
- AND rule_name = 'maxissueqty'
- ) ";
+ $checkouts = $patron->checkouts->search(
+ { 'item.homebranch' => $maxissueqty_rule->branchcode },
+ { prefetch => 'item' } );
}
- push @bind_params, $maxissueqty_rule->branchcode;
- push @bind_params, $maxissueqty_rule->categorycode;
- push @bind_params, $cat_borrower;
} else {
- # rule has specific item type, so count loans of that
- # specific item type
- if (C4::Context->preference('item-level_itypes')) {
- $count_query .= " WHERE items.itype = ? ";
- } else {
- $count_query .= " JOIN biblioitems USING (biblionumber)
- WHERE biblioitems.itemtype= ? ";
- }
- push @bind_params, $type;
+ $checkouts = $patron->checkouts; # if rule is not branch specific then count all loans by patron
}
+ my $sum_checkouts;
+ my $rule_itemtype = $maxissueqty_rule->itemtype;
+ while ( my $c = $checkouts->next ) {
+ my $itemtype = $c->item->effective_itemtype;
+ my @types;
+ unless ( $rule_itemtype ) {
+ # matching rule has the default item type, so count only
+ # those existing loans that don't fall under a more
+ # specific rule
+ @types = Koha::CirculationRules->search(
+ {
+ branchcode => $maxissueqty_rule->branchcode,
+ categorycode => [ $maxissueqty_rule->categorycode, $cat_borrower ],
+ itemtype => { '!=' => undef },
+ rule_name => 'maxissueqty'
+ }
+ )->get_column('itemtype');
- $count_query .= " AND borrowernumber = ? ";
- push @bind_params, $borrower->{'borrowernumber'};
- my $rule_branch = $maxissueqty_rule->branchcode;
- if ($rule_branch) {
- if (C4::Context->preference('CircControl') eq 'PickupLibrary') {
- $count_query .= " AND issues.branchcode = ? ";
- push @bind_params, $rule_branch;
- } elsif (C4::Context->preference('CircControl') eq 'PatronLibrary') {
- ; # if branch is the patron's home branch, then count all loans by patron
+ next if grep {$_ eq $itemtype} @types;
} else {
- $count_query .= " AND items.homebranch = ? ";
- push @bind_params, $rule_branch;
+ my @types;
+ if ( $parent_maxissueqty_rule ) {
+ # if we have a parent item type then we count loans of the
+ # specific item type or its siblings or parent
+ my $children = Koha::ItemTypes->search({ parent_type => $parent_type });
+ @types = $children->get_column('itemtype');
+ push @types, $parent_type;
+ } elsif ( $child_types ) {
+ # If we are a parent type, we need to count all child types and our own type
+ @types = $child_types->get_column('itemtype');
+ push @types, $type; # And don't forget to count our own types
+ } else { push @types, $type; } # Otherwise only count the specific itemtype
+
+ next unless grep {$_ eq $itemtype} @types;
}
+ $sum_checkouts->{total}++;
+ $sum_checkouts->{onsite_checkouts}++ if $c->onsite_checkout;
+ $sum_checkouts->{itemtype}->{$itemtype}++;
}
- my ( $checkout_count, $onsite_checkout_count ) = $dbh->selectrow_array( $count_query, {}, @bind_params );
-
- my $max_checkouts_allowed = $maxissueqty_rule ? $maxissueqty_rule->rule_value : undef;
- my $max_onsite_checkouts_allowed = $maxonsiteissueqty_rule ? $maxonsiteissueqty_rule->rule_value : undef;
-
- if ( $onsite_checkout and $max_onsite_checkouts_allowed ne '' ) {
- if ( $onsite_checkout_count >= $max_onsite_checkouts_allowed ) {
- return {
- reason => 'TOO_MANY_ONSITE_CHECKOUTS',
- count => $onsite_checkout_count,
- max_allowed => $max_onsite_checkouts_allowed,
- }
- }
- }
- if ( C4::Context->preference('ConsiderOnSiteCheckoutsAsNormalCheckouts') ) {
- my $delta = $switch_onsite_checkout ? 1 : 0;
- if ( $checkout_count >= $max_checkouts_allowed + $delta ) {
- return {
- reason => 'TOO_MANY_CHECKOUTS',
- count => $checkout_count,
- max_allowed => $max_checkouts_allowed,
- };
- }
- } elsif ( not $onsite_checkout ) {
- if ( $checkout_count - $onsite_checkout_count >= $max_checkouts_allowed ) {
- return {
- reason => 'TOO_MANY_CHECKOUTS',
- count => $checkout_count - $onsite_checkout_count,
- max_allowed => $max_checkouts_allowed,
- };
+ my $checkout_count_type = $sum_checkouts->{itemtype}->{$type} || 0;
+ my $checkout_count = $sum_checkouts->{total} || 0;
+ my $onsite_checkout_count = $sum_checkouts->{onsite_checkouts} || 0;
+
+ my $checkout_rules = {
+ checkout_count => $checkout_count,
+ onsite_checkout_count => $onsite_checkout_count,
+ onsite_checkout => $onsite_checkout,
+ max_checkouts_allowed => $maxissueqty_rule ? $maxissueqty_rule->rule_value : undef,
+ max_onsite_checkouts_allowed => $maxonsiteissueqty_rule ? $maxonsiteissueqty_rule->rule_value : undef,
+ switch_onsite_checkout => $switch_onsite_checkout,
+ };
+ # If parent rules exists
+ if ( defined($parent_maxissueqty_rule) and defined($parent_maxissueqty_rule->rule_value) ){
+ $checkout_rules->{max_checkouts_allowed} = $parent_maxissueqty_rule ? $parent_maxissueqty_rule->rule_value : undef;
+ my $qty_over = _check_max_qty($checkout_rules);
+ return $qty_over if defined $qty_over;
+
+ # If the parent rule is less than or equal to the child, we only need check the parent
+ if( $maxissueqty_rule->rule_value < $parent_maxissueqty_rule->rule_value && defined($maxissueqty_rule->itemtype) ) {
+ $checkout_rules->{checkout_count} = $checkout_count_type;
+ $checkout_rules->{max_checkouts_allowed} = $maxissueqty_rule ? $maxissueqty_rule->rule_value : undef;
+ my $qty_over = _check_max_qty($checkout_rules);
+ return $qty_over if defined $qty_over;
}
+ } else {
+ my $qty_over = _check_max_qty($checkout_rules);
+ return $qty_over if defined $qty_over;
}
}
# Now count total loans against the limit for the branch
my $branch_borrower_circ_rule = GetBranchBorrowerCircRule($branch, $cat_borrower);
if (defined($branch_borrower_circ_rule->{patron_maxissueqty}) and $branch_borrower_circ_rule->{patron_maxissueqty} ne '') {
- my @bind_params = ();
- my $branch_count_query = q|
- SELECT COUNT(*) AS total, COALESCE(SUM(onsite_checkout), 0) AS onsite_checkouts
- FROM issues
- JOIN items USING (itemnumber)
- WHERE borrowernumber = ?
- |;
- push @bind_params, $borrower->{borrowernumber};
-
- if (C4::Context->preference('CircControl') eq 'PickupLibrary') {
- $branch_count_query .= " AND issues.branchcode = ? ";
- push @bind_params, $branch;
+ my $checkouts;
+ if ( C4::Context->preference('CircControl') eq 'PickupLibrary' ) {
+ $checkouts = $patron->checkouts->search(
+ { 'me.branchcode' => $branch} );
} elsif (C4::Context->preference('CircControl') eq 'PatronLibrary') {
; # if branch is the patron's home branch, then count all loans by patron
} else {
- $branch_count_query .= " AND items.homebranch = ? ";
- push @bind_params, $branch;
+ $checkouts = $patron->checkouts->search(
+ { 'item.homebranch' => $branch} );
}
- my ( $checkout_count, $onsite_checkout_count ) = $dbh->selectrow_array( $branch_count_query, {}, @bind_params );
+
+ my $checkout_count = $checkouts->count;
+ my $onsite_checkout_count = $checkouts->search({ onsite_checkout => 1 })->count;
my $max_checkouts_allowed = $branch_borrower_circ_rule->{patron_maxissueqty};
- my $max_onsite_checkouts_allowed = $branch_borrower_circ_rule->{patron_maxonsiteissueqty};
-
- if ( $onsite_checkout and $max_onsite_checkouts_allowed ne '' ) {
- if ( $onsite_checkout_count >= $max_onsite_checkouts_allowed ) {
- return {
- reason => 'TOO_MANY_ONSITE_CHECKOUTS',
- count => $onsite_checkout_count,
- max_allowed => $max_onsite_checkouts_allowed,
- }
- }
- }
- if ( C4::Context->preference('ConsiderOnSiteCheckoutsAsNormalCheckouts') ) {
- my $delta = $switch_onsite_checkout ? 1 : 0;
- if ( $checkout_count >= $max_checkouts_allowed + $delta ) {
- return {
- reason => 'TOO_MANY_CHECKOUTS',
- count => $checkout_count,
- max_allowed => $max_checkouts_allowed,
- };
- }
- } elsif ( not $onsite_checkout ) {
- if ( $checkout_count - $onsite_checkout_count >= $max_checkouts_allowed ) {
- return {
- reason => 'TOO_MANY_CHECKOUTS',
- count => $checkout_count - $onsite_checkout_count,
- max_allowed => $max_checkouts_allowed,
- };
+ my $max_onsite_checkouts_allowed = $branch_borrower_circ_rule->{patron_maxonsiteissueqty} || undef;
+
+ my $qty_over = _check_max_qty(
+ {
+ checkout_count => $checkout_count,
+ onsite_checkout_count => $onsite_checkout_count,
+ onsite_checkout => $onsite_checkout,
+ max_checkouts_allowed => $max_checkouts_allowed,
+ max_onsite_checkouts_allowed => $max_onsite_checkouts_allowed,
+ switch_onsite_checkout => $switch_onsite_checkout
}
- }
+ );
+ return $qty_over if defined $qty_over;
}
if ( not defined( $maxissueqty_rule ) and not defined($branch_borrower_circ_rule->{patron_maxissueqty}) ) {
return;
}
+sub _check_max_qty {
+ my $params = shift;
+ my $checkout_count = $params->{checkout_count};
+ my $onsite_checkout_count = $params->{onsite_checkout_count};
+ my $onsite_checkout = $params->{onsite_checkout};
+ my $max_checkouts_allowed = $params->{max_checkouts_allowed};
+ my $max_onsite_checkouts_allowed = $params->{max_onsite_checkouts_allowed};
+ my $switch_onsite_checkout = $params->{switch_onsite_checkout};
+
+ if ( $onsite_checkout and defined $max_onsite_checkouts_allowed ) {
+ if ( $max_onsite_checkouts_allowed eq '' ) { return; }
+ if ( $onsite_checkout_count >= $max_onsite_checkouts_allowed ) {
+ return {
+ reason => 'TOO_MANY_ONSITE_CHECKOUTS',
+ count => $onsite_checkout_count,
+ max_allowed => $max_onsite_checkouts_allowed,
+ };
+ }
+ }
+ if ( C4::Context->preference('ConsiderOnSiteCheckoutsAsNormalCheckouts') ) {
+ if ( $max_checkouts_allowed eq '' ) { return; }
+ my $delta = $switch_onsite_checkout ? 1 : 0;
+ if ( $checkout_count >= $max_checkouts_allowed + $delta ) {
+ return {
+ reason => 'TOO_MANY_CHECKOUTS',
+ count => $checkout_count,
+ max_allowed => $max_checkouts_allowed,
+ };
+ }
+ }
+ elsif ( not $onsite_checkout ) {
+ if ( $max_checkouts_allowed eq '' ) { return; }
+ if (
+ $checkout_count - $onsite_checkout_count >= $max_checkouts_allowed )
+ {
+ return {
+ reason => 'TOO_MANY_CHECKOUTS',
+ count => $checkout_count - $onsite_checkout_count,
+ max_allowed => $max_checkouts_allowed,
+ };
+ }
+ }
+
+ return;
+}
+
=head2 CanBookBeIssued
( $issuingimpossible, $needsconfirmation, [ $alerts ] ) = CanBookBeIssued( $patron,
}
}
+ # Additional Materials Check
+ if ( C4::Context->preference("CircConfirmParts") ) {
+ my $no_of_parts = $item_object->materials || 0;
+ if ( $no_of_parts > 0 ) {
+ $needsconfirmation{additional_materials} = $no_of_parts;
+ }
+ }
+
#
# CHECK IF BOOK ALREADY ISSUED TO THIS BORROWER
#
$needsconfirmation{'resborrowernumber'} = $patron->borrowernumber;
$needsconfirmation{'resbranchcode'} = $res->{branchcode};
$needsconfirmation{'reswaitingdate'} = $res->{'waitingdate'};
+ $needsconfirmation{'reserve_id'} = $res->{reserve_id};
}
elsif ( $restype eq "Reserved" ) {
# The item is on reserve for someone else.
$needsconfirmation{'resborrowernumber'} = $patron->borrowernumber;
$needsconfirmation{'resbranchcode'} = $patron->branchcode;
$needsconfirmation{'resreservedate'} = $res->{reservedate};
+ $needsconfirmation{'reserve_id'} = $res->{reserve_id};
}
}
}
}
# Remove any items that are not holdable for this patron
- @items = grep { CanItemBeReserved( $borrower->{borrowernumber}, $_->itemnumber )->{status} eq 'OK' } @items;
+ @items = grep { CanItemBeReserved( $borrower->{borrowernumber}, $_->itemnumber, undef, { ignore_found_holds => 1 } )->{status} eq 'OK' } @items;
my $items_count = scalar @items;
my $issuedate = dt_from_string();
- my $calendar = Koha::Calendar->new( branchcode => $branchcode );
-
my $itype = $item_object->effective_itemtype;
+ my $daysmode = Koha::CirculationRules->get_effective_daysmode(
+ {
+ categorycode => $borrower->{categorycode},
+ itemtype => $itype,
+ branchcode => $branchcode,
+ }
+ );
+ my $calendar = Koha::Calendar->new( branchcode => $branchcode, days_mode => $daysmode );
+
my $orig_due = C4::Circulation::CalcDateDue( $issuedate, $itype, $branchcode, $borrower );
my $decreaseLoanHighHoldsDuration = C4::Context->preference('decreaseLoanHighHoldsDuration');
UpdateTotalIssues( $item_object->biblionumber, 1 );
}
- ## If item was lost, it has now been found, reverse any list item charges if necessary.
- if ( $item_object->itemlost ) {
- if (
- Koha::RefundLostItemFeeRules->should_refund(
- {
- current_branch => C4::Context->userenv->{branch},
- item_home_branch => $item_object->homebranch,
- item_holding_branch => $item_object->holdingbranch,
- }
- )
- )
- {
- _FixAccountForLostAndFound( $item_object->itemnumber, undef,
- $item_object->barcode );
- }
- }
-
$item_object->issues( ( $item_object->issues || 0 ) + 1);
$item_object->holdingbranch(C4::Context->userenv->{'branch'});
$item_object->itemlost(0);
$borrower->{'borrowernumber'},
$item_object->itemnumber,
) if C4::Context->preference("IssueLog");
+
+ Koha::Plugins->call('after_circ_action', {
+ action => 'checkout',
+ payload => {
+ type => ( $onsite_checkout ? 'onsite_checkout' : 'issue' ),
+ checkout => $issue->get_from_storage
+ }
+ });
}
}
return $issue;
sub GetLoanLength {
my ( $categorycode, $itemtype, $branchcode ) = @_;
- # Set search precedences
- my @params = (
- {
- categorycode => $categorycode,
- itemtype => $itemtype,
- branchcode => $branchcode,
- },
- {
- categorycode => $categorycode,
- itemtype => undef,
- branchcode => $branchcode,
- },
- {
- categorycode => undef,
- itemtype => $itemtype,
- branchcode => $branchcode,
- },
- {
- categorycode => undef,
- itemtype => undef,
- branchcode => $branchcode,
- },
- {
- categorycode => $categorycode,
- itemtype => $itemtype,
- branchcode => undef,
- },
- {
- categorycode => $categorycode,
- itemtype => undef,
- branchcode => undef,
- },
- {
- categorycode => undef,
- itemtype => $itemtype,
- branchcode => undef,
- },
- {
- categorycode => undef,
- itemtype => undef,
- branchcode => undef,
- },
- );
-
# Initialize default values
my $rules = {
issuelength => 0,
lengthunit => 'days',
};
- # Search for rules!
- foreach my $rule_name (qw( issuelength renewalperiod lengthunit )) {
- foreach my $params (@params) {
- my $rule = Koha::CirculationRules->search(
- {
- rule_name => $rule_name,
- %$params,
- }
- )->next();
+ my $found = Koha::CirculationRules->get_effective_rules( {
+ branchcode => $branchcode,
+ categorycode => $categorycode,
+ itemtype => $itemtype,
+ rules => [
+ 'issuelength',
+ 'renewalperiod',
+ 'lengthunit'
+ ],
+ } );
- if ($rule) {
- $rules->{$rule_name} = $rule->rule_value;
- last;
- }
- }
+ # Search for rules!
+ foreach my $rule_name (keys %$found) {
+ $rules->{$rule_name} = $found->{$rule_name};
}
return $rules;
my $messages;
my $patron;
my $doreturn = 1;
- my $validTransfert = 0;
+ my $validTransfer = 1;
my $stat_type = 'return';
# get information on item
# the holdingbranch is updated if the document is returned to another location.
# this is always done regardless of whether the item was on loan or not
- my $item_holding_branch = $item->holdingbranch;
if ($item->holdingbranch ne $branch) {
$item->holdingbranch($branch)->store;
}
+ my $item_was_lost = $item->itemlost;
my $leave_item_lost = C4::Context->preference("BlockReturnOfLostItems") ? 1 : 0;
- ModDateLastSeen( $item->itemnumber, $leave_item_lost );
+ my $updated_item = ModDateLastSeen( $item->itemnumber, $leave_item_lost ); # will unset itemlost if needed
+
+ # fix up the accounts.....
+ if ( $item_was_lost ) {
+ $messages->{'WasLost'} = 1;
+ unless ( C4::Context->preference("BlockReturnOfLostItems") ) {
+ $messages->{'LostItemFeeRefunded'} = $updated_item->{_refunded};
+ }
+ }
# check if we have a transfer for this document
my ($datesent,$frombranch,$tobranch) = GetTransfers( $item->itemnumber );
# if we have a transfer to do, we update the line of transfers with the datearrived
my $is_in_rotating_collection = C4::RotatingCollections::isItemInAnyCollection( $item->itemnumber );
if ($datesent) {
+ # At this point we will either fill the transfer or it is a wrong transfer
+ # either way we should not now generate a new transfer
+ $validTransfer = 0;
if ( $tobranch eq $branch ) {
my $sth = C4::Context->dbh->prepare(
"UPDATE branchtransfers SET datearrived = now() WHERE itemnumber= ? AND datearrived IS NULL"
);
$sth->execute( $item->itemnumber );
- # if we have a reservation with valid transfer, we can set it's status to 'W'
- C4::Reserves::ModReserveStatus($item->itemnumber, 'W');
} else {
$messages->{'WrongTransfer'} = $tobranch;
$messages->{'WrongTransferItem'} = $item->itemnumber;
}
- $validTransfert = 1;
- }
-
- # fix up the accounts.....
- if ( $item->itemlost ) {
- $messages->{'WasLost'} = 1;
- unless ( C4::Context->preference("BlockReturnOfLostItems") ) {
- if (
- Koha::RefundLostItemFeeRules->should_refund(
- {
- current_branch => C4::Context->userenv->{branch},
- item_home_branch => $item->homebranch,
- item_holding_branch => $item_holding_branch
- }
- )
- )
- {
- _FixAccountForLostAndFound( $item->itemnumber,
- $borrowernumber, $barcode );
- $messages->{'LostItemFeeRefunded'} = 1;
- }
- }
}
# fix up the overdues in accounts...
if ($borrowernumber) {
my $fix = _FixOverduesOnReturn( $borrowernumber, $item->itemnumber, $exemptfine, 'RETURNED' );
- defined($fix) or warn "_FixOverduesOnReturn($borrowernumber, $item->itemnumber...) failed!"; # zero is OK, check defined
+ defined($fix) or warn "_FixOverduesOnReturn($borrowernumber, ".$item->itemnumber."...) failed!"; # zero is OK, check defined
- if ( $issue and $issue->is_overdue ) {
+ if ( $issue and $issue->is_overdue($return_date) ) {
# fix fine days
my ($debardate,$reminder) = _debar_user_on_return( $patron_unblessed, $item->unblessed, dt_from_string($issue->date_due), $return_date );
if ($reminder){
my $lookahead= C4::Context->preference('ConfirmFutureHolds'); #number of days to look for future holds
($resfound, $resrec, undef) = C4::Reserves::CheckReserves( $item->itemnumber, undef, $lookahead ) unless ( $item->withdrawn );
# if a hold is found and is waiting at another branch, change the priority back to 1 and trigger the hold (this will trigger a transfer and update the hold status properly)
- if ( $resfound eq "Waiting" and $branch ne $resrec->{branchcode} ) {
+ if ( $resfound and $resfound eq "Waiting" and $branch ne $resrec->{branchcode} ) {
my $hold = C4::Reserves::RevertWaitingStatus( { itemnumber => $item->itemnumber } );
$resfound = 'Reserved';
$resrec = $hold->unblessed;
type => $stat_type,
itemnumber => $itemnumber,
itemtype => $itemtype,
+ location => $item->location,
borrowernumber => $borrowernumber,
ccode => $item->ccode,
});
if C4::Context->preference("ReturnLog");
}
- # Remove any OVERDUES related debarment if the borrower has no overdues
- if ( $borrowernumber
- && $patron->debarred
- && C4::Context->preference('AutoRemoveOverduesRestrictions')
- && !Koha::Patrons->find( $borrowernumber )->has_overdues
- && @{ GetDebarments({ borrowernumber => $borrowernumber, type => 'OVERDUES' }) }
- ) {
- DelUniqueDebarment({ borrowernumber => $borrowernumber, type => 'OVERDUES' });
- }
-
# Check if this item belongs to a biblio record that is attached to an
# ILL request, if it is we need to update the ILL request's status
- if (C4::Context->preference('CirculateILL')) {
+ if ( $doreturn and C4::Context->preference('CirculateILL')) {
my $request = Koha::Illrequests->find(
{ biblio_id => $item->biblio->biblionumber }
);
}
# Transfer to returnbranch if Automatic transfer set or append message NeedsTransfer
- if (!$is_in_rotating_collection && ($doreturn or $messages->{'NotIssued'}) and !$resfound and ($branch ne $returnbranch) and not $messages->{'WrongTransfer'}){
+ if ($validTransfer && !$is_in_rotating_collection && ($doreturn or $messages->{'NotIssued'}) and !$resfound and ($branch ne $returnbranch) ){
my $BranchTransferLimitsType = C4::Context->preference("BranchTransferLimitsType") eq 'itemtype' ? 'effective_itemtype' : 'ccode';
if (C4::Context->preference("AutomaticItemReturn" ) or
(C4::Context->preference("UseBranchTransferLimits") and
}
}
+ if ( $doreturn and $issue ) {
+ my $checkin = Koha::Old::Checkouts->find($issue->id);
+
+ Koha::Plugins->call('after_circ_action', {
+ action => 'checkin',
+ payload => {
+ checkout=> $checkin
+ }
+ });
+ }
+
return ( $doreturn, $messages, $issue, ( $patron ? $patron->unblessed : {} ));
}
# FIXME Improve the return value and handle it from callers
$schema->txn_do(sub {
+ my $patron = Koha::Patrons->find( $borrowernumber );
+
# Update the returndate value
if ( $returndate ) {
$issue->returndate( $returndate )->store->discard_changes; # update and refetch
if ( C4::Context->preference('StoreLastBorrower') ) {
my $item = Koha::Items->find( $itemnumber );
- my $patron = Koha::Patrons->find( $borrowernumber );
$item->last_returned_by( $patron );
}
+
+ # Remove any OVERDUES related debarment if the borrower has no overdues
+ if ( C4::Context->preference('AutoRemoveOverduesRestrictions')
+ && $patron->debarred
+ && !$patron->has_overdues
+ && @{ GetDebarments({ borrowernumber => $borrowernumber, type => 'OVERDUES' }) }
+ ) {
+ DelUniqueDebarment({ borrowernumber => $borrowernumber, type => 'OVERDUES' });
+ }
+
});
return $issue_id;
return 0 unless $accountlines->count; # no warning, there's just nothing to fix
my $accountline = $accountlines->next;
- if ($exemptfine) {
- my $amountoutstanding = $accountline->amountoutstanding;
-
- return if $amountoutstanding <= 0;
+ my $payments = $accountline->credits;
+ my $amountoutstanding = $accountline->amountoutstanding;
+ if ( $accountline->amount == 0 && $payments->count == 0 ) {
+ $accountline->delete;
+ } elsif ($exemptfine && ($amountoutstanding != 0)) {
my $account = Koha::Account->new({patron_id => $borrowernumber});
my $credit = $account->add_credit(
{
$credit->apply({ debits => [ $accountline ], offset_type => 'Forgiven' });
- $accountline->status('FORGIVEN');
-
if (C4::Context->preference("FinesLog")) {
&logaction("FINES", 'MODIFY',$borrowernumber,"Overdue forgiven: item $item");
}
+
+ $accountline->status('FORGIVEN');
+ $accountline->store();
} else {
$accountline->status($status);
- }
+ $accountline->store();
- return $accountline->store();
+ }
}
);
return $result;
}
-=head2 _FixAccountForLostAndFound
-
- &_FixAccountForLostAndFound($itemnumber, [$borrowernumber, $barcode]);
-
-Finds the most recent lost item charge for this item and refunds the borrower
-appropriatly, taking into account any payments or writeoffs already applied
-against the charge.
-
-Internal function, not exported, called only by AddReturn.
-
-=cut
-
-sub _FixAccountForLostAndFound {
- my $itemnumber = shift or return;
- my $borrowernumber = @_ ? shift : undef;
- my $item_id = @_ ? shift : $itemnumber; # Send the barcode if you want that logged in the description
-
- my $credit;
-
- # check for charge made for lost book
- my $accountlines = Koha::Account::Lines->search(
- {
- itemnumber => $itemnumber,
- debit_type_code => 'LOST',
- status => [ undef, { '<>' => 'FOUND' } ]
- },
- {
- order_by => { -desc => [ 'date', 'accountlines_id' ] }
- }
- );
-
- return unless $accountlines->count > 0;
- my $accountline = $accountlines->next;
- my $total_to_refund = 0;
-
- return unless $accountline->borrowernumber;
- my $patron = Koha::Patrons->find( $accountline->borrowernumber );
- return unless $patron; # Patron has been deleted, nobody to credit the return to
-
- my $account = $patron->account;
-
- # Use cases
- if ( $accountline->amount > $accountline->amountoutstanding ) {
- # some amount has been cancelled. collect the offsets that are not writeoffs
- # this works because the only way to subtract from this kind of a debt is
- # using the UI buttons 'Pay' and 'Write off'
- my $credits_offsets = Koha::Account::Offsets->search({
- debit_id => $accountline->id,
- credit_id => { '!=' => undef }, # it is not the debit itself
- type => { '!=' => 'Writeoff' },
- amount => { '<' => 0 } # credits are negative on the DB
- });
-
- $total_to_refund = ( $credits_offsets->count > 0 )
- ? $credits_offsets->total * -1 # credits are negative on the DB
- : 0;
- }
-
- my $credit_total = $accountline->amountoutstanding + $total_to_refund;
-
- if ( $credit_total > 0 ) {
- my $branchcode = C4::Context->userenv ? C4::Context->userenv->{'branch'} : undef;
- $credit = $account->add_credit(
- {
- amount => $credit_total,
- description => 'Item found ' . $item_id,
- type => 'LOST_FOUND',
- interface => C4::Context->interface,
- library_id => $branchcode,
- item_id => $itemnumber
- }
- );
-
- $credit->apply( { debits => [ $accountline ] } );
- }
-
- # Update the account status
- $accountline->discard_changes->status('FOUND');
- $accountline->store;
-
- $accountline->item->paidfor('')->store({ log_action => 0 });
-
- if ( defined $account and C4::Context->preference('AccountAutoReconcile') ) {
- $account->reconcile_balance;
- }
-
- return ($credit) ? $credit->id : undef;
-}
-
=head2 _GetCircControlBranch
my $circ_control_branch = _GetCircControlBranch($iteminfos, $borrower);
=cut
sub CanBookBeRenewed {
- my ( $borrowernumber, $itemnumber, $override_limit ) = @_;
+ my ( $borrowernumber, $itemnumber, $override_limit, $cron ) = @_;
my $dbh = C4::Context->dbh;
my $renews = 1;
- my $auto_renew = 0;
+ my $auto_renew = "no";
my $item = Koha::Items->find($itemnumber) or return ( 0, 'no_item' );
my $issue = $item->checkout or return ( 0, 'no_checkout' );
if ( $soonestrenewal > dt_from_string() )
{
- return ( 0, "auto_too_soon" ) if $issue->auto_renew && $patron->autorenew_checkouts;
- return ( 0, "too_soon" );
+ $auto_renew = ($issue->auto_renew && $patron->autorenew_checkouts) ? "auto_too_soon" : "too_soon";
}
elsif ( $issue->auto_renew && $patron->autorenew_checkouts ) {
- $auto_renew = 1;
+ $auto_renew = "ok";
}
}
# Fallback for automatic renewals:
# If norenewalbefore is undef, don't renew before due date.
- if ( $issue->auto_renew && !$auto_renew && $patron->autorenew_checkouts ) {
+ if ( $issue->auto_renew && $auto_renew eq "no" && $patron->autorenew_checkouts ) {
my $now = dt_from_string;
if ( $now >= dt_from_string( $issue->date_due, 'sql' ) ){
- $auto_renew = 1;
+ $auto_renew = "ok";
} else {
- return ( 0, "auto_too_soon" );
+ $auto_renew = "auto_too_soon";
}
}
}
my ( $resfound, $resrec, undef ) = C4::Reserves::CheckReserves($itemnumber);
+ # If next hold is non priority, then check if any hold with priority (non_priority = 0) exists for the same biblionumber.
+ if ( $resfound && $resrec->{non_priority} ) {
+ $resfound = Koha::Holds->search(
+ { biblionumber => $resrec->{biblionumber}, non_priority => 0 } )
+ ->count > 0;
+ }
+
+
+
# This item can fill one or more unfilled reserve, can those unfilled reserves
# all be filled by other available items?
if ( $resfound
}
}
}
- return ( 0, "on_reserve" ) if $resfound; # '' when no hold was found
- return ( 0, "auto_renew" ) if $auto_renew && !$override_limit; # 0 if auto-renewal should not succeed
+ if( $cron ) { #The cron wants to return 'too_soon' over 'on_reserve'
+ return ( 0, $auto_renew ) if $auto_renew =~ 'too_soon';#$auto_renew ne "no" && $auto_renew ne "ok";
+ return ( 0, "on_reserve" ) if $resfound; # '' when no hold was found
+ } else { # For other purposes we want 'on_reserve' before 'too_soon'
+ return ( 0, "on_reserve" ) if $resfound; # '' when no hold was found
+ return ( 0, $auto_renew ) if $auto_renew =~ 'too_soon';#$auto_renew ne "no" && $auto_renew ne "ok";
+ }
+
+ return ( 0, "auto_renew" ) if $auto_renew eq "ok" && !$override_limit; # 0 if auto-renewal should not succeed
return ( 1, undef );
}
#Log the renewal
logaction("CIRCULATION", "RENEWAL", $borrowernumber, $itemnumber) if C4::Context->preference("RenewalLog");
+
+ Koha::Plugins->call('after_circ_action', {
+ action => 'renewal',
+ payload => {
+ checkout => $issue->get_from_storage
+ }
+ });
});
return $datedue;
$newdatedue = CalcDateDue($startdate,$itemtype,$branchcode,$borrower);
this function calculates the due date given the start date and configured circulation rules,
-checking against the holidays calendar as per the 'useDaysMode' syspref.
+checking against the holidays calendar as per the daysmode circulation rule.
C<$startdate> = DateTime object representing start date of loan period (assumed to be today)
C<$itemtype> = itemtype code of item in question
C<$branch> = location whose calendar to use
}
+ my $daysmode = Koha::CirculationRules->get_effective_daysmode(
+ {
+ categorycode => $borrower->{categorycode},
+ itemtype => $itemtype,
+ branchcode => $branch,
+ }
+ );
+
# calculate the datedue as normal
- if ( C4::Context->preference('useDaysMode') eq 'Days' )
+ if ( $daysmode eq 'Days' )
{ # ignoring calendar
if ( $loanlength->{lengthunit} eq 'hours' ) {
$datedue->add( hours => $loanlength->{$length_key} );
else { # days
$dur = DateTime::Duration->new( days => $loanlength->{$length_key});
}
- my $calendar = Koha::Calendar->new( branchcode => $branch );
+ my $calendar = Koha::Calendar->new( branchcode => $branch, days_mode => $daysmode );
$datedue = $calendar->addDate( $datedue, $dur, $loanlength->{lengthunit} );
if ($loanlength->{lengthunit} eq 'days') {
$datedue->set_hour(23);
$datedue = $expiry_dt->clone->set_time_zone( C4::Context->tz );
}
}
- if ( C4::Context->preference('useDaysMode') ne 'Days' ) {
- my $calendar = Koha::Calendar->new( branchcode => $branch );
+ if ( $daysmode ne 'Days' ) {
+ my $calendar = Koha::Calendar->new( branchcode => $branch, days_mode => $daysmode );
if ( $calendar->is_holiday($datedue) ) {
# Don't return on a closed day
$datedue = $calendar->prev_open_days( $datedue, 1 );
MarkIssueReturned( $borrowernumber, $itemnum );
}
+=head2 LostItem
+
+ LostItem( $itemnumber, $mark_lost_from, $force_mark_returned, [$params] );
+
+The final optional parameter, C<$params>, expected to contain
+'skip_record_index' key, which relayed down to Koha::Item/store,
+there it prevents calling of ModZebra index_records,
+which takes most of the time in batch adds/deletes: index_records better
+to be called later in C<additem.pl> after the whole loop.
+
+$params:
+ skip_record_index => 1|0
+
+=cut
sub LostItem{
- my ($itemnumber, $mark_lost_from, $force_mark_returned) = @_;
+ my ($itemnumber, $mark_lost_from, $force_mark_returned, $params) = @_;
unless ( $mark_lost_from ) {
# Temporary check to avoid regressions
#When item is marked lost automatically cancel its outstanding transfers and set items holdingbranch to the transfer source branch (frombranch)
if (my ( $datesent,$frombranch,$tobranch ) = GetTransfers($itemnumber)) {
- Koha::Items->find($itemnumber)->holdingbranch($frombranch)->store;
+ Koha::Items->find($itemnumber)->holdingbranch($frombranch)->store({ skip_record_index => $params->{skip_record_index} });
}
my $transferdeleted = DeleteTransfer($itemnumber);
}
return @$rows;
}
+=head2 Internal methods
+
+=cut
+
sub _CalculateAndUpdateFine {
my ($params) = @_;
return 0;
}
-
1;
__END__