@@ -, +, @@ --After each step, a message will be displayed in the action_logs table and in Tools > Log viewer -- --- C4/Circulation.pm | 166 ++++++++++++++++++++++++++++++++++++++++---- circ/circulation.pl | 35 +++++++++- circ/renew.pl | 50 ++++++++++++- circ/returns.pl | 26 ++++++- reserve/request.pl | 69 ++++++++++++++++-- 5 files changed, 319 insertions(+), 27 deletions(-) --- a/C4/Circulation.pm +++ a/C4/Circulation.pm @@ -22,6 +22,7 @@ use Modern::Perl; use DateTime; use POSIX qw( floor ); use Encode; +use JSON; use C4::Context; use C4::Stats qw( UpdateStats ); @@ -317,6 +318,21 @@ A recall for this item was found, and the item needs to be transferred to the re =cut +my $query = CGI->new; + +my $stickyduedate = $query->param('stickyduedate'); +my $duedatespec = $query->param('duedatespec'); +my $restoreduedatespec = $query->param('restoreduedatespec') || $duedatespec; +if ( $restoreduedatespec && $restoreduedatespec eq "highholds_empty" ) { + undef $restoreduedatespec; +} +my $issueconfirmed = $query->param('issueconfirmed'); +my $cancelreserve = $query->param('cancelreserve'); +my $cancel_recall = $query->param('cancel_recall'); +my $recall_id = $query->param('recall_id'); +my $debt_confirmed = $query->param('debt_confirmed') || 0; # Don't show the debt error dialog twice +my $charges = $query->param('charges') || q{}; + sub transferbook { my $params = shift; my $tbr = $params->{to_branch}; @@ -801,9 +817,22 @@ sub CanBookBeIssued { my $circ_library = Koha::Libraries->find( _GetCircControlBranch($item_object, $patron) ); my $now = dt_from_string(); + my $message; + my @message; + $duedate ||= CalcDateDue( $now, $effective_itemtype, $circ_library->branchcode, $patron ); + if (DateTime->compare($duedate,$now) == -1 ) { # duedate cannot be before now $needsconfirmation{INVALID_DATE} = $duedate; + if ($issueconfirmed) { + if ($message ) { + $message = "$message + sticky due date is invalid or due date in the past"; + } else { + $message = "sticky due date is invalid or due date in the past"; + } + push(@message, "sticky due date is invalid or due date in the past"); + } + } my $fees = Koha::Charges::Fees->new( @@ -914,10 +943,26 @@ sub CanBookBeIssued { else { if ( $non_issues_charges > $amountlimit && $allowfineoverride ) { $needsconfirmation{DEBT} = $non_issues_charges; + if ($issueconfirmed) { + if ($message) { + $message = "$message + borrower had amend"; + } else { + $message = "borrower had amend"; + } + push(@message, "borrower had amend"); + } } elsif ( $non_issues_charges > $amountlimit && !$allowfineoverride) { $issuingimpossible{DEBT} = $non_issues_charges; } elsif ( $non_issues_charges > 0 && $allfinesneedoverride ) { $needsconfirmation{DEBT} = $non_issues_charges; + if ($issueconfirmed) { + if ($message) { + $message = "$message + borrower had amend"; + } else { + $message = "borrower had amend"; + } + push(@message, "borrower had amend"); + } } } @@ -994,13 +1039,20 @@ sub CanBookBeIssued { } else { if ( C4::Context->preference('AutoReturnCheckedOutItems') ) { $alerts{RETURNED_FROM_ANOTHER} = { patron => $patron }; - } - else { + } else { $needsconfirmation{ISSUED_TO_ANOTHER} = 1; $needsconfirmation{issued_firstname} = $patron->firstname; $needsconfirmation{issued_surname} = $patron->surname; $needsconfirmation{issued_cardnumber} = $patron->cardnumber; $needsconfirmation{issued_borrowernumber} = $patron->borrowernumber; + if ($issueconfirmed) { + if ($message) { + $message = "$message + item is checked out for someone else "; + } else { + $message = "item is checked out for someone else"; + } + push(@message, "item is checked out for someone else"); + } } } } @@ -1024,6 +1076,14 @@ sub CanBookBeIssued { $needsconfirmation{current_loan_count} = $toomany->{count}; $needsconfirmation{max_loans_allowed} = $toomany->{max_allowed}; $needsconfirmation{circulation_rule_TOO_MANY} = $toomany->{circulation_rule}; + if ($issueconfirmed) { + if ($message) { + $message = "$message + too many checkout"; + } else { + $message = "too many checkout"; + } + push(@message, "too many checkout"); + } } else { $issuingimpossible{TOO_MANY} = $toomany->{reason}; $issuingimpossible{current_loan_count} = $toomany->{count}; @@ -1051,6 +1111,14 @@ sub CanBookBeIssued { }else{ $needsconfirmation{NOT_FOR_LOAN_FORCING} = 1; $needsconfirmation{item_notforloan} = $item_object->notforloan; + if ($issueconfirmed) { + if ($message) { + $message = "$message + item not for loan"; + } else { + $message = "item not for loan"; + } + push(@message, "item not for loan"); + } } } else { @@ -1097,6 +1165,14 @@ sub CanBookBeIssued { my $code = $av->count ? $av->next->lib : ''; $needsconfirmation{ITEM_LOST} = $code if ( C4::Context->preference("IssueLostItem") eq 'confirm' ); $alerts{ITEM_LOST} = $code if ( C4::Context->preference("IssueLostItem") eq 'alert' ); + if ($issueconfirmed) { + if ($message) { + $message = "$message + item lost"; + } else { + $message = "item lost"; + } + push(@message, "item lost"); + } } if ( C4::Context->preference("IndependentBranches") ) { my $userenv = C4::Context->userenv; @@ -1193,6 +1269,14 @@ sub CanBookBeIssued { $needsconfirmation{'resbranchcode'} = $res->{branchcode}; $needsconfirmation{'reswaitingdate'} = $res->{'waitingdate'}; $needsconfirmation{'reserve_id'} = $res->{reserve_id}; + if ($issueconfirmed) { + if($message) { + $message = "$message + item is on reserve and waiting, but has been reserved by some other patron."; + } else { + $message = "item is on reserve and waiting, but has been reserved by some other patron."; + } + push(@message, "item is on reserve and waiting, but has been reserved by some other patron"); + } } elsif ( $restype eq "Reserved" ) { # The item is on reserve for someone else. @@ -1204,6 +1288,14 @@ sub CanBookBeIssued { $needsconfirmation{'resbranchcode'} = $res->{branchcode}; $needsconfirmation{'resreservedate'} = $res->{reservedate}; $needsconfirmation{'reserve_id'} = $res->{reserve_id}; + if ($issueconfirmed) { + if($message) { + $message = "$message + item is on reserve for someone else"; + } else { + $message = "item is on reserve for someone else"; + } + push(@message, "item is on reserve for someone else"); + } } elsif ( $restype eq "Transferred" ) { # The item is determined hold being transferred for someone else. @@ -1215,6 +1307,14 @@ sub CanBookBeIssued { $needsconfirmation{'resbranchcode'} = $res->{branchcode}; $needsconfirmation{'resreservedate'} = $res->{reservedate}; $needsconfirmation{'reserve_id'} = $res->{reserve_id}; + if ($issueconfirmed) { + if($message) { + $message = "$message + item is determined hold being transferred for someone else"; + } else { + $message = "item is determined hold being transferred for someone else"; + } + push(@message, "item is determined hold being transferred for someone else"); + } } elsif ( $restype eq "Processing" ) { # The item is determined hold being processed for someone else. @@ -1226,6 +1326,14 @@ sub CanBookBeIssued { $needsconfirmation{'resbranchcode'} = $res->{branchcode}; $needsconfirmation{'resreservedate'} = $res->{reservedate}; $needsconfirmation{'reserve_id'} = $res->{reserve_id}; + if ($issueconfirmed) { + if($message) { + $message = "$message + item is determined hold being processed for someone else"; + } else { + $message = "item is determined hold being processed for someone else"; + } + push(@message, "item is determined hold being processed for someone else"); + } } } } @@ -1269,6 +1377,12 @@ sub CanBookBeIssued { if ( $daysToAgeRestriction && $daysToAgeRestriction > 0 ) { if ( C4::Context->preference('AgeRestrictionOverride') ) { $needsconfirmation{AGE_RESTRICTION} = "$agerestriction"; + if($message) { + $message = "$message + age restriction"; + } else { + $message = "age restriction"; + } + push(@message, "age restriction"); } else { $issuingimpossible{AGE_RESTRICTION} = "$agerestriction"; @@ -1325,10 +1439,39 @@ sub CanBookBeIssued { } } + my $borrower = $patron; + #my $borrowernumber = $patron->cardnumber; + my $user = C4::Context->userenv->{number}; + my $branchcode = C4::Context->userenv->{branch}; + my $item = Koha::Items->find( { barcode => $barcode } ); + my $itemnumber = $item->itemnumber; + + # action, cardnumber, barcode, date, heure, user, branche + if ($issueconfirmed) { + + my $infos = ({ + message => \@message, + borrowernumber => $borrower->borrowernumber, + barcode => $barcode, + manager_id => $user, + branchcode => $branchcode, + }); + + my $json_infos = JSON->new->utf8->pretty->encode($infos); + $json_infos =~ s/"/'/g; + + logaction( + "CIRCULATION", "ISSUE", + $borrower->{'borrowernumber'}, + $json_infos, + ) if C4::Context->preference("IssueLog"); + } + + return ( \%issuingimpossible, \%needsconfirmation, \%alerts, \%messages, ); } -=head2 CanBookBeReturned +=head2 CanBookBeReturnedcd ($returnallowed, $message) = CanBookBeReturned($item, $branch) @@ -2344,8 +2487,7 @@ sub AddReturn { $transfer->receive; $messages->{'TransferArrived'} = $transfer->frombranch; # validTransfer=1 allows us returning the item back if the reserve is cancelled - $validTransfer = 1 - if defined $transfer->reason && $transfer->reason eq 'Reserve'; + $validTransfer = 1 if $transfer->reason eq 'Reserve'; } else { $messages->{'WrongTransfer'} = $transfer->tobranch; @@ -3181,9 +3323,6 @@ fallback to a true value C<$automatic> is a boolean flag indicating the renewal was triggered automatically and not by a person ( librarian or patron ) -C<$skip_record_index> is an optional boolean flag to indicate whether queuing the search indexing -should be skipped for this renewal. - =cut sub AddRenewal { @@ -3294,7 +3433,7 @@ sub AddRenewal { $renews = ( $item_object->renewals || 0 ) + 1; $item_object->renewals($renews); $item_object->onloan($datedue); - # Don't index as we are in a transaction, skip hardcoded here + # Don't index as we are in a transaction $item_object->store({ log_action => 0, skip_record_index => 1 }); # Charge a new rental fee, if applicable @@ -3383,12 +3522,9 @@ sub AddRenewal { } }); }); - - unless( $skip_record_index ){ - # We index now, after the transaction is committed - my $indexer = Koha::SearchEngine::Indexer->new({ index => $Koha::SearchEngine::BIBLIOS_INDEX }); - $indexer->index_records( $item_object->biblionumber, "specialUpdate", "biblioserver" ); - } + # We index now, after the transaction is committed + my $indexer = Koha::SearchEngine::Indexer->new({ index => $Koha::SearchEngine::BIBLIOS_INDEX }); + $indexer->index_records( $item_object->biblionumber, "specialUpdate", "biblioserver" ); return $datedue; } --- a/circ/circulation.pl +++ a/circ/circulation.pl @@ -38,7 +38,8 @@ use C4::Circulation qw( barcodedecode CanBookBeIssued AddIssue AddReturn ); use C4::Members; use C4::Biblio qw( TransformMarcToKoha ); use C4::Search qw( new_record_from_zebra ); -use C4::Reserves qw( ModReserveAffect ); +use C4::Reserves; +use C4::Log qw( logaction ); use Koha::Holds; use C4::Context; use CGI::Session; @@ -239,7 +240,7 @@ if ( @$barcodes == 0 && $charges eq 'yes' ) { # STEP 2 : FIND BORROWER # if there is a list of find borrowers.... # -my $message; + if ($findborrower) { Koha::Plugins->call( 'patron_barcode_transform', \$findborrower ); my $patron = Koha::Patrons->find( { cardnumber => $findborrower } ); @@ -313,6 +314,9 @@ if ($patron) { # STEP 3 : ISSUING # # +my $message; +my @message; + if (@$barcodes && $op eq 'cud-checkout') { my $checkout_infos; for my $barcode ( @$barcodes ) { @@ -463,6 +467,7 @@ if (@$barcodes && $op eq 'cud-checkout') { } ); $recall_id = ( $recall and $recall->id ) ? $recall->id : undef; + } # If booked (alerts or confirmation) update datedue to end of booking @@ -480,6 +485,32 @@ if (@$barcodes && $op eq 'cud-checkout') { } ); $template_params->{issue} = $issue; + + my $borrower = $patron; + my $borrowernumber = $patron->borrowernumber; + my $user = C4::Context->userenv->{number}; + my $branchcode = C4::Context->userenv->{branch}; + $message = "Restriction overridden temporarily"; + @message = ("Restriction overridden temporarily"); + + if ($issueconfirmed) { + my $infos = ({ + message => \@message, + borrowernumber => $borrowernumber, + barcode => $barcode, + manager_id => $user, + branchcode => $branchcode, + }); + + my $json_infos = JSON->new->utf8->pretty->encode($infos); + $json_infos =~ s/"/'/g; + + logaction( + "CIRCULATION", "ISSUE", + $borrower->{'borrowernumber'}, + $json_infos, + ) if C4::Context->preference("IssueLog"); + } $session->clear('auto_renew'); $inprocess = 1; } --- a/circ/renew.pl +++ a/circ/renew.pl @@ -24,6 +24,7 @@ use C4::Context; use C4::Auth qw( get_template_and_user ); use C4::Output qw( output_html_with_http_headers ); use C4::Circulation qw( barcodedecode CanBookBeRenewed GetLatestAutoRenewDate AddRenewal ); +use C4::Log qw( logaction ); use Koha::DateUtils qw( dt_from_string ); use Koha::Database; use Koha::BiblioFrameworks; @@ -63,9 +64,14 @@ if ($op eq 'cud-renew' && $barcode) { if ($checkout) { - $patron = $checkout->patron; + $borrower = $issue->patron(); + my $borrowernumber = $borrower->borrowernumber; + my $user = C4::Context->userenv->{number}; + my $branchcode = C4::Context->userenv->{branch}; + my $message; + my @message; - if ( ( $patron->is_debarred || q{} ) lt dt_from_string()->ymd() ) { + if ( ( $borrower->debarred() || q{} ) lt dt_from_string()->ymd() ) { my $can_renew; my $info; ( $can_renew, $error, $info ) = @@ -75,6 +81,26 @@ if ($op eq 'cud-renew' && $barcode) { if ($override_holds) { $can_renew = 1; $error = undef; + + $message = "Override Renew hold for another"; + @message = ("Override Renew hold for another"); + + my $infos = ({ + message => \@message, + borrowernumber => $borrowernumber, + barcode => $barcode, + manager_id => $user, + branchcode => $branchcode, + }); + + my $json_infos = JSON->new->utf8->pretty->encode($infos); + $json_infos =~ s/"/'/g; + + logaction( + "CIRCULATION", "RENEWAL", + $borrower->{'borrowernumber'}, + $json_infos, + ) if C4::Context->preference("RenewalLog"); } else { $can_renew = 0; @@ -107,6 +133,26 @@ if ($op eq 'cud-renew' && $barcode) { } ); $template->param( date_due => $date_due ); + + $message = "Override limit Renew"; + @message = ("Override limit Renew"); + + my $infos = ({ + message => \@message, + borrowernumber => $borrowernumber, + barcode => $barcode, + manager_id => $user, + branchcode => $branchcode, + }); + + my $json_infos = JSON->new->utf8->pretty->encode($infos); + $json_infos =~ s/"/'/g; + + logaction( + "CIRCULATION", "RENEWAL", + $borrower->{'borrowernumber'}, + $json_infos, + ) if C4::Context->preference("RenewalLog"); } } else { --- a/circ/returns.pl +++ a/circ/returns.pl @@ -43,6 +43,7 @@ use C4::Members; use C4::Output qw( output_html_with_http_headers ); use C4::Reserves qw( ModReserve ModReserveAffect GetOtherReserves ); use C4::RotatingCollections; +use C4::Log qw( logaction ); use Koha::AuthorisedValues; use Koha::BiblioFrameworks; use Koha::Calendar; @@ -179,6 +180,29 @@ if ( $query->param('reserve_id') && $op eq 'cud-affect_reserve') { diffbranch => 1, ); } +} else { + my $message = "return an element that is reserved"; + my @message = ("return an element that is reserved"); + my $user = C4::Context->userenv->{number}; + my $branchcode = C4::Context->userenv->{branch}; + my $barcode = $query->param('barcode'); + + my $infos = ({ + message => \@message, + #'card number' => $cardnumber, + barcode => $barcode, + manager_id => $user, + branchcode => $branchcode, + }); + + my $json_infos = JSON->new->utf8->pretty->encode($infos); + $json_infos =~ s/"/'/g; + + logaction( + "CIRCULATION", "RETURN", + $session, + $json_infos, + ) if C4::Context->preference("ReturnLog"); } if ( $query->param('recall_id') && $op eq 'cud-affect_recall' ) { @@ -432,7 +456,7 @@ if ($barcode && $op eq 'cud-checkin') { } # Mark missing bundle items as lost and report unexpected items - if ( $item && $item->is_bundle && $query->param('confirm_items_bundle_return') && !$query->param('do_not_verify_items_bundle_contents') ) { + if ( $item && $item->is_bundle && $query->param('confirm_items_bundle_return') ) { my $BundleLostValue = C4::Context->preference('BundleLostValue'); my $barcodes = $query->param('verify-items-bundle-contents-barcodes'); my @barcodes = map { s/^\s+|\s+$//gr } ( split /\n/, $barcodes ); --- a/reserve/request.pl +++ a/reserve/request.pl @@ -27,6 +27,7 @@ script to place reserves/requests =cut use Modern::Perl; +use Data::Dumper; use CGI qw ( -utf8 ); use List::MoreUtils qw( uniq ); @@ -40,6 +41,7 @@ use C4::Serials qw( CountSubscriptionFromBiblionumber ); use C4::Circulation qw( _GetCircControlBranch GetBranchItemRule ); use Koha::DateUtils qw( dt_from_string ); use C4::Search qw( enabled_staff_search_views ); +use C4::Log qw( logaction ); use Koha::Biblios; use Koha::Checkouts; @@ -509,7 +511,7 @@ if ( ( $findborrower && $borrowernumber_hold || $findclub && $club_hold ) && !$exceeded_maxreserves && $can_item_be_reserved eq 'OK' && IsAvailableForItemLevelRequest($item_object, $patron, undef) - ) || C4::Context->preference('AllowHoldPolicyOverride') + ) #|| C4::Context->preference('AllowHoldPolicyOverride') # If AllowHoldPolicyOverride is set, it overrides EVERY restriction # not just branch item rules ) @@ -524,17 +526,70 @@ if ( ( $findborrower && $borrowernumber_hold || $findclub && $club_hold ) my $default_pickup_location = $pickup_locations->search({ branchcode => $default_pickup_branch })->next; $item->{default_pickup_location} = $default_pickup_location; } - elsif ( C4::Context->preference('AllowHoldPolicyOverride') ){ - $num_items_available++; - $item->{override} = 1; - my $default_pickup_location = $pickup_locations->search({ branchcode => $default_pickup_branch })->next; - $item->{default_pickup_location} = $default_pickup_location; - } + # elsif ( C4::Context->preference('AllowHoldPolicyOverride') ){ + # $num_items_available++; + # $item->{override} = 1; + # my $default_pickup_location = $pickup_locations->search({ branchcode => $default_pickup_branch })->next; + # $item->{default_pickup_location} = $default_pickup_location; + # + # } else { $item->{available} = 0; $item->{not_holdable} = "no_valid_pickup_location"; } + push( @available_itemtypes, $item->{itype} ); + } + elsif ( C4::Context->preference('AllowHoldPolicyOverride') ) { + # If AllowHoldPolicyOverride is set, it should override EVERY restriction, not just branch item rules + # with the exception of itemAlreadyOnHold because, you know, the item is already on hold + if ( $can_item_be_reserved ne 'itemAlreadyOnHold' ) { + # Send the pickup locations count to the UI, the pickup locations will be pulled using the API + my @pickup_locations = $item_object->pickup_locations({ patron => $patron })->as_list; + $item->{pickup_locations_count} = scalar @pickup_locations; + + if ( @pickup_locations ) { + $num_items_available++; + $item->{override} = 1; + + my $default_pickup_location; + + ($default_pickup_location) = grep { $_->branchcode eq $default_pickup_branch } @pickup_locations; + + $item->{default_pickup_location} = $default_pickup_location; + } + else { + $item->{available} = 0; + $item->{not_holdable} = "no_valid_pickup_location"; + } + } else { $num_alreadyheld++ } + + my $borrower = $patron; + my $borrowernumber = $patron->borrowernumber; + my $user = C4::Context->userenv->{number}; + my $branchcode = C4::Context->userenv->{branch}; + my $message = "Override limitation of hold"; + my @message = ("Override limitation of hold"); + my $barcode = $input->param('barcode'); + + + my $infos = ({ + message => \@message, + borrowernumber => $borrowernumber, + barcode => $barcode, + manager_id => $user, + branchcode => $branchcode, + }); + + my $json_infos = JSON->new->utf8->pretty->encode($infos); + $json_infos =~ s/"/'/g; + + logaction( + "HOLD", "ISSUE", + $borrower->{'borrowernumber'}, + $json_infos, + ) if C4::Context->preference("IssueLog"); + push( @available_itemtypes, $item->{itype} ); } else { # If none of the conditions hold true, then neither override nor available is set and the item cannot be checked --