@@ -, +, @@ --- C4/Circulation.pm | 932 +++++++++----------- C4/Overdues.pm | 81 +-- C4/Reserves.pm | 631 ++++++++++---- admin/branch_transfer_limits.pl | 22 +- admin/smart-rules.pl | 565 +++---------- circ/branchtransfers.pl | 4 +- circ/circulation.pl | 282 +++++-- circ/returns.pl | 137 +++- .../en/modules/admin/branch_transfer_limits.tmpl | 15 +- .../prog/en/modules/admin/clone-rules.tmpl | 3 +- .../prog/en/modules/admin/smart-rules.tmpl | 639 ++++++++------ .../prog/en/modules/circ/circulation.tmpl | 439 ++++++---- .../prog/en/modules/circ/returns.tmpl | 151 ++-- .../prog/en/modules/reserve/request.tmpl | 30 +- .../opac-tmpl/prog/en/modules/opac-reserve.tmpl | 93 ++- members/member.pl | 22 +- opac/opac-modrequest.pl | 4 +- opac/opac-reserve.pl | 63 +- reserve/modrequest.pl | 19 +- reserve/placerequest.pl | 92 +- reserve/renewscript.pl | 57 ++- reserve/request.pl | 48 +- t/db_dependent/lib/KohaTest/Circulation.pm | 2 - .../lib/KohaTest/Members/GetMemberDetails.pm | 6 +- t/db_dependent/lib/KohaTest/Overdues.pm | 1 - 25 files changed, 2379 insertions(+), 1959 deletions(-) --- a/C4/Circulation.pm +++ a/C4/Circulation.pm @@ -27,13 +27,16 @@ use C4::Koha; use C4::Biblio; use C4::Items; use C4::Members; -use C4::Dates; +use C4::IssuingRules; +use C4::Dates qw/format_date/; use C4::Calendar; use C4::Accounts; +use C4::Overdues ; use C4::ItemCirculationAlertPreference; use C4::Dates qw(format_date); use C4::Message; use C4::Debug; +use YAML; use Date::Calc qw( Today Today_and_Now @@ -42,6 +45,9 @@ use Date::Calc qw( Date_to_Days Day_of_Week Add_Delta_Days + Delta_Days + check_date + Add_Delta_Days ); use POSIX qw(strftime); use C4::Branch; # GetBranches @@ -73,9 +79,6 @@ BEGIN { &GetItemIssues &GetBorrowerIssues &GetIssuingCharges - &GetIssuingRule - &GetBranchBorrowerCircRule - &GetBranchItemRule &GetBiblioIssues &GetOpenIssue &AnonymiseIssueHistory @@ -98,6 +101,15 @@ BEGIN { &CreateBranchTransferLimit &DeleteBranchTransferLimits ); + + # subs to deal with offline circulation + push @EXPORT, qw( + &GetOfflineOperations + &GetOfflineOperation + &AddOfflineOperation + &DeleteOfflineOperation + &ProcessOfflineOperation + ); } =head1 NAME @@ -346,116 +358,97 @@ sub TooMany { my $item = shift; my $cat_borrower = $borrower->{'categorycode'}; my $dbh = C4::Context->dbh; - my $branch; + my $exactbranch; # Get which branchcode we need - $branch = _GetCircControlBranch($item,$borrower); - my $type = (C4::Context->preference('item-level_itypes')) + $exactbranch = _GetCircControlBranch($item,$borrower); + my $itype = (C4::Context->preference('item-level_itypes')) ? $item->{'itype'} # item-level : $item->{'itemtype'}; # biblio-level # given branch, patron category, and item type, determine # applicable issuing rule - my $issuing_rule = GetIssuingRule($cat_borrower, $type, $branch); - - # 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($issuing_rule) and defined($issuing_rule->{'maxissueqty'})) { - my @bind_params; - my $count_query = "SELECT COUNT(*) FROM issues - JOIN items USING (itemnumber) "; - - my $rule_itemtype = $issuing_rule->{itemtype}; - if ($rule_itemtype eq "*") { - # 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 issuingrules - WHERE branchcode = ? - AND (categorycode = ? OR categorycode = ?) - AND itemtype <> '*' - ) "; - } else { - $count_query .= " JOIN biblioitems USING (biblionumber) - WHERE biblioitems.itemtype NOT IN ( - SELECT itemtype FROM issuingrules - WHERE branchcode = ? - AND (categorycode = ? OR categorycode = ?) - AND itemtype <> '*' - ) "; - } - push @bind_params, $issuing_rule->{branchcode}; - push @bind_params, $issuing_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; - } - - $count_query .= " AND borrowernumber = ? "; - push @bind_params, $borrower->{'borrowernumber'}; - my $rule_branch = $issuing_rule->{branchcode}; - if ($rule_branch ne "*") { - if (C4::Context->preference('CircControl') eq 'PickupLibrary') { - $count_query .= " AND issues.branchcode = ? "; - push @bind_params, $branch; - } elsif (C4::Context->preference('CircControl') eq 'PatronLibrary') { - ; # if branch is the patron's home branch, then count all loans by patron - } else { - $count_query .= " AND items.homebranch = ? "; - push @bind_params, $branch; - } - } - - my $count_sth = $dbh->prepare($count_query); - $count_sth->execute(@bind_params); - my ($current_loan_count) = $count_sth->fetchrow_array; + my $branchfield = C4::Context->Preference('HomeOrHoldingBranch') || "homebranch"; + #By default, Patron is supposed not to be able to borrow + my $toomany = 1; + + foreach my $branch ( $exactbranch, '*' ) { + foreach my $type ( $itype, '*' ) { + my $issuing_rule = GetIssuingRule( $cat_borrower, $type, $branch ); + + # 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($issuing_rule) and defined( $issuing_rule->{'maxissueqty'} ) ) { + my @bind_params; + my $count_query = "SELECT COUNT(*) FROM issues + JOIN items USING (itemnumber) "; + + my $rule_itemtype = $issuing_rule->{itemtype}; + if ($rule_itemtype eq "*") { + # matching rule has the default item type, so count all the items issued for that branch no + # those existing loans that don't fall under a more + # specific rule Not QUITE + if (C4::Context->preference('item-level_itypes')) { + $count_query .= " WHERE items.itype NOT IN ( + SELECT itemtype FROM issuingrules + WHERE branchcode = ? + AND (categorycode = ? OR categorycode = ?) + AND itemtype <> '*' + )"; + } else { + $count_query .= " JOIN biblioitems USING (biblionumber) + WHERE biblioitems.itemtype NOT IN ( + SELECT itemtype FROM issuingrules + WHERE branchcode = ? + AND (categorycode = ? OR categorycode = ?) + AND itemtype <> '*' + )"; + } + push @bind_params, $issuing_rule->{branchcode}; + push @bind_params, $issuing_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; + } - my $max_loans_allowed = $issuing_rule->{'maxissueqty'}; - if ($current_loan_count >= $max_loans_allowed) { - return ($current_loan_count, $max_loans_allowed); - } - } + $count_query .= " AND borrowernumber = ? "; + push @bind_params, $borrower->{'borrowernumber'}; + my $rule_branch = $issuing_rule->{branchcode}; + if ( $rule_branch ne "*" ) { + if ( C4::Context->preference('CircControl') eq 'PickupLibrary' ) { + $count_query .= " AND issues.branchcode = ? "; + push @bind_params, $branch; + } elsif ( C4::Context->preference('CircControl') eq 'PatronLibrary' ) { + ; # if branch is the patron's home branch, then count all loans by patron + } else { + $count_query .= " AND items.$branchfield = ? "; + push @bind_params, $branch; + } + } - # 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->{maxissueqty})) { - my @bind_params = (); - my $branch_count_query = "SELECT COUNT(*) 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; - } 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; - } - my $branch_count_sth = $dbh->prepare($branch_count_query); - $branch_count_sth->execute(@bind_params); - my ($current_loan_count) = $branch_count_sth->fetchrow_array; + my $count_sth = $dbh->prepare($count_query); + $count_sth->execute(@bind_params); + my ($current_loan_count) = $count_sth->fetchrow_array; - my $max_loans_allowed = $branch_borrower_circ_rule->{maxissueqty}; - if ($current_loan_count >= $max_loans_allowed) { - return ($current_loan_count, $max_loans_allowed); + my $max_loans_allowed = $issuing_rule->{'maxissueqty'}; + if ( $current_loan_count >= $max_loans_allowed ) { + return 1,$current_loan_count,$max_loans_allowed; + } + else { + $toomany=0; + } + } } } - - # OK, the patron can issue !!! - return; + return $toomany; } =head2 itemissues @@ -684,7 +677,10 @@ sub CanBookBeIssued { my $branch = _GetCircControlBranch($item,$borrower); my $itype = ( C4::Context->preference('item-level_itypes') ) ? $item->{'itype'} : $biblioitem->{'itemtype'}; my $loanlength = GetLoanLength( $borrower->{'categorycode'}, $itype, $branch ); - $duedate = CalcDateDue( C4::Dates->new( $issuedate, 'iso' ), $loanlength, $branch, $borrower ); + unless ($loanlength or C4::Context->preference("AllowNotForLoanOverride")) { + $issuingimpossible{LOAN_LENGTH_UNDEFINED} = "$borrower->{'categorycode'}, $itype, $branch"; + } + $duedate = CalcDateDue( C4::Dates->new( $issuedate, 'iso' ), $loanlength, $branch, $borrower ) ; # Offline circ calls AddIssue directly, doesn't run through here # So issuingimpossible should be ok. @@ -712,15 +708,22 @@ sub CanBookBeIssued { $issuingimpossible{CARD_LOST} = 1; } if ( $borrower->{flags}->{'DBARRED'} ) { - $issuingimpossible{DEBARRED} = 1; + if (my $dateenddebarred=$borrower->{flags}->{'DBARRED'}->{'dateend'}){ + $issuingimpossible{DEBARRED} = format_date($dateenddebarred); + } + else { + $issuingimpossible{DEBARRED} = 1; + } } if ( $borrower->{'dateexpiry'} eq '0000-00-00') { $issuingimpossible{EXPIRED} = 1; } else { - my @expirydate= split /-/,$borrower->{'dateexpiry'}; - if($expirydate[0]==0 || $expirydate[1]==0|| $expirydate[2]==0 || - Date_to_Days(Today) > Date_to_Days( @expirydate )) { - $issuingimpossible{EXPIRED} = 1; + my @expirydate = split (/-/, $borrower->{'dateexpiry'}); + if ( $expirydate[0] == 0 + || $expirydate[1] == 0 + || $expirydate[2] == 0 + || Date_to_Days(Today) > Date_to_Days(@expirydate) ) { + $issuingimpossible{EXPIRED} = 1; } } # @@ -769,18 +772,15 @@ sub CanBookBeIssued { # # JB34 CHECKS IF BORROWERS DONT HAVE ISSUE TOO MANY BOOKS # - my ($current_loan_count, $max_loans_allowed) = TooMany( $borrower, $item->{biblionumber}, $item ); - # if TooMany max_loans_allowed returns 0 the user doesn't have permission to check out this book - if ($max_loans_allowed eq 0) { + my @toomany = TooMany( $borrower, $item->{biblionumber}, $item ); + + if ( $toomany[0] == 1 && scalar(@toomany)<3) { $needsconfirmation{PATRON_CANT} = 1; - } else { - if($max_loans_allowed){ - $needsconfirmation{TOO_MANY} = 1; - $needsconfirmation{current_loan_count} = $current_loan_count; - $needsconfirmation{max_loans_allowed} = $max_loans_allowed; - } + } elsif (scalar(@toomany)==3) { + $needsconfirmation{TOO_MANY} = "$toomany[1] / $toomany[2]"; } + # # ITEM CHECKING # @@ -817,8 +817,18 @@ sub CanBookBeIssued { } } } - if ( $item->{'wthdrawn'} && $item->{'wthdrawn'} == 1 ) - { + if ( $item->{'damaged'} && $item->{'damaged'} > 0 ){ + $needsconfirmation{DAMAGED} = $item->{'damaged'}; + my $fw = GetFrameworkCode($item->{biblionumber}); + my $category = GetAuthValCode('items.damaged',$fw); + my $authorizedvalues = GetAuthorisedValues($category, $item->{damaged}); + + foreach my $authvalue (@$authorizedvalues){ + $needsconfirmation{DAMAGED} = $authvalue->{lib} if $authvalue->{'authorised_value'} eq $item->{'damaged'}; + } + + } + if ( $item->{'wthdrawn'} && $item->{'wthdrawn'} == 1 ) { $issuingimpossible{WTHDRAWN} = 1; } if ( $item->{'restricted'} @@ -826,15 +836,23 @@ sub CanBookBeIssued { { $issuingimpossible{RESTRICTED} = 1; } + my $userenv = C4::Context->userenv; + my $branch=$userenv->{branch}; + my $hbr= $item->{ C4::Context->preference("HomeOrHoldingBranch") }; if ( C4::Context->preference("IndependantBranches") ) { - my $userenv = C4::Context->userenv; if ( ($userenv) && ( $userenv->{flags} % 2 != 1 ) ) { $issuingimpossible{ITEMNOTSAMEBRANCH} = 1 - if ( $item->{C4::Context->preference("HomeOrHoldingBranch")} ne $userenv->{branch} ); + if ( $hbr ne $branch ); $needsconfirmation{BORRNOTSAMEBRANCH} = GetBranchName( $borrower->{'branchcode'} ) if ( $borrower->{'branchcode'} ne $userenv->{branch} ); } } + my $branchtransferfield=C4::Context->preference("BranchTransferLimitsType") eq "ccode" ? "ccode" : "itype"; + if ( C4::Context->preference("UseBranchTransferLimits") + and !IsBranchTransferAllowed( $branch, $hbr, $item->{ $branchtransferfield } ) ) { + $needsconfirmation{BRANCH_TRANSFER_NOT_ALLOWED} = $hbr; + } + # # CHECK IF BOOK ALREADY ISSUED TO THIS BORROWER @@ -875,30 +893,21 @@ sub CanBookBeIssued { my ( $resborrower ) = C4::Members::GetMemberDetails( $resbor, 0 ); my $branches = GetBranches(); my $branchname = $branches->{ $res->{'branchcode'} }->{'branchname'}; - if ( $resbor ne $borrower->{'borrowernumber'} && $restype eq "Waiting" ) - { - # The item is on reserve and waiting, but has been - # reserved by some other patron. - $needsconfirmation{RESERVE_WAITING} = 1; - $needsconfirmation{'resfirstname'} = $resborrower->{'firstname'}; - $needsconfirmation{'ressurname'} = $resborrower->{'surname'}; - $needsconfirmation{'rescardnumber'} = $resborrower->{'cardnumber'}; - $needsconfirmation{'resborrowernumber'} = $resborrower->{'borrowernumber'}; - $needsconfirmation{'resbranchname'} = $branchname; - $needsconfirmation{'reswaitingdate'} = format_date($res->{'waitingdate'}); - } - elsif ( $restype eq "Reserved" ) { - # The item is on reserve for someone else. - $needsconfirmation{RESERVED} = 1; - $needsconfirmation{'resfirstname'} = $resborrower->{'firstname'}; - $needsconfirmation{'ressurname'} = $resborrower->{'surname'}; - $needsconfirmation{'rescardnumber'} = $resborrower->{'cardnumber'}; - $needsconfirmation{'resborrowernumber'} = $resborrower->{'borrowernumber'}; - $needsconfirmation{'resbranchname'} = $branchname; - $needsconfirmation{'resreservedate'} = format_date($res->{'reservedate'}); + if( $resbor ne $borrower->{'borrowernumber'}){ + if ( $restype eq "Waiting" ) { + # The item is on reserve and waiting, but has been + # reserved by some other patron. + $needsconfirmation{RESERVE_WAITING} = + "$resborrower->{'firstname'} $resborrower->{'surname'} ($resborrower->{'cardnumber'}, $branchname)"; + } + elsif ( $restype eq "Reserved" ) { + # The item is on reserve for someone else. + $needsconfirmation{RESERVED} = + "$res->{'reservedate'} : $resborrower->{'firstname'} $resborrower->{'surname'} ($resborrower->{'cardnumber'})"; + } } } - return ( \%issuingimpossible, \%needsconfirmation ); + return ( \%issuingimpossible, \%needsconfirmation ); } =head2 AddIssue @@ -979,7 +988,10 @@ sub AddIssue { # who wants to borrow it now. mark it returned before issuing to the new borrower AddReturn( $item->{'barcode'}, - C4::Context->userenv->{'branch'} + C4::Context->userenv->{'branch'}, + undef, + undef, + 1 ); } @@ -1012,7 +1024,9 @@ sub AddIssue { ModReserve(1, $res->{'biblionumber'}, $res->{'borrowernumber'}, - $res->{'branchcode'} + $res->{'branchcode'}, + undef, + $res->{'reservenumber'} ); } } @@ -1102,10 +1116,11 @@ sub AddIssue { branch => $branch, }); } + + logaction( "CIRCULATION", "ISSUE", $borrower->{'borrowernumber'}, $item->{'itemnumber'} ) + if C4::Context->preference("IssueLog"); } - logaction("CIRCULATION", "ISSUE", $borrower->{'borrowernumber'}, $biblio->{'biblionumber'}) - if C4::Context->preference("IssueLog"); } return ($datedue); # not necessarily the same as when it came in! } @@ -1120,254 +1135,8 @@ Get loan length for an itemtype, a borrower type and a branch sub GetLoanLength { my ( $borrowertype, $itemtype, $branchcode ) = @_; - my $dbh = C4::Context->dbh; - my $sth = - $dbh->prepare( -"select issuelength from issuingrules where categorycode=? and itemtype=? and branchcode=? and issuelength is not null" - ); -# warn "in get loan lenght $borrowertype $itemtype $branchcode "; -# try to find issuelength & return the 1st available. -# check with borrowertype, itemtype and branchcode, then without one of those parameters - $sth->execute( $borrowertype, $itemtype, $branchcode ); - my $loanlength = $sth->fetchrow_hashref; - return $loanlength->{issuelength} - if defined($loanlength) && $loanlength->{issuelength} ne 'NULL'; - - $sth->execute( $borrowertype, "*", $branchcode ); - $loanlength = $sth->fetchrow_hashref; - return $loanlength->{issuelength} - if defined($loanlength) && $loanlength->{issuelength} ne 'NULL'; - - $sth->execute( "*", $itemtype, $branchcode ); - $loanlength = $sth->fetchrow_hashref; - return $loanlength->{issuelength} - if defined($loanlength) && $loanlength->{issuelength} ne 'NULL'; - - $sth->execute( "*", "*", $branchcode ); - $loanlength = $sth->fetchrow_hashref; - return $loanlength->{issuelength} - if defined($loanlength) && $loanlength->{issuelength} ne 'NULL'; - - $sth->execute( $borrowertype, $itemtype, "*" ); - $loanlength = $sth->fetchrow_hashref; - return $loanlength->{issuelength} - if defined($loanlength) && $loanlength->{issuelength} ne 'NULL'; - - $sth->execute( $borrowertype, "*", "*" ); - $loanlength = $sth->fetchrow_hashref; - return $loanlength->{issuelength} - if defined($loanlength) && $loanlength->{issuelength} ne 'NULL'; - - $sth->execute( "*", $itemtype, "*" ); - $loanlength = $sth->fetchrow_hashref; - return $loanlength->{issuelength} - if defined($loanlength) && $loanlength->{issuelength} ne 'NULL'; - - $sth->execute( "*", "*", "*" ); - $loanlength = $sth->fetchrow_hashref; - return $loanlength->{issuelength} - if defined($loanlength) && $loanlength->{issuelength} ne 'NULL'; - - # if no rule is set => 21 days (hardcoded) - return 21; -} - -=head2 GetIssuingRule - - my $irule = &GetIssuingRule($borrowertype,$itemtype,branchcode) - -FIXME - This is a copy-paste of GetLoanLength -as a stop-gap. Do not wish to change API for GetLoanLength -this close to release, however, Overdues::GetIssuingRules is broken. - -Get the issuing rule for an itemtype, a borrower type and a branch -Returns a hashref from the issuingrules table. - -=cut - -sub GetIssuingRule { - my ( $borrowertype, $itemtype, $branchcode ) = @_; - my $dbh = C4::Context->dbh; - my $sth = $dbh->prepare( "select * from issuingrules where categorycode=? and itemtype=? and branchcode=? and issuelength is not null" ); - my $irule; - - $sth->execute( $borrowertype, $itemtype, $branchcode ); - $irule = $sth->fetchrow_hashref; - return $irule if defined($irule) ; - - $sth->execute( $borrowertype, "*", $branchcode ); - $irule = $sth->fetchrow_hashref; - return $irule if defined($irule) ; - - $sth->execute( "*", $itemtype, $branchcode ); - $irule = $sth->fetchrow_hashref; - return $irule if defined($irule) ; - - $sth->execute( "*", "*", $branchcode ); - $irule = $sth->fetchrow_hashref; - return $irule if defined($irule) ; - - $sth->execute( $borrowertype, $itemtype, "*" ); - $irule = $sth->fetchrow_hashref; - return $irule if defined($irule) ; - - $sth->execute( $borrowertype, "*", "*" ); - $irule = $sth->fetchrow_hashref; - return $irule if defined($irule) ; - - $sth->execute( "*", $itemtype, "*" ); - $irule = $sth->fetchrow_hashref; - return $irule if defined($irule) ; - - $sth->execute( "*", "*", "*" ); - $irule = $sth->fetchrow_hashref; - return $irule if defined($irule) ; - - # if no rule matches, - return undef; -} - -=head2 GetBranchBorrowerCircRule - - my $branch_cat_rule = GetBranchBorrowerCircRule($branchcode, $categorycode); - -Retrieves circulation rule attributes that apply to the given -branch and patron category, regardless of item type. -The return value is a hashref containing the following key: - -maxissueqty - maximum number of loans that a -patron of the given category can have at the given -branch. If the value is undef, no limit. - -This will first check for a specific branch and -category match from branch_borrower_circ_rules. - -If no rule is found, it will then check default_branch_circ_rules -(same branch, default category). If no rule is found, -it will then check default_borrower_circ_rules (default -branch, same category), then failing that, default_circ_rules -(default branch, default category). - -If no rule has been found in the database, it will default to -the buillt in rule: - -maxissueqty - undef - -C<$branchcode> and C<$categorycode> should contain the -literal branch code and patron category code, respectively - no -wildcards. - -=cut - -sub GetBranchBorrowerCircRule { - my $branchcode = shift; - my $categorycode = shift; - - my $branch_cat_query = "SELECT maxissueqty - FROM branch_borrower_circ_rules - WHERE branchcode = ? - AND categorycode = ?"; - my $dbh = C4::Context->dbh(); - my $sth = $dbh->prepare($branch_cat_query); - $sth->execute($branchcode, $categorycode); - my $result; - if ($result = $sth->fetchrow_hashref()) { - return $result; - } - - # try same branch, default borrower category - my $branch_query = "SELECT maxissueqty - FROM default_branch_circ_rules - WHERE branchcode = ?"; - $sth = $dbh->prepare($branch_query); - $sth->execute($branchcode); - if ($result = $sth->fetchrow_hashref()) { - return $result; - } - - # try default branch, same borrower category - my $category_query = "SELECT maxissueqty - FROM default_borrower_circ_rules - WHERE categorycode = ?"; - $sth = $dbh->prepare($category_query); - $sth->execute($categorycode); - if ($result = $sth->fetchrow_hashref()) { - return $result; - } - - # try default branch, default borrower category - my $default_query = "SELECT maxissueqty - FROM default_circ_rules"; - $sth = $dbh->prepare($default_query); - $sth->execute(); - if ($result = $sth->fetchrow_hashref()) { - return $result; - } - - # built-in default circulation rule - return { - maxissueqty => undef, - }; -} - -=head2 GetBranchItemRule - - my $branch_item_rule = GetBranchItemRule($branchcode, $itemtype); - -Retrieves circulation rule attributes that apply to the given -branch and item type, regardless of patron category. - -The return value is a hashref containing the following key: - -holdallowed => Hold policy for this branch and itemtype. Possible values: - 0: No holds allowed. - 1: Holds allowed only by patrons that have the same homebranch as the item. - 2: Holds allowed from any patron. - -This searches branchitemrules in the following order: - - * Same branchcode and itemtype - * Same branchcode, itemtype '*' - * branchcode '*', same itemtype - * branchcode and itemtype '*' - -Neither C<$branchcode> nor C<$categorycode> should be '*'. - -=cut - -sub GetBranchItemRule { - my ( $branchcode, $itemtype ) = @_; - my $dbh = C4::Context->dbh(); - my $result = {}; - - my @attempts = ( - ['SELECT holdallowed - FROM branch_item_rules - WHERE branchcode = ? - AND itemtype = ?', $branchcode, $itemtype], - ['SELECT holdallowed - FROM default_branch_circ_rules - WHERE branchcode = ?', $branchcode], - ['SELECT holdallowed - FROM default_branch_item_rules - WHERE itemtype = ?', $itemtype], - ['SELECT holdallowed - FROM default_circ_rules'], - ); - - foreach my $attempt (@attempts) { - my ($query, @bind_params) = @{$attempt}; - - # Since branch/category and branch/itemtype use the same per-branch - # defaults tables, we have to check that the key we want is set, not - # just that a row was returned - return $result if ( defined( $result->{'holdallowed'} = $dbh->selectrow_array( $query, {}, @bind_params ) ) ); - } - - # built-in default circulation rule - return { - holdallowed => 2, - }; + my $loanlength=GetIssuingRule($borrowertype,$itemtype,$branchcode); + return $loanlength->{issuelength}; } =head2 AddReturn @@ -1445,8 +1214,8 @@ patron who last borrowed the book. =cut sub AddReturn { - my ( $barcode, $branch, $exemptfine, $dropbox ) = @_; - if ($branch and not GetBranchDetail($branch)) { + my ( $barcode, $branch, $exemptfine, $dropbox, $force) = @_; + if ( $branch and not GetBranchDetail($branch) ) { warn "AddReturn error: branch '$branch' not found. Reverting to " . C4::Context->userenv->{'branch'}; undef $branch; } @@ -1490,9 +1259,17 @@ sub AddReturn { my $branches = GetBranches(); # a potentially expensive call for a non-feature. $branches->{$hbr}->{PE} and $messages->{'IsPermanent'} = $hbr; } - + my $branchtransferfield=C4::Context->preference("BranchTransferLimitsType") eq "ccode" ? "ccode" : "itype"; + $debug && warn "$branch, $hbr, ",C4::Context->preference("BranchTransferLimitsType")," ,",$item->{ $branchtransferfield } ; + $debug && warn Dump($item); + $debug && warn IsBranchTransferAllowed( $branch, $hbr, $item->{ C4::Context->preference("BranchTransferLimitsType") } ); # if indy branches and returning to different branch, refuse the return - if ($hbr ne $branch && C4::Context->preference("IndependantBranches")){ + if ( !$force && ($hbr ne $branch) + && (C4::Context->preference("IndependantBranches") + or ( C4::Context->preference("UseBranchTransferLimits") + and !IsBranchTransferAllowed( $branch, $hbr, $item->{$branchtransferfield } ) ) + ) + ){ $messages->{'Wrongbranch'} = { Wrongbranch => $branch, Rightbranch => $hbr, @@ -1503,7 +1280,6 @@ sub AddReturn { # FIXME - even in an indy branches situation, there should # still be an option for the library to accept the item # and transfer it to its owning library. - return ( $doreturn, $messages, $issue, $borrower ); } if ( $item->{'wthdrawn'} ) { # book has been cancelled @@ -1514,12 +1290,11 @@ sub AddReturn { # case of a return of document (deal with issues and holdingbranch) if ($doreturn) { $borrower or warn "AddReturn without current borrower"; - my $circControlBranch; + my $circControlBranch; if ($dropbox) { - # define circControlBranch only if dropbox mode is set - # don't allow dropbox mode to create an invalid entry in issues (issuedate > today) - # FIXME: check issuedate > returndate, factoring in holidays - $circControlBranch = _GetCircControlBranch($item,$borrower) unless ( $item->{'issuedate'} eq C4::Dates->today('iso') );; + $circControlBranch = _GetCircControlBranch( $item, $borrower ); + # don't allow dropbox mode to create an invalid entry in issues (issuedate > returndate) FIXME: actually checks eq, not gt + undef($dropbox) if ( $item->{'issuedate'} eq C4::Dates->today('iso') ); } if ($borrowernumber) { @@ -1527,15 +1302,15 @@ sub AddReturn { $messages->{'WasReturned'} = 1; # FIXME is the "= 1" right? This could be the borrower hash. } - ModItem({ onloan => undef }, $issue->{'biblionumber'}, $item->{'itemnumber'}); + ModItem( { renewals=>0, onloan => undef }, $issue->{'biblionumber'}, $item->{'itemnumber'} ); + # 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 + if ( $item->{'holdingbranch'} ne $branch ) { + UpdateHoldingbranch( $branch, $item->{'itemnumber'} ); + $item->{'holdingbranch'} = $branch; # update item data holdingbranch too + } } - # 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 - if ($item->{'holdingbranch'} ne $branch) { - UpdateHoldingbranch($branch, $item->{'itemnumber'}); - $item->{'holdingbranch'} = $branch; # update item data holdingbranch too - } ModDateLastSeen( $item->{'itemnumber'} ); # check if we have a transfer for this document @@ -1562,11 +1337,26 @@ sub AddReturn { _FixAccountForLostAndReturned($item->{'itemnumber'}, $borrowernumber, $barcode); # can tolerate undef $borrowernumber $messages->{'WasLost'} = 1; } + if ($item->{'notforloan'}){ + $messages->{'NotForLoan'} = $item->{'notforloan'}; + } + if ($item->{'damaged'}){ + $messages->{'Damaged'} = $item->{'damaged'}; + } - # fix up the overdues in accounts... - if ($borrowernumber) { + if ($borrowernumber && $doreturn) { + # fix up the overdues in accounts... my $fix = _FixOverduesOnReturn($borrowernumber, $item->{itemnumber}, $exemptfine, $dropbox); defined($fix) or warn "_FixOverduesOnReturn($borrowernumber, $item->{itemnumber}...) failed!"; # zero is OK, check defined + + # fix fine days + my $debardate = _FixFineDaysOnReturn($borrower, $item, $issue->{date_due}); + $messages->{'Debarred'} = $debardate if($debardate); + + # get fines for the borrower + my $fineamount = C4::Overdues::GetFine($borrowernumber); + $messages->{'HaveFines'} = $fineamount if($fineamount); + } # find reserves..... @@ -1602,20 +1392,20 @@ sub AddReturn { branch => $branch, }); } - - logaction("CIRCULATION", "RETURN", $borrowernumber, $item->{'biblionumber'}) - if C4::Context->preference("ReturnLog"); - + + logaction( "CIRCULATION", "RETURN", $borrowernumber, $item->{'itemnumber'} ) + if C4::Context->preference("ReturnLog"); + # FIXME: make this comment intelligible. #adding message if holdingbranch is non equal a userenv branch to return the document to homebranch #we check, if we don't have reserv or transfert for this document, if not, return it to homebranch . - if ($doreturn and ($branch ne $hbr) and not $messages->{'WrongTransfer'} and ($validTransfert ne 1) ){ - if ( C4::Context->preference("AutomaticItemReturn" ) or - (C4::Context->preference("UseBranchTransferLimits") and - ! IsBranchTransferAllowed($branch, $hbr, $item->{C4::Context->preference("BranchTransferLimitsType")} ) - )) { - $debug and warn sprintf "about to call ModItemTransfer(%s, %s, %s)", $item->{'itemnumber'},$branch, $hbr; + if ( $doreturn and ( $branch ne $hbr ) and not $messages->{'WrongTransfer'} and ( $validTransfert ne 1 ) ) { + if (C4::Context->preference("AutomaticItemReturn") + or ( C4::Context->preference("UseBranchTransferLimits") + and !IsBranchTransferAllowed( $branch, $hbr, $item->{ $branchtransferfield } ) ) + ) { + $debug and warn sprintf "about to call ModItemTransfer(%s, %s, %s)", $item->{'itemnumber'}, $branch, $hbr; $debug and warn "item: " . Dumper($item); ModItemTransfer($item->{'itemnumber'}, $branch, $hbr); $messages->{'WasTransfered'} = 1; @@ -1690,6 +1480,61 @@ sub MarkIssueReturned { $sth_del->execute($borrowernumber, $itemnumber); } +=head2 _FixFineDaysOnReturn + + &_FixFineDaysOnReturn($borrower, $item, $datedue); + +C<$borrower> borrower hashref + +C<$item> item hashref + +C<$datedue> date due + +Internal function, called only by AddReturn that calculate and update the user fine days, and debars him + +=cut + +sub _FixFineDaysOnReturn { + my ($borrower, $item, $datedue) = @_; + + if($datedue){ + $datedue = C4::Dates->new($datedue,"iso"); + }else{ + return; + } + + my $branchcode =_GetCircControlBranch($item, $borrower); + my $calendar = C4::Calendar->new( branchcode => $branchcode ); + my $today = C4::Dates->new(); + + my $deltadays = $calendar->daysBetween($datedue, C4::Dates->new()); + + my $circcontrol = C4::Context::preference('CircControl'); + my $issuingrule = GetIssuingRule($borrower->{categorycode}, $item->{itype}, $branchcode); + my $finedays = $issuingrule->{finedays}; + # exit if no finedays defined + return unless $finedays; + my $grace = $issuingrule->{firstremind}; + + if( $deltadays - $grace > 0){ + my @newdate = Add_Delta_Days(Today(), $deltadays * $finedays ); + my $isonewdate = join('-',@newdate); + my ($deby, $debm, $debd) = split(/-/,$borrower->{debarred}); + if(check_date($deby, $debm, $debd)){ + my @olddate = split(/-/, $borrower->{debarred}); + + if(Delta_Days(@olddate,@newdate) > 0){ + C4::Members::DebarMember($borrower->{borrowernumber}, $isonewdate); + return $isonewdate; + } + }else{ + C4::Members::DebarMember($borrower->{borrowernumber}, $isonewdate); + return $isonewdate; + } + } +} + + =head2 _FixOverduesOnReturn &_FixOverduesOnReturn($brn,$itm, $exemptfine, $dropboxmode); @@ -1864,7 +1709,7 @@ sub _GetCircControlBranch { my $circcontrol = C4::Context->preference('CircControl'); my $branch; - if ($circcontrol eq 'PickupLibrary') { + if ($circcontrol eq 'PickupLibrary' && C4::Context->userenv->{'branch'}) { $branch= C4::Context->userenv->{'branch'}; } elsif ($circcontrol eq 'PatronLibrary') { $branch=$borrower->{branchcode}; @@ -1901,7 +1746,7 @@ sub GetItemIssue { my ($itemnumber) = @_; return unless $itemnumber; my $sth = C4::Context->dbh->prepare( - "SELECT * + "SELECT *, issues.renewals as 'issues.renewals' FROM issues LEFT JOIN items ON issues.itemnumber=items.itemnumber WHERE issues.itemnumber=?"); @@ -1972,7 +1817,7 @@ sub GetItemIssues { } my $results = $sth->fetchall_arrayref({}); foreach (@$results) { - $_->{'overdue'} = ($_->{'date_due'} lt $today) ? 1 : 0; + $_->{'overdue'} = ( $_->{'date_due'} lt $today && !defined($_->{return_date}) ) ? 1 : 0; } return $results; } @@ -2078,69 +1923,42 @@ already renewed the loan. $error will contain the reason the renewal can not pro sub CanBookBeRenewed { # check renewal status - my ( $borrowernumber, $itemnumber, $override_limit ) = @_; + my ( $borrowernumber, $itemnumber ) = @_; my $dbh = C4::Context->dbh; my $renews = 1; - my $renewokay = 0; - my $error; + my $renewokay = 1; + my $error; # Look in the issues table for this item, lent to this borrower, # and not yet returned. # Look in the issues table for this item, lent to this borrower, # and not yet returned. - my %branch = ( - 'ItemHomeLibrary' => 'items.homebranch', - 'PickupLibrary' => 'items.holdingbranch', - 'PatronLibrary' => 'borrowers.branchcode' - ); - my $controlbranch = $branch{C4::Context->preference('CircControl')}; - my $itype = C4::Context->preference('item-level_itypes') ? 'items.itype' : 'biblioitems.itemtype'; + my $borrower = C4::Members::GetMemberDetails( $borrowernumber, 0 ) or return undef; + my $item = GetItem($itemnumber) or return undef; + my $itemissue = GetItemIssue($itemnumber) or return undef; + my $branchcode = _GetCircControlBranch($item, $borrower); + if ($itemissue->{'overdue'}){ + $renewokay=0; + $error->{message}='overdue'; + } - my $sthcount = $dbh->prepare(" - SELECT - borrowers.categorycode, biblioitems.itemtype, issues.renewals, renewalsallowed, $controlbranch - FROM issuingrules, - issues - LEFT JOIN items USING (itemnumber) - LEFT JOIN borrowers USING (borrowernumber) - LEFT JOIN biblioitems USING (biblioitemnumber) - - WHERE - (issuingrules.categorycode = borrowers.categorycode OR issuingrules.categorycode = '*') - AND - (issuingrules.itemtype = $itype OR issuingrules.itemtype = '*') - AND - (issuingrules.branchcode = $controlbranch OR issuingrules.branchcode = '*') - AND - borrowernumber = ? - AND - itemnumber = ? - ORDER BY - issuingrules.categorycode desc, - issuingrules.itemtype desc, - issuingrules.branchcode desc - LIMIT 1; - "); - - $sthcount->execute( $borrowernumber, $itemnumber ); - if ( my $data1 = $sthcount->fetchrow_hashref ) { - - if ( ( $data1->{renewalsallowed} && $data1->{renewalsallowed} > $data1->{renewals} ) || $override_limit ) { - $renewokay = 1; - } - else { - $error="too_many"; - } - - my ( $resfound, $resrec ) = C4::Reserves::CheckReserves($itemnumber); - if ($resfound) { - $renewokay = 0; - $error="on_reserve" - } + my $issuingrule = GetIssuingRule($borrower->{categorycode}, $item->{itype}, $branchcode); + + if ( $issuingrule->{renewalsallowed} <= $itemissue->{'issues.renewals'} ) { + $renewokay=0; + $error->{message} = "too_many"; + } + my ( $resfound, $resrec ) = C4::Reserves::CheckReserves($itemnumber); + if ($resfound) { + $renewokay = 0; + $error->{message} = "on_reserve"; } - return ($renewokay,$error); + $error->{renewals} = $itemissue->{'issues.renewals'}; + $error->{renewalsallowed}= $issuingrule->{renewalsallowed}; + + return ( $renewokay, $error ); } =head2 AddRenewal @@ -2195,22 +2013,28 @@ sub AddRenewal { # based on the value of the RenewalPeriodBase syspref. unless ($datedue) { - my $borrower = C4::Members::GetMemberDetails( $borrowernumber, 0 ) or return undef; - my $loanlength = GetLoanLength( - $borrower->{'categorycode'}, - (C4::Context->preference('item-level_itypes')) ? $biblio->{'itype'} : $biblio->{'itemtype'} , - $issuedata->{'branchcode'} ); # that's the circ control branch. + my $borrower = C4::Members::GetMemberDetails( $borrowernumber, 0 ) or return undef; + my $branchcode = _GetCircControlBranch($item, $borrower); + my $loanlength = GetIssuingRule( $borrower->{categorycode}, $item->{itype}, $branchcode ); + + $datedue = + ( C4::Context->preference('RenewalPeriodBase') eq 'date_due' ) + ? C4::Dates->new( $issuedata->{date_due}, 'iso' ) + : C4::Dates->new(); + + my $itype = ( C4::Context->preference('item-level_itypes') ) ? $biblio->{'itype'} : $biblio->{'itemtype'}; + my $controlbranch = _GetCircControlBranch( $item, $borrower ); + my $renewalperiod = $loanlength->{renewalperiod} || GetLoanLength( $borrower->{'categorycode'}, $itype, $controlbranch ); + + $datedue = CalcDateDue( $datedue, $renewalperiod, $issuedata->{'branchcode'}, $borrower ); - $datedue = (C4::Context->preference('RenewalPeriodBase') eq 'date_due') ? - C4::Dates->new($issuedata->{date_due}, 'iso') : - C4::Dates->new(); - $datedue = CalcDateDue($datedue,$loanlength,$issuedata->{'branchcode'},$borrower); } # Update the issues record to have the new due date, and a new count # of how many times it has been renewed. my $renews = $issuedata->{'renewals'} + 1; - $sth = $dbh->prepare("UPDATE issues SET date_due = ?, renewals = ?, lastreneweddate = ? + $sth = $dbh->prepare( + "UPDATE issues SET date_due = ?, renewals = ?, lastreneweddate = ? WHERE borrowernumber=? AND itemnumber=?" ); @@ -2218,8 +2042,8 @@ sub AddRenewal { $sth->finish; # Update the renewal count on the item, and tell zebra to reindex - $renews = $biblio->{'renewals'} + 1; - ModItem({ renewals => $renews, onloan => $datedue->output('iso') }, $biblio->{'biblionumber'}, $itemnumber); + $renews = $item->{'renewals'} + 1; + ModItem( { renewals => $renews, onloan => $datedue->output('iso') }, undef, $itemnumber ); # Charge a new rental fee, if applicable? my ( $charge, $type ) = GetIssuingCharges( $itemnumber, $borrowernumber ); @@ -2600,15 +2424,8 @@ sub CalcDateDue { my ($startdate,$loanlength,$branch,$borrower) = @_; my $datedue; - if(C4::Context->preference('useDaysMode') eq 'Days') { # ignoring calendar - my $timedue = time + ($loanlength) * 86400; - #FIXME - assumes now even though we take a startdate - my @datearr = localtime($timedue); - $datedue = C4::Dates->new( sprintf("%04d-%02d-%02d", 1900 + $datearr[5], $datearr[4] + 1, $datearr[3]), 'iso'); - } else { - my $calendar = C4::Calendar->new( branchcode => $branch ); - $datedue = $calendar->addDate($startdate, $loanlength); - } + my $calendar = C4::Calendar->new( branchcode => $branch ); + $datedue = $calendar->addDate($startdate, $loanlength); # if ReturnBeforeExpiry ON the datedue can't be after borrower expirydate if ( C4::Context->preference('ReturnBeforeExpiry') && $datedue->output('iso') gt $borrower->{dateexpiry} ) { @@ -2818,18 +2635,121 @@ sub CreateBranchTransferLimit { =head2 DeleteBranchTransferLimits - DeleteBranchTransferLimits(); + DeleteBranchTransferLimits($tobranch); =cut sub DeleteBranchTransferLimits { - my $dbh = C4::Context->dbh; - my $sth = $dbh->prepare("TRUNCATE TABLE branch_transfer_limits"); - $sth->execute(); + my $branch = shift; + my $dbh = C4::Context->dbh; + my $sth = $dbh->prepare("DELETE FROM branch_transfer_limits WHERE toBranch = ?"); + $sth->execute($branch); +} + +sub GetOfflineOperations { + my $dbh = C4::Context->dbh; + my $sth = $dbh->prepare("SELECT * FROM pending_offline_operations WHERE branchcode=? ORDER BY timestamp"); + $sth->execute(C4::Context->userenv->{'branch'}); + my $results = $sth->fetchall_arrayref({}); + $sth->finish; + return $results; +} + +sub GetOfflineOperation { + my $dbh = C4::Context->dbh; + my $sth = $dbh->prepare("SELECT * FROM pending_offline_operations WHERE operationid=?"); + $sth->execute( shift ); + my $result = $sth->fetchrow_hashref; + $sth->finish; + return $result; +} + +sub AddOfflineOperation { + my $dbh = C4::Context->dbh; + warn Data::Dumper::Dumper(@_); + my $sth = $dbh->prepare("INSERT INTO pending_offline_operations VALUES('',?,?,?,?,?,?)"); + $sth->execute( @_ ); + return "Added."; +} + +sub DeleteOfflineOperation { + my $dbh = C4::Context->dbh; + my $sth = $dbh->prepare("DELETE FROM pending_offline_operations WHERE operationid=?"); + $sth->execute( shift ); + return "Deleted."; +} + +sub ProcessOfflineOperation { + my $operation = shift; + + my $report; + if ( $operation->{action} eq 'return' ) { + $report = ProcessOfflineReturn( $operation ); + } elsif ( $operation->{action} eq 'issue' ) { + $report = ProcessOfflineIssue( $operation ); + } + + DeleteOfflineOperation( $operation->{operationid} ) if $operation->{operationid}; + + return $report; +} + +sub ProcessOfflineReturn { + my $operation = shift; + + my $itemnumber = C4::Items::GetItemnumberFromBarcode( $operation->{barcode} ); + + if ( $itemnumber ) { + my $issue = GetOpenIssue( $itemnumber ); + if ( $issue ) { + MarkIssueReturned( + $issue->{borrowernumber}, + $itemnumber, + undef, + $operation->{timestamp}, + ); + return "Success."; + } else { + return "Item not issued."; + } + } else { + return "Item not found."; + } +} + +sub ProcessOfflineIssue { + my $operation = shift; + + my $borrower = C4::Members::GetMemberDetails( undef, $operation->{cardnumber} ); # Get borrower from operation cardnumber + + if ( $borrower->{borrowernumber} ) { + my $itemnumber = C4::Items::GetItemnumberFromBarcode( $operation->{barcode} ); + my $issue = GetOpenIssue( $itemnumber ); + + if ( $issue and ( $issue->{borrowernumber} ne $borrower->{borrowernumber} ) ) { # Item already issued to another borrower, mark it returned + MarkIssueReturned( + $issue->{borrowernumber}, + $itemnumber, + undef, + $operation->{timestamp}, + ); + } + AddIssue( + $borrower, + $operation->{'barcode'}, + undef, + 1, + $operation->{timestamp}, + undef, + ); + return "Success."; + } else { + return "Borrower not found."; + } } - 1; +1; __END__ --- a/C4/Overdues.pm +++ a/C4/Overdues.pm @@ -23,6 +23,7 @@ use strict; use Date::Calc qw/Today Date_to_Days/; use Date::Manip qw/UnixDate/; use C4::Circulation; +use C4::IssuingRules; use C4::Context; use C4::Accounts; use C4::Log; # logaction @@ -71,9 +72,6 @@ BEGIN { push @EXPORT, qw( &GetIssuesIteminfo ); - # - # &GetIssuingRules - delete. - # use C4::Circulation::GetIssuingRule instead. # subs to move to Members.pm push @EXPORT, qw( @@ -124,7 +122,7 @@ sub Getoverdues { SELECT issues.*, items.itype as itemtype, items.homebranch, items.barcode FROM issues LEFT JOIN items USING (itemnumber) - WHERE date_due < CURDATE() + WHERE DATE(date_due) < CURDATE() "; } else { $statement = " @@ -132,7 +130,7 @@ LEFT JOIN items USING (itemnumber) FROM issues LEFT JOIN items USING (itemnumber) LEFT JOIN biblioitems USING (biblioitemnumber) - WHERE date_due < CURDATE() + WHERE DATE(date_due) < CURDATE() "; } @@ -170,7 +168,7 @@ sub checkoverdues { LEFT JOIN biblio ON items.biblionumber = biblio.biblionumber LEFT JOIN biblioitems ON items.biblioitemnumber = biblioitems.biblioitemnumber WHERE issues.borrowernumber = ? - AND issues.date_due < CURDATE()" + AND DATE(issues.date_due) < CURDATE()" ); # FIXME: SELECT * across 4 tables? do we really need the marc AND marcxml blobs?? $sth->execute($borrowernumber); @@ -245,7 +243,7 @@ sub CalcFine { my $daystocharge; # get issuingrules (fines part will be used) $debug and warn sprintf("CalcFine calling GetIssuingRule(%s, %s, %s)", $bortype, $item->{'itemtype'}, $branchcode); - my $data = C4::Circulation::GetIssuingRule($bortype, $item->{'itemtype'}, $branchcode); + my $data = GetIssuingRule($bortype, $item->{'itemtype'}, $branchcode); if($difference) { # if $difference is supplied, the difference has already been calculated, but we still need to adjust for the calendar. # use copy-pasted functions from calendar module. (deprecated -- these functions will be removed from C4::Overdues ). @@ -269,7 +267,7 @@ sub CalcFine { } else { # a zero (or null) chargeperiod means no charge. } - $amount = C4::Context->preference('maxFine') if(C4::Context->preference('maxFine') && ( $amount > C4::Context->preference('maxFine'))); + $amount = C4::Context->preference('MaxFine') if ( C4::Context->preference('MaxFine') && ( $amount > C4::Context->preference('MaxFine') ) ); $debug and warn sprintf("CalcFine returning (%s, %s, %s, %s)", $amount, $data->{'chargename'}, $days_minus_grace, $daystocharge); return ($amount, $data->{'chargename'}, $days_minus_grace, $daystocharge); # FIXME: chargename is NEVER populated anywhere. @@ -585,7 +583,7 @@ Returns the replacement cost of the item with the given item number. =cut -#' + sub ReplacementCost { my ($itemnum) = @_; my $dbh = C4::Context->dbh; @@ -604,60 +602,22 @@ sub ReplacementCost { return the total of fine -C<$itemnum> is item number - C<$borrowernumber> is the borrowernumber -=cut - +=cut sub GetFine { - my ( $itemnum, $borrowernumber ) = @_; + my ( $borrowernumber ) = @_; my $dbh = C4::Context->dbh(); my $query = "SELECT sum(amountoutstanding) FROM accountlines where accounttype like 'F%' - AND amountoutstanding > 0 AND itemnumber = ? AND borrowernumber=?"; + AND amountoutstanding > 0 AND borrowernumber=?"; my $sth = $dbh->prepare($query); - $sth->execute( $itemnum, $borrowernumber ); + $sth->execute( $borrowernumber ); my $data = $sth->fetchrow_hashref(); return ( $data->{'sum(amountoutstanding)'} ); } - -=head2 GetIssuingRules - -FIXME - This sub should be deprecated and removed. -It ignores branch and defaults. - - $data = &GetIssuingRules($itemtype,$categorycode); - -Looks up for all issuingrules an item info - -C<$itemnumber> is a reference-to-hash whose keys are all of the fields -from the borrowers and categories tables of the Koha database. Thus, - -C<$categorycode> contains information about borrowers category - -C<$data> contains all information about both the borrower and -category he or she belongs to. -=cut - -sub GetIssuingRules { - warn "GetIssuingRules is deprecated: use GetIssuingRule from C4::Circulation instead."; - my ($itemtype,$categorycode)=@_; - my $dbh = C4::Context->dbh(); - my $query=qq|SELECT * - FROM issuingrules - WHERE issuingrules.itemtype=? - AND issuingrules.categorycode=? - |; - my $sth = $dbh->prepare($query); - # print $query; - $sth->execute($itemtype,$categorycode); - return $sth->fetchrow_hashref; -} - - sub ReplacementCost2 { my ( $itemnum, $borrowernumber ) = @_; my $dbh = C4::Context->dbh(); @@ -956,7 +916,7 @@ returns a list of branch codes for branches with overdue rules defined. sub GetBranchcodesWithOverdueRules { my $dbh = C4::Context->dbh; - my $rqoverduebranches = $dbh->prepare("SELECT DISTINCT branchcode FROM overduerules WHERE delay1 IS NOT NULL AND branchcode <> ''"); + my $rqoverduebranches = $dbh->prepare("SELECT DISTINCT branchcode FROM overduerules WHERE delay1 IS NOT NULL "); $rqoverduebranches->execute; my @branches = map { shift @$_ } @{ $rqoverduebranches->fetchall_arrayref }; return @branches; @@ -1029,7 +989,7 @@ sub GetOverduerules { Check if the borrowers is already debarred -C<$debarredstatus> return 0 for not debarred and return 1 for debarred +C<$debarredstatus> return undef for not debarred and return end of debar date for debarred C<$borrowernumber> contains the borrower number @@ -1043,32 +1003,35 @@ sub CheckBorrowerDebarred { SELECT debarred FROM borrowers WHERE borrowernumber=? + AND debarred > NOW() |; my $sth = $dbh->prepare($query); $sth->execute($borrowernumber); - my ($debarredstatus) = $sth->fetchrow; - return ( $debarredstatus eq '1' ? 1 : 0 ); + my $debarredstatus= $sth->fetchrow; + return $debarredstatus; + } =head2 UpdateBorrowerDebarred - ($borrowerstatut) = &UpdateBorrowerDebarred($borrowernumber); + ($borrowerstatut) = &UpdateBorrowerDebarred($borrowernumber, $todate); update status of borrowers in borrowers table (field debarred) C<$borrowernumber> borrower number +C<$todate> end of bare =cut sub UpdateBorrowerDebarred{ - my($borrowernumber) = @_; + my($borrowernumber, $todate) = @_; my $dbh = C4::Context->dbh; my $query=qq|UPDATE borrowers - SET debarred='1' + SET debarred=? WHERE borrowernumber=? |; my $sth=$dbh->prepare($query); - $sth->execute($borrowernumber); + $sth->execute($todate, $borrowernumber); $sth->finish; return 1; } --- a/C4/Reserves.pm +++ a/C4/Reserves.pm @@ -22,6 +22,7 @@ package C4::Reserves; use strict; #use warnings; FIXME - Bug 2505 +use Date::Calc qw( Add_Delta_Days Time_to_Date Today); use C4::Context; use C4::Biblio; use C4::Members; @@ -32,10 +33,11 @@ use C4::Accounts; # for _koha_notify_reserve use C4::Members::Messaging; -use C4::Members qw(); +use C4::Members ; use C4::Letters; use C4::Branch qw( GetBranchDetail ); use C4::Dates qw( format_date_in_iso ); +use C4::IssuingRules; use List::MoreUtils qw( firstidx ); use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS); @@ -91,11 +93,14 @@ BEGIN { @EXPORT = qw( &AddReserve + &GetMaxPickupDelay + &GetMaxPickupDate &GetReservesFromItemnumber &GetReservesFromBiblionumber &GetReservesFromBorrowernumber &GetReservesForBranch &GetReservesToBranch + &GetReservesControlBranch &GetReserveCount &GetReserveFee &GetReserveInfo @@ -113,13 +118,16 @@ BEGIN { &CheckReserves &CanBookBeReserved &CanItemBeReserved - &CancelReserve - &CancelExpiredReserves + &CanHoldOnShelf + &CancelReserve + &CancelExpiredReserves - &IsAvailableForItemLevelRequest - - &AlterPriority - &ToggleLowestPriority + &IsAvailableForItemLevelRequest + + &AlterPriority + &ToggleLowestPriority + &CanHoldMultipleItems + &BorrowerHasReserve ); } @@ -142,11 +150,17 @@ sub AddReserve { my $const = lc substr( $constraint, 0, 1 ); $resdate = format_date_in_iso( $resdate ) if ( $resdate ); $resdate = C4::Dates->today( 'iso' ) unless ( $resdate ); + my $item = C4::Items::GetItem($checkitem); + my @maxPickupDate = GetMaxPickupDate($resdate, $borrowernumber, $item); + my $maxpickupdate = sprintf( "%d-%02d-%02d", @maxPickupDate ); if ($expdate) { $expdate = format_date_in_iso( $expdate ); + my @expdate = split("-", $expdate); + $expdate = $maxpickupdate if Delta_Days(@expdate[ 0 .. 2 ], @maxPickupDate[ 0 .. 2 ]) < 0; } else { - undef $expdate; # make reserves.expirationdate default to null rather than '0000-00-00' + $expdate = $maxpickupdate; } + undef $expdate if $resdate eq $maxpickupdate; # make reserves.expirationdate default to null rather than '0000-00-00' if ( C4::Context->preference( 'AllowHoldDateInFuture' ) ) { # Make room in reserves for this before those of a later reserve date $priority = _ShiftPriorityByDateAndPriority( $biblionumber, $resdate, $priority ); @@ -250,24 +264,27 @@ sub GetReservesFromBiblionumber { # Find the desired items in the reserves my $query = " - SELECT branchcode, - timestamp AS rtimestamp, - priority, - biblionumber, - borrowernumber, - reservedate, - constrainttype, - found, - itemnumber, - reservenotes, - expirationdate, - lowestPriority + SELECT reserves.reservenumber, + reserves.branchcode, + reserves.timestamp AS rtimestamp, + reserves.priority, + reserves.biblionumber, + reserves.borrowernumber, + reserves.reservedate, + reserves.constrainttype, + reserves.found, + reserves.itemnumber, + reserves.reservenotes, + reserves.expirationdate, + reserves.lowestPriority, + biblioitems.itemtype FROM reserves - WHERE biblionumber = ? "; + LEFT JOIN biblioitems ON biblioitems.biblionumber = reserves.biblionumber + WHERE reserves.biblionumber = ? "; unless ( $all_dates ) { $query .= "AND reservedate <= CURRENT_DATE()"; } - $query .= "ORDER BY priority"; + $query .= "ORDER BY reserves.priority"; my $sth = $dbh->prepare($query); $sth->execute($biblionumber); my @results; @@ -373,9 +390,40 @@ sub GetReservesFromBorrowernumber { return @$data; } #------------------------------------------------------------------------------------- +sub BorrowerHasReserve { + my ( $borrowernumber, $biblionumber, $itemnumber ) = @_; + my $dbh = C4::Context->dbh; + my $sth; + + if ( $biblionumber ) { + $sth = $dbh->prepare(" + SELECT COUNT(*) AS hasReserve + FROM reserves + WHERE borrowernumber = ? + AND biblionumber = ? + ORDER BY reservedate + "); + $sth->execute( $borrowernumber, $biblionumber ); + } elsif ( $itemnumber ) { + $sth = $dbh->prepare(" + SELECT COUNT(*) AS hasReserve + FROM reserves + WHERE borrowernumber = ? + AND itemnumber = ? + ORDER BY reservedate + "); + $sth->execute( $borrowernumber, $itemnumber ); + } else { + return -1; + } + + my $data = $sth->fetchrow_hashref(); + return $data->{'hasReserve'}; +} + =head2 CanBookBeReserved - $error = &CanBookBeReserved($borrowernumber, $biblionumber) +$error = &CanBookBeReserved($borrowernumber, $biblionumber) =cut @@ -399,7 +447,7 @@ This function return 1 if an item can be issued by this borrower. sub CanItemBeReserved{ my ($borrowernumber, $itemnumber) = @_; - + my $dbh = C4::Context->dbh; my $allowedreserves = 0; @@ -407,7 +455,7 @@ sub CanItemBeReserved{ my $itype = C4::Context->preference('item-level_itypes') ? "itype" : "itemtype"; # we retrieve borrowers and items informations # - my $item = GetItem($itemnumber); + my $item = C4::Items::GetItem($itemnumber); my $borrower = C4::Members::GetMember('borrowernumber'=>$borrowernumber); # we retrieve user rights on this itemtype and branchcode @@ -438,22 +486,31 @@ sub CanItemBeReserved{ my $branchfield = "reserves.branchcode"; if( $controlbranch eq "ItemHomeLibrary" ){ - $branchfield = "items.homebranch"; - $branchcode = $item->{homebranch}; + my $field=C4::Context->preference("HomeOrHoldingBranch")||"homebranch"; + $branchfield = "items.$field"; + $branchcode = $item->{$field}; }elsif( $controlbranch eq "PatronLibrary" ){ $branchfield = "borrowers.branchcode"; $branchcode = $borrower->{branchcode}; } - # we retrieve rights - $sth->execute($categorycode, $itemtype, $branchcode); - if(my $rights = $sth->fetchrow_hashref()){ - $itemtype = $rights->{itemtype}; - $allowedreserves = $rights->{reservesallowed}; + # we retrieve user rights on this itemtype and branchcode + my $issuingrule = GetIssuingRule($borrower->{categorycode}, $item->{$itype}, $branchcode); + return 0 if( defined $issuingrule->{reservesallowed} && not $issuingrule->{reservesallowed} ); + + # We retrieve the count of reserves allowed for this category code + $issuingrule = GetIssuingRule ($borrower->{categorycode}, "*", "*"); + + if($issuingrule){ + $itemtype = $issuingrule->{itemtype}; + $allowedreserves = $issuingrule->{reservesallowed}; }else{ $itemtype = '*'; } + return 0 if ($issuingrule->{reservesallowed}==0 || + ($issuingrule->{holdrestricted}== 1 && !($branchcode eq $borrower->{branchcode})) + ); # we retrieve count $querycount .= "AND $branchfield = ?"; @@ -479,6 +536,106 @@ sub CanItemBeReserved{ return 0; } } + +=item GetMaxPickupDelay + +$resallowed = &GetMaxPickupDelay($borrowernumber, $itemnumber) + +this function return the number of allowed reserves. + +=cut + +sub GetMaxPickupDelay { + my ($borrowernumber, $itemnumber) = @_; + + my $dbh = C4::Context->dbh; + my $allowedreserves = 0; + + my $itype = C4::Context->preference('item-level_itypes') ? "itype" : "itemtype"; + + # we retrieve borrowers and items informations # + my $item = C4::Items::GetItem($itemnumber); + my $borrower = C4::Members::GetMember('borrowernumber'=>$borrowernumber); + my $controlbranch = GetReservesControlBranch($borrower,$item); + + # we retrieve user rights on this itemtype and branchcode + my $sth = $dbh->prepare("SELECT holdspickupdelay + FROM issuingrules + WHERE categorycode=? + AND itemtype=? + AND branchcode=? + AND holdspickupdelay IS NOT NULL" + ); + + my $itemtype = $item->{$itype}; + my $borrowertype = $borrower->{categorycode}; + my $branchcode = $controlbranch; + + $sth->execute( $borrowertype, $itemtype, $branchcode ); + my $pickupdelay = $sth->fetchrow_hashref; + return $pickupdelay->{holdspickupdelay} + if defined($pickupdelay) && $pickupdelay->{holdspickupdelay} ne 'NULL'; + + $sth->execute( $borrowertype, "*", $branchcode ); + $pickupdelay = $sth->fetchrow_hashref; + return $pickupdelay->{holdspickupdelay} + if defined($pickupdelay) && $pickupdelay->{holdspickupdelay} ne 'NULL'; + + $sth->execute( "*", $itemtype, $branchcode ); + $pickupdelay = $sth->fetchrow_hashref; + return $pickupdelay->{holdspickupdelay} + if defined($pickupdelay) && $pickupdelay->{holdspickupdelay} ne 'NULL'; + + $sth->execute( "*", "*", $branchcode ); + $pickupdelay = $sth->fetchrow_hashref; + return $pickupdelay->{holdspickupdelay} + if defined($pickupdelay) && $pickupdelay->{holdspickupdelay} ne 'NULL'; + + $sth->execute( $borrowertype, $itemtype, "*" ); + $pickupdelay = $sth->fetchrow_hashref; + return $pickupdelay->{holdspickupdelay} + if defined($pickupdelay) && $pickupdelay->{holdspickupdelay} ne 'NULL'; + + $sth->execute( $borrowertype, "*", "*" ); + $pickupdelay = $sth->fetchrow_hashref; + return $pickupdelay->{holdspickupdelay} + if defined($pickupdelay) && $pickupdelay->{holdspickupdelay} ne 'NULL'; + + $sth->execute( "*", $itemtype, "*" ); + $pickupdelay = $sth->fetchrow_hashref; + return $pickupdelay->{holdspickupdelay} + if defined($pickupdelay) && $pickupdelay->{holdspickupdelay} ne 'NULL'; + + $sth->execute( "*", "*", "*" ); + $pickupdelay = $sth->fetchrow_hashref; + return $pickupdelay->{holdspickupdelay} + if defined($pickupdelay) && $pickupdelay->{holdspickupdelay} ne 'NULL'; + + # if nothing found, return no rights + return 0; +} + +=item GetMaxPickupDate + +$maxpickupdate = &GetMaxPickupDate($date); + +this function return the max pickup date. +(e.g. : the date after which the hold will be considered cancelled) + +=cut + +sub GetMaxPickupDate{ + my $inputdate=shift; + my $borrowernumber=shift; + my $item=shift; + return unless $inputdate; + my @date = split(/-/,$inputdate); + my $delay = GetMaxPickupDelay($borrowernumber, $item->{'itemnumber'}); + ( @date ) = + Add_Delta_Days( @date[0..2], $delay); + return @date; +} + #-------------------------------------------------------------------------------- =head2 GetReserveCount @@ -518,21 +675,22 @@ sub GetOtherReserves { my $nextreservinfo; my ( $restype, $checkreserves ) = CheckReserves($itemnumber); if ($checkreserves) { - my $iteminfo = GetItem($itemnumber); + my $iteminfo = C4::Items::GetItem($itemnumber); if ( $iteminfo->{'holdingbranch'} ne $checkreserves->{'branchcode'} ) { $messages->{'transfert'} = $checkreserves->{'branchcode'}; #minus priorities of others reservs ModReserveMinusPriority( $itemnumber, $checkreserves->{'borrowernumber'}, - $iteminfo->{'biblionumber'} + $iteminfo->{'biblionumber'}, + $checkreserves->{'reservenumber'} ); #launch the subroutine dotransfer C4::Items::ModItemTransfer( $itemnumber, $iteminfo->{'holdingbranch'}, - $checkreserves->{'branchcode'} + $checkreserves->{'branchcode'}, ), ; } @@ -543,7 +701,8 @@ sub GetOtherReserves { ModReserveMinusPriority( $itemnumber, $checkreserves->{'borrowernumber'}, - $iteminfo->{'biblionumber'} + $iteminfo->{'biblionumber'}, + $checkreserves->{'reservenumber'} ); ModReserveStatus($itemnumber,'W'); } @@ -657,6 +816,26 @@ sub GetReserveFee { return $fee; } +=head2 GetReservesControlBranch + +$branchcode = &GetReservesControlBranch($borrower, $item) + +Returns the branchcode to consider to check hold rules against + +=cut +sub GetReservesControlBranch{ + my ($borrower, $item) = @_; + my $controlbranch = C4::Context->preference('ReservesControlBranch'); + my $hbr = C4::Context->preference('HomeOrHoldingBranch')||"homebranch"; + my $branchcode="*"; + if($controlbranch eq "ItemHomeLibrary"){ + $branchcode = $item->{$hbr}; + } elsif($controlbranch eq "PatronLibrary"){ + $branchcode = $borrower->{'branchcode'}; + } + return $branchcode; +} + =head2 GetReservesToBranch @transreserv = GetReservesToBranch( $frombranch ); @@ -840,23 +1019,18 @@ sub CancelExpiredReserves { $sth->execute(); while ( my $res = $sth->fetchrow_hashref() ) { - CancelReserve( $res->{'biblionumber'}, '', $res->{'borrowernumber'} ); + CancelReserve( $res->{'reservenumber'}, $res->{'biblionumber'} ); } } =head2 CancelReserve - &CancelReserve($biblionumber, $itemnumber, $borrowernumber); + &CancelReserve( $reservenumber, $biblionumber ); Cancels a reserve. -Use either C<$biblionumber> or C<$itemnumber> to specify the item to -cancel, but not both: if both are given, C<&CancelReserve> does -nothing. - -C<$borrowernumber> is the borrower number of the patron on whose -behalf the book was reserved. +C<$reservenumber> is the unique key for the reserve to be canceled. If C<$biblionumber> was given, C<&CancelReserve> also adjusts the priorities of the other people who are waiting on the book. @@ -864,9 +1038,14 @@ priorities of the other people who are waiting on the book. =cut sub CancelReserve { - my ( $biblio, $item, $borr ) = @_; + my ( $reservenumber, $biblio ) = @_; my $dbh = C4::Context->dbh; - if ( $item and $borr ) { + + my $sth = $dbh->prepare('SELECT * FROM reserves WHERE reservenumber = ?'); + $sth->execute( $reservenumber ); + my $reserve = $sth->fetchrow_hashref(); + + if ( $reserve->{'found'} eq 'W' ) { # removing a waiting reserve record.... # update the database... my $query = " @@ -874,27 +1053,46 @@ sub CancelReserve { SET cancellationdate = now(), found = Null, priority = 0 - WHERE itemnumber = ? - AND borrowernumber = ? + WHERE reservenumber = ? "; my $sth = $dbh->prepare($query); - $sth->execute( $item, $borr ); - $sth->finish; + $sth->execute( $reservenumber ); + + # get reserve information to place into old_reserves $query = " INSERT INTO old_reserves SELECT * FROM reserves - WHERE itemnumber = ? - AND borrowernumber = ? + WHERE reservenumber = ? "; $sth = $dbh->prepare($query); - $sth->execute( $item, $borr ); + $sth->execute( $reservenumber ); + my $holditem = $sth->fetchrow_hashref; + my $insert_fields = ''; + my $value_fields = ''; + foreach my $column ('borrowernumber','reservedate','biblionumber','constrainttype','branchcode','notificationdate','reminderdate','cancellationdate','reservenotes','priority','found','itemnumber','waitingdate','expirationdate') { + if (defined($holditem->{$column})) { + if (length($insert_fields)) { + $insert_fields .= ",$column"; + $value_fields .= ",\'$holditem->{$column}\'"; + } + else { + $insert_fields .= "$column"; + $value_fields .= "\'$holditem->{$column}\'"; + } + } + } + $query = qq/ + INSERT INTO old_reserves ($insert_fields) + VALUES ($value_fields) + /; + $sth = $dbh->prepare($query); + $sth->execute(); $query = " DELETE FROM reserves - WHERE itemnumber = ? - AND borrowernumber = ? + WHERE reservenumber = ? "; $sth = $dbh->prepare($query); - $sth->execute( $item, $borr ); + $sth->execute( $reservenumber ); } else { # removing a reserve record.... @@ -902,52 +1100,72 @@ sub CancelReserve { my $priority; my $query = qq/ SELECT priority FROM reserves - WHERE biblionumber = ? - AND borrowernumber = ? + WHERE reservenumber = ? AND cancellationdate IS NULL AND itemnumber IS NULL /; my $sth = $dbh->prepare($query); - $sth->execute( $biblio, $borr ); + $sth->execute( $reservenumber ); ($priority) = $sth->fetchrow_array; $sth->finish; $query = qq/ UPDATE reserves SET cancellationdate = now(), found = Null, - priority = 0 - WHERE biblionumber = ? - AND borrowernumber = ? + priority = 0, + expirationdate = NULL + WHERE reservenumber = ? /; # update the database, removing the record... $sth = $dbh->prepare($query); - $sth->execute( $biblio, $borr ); - $sth->finish; + $sth->execute( $reservenumber ); $query = qq/ INSERT INTO old_reserves SELECT * FROM reserves - WHERE biblionumber = ? - AND borrowernumber = ? + WHERE reservenumber = ? + /; + $sth = $dbh->prepare($query); + $sth->execute( $reservenumber ); + my $holditem = $sth->fetchrow_hashref; + + my $insert_fields = ''; + my $value_fields = ''; + foreach my $column ('borrowernumber','reservedate','biblionumber','constrainttype','branchcode','notificationdate','reminderdate','cancellationdate','reservenotes','priority','found','itemnumber','waitingdate','expirationdate') { + if (defined($holditem->{$column})) { + if (length($insert_fields)) { + $insert_fields .= ",$column"; + $value_fields .= ",\'$holditem->{$column}\'"; + } + else { + $insert_fields .= "$column"; + $value_fields .= "\'$holditem->{$column}\'"; + } + } + } + $query = qq/ + INSERT INTO old_reserves ($insert_fields) + VALUES ($value_fields) /; $sth = $dbh->prepare($query); - $sth->execute( $biblio, $borr ); + $sth->execute(); $query = qq/ DELETE FROM reserves - WHERE biblionumber = ? - AND borrowernumber = ? + WHERE reservenumber = ? /; $sth = $dbh->prepare($query); - $sth->execute( $biblio, $borr ); + $sth->execute( $reservenumber ); # now fix the priority on the others.... - _FixPriority( $biblio, $borr ); + _FixPriority( '', '', $priority,'', $reservenumber ); } } -=head2 ModReserve +=item ModReserve + +=over 4 ModReserve($rank, $biblio, $borrower, $branch[, $itemnumber]) @@ -980,52 +1198,48 @@ itemnumber and supplying itemnumber. sub ModReserve { #subroutine to update a reserve - my ( $rank, $biblio, $borrower, $branch , $itemnumber) = @_; + my ( $rank, $biblio, $borrower, $branch , $itemnumber, $reservenumber) = @_; return if $rank eq "W"; return if $rank eq "n"; my $dbh = C4::Context->dbh; if ( $rank eq "del" ) { my $query = qq/ UPDATE reserves - SET cancellationdate=now() - WHERE biblionumber = ? - AND borrowernumber = ? + SET cancellationdate=now(), + expirationdate = NULL + WHERE reservenumber = ? /; my $sth = $dbh->prepare($query); - $sth->execute( $biblio, $borrower ); + $sth->execute( $reservenumber ); $sth->finish; $query = qq/ INSERT INTO old_reserves - SELECT * - FROM reserves - WHERE biblionumber = ? - AND borrowernumber = ? + SELECT * FROM reserves + WHERE reservenumber = ? /; $sth = $dbh->prepare($query); - $sth->execute( $biblio, $borrower ); + $sth->execute( $reservenumber ); $query = qq/ DELETE FROM reserves - WHERE biblionumber = ? - AND borrowernumber = ? + WHERE reservenumber = ? /; $sth = $dbh->prepare($query); - $sth->execute( $biblio, $borrower ); + $sth->execute( $reservenumber ); } elsif ($rank =~ /^\d+/ and $rank > 0) { my $query = qq/ UPDATE reserves SET priority = ? ,branchcode = ?, itemnumber = ?, found = NULL, waitingdate = NULL - WHERE biblionumber = ? - AND borrowernumber = ? + WHERE reservenumber = ? /; my $sth = $dbh->prepare($query); - $sth->execute( $rank, $branch,$itemnumber, $biblio, $borrower); + $sth->execute( $rank, $branch, $itemnumber, $reservenumber ); $sth->finish; - _FixPriority( $biblio, $borrower, $rank); + _FixPriority( $biblio, $borrower, $rank, '', $reservenumber); } } -=head2 ModReserveFill +=item ModReserveFill &ModReserveFill($reserve); @@ -1044,16 +1258,17 @@ sub ModReserveFill { my $biblionumber = $res->{'biblionumber'}; my $borrowernumber = $res->{'borrowernumber'}; my $resdate = $res->{'reservedate'}; + my $resnumber = $res->{'reservenumber'}; # get the priority on this record.... my $priority; my $query = "SELECT priority FROM reserves - WHERE biblionumber = ? - AND borrowernumber = ? + WHERE borrowernumber = ? + AND reservenumber = ? AND reservedate = ?"; my $sth = $dbh->prepare($query); - $sth->execute( $biblionumber, $borrowernumber, $resdate ); + $sth->execute( $resnumber, $borrowernumber, $resdate ); ($priority) = $sth->fetchrow_array; $sth->finish; @@ -1061,35 +1276,35 @@ sub ModReserveFill { $query = "UPDATE reserves SET found = 'F', priority = 0 - WHERE biblionumber = ? + WHERE reservenumber = ? AND reservedate = ? AND borrowernumber = ? "; $sth = $dbh->prepare($query); - $sth->execute( $biblionumber, $resdate, $borrowernumber ); + $sth->execute( $resnumber, $resdate, $borrowernumber ); $sth->finish; # move to old_reserves $query = "INSERT INTO old_reserves SELECT * FROM reserves - WHERE biblionumber = ? + WHERE reservenumber = ? AND reservedate = ? AND borrowernumber = ? "; $sth = $dbh->prepare($query); - $sth->execute( $biblionumber, $resdate, $borrowernumber ); + $sth->execute( $resnumber, $resdate, $borrowernumber ); $query = "DELETE FROM reserves - WHERE biblionumber = ? + WHERE reservenumber = ? AND reservedate = ? AND borrowernumber = ? "; $sth = $dbh->prepare($query); - $sth->execute( $biblionumber, $resdate, $borrowernumber ); + $sth->execute( $resnumber, $resdate, $borrowernumber ); # now fix the priority on the others (if the priority wasn't # already sorted!).... unless ( $priority == 0 ) { - _FixPriority( $biblionumber, $borrowernumber ); + _FixPriority( $priority, $biblionumber,undef,undef, $res->{reservenumber} ); } } @@ -1106,7 +1321,6 @@ $newstatus is the new status. =cut sub ModReserveStatus { - #first : check if we have a reservation for this item . my ($itemnumber, $newstatus) = @_; my $dbh = C4::Context->dbh; @@ -1140,9 +1354,8 @@ take care of the waiting status =cut sub ModReserveAffect { - my ( $itemnumber, $borrowernumber,$transferToDo ) = @_; + my ( $itemnumber, $borrowernumber,$transferToDo, $reservenumber ) = @_; my $dbh = C4::Context->dbh; - # we want to attach $itemnumber to $borrowernumber, find the biblionumber # attached to $itemnumber my $sth = $dbh->prepare("SELECT biblionumber FROM items WHERE itemnumber=?"); @@ -1160,27 +1373,52 @@ sub ModReserveAffect { $query = " UPDATE reserves SET priority = 0, - itemnumber = ?, - found = 'T' - WHERE borrowernumber = ? - AND biblionumber = ? + itemnumber = ? + WHERE reservenumber = ? "; } else { # affect the reserve to Waiting as well. - $query = " - UPDATE reserves - SET priority = 0, - found = 'W', - waitingdate=now(), - itemnumber = ? - WHERE borrowernumber = ? - AND biblionumber = ? - "; + my $holdperiod = C4::Context->preference('ReservesMaxPickUpDelay'); + if ((!defined($holdperiod)) || ($holdperiod eq '') || ($holdperiod == 0)) { + $query = " + UPDATE reserves + SET priority = 0, + found = 'W', + waitingdate=now(), + itemnumber = ? + WHERE reservenumber = ? + "; + } + else { + my ($holdexpyear,$holdexpmonth,$holdexpday) = Today(); + my $holdstartdate = C4::Dates->new(sprintf "%02d/%02d/%04d",$holdexpmonth,$holdexpday,$holdexpyear, 'us'); + + # Grab branch for calendar purposes + $sth = $dbh->prepare("SELECT branchcode FROM reserves WHERE borrowernumber=? AND biblionumber=?"); + $sth->execute($borrowernumber,$biblionumber); + my ($branch) = $sth->fetchrow; + + # Check to see if hold expiration date falls on a closed library day. + # Note the useDaysMode syspref will need to be set to Calendar for + # the code to advance to the next non-closed day. + my $calendar = C4::Calendar->new( branchcode => $branch); + my $holdexpdate = $calendar->addDate($holdstartdate, $holdperiod); + my $sqlexpdate = $holdexpdate->output('iso'); + $query = " + UPDATE reserves + SET priority = 0, + found = 'W', + waitingdate=now(), + itemnumber = ?, + expirationdate='$sqlexpdate' + WHERE reservenumber = ? + "; + } } $sth = $dbh->prepare($query); - $sth->execute( $itemnumber, $borrowernumber,$biblionumber); - _koha_notify_reserve( $itemnumber, $borrowernumber, $biblionumber ) if ( !$transferToDo && !$already_on_shelf ); + $sth->execute( $itemnumber, $reservenumber ); + _koha_notify_reserve( $itemnumber, $borrowernumber, $biblionumber, $reservenumber ) if ( !$transferToDo && !$already_on_shelf ); if ( C4::Context->preference("ReturnToShelvingCart") ) { CartToShelf( $itemnumber ); @@ -1220,23 +1458,21 @@ Reduce the values of queuded list =cut sub ModReserveMinusPriority { - my ( $itemnumber, $borrowernumber, $biblionumber ) = @_; - + my ( $itemnumber, $borrowernumber, $biblionumber, $reservenumber ) = @_; #first step update the value of the first person on reserv my $dbh = C4::Context->dbh; my $query = " UPDATE reserves SET priority = 0 , itemnumber = ? - WHERE borrowernumber=? - AND biblionumber=? + WHERE reservenumber=? "; my $sth_upd = $dbh->prepare($query); - $sth_upd->execute( $itemnumber, $borrowernumber, $biblionumber ); + $sth_upd->execute( $itemnumber, $reservenumber ); # second step update all others reservs - _FixPriority($biblionumber, $borrowernumber, '0'); + _FixPriority($biblionumber, $borrowernumber, '0', '', $reservenumber); } -=head2 GetReserveInfo +=item GetReserveInfo &GetReserveInfo($borrowernumber,$biblionumber); @@ -1321,7 +1557,7 @@ and canreservefromotherbranches. sub IsAvailableForItemLevelRequest { my $itemnumber = shift; - my $item = GetItem($itemnumber); + my $item = C4::Items::GetItem($itemnumber); # must check the notforloan setting of the itemtype # FIXME - a lot of places in the code do this @@ -1356,10 +1592,10 @@ sub IsAvailableForItemLevelRequest { $notforloan_per_itemtype; - if (C4::Context->preference('AllowOnShelfHolds')) { + if (CanHoldOnShelf($itemnumber)) { return $available_per_item; } else { - return ($available_per_item and ($item->{onloan} or GetReserveStatus($itemnumber) eq "W")); + return ($available_per_item and ($item->{onloan} or GetReserveStatus($itemnumber) eq "W")); } } @@ -1387,15 +1623,15 @@ sub AlterPriority { my $priority = $reserve->{'priority'}; $priority = $where eq 'up' ? $priority - 1 : $priority + 1; - _FixPriority( $biblionumber, $borrowernumber, $priority ) + _FixPriority( $biblionumber, $borrowernumber, $priority ,undef,$reserve->{reservenumber}) } elsif ( $where eq 'top' ) { - _FixPriority( $biblionumber, $borrowernumber, '1' ) + _FixPriority( $biblionumber, $borrowernumber, '1',undef,$reserve->{reservenumber} ) } elsif ( $where eq 'bottom' ) { - _FixPriority( $biblionumber, $borrowernumber, '999999' ) + _FixPriority( $biblionumber, $borrowernumber, '999999',undef,$reserve->{reservenumber} ) } } @@ -1418,18 +1654,49 @@ sub ToggleLowestPriority { WHERE biblionumber = ? AND borrowernumber = ?" ); - $sth->execute( - $biblionumber, - $borrowernumber, - ); + $sth->execute( $biblionumber, $borrowernumber, ); $sth->finish; _FixPriority( $biblionumber, $borrowernumber, '999999' ); } +=head2 CanHoldOnShelf + +my $canhold = &CanHoldOnShelf($itemnumber); + + Check if a book can be hold on shelf. + +=cut + +sub CanHoldOnShelf { + my ($itemnumber) = @_; + + my $item = C4::Items::GetItem($itemnumber); + my $itemtype = C4::Context->preference('item-level_itypes'); + $itemtype = $itemtype ? $item->{itype} : $item->{itemtype} ; + my $branch = $item->{C4::Context->preference('HomeOrHoldingBranch')}; + + my $issuingrule = GetIssuingRule('*', $itemtype, $branch); + return $issuingrule->{allowonshelfholds}; + +} + +sub CanHoldMultipleItems { + my ( $itemtype ) = @_; + + my @multi_itemtypes = split( / /, C4::Context->preference('AllowMultipleHoldsPerBib') ); + for my $mtype ( @multi_itemtypes ) { + if ( $itemtype eq $mtype ) { + return 1; + } + } + + return 0; +} + =head2 _FixPriority - &_FixPriority($biblio,$borrowernumber,$rank,$ignoreSetLowestRank); +&_FixPriority($biblio,$borrowernumber,$rank,$ignoreSetLowestRank,$reservenumber); Only used internally (so don't export it) Changed how this functions works # @@ -1441,7 +1708,7 @@ in new priority rank =cut sub _FixPriority { - my ( $biblio, $borrowernumber, $rank, $ignoreSetLowestRank ) = @_; + my ( $biblio, $borrowernumber, $rank, $ignoreSetLowestRank, $reservenumber ) = @_; my $dbh = C4::Context->dbh; if ( $rank eq "del" ) { CancelReserve( $biblio, undef, $borrowernumber ); @@ -1452,12 +1719,11 @@ sub _FixPriority { my $query = qq/ UPDATE reserves SET priority = 0 - WHERE biblionumber = ? - AND borrowernumber = ? - AND found IN ('W', 'T') + WHERE reservenumber = ? + AND found IN ('W',"T") /; my $sth = $dbh->prepare($query); - $sth->execute( $biblio, $borrowernumber ); + $sth->execute( $reservenumber ); } my @priority; my @reservedates; @@ -1468,7 +1734,7 @@ sub _FixPriority { # This is wrong a waiting reserve has W set # The assumption that having an itemnumber set means waiting is wrong and should be corrected any place it occurs my $query = qq/ - SELECT borrowernumber, reservedate, constrainttype + SELECT borrowernumber, reservedate, constrainttype, reservenumber FROM reserves WHERE biblionumber = ? AND ((found <> 'W' AND found <> 'T') or found is NULL) @@ -1503,16 +1769,15 @@ sub _FixPriority { $query = " UPDATE reserves SET priority = ? - WHERE biblionumber = ? - AND borrowernumber = ? + WHERE reservenumber = ? AND reservedate = ? AND found IS NULL "; $sth = $dbh->prepare($query); for ( my $j = 0 ; $j < @priority ; $j++ ) { $sth->execute( - $j + 1, $biblio, - $priority[$j]->{'borrowernumber'}, + $j + 1, + $priority[$j]->{'reservenumber'}, $priority[$j]->{'reservedate'} ); $sth->finish; @@ -1552,15 +1817,16 @@ sub _Findgroupreserve { # TODO: consolidate at least the SELECT portion of the first 2 queries to a common $select var. # check for exact targetted match my $item_level_target_query = qq/ - SELECT reserves.biblionumber AS biblionumber, - reserves.borrowernumber AS borrowernumber, - reserves.reservedate AS reservedate, - reserves.branchcode AS branchcode, - reserves.cancellationdate AS cancellationdate, - reserves.found AS found, - reserves.reservenotes AS reservenotes, - reserves.priority AS priority, - reserves.timestamp AS timestamp, + SELECT reserves.reservenumber AS reservenumber, + reserves.biblionumber AS biblionumber, + reserves.borrowernumber AS borrowernumber, + reserves.reservedate AS reservedate, + reserves.branchcode AS branchcode, + reserves.cancellationdate AS cancellationdate, + reserves.found AS found, + reserves.reservenotes AS reservenotes, + reserves.priority AS priority, + reserves.timestamp AS timestamp, biblioitems.biblioitemnumber AS biblioitemnumber, reserves.itemnumber AS itemnumber FROM reserves @@ -1582,15 +1848,16 @@ sub _Findgroupreserve { # check for title-level targetted match my $title_level_target_query = qq/ - SELECT reserves.biblionumber AS biblionumber, - reserves.borrowernumber AS borrowernumber, - reserves.reservedate AS reservedate, - reserves.branchcode AS branchcode, - reserves.cancellationdate AS cancellationdate, - reserves.found AS found, - reserves.reservenotes AS reservenotes, - reserves.priority AS priority, - reserves.timestamp AS timestamp, + SELECT reserves.reservenumber AS reservenumber, + reserves.biblionumber AS biblionumber, + reserves.borrowernumber AS borrowernumber, + reserves.reservedate AS reservedate, + reserves.branchcode AS branchcode, + reserves.cancellationdate AS cancellationdate, + reserves.found AS found, + reserves.reservenotes AS reservenotes, + reserves.priority AS priority, + reserves.timestamp AS timestamp, biblioitems.biblioitemnumber AS biblioitemnumber, reserves.itemnumber AS itemnumber FROM reserves @@ -1611,16 +1878,16 @@ sub _Findgroupreserve { return @results if @results; my $query = qq/ - SELECT reserves.biblionumber AS biblionumber, - reserves.borrowernumber AS borrowernumber, - reserves.reservedate AS reservedate, - reserves.waitingdate AS waitingdate, - reserves.branchcode AS branchcode, - reserves.cancellationdate AS cancellationdate, - reserves.found AS found, - reserves.reservenotes AS reservenotes, - reserves.priority AS priority, - reserves.timestamp AS timestamp, + SELECT reserves.reservenumber AS reservenumber, + reserves.biblionumber AS biblionumber, + reserves.borrowernumber AS borrowernumber, + reserves.reservedate AS reservedate, + reserves.branchcode AS branchcode, + reserves.cancellationdate AS cancellationdate, + reserves.found AS found, + reserves.reservenotes AS reservenotes, + reserves.priority AS priority, + reserves.timestamp AS timestamp, reserveconstraints.biblioitemnumber AS biblioitemnumber, reserves.itemnumber AS itemnumber FROM reserves @@ -1652,30 +1919,29 @@ ModReserveAffect, _not_ ModReserveFill) =cut sub _koha_notify_reserve { - my ($itemnumber, $borrowernumber, $biblionumber) = @_; + my ($itemnumber, $borrowernumber, $biblionumber, $reservenumber) = @_; - my $dbh = C4::Context->dbh; - my $borrower = C4::Members::GetMember(borrowernumber => $borrowernumber); + my $dbh = C4::Context->dbh; + my $borrower = C4::Members::GetMember( 'borrowernumber'=> $borrowernumber ); my $letter_code; my $print_mode = 0; my $messagingprefs; - if ( $borrower->{'email'} || $borrower->{'smsalertnumber'} ) { + if ( C4::Members::GetFirstValidEmailAddress($borrowernumber) || $borrower->{'smsalertnumber'} ) { $messagingprefs = C4::Members::Messaging::GetMessagingPreferences( { borrowernumber => $borrowernumber, message_name => 'Hold Filled' } ); return if ( !defined( $messagingprefs->{'letter_code'} ) ); $letter_code = $messagingprefs->{'letter_code'}; } else { - $letter_code = 'HOLD_PRINT'; + $letter_code = 'HOLD'; $print_mode = 1; } my $sth = $dbh->prepare(" SELECT * FROM reserves - WHERE borrowernumber = ? - AND biblionumber = ? + WHERE reservenumber = ? "); - $sth->execute( $borrowernumber, $biblionumber ); + $sth->execute( $reservenumber ); my $reserve = $sth->fetchrow_hashref; my $branch_details = GetBranchDetail( $reserve->{'branchcode'} ); @@ -1688,6 +1954,7 @@ sub _koha_notify_reserve { C4::Letters::parseletter( $letter, 'borrowers', $borrowernumber ); C4::Letters::parseletter( $letter, 'biblio', $biblionumber ); C4::Letters::parseletter( $letter, 'reserves', $borrowernumber, $biblionumber ); + C4::Letters::parseletter( $letter, 'items', $itemnumber ); if ( $reserve->{'itemnumber'} ) { C4::Letters::parseletter( $letter, 'items', $reserve->{'itemnumber'} ); --- a/admin/branch_transfer_limits.pl +++ a/admin/branch_transfer_limits.pl @@ -91,13 +91,13 @@ while ( my $row = $sth->fetchrow_hashref ) { ## If Form Data Passed, Update the Database if ( $input->param('updateLimits') ) { - DeleteBranchTransferLimits(); + DeleteBranchTransferLimits($branchcode); foreach my $code ( @codes ) { - foreach my $toBranch ( @branchcodes ) { - my $isSet = not $input->param( $code . "_" . $toBranch); + foreach my $fromBranch ( @branchcodes ) { + my $isSet = not $input->param( $code . "_" . $fromBranch); if ( $isSet ) { - CreateBranchTransferLimit( $toBranch, $branchcode, $code ); + CreateBranchTransferLimit( $branchcode, $fromBranch, $code ); } } } @@ -115,18 +115,18 @@ my $branchcount = scalar(@branchcode_loop); ## Build the default data my @codes_loop; foreach my $code ( @codes ) { - my @to_branch_loop; + my @from_branch_loop; my %row_data; $row_data{ code } = $code; - $row_data{ to_branch_loop } = \@to_branch_loop; - foreach my $toBranch ( @branchcodes ) { + $row_data{ from_branch_loop } = \@from_branch_loop; + foreach my $fromBranch ( @branchcodes ) { my %row_data; - my $isChecked = IsBranchTransferAllowed( $toBranch, $branchcode, $code ); + my $isChecked = IsBranchTransferAllowed( $branchcode, $fromBranch, $code ); $row_data{ code } = $code; - $row_data{ toBranch } = $toBranch; + $row_data{ fromBranch } = $fromBranch; $row_data{ isChecked } = $isChecked; - $row_data{ toBranchname } = GetBranchName($toBranch); - push( @to_branch_loop, \%row_data ); + $row_data{ fromBranchname } = GetBranchName($fromBranch); + push( @from_branch_loop, \%row_data ); } push( @codes_loop, \%row_data ); --- a/admin/smart-rules.pl +++ a/admin/smart-rules.pl @@ -25,317 +25,32 @@ use C4::Output; use C4::Auth; use C4::Koha; use C4::Debug; -use C4::Branch; # GetBranches +use C4::Branch; +use C4::IssuingRules; +use C4::Circulation; my $input = new CGI; my $dbh = C4::Context->dbh; -# my $flagsrequired; -# $flagsrequired->{circulation}=1; -my ($template, $loggedinuser, $cookie) - = get_template_and_user({template_name => "admin/smart-rules.tmpl", - query => $input, - type => "intranet", - authnotrequired => 0, - flagsrequired => {parameters => 1}, - debug => 1, - }); - -my $type=$input->param('type'); -my $branch = $input->param('branch') || ( C4::Branch::onlymine() ? ( C4::Branch::mybranch() || '*' ) : '*' ); -my $op = $input->param('op'); - -if ($op eq 'delete') { - my $itemtype = $input->param('itemtype'); - my $categorycode = $input->param('categorycode'); - $debug and warn "deleting $1 $2 $branch"; - - my $sth_Idelete = $dbh->prepare("delete from issuingrules where branchcode=? and categorycode=? and itemtype=?"); - $sth_Idelete->execute($branch, $categorycode, $itemtype); -} -elsif ($op eq 'delete-branch-cat') { - my $categorycode = $input->param('categorycode'); - if ($branch eq "*") { - if ($categorycode eq "*") { - my $sth_delete = $dbh->prepare("DELETE FROM default_circ_rules"); - $sth_delete->execute(); - } else { - my $sth_delete = $dbh->prepare("DELETE FROM default_borrower_circ_rules - WHERE categorycode = ?"); - $sth_delete->execute($categorycode); - } - } elsif ($categorycode eq "*") { - my $sth_delete = $dbh->prepare("DELETE FROM default_branch_circ_rules - WHERE branchcode = ?"); - $sth_delete->execute($branch); - } else { - my $sth_delete = $dbh->prepare("DELETE FROM branch_borrower_circ_rules - WHERE branchcode = ? - AND categorycode = ?"); - $sth_delete->execute($branch, $categorycode); - } -} -elsif ($op eq 'delete-branch-item') { - my $itemtype = $input->param('itemtype'); - if ($branch eq "*") { - if ($itemtype eq "*") { - my $sth_delete = $dbh->prepare("DELETE FROM default_circ_rules"); - $sth_delete->execute(); - } else { - my $sth_delete = $dbh->prepare("DELETE FROM default_branch_item_rules - WHERE itemtype = ?"); - $sth_delete->execute($itemtype); - } - } elsif ($itemtype eq "*") { - my $sth_delete = $dbh->prepare("DELETE FROM default_branch_circ_rules - WHERE branchcode = ?"); - $sth_delete->execute($branch); - } else { - my $sth_delete = $dbh->prepare("DELETE FROM branch_item_rules - WHERE branchcode = ? - AND itemtype = ?"); - $sth_delete->execute($branch, $itemtype); - } -} -# save the values entered -elsif ($op eq 'add') { - my $sth_search = $dbh->prepare("SELECT COUNT(*) AS total FROM issuingrules WHERE branchcode=? AND categorycode=? AND itemtype=?"); - my $sth_insert = $dbh->prepare("INSERT INTO issuingrules (branchcode, categorycode, itemtype, maxissueqty, renewalsallowed, reservesallowed, issuelength, fine, finedays, firstremind, chargeperiod,rentaldiscount) VALUES(?,?,?,?,?,?,?,?,?,?,?,?)"); - my $sth_update=$dbh->prepare("UPDATE issuingrules SET fine=?, finedays=?, firstremind=?, chargeperiod=?, maxissueqty=?, renewalsallowed=?, reservesallowed=?, issuelength=?, rentaldiscount=? WHERE branchcode=? AND categorycode=? AND itemtype=?"); - - my $br = $branch; # branch - my $bor = $input->param('categorycode'); # borrower category - my $cat = $input->param('itemtype'); # item type - my $fine = $input->param('fine'); - my $finedays = $input->param('finedays'); - my $firstremind = $input->param('firstremind'); - my $chargeperiod = $input->param('chargeperiod'); - my $maxissueqty = $input->param('maxissueqty'); - my $renewalsallowed = $input->param('renewalsallowed'); - my $reservesallowed = $input->param('reservesallowed'); - $maxissueqty =~ s/\s//g; - $maxissueqty = undef if $maxissueqty !~ /^\d+/; - my $issuelength = $input->param('issuelength'); - my $rentaldiscount = $input->param('rentaldiscount'); - $debug and warn "Adding $br, $bor, $cat, $fine, $maxissueqty"; - - $sth_search->execute($br,$bor,$cat); - my $res = $sth_search->fetchrow_hashref(); - if ($res->{total}) { - $sth_update->execute($fine, $finedays,$firstremind, $chargeperiod, $maxissueqty, $renewalsallowed,$reservesallowed, $issuelength,$rentaldiscount, $br,$bor,$cat); - } else { - $sth_insert->execute($br,$bor,$cat,$maxissueqty,$renewalsallowed,$reservesallowed,$issuelength,$fine,$finedays,$firstremind,$chargeperiod,$rentaldiscount); - } -} -elsif ($op eq "set-branch-defaults") { - my $categorycode = $input->param('categorycode'); - my $maxissueqty = $input->param('maxissueqty'); - my $holdallowed = $input->param('holdallowed'); - $maxissueqty =~ s/\s//g; - $maxissueqty = undef if $maxissueqty !~ /^\d+/; - $holdallowed =~ s/\s//g; - $holdallowed = undef if $holdallowed !~ /^\d+/; - - if ($branch eq "*") { - my $sth_search = $dbh->prepare("SELECT count(*) AS total - FROM default_circ_rules"); - my $sth_insert = $dbh->prepare("INSERT INTO default_circ_rules - (maxissueqty, holdallowed) - VALUES (?, ?)"); - my $sth_update = $dbh->prepare("UPDATE default_circ_rules - SET maxissueqty = ?, holdallowed = ?"); - - $sth_search->execute(); - my $res = $sth_search->fetchrow_hashref(); - if ($res->{total}) { - $sth_update->execute($maxissueqty, $holdallowed); - } else { - $sth_insert->execute($maxissueqty, $holdallowed); - } - } else { - my $sth_search = $dbh->prepare("SELECT count(*) AS total - FROM default_branch_circ_rules - WHERE branchcode = ?"); - my $sth_insert = $dbh->prepare("INSERT INTO default_branch_circ_rules - (branchcode, maxissueqty, holdallowed) - VALUES (?, ?, ?)"); - my $sth_update = $dbh->prepare("UPDATE default_branch_circ_rules - SET maxissueqty = ?, holdallowed = ? - WHERE branchcode = ?"); - $sth_search->execute($branch); - my $res = $sth_search->fetchrow_hashref(); - if ($res->{total}) { - $sth_update->execute($maxissueqty, $holdallowed, $branch); - } else { - $sth_insert->execute($branch, $maxissueqty, $holdallowed); - } - } -} -elsif ($op eq "add-branch-cat") { - my $categorycode = $input->param('categorycode'); - my $maxissueqty = $input->param('maxissueqty'); - $maxissueqty =~ s/\s//g; - $maxissueqty = undef if $maxissueqty !~ /^\d+/; - - if ($branch eq "*") { - if ($categorycode eq "*") { - my $sth_search = $dbh->prepare("SELECT count(*) AS total - FROM default_circ_rules"); - my $sth_insert = $dbh->prepare("INSERT INTO default_circ_rules - (maxissueqty) - VALUES (?)"); - my $sth_update = $dbh->prepare("UPDATE default_circ_rules - SET maxissueqty = ?"); - - $sth_search->execute(); - my $res = $sth_search->fetchrow_hashref(); - if ($res->{total}) { - $sth_update->execute($maxissueqty); - } else { - $sth_insert->execute($maxissueqty); - } - } else { - my $sth_search = $dbh->prepare("SELECT count(*) AS total - FROM default_borrower_circ_rules - WHERE categorycode = ?"); - my $sth_insert = $dbh->prepare("INSERT INTO default_borrower_circ_rules - (categorycode, maxissueqty) - VALUES (?, ?)"); - my $sth_update = $dbh->prepare("UPDATE default_borrower_circ_rules - SET maxissueqty = ? - WHERE categorycode = ?"); - $sth_search->execute($branch); - my $res = $sth_search->fetchrow_hashref(); - if ($res->{total}) { - $sth_update->execute($maxissueqty, $categorycode); - } else { - $sth_insert->execute($categorycode, $maxissueqty); - } - } - } elsif ($categorycode eq "*") { - my $sth_search = $dbh->prepare("SELECT count(*) AS total - FROM default_branch_circ_rules - WHERE branchcode = ?"); - my $sth_insert = $dbh->prepare("INSERT INTO default_branch_circ_rules - (branchcode, maxissueqty) - VALUES (?, ?)"); - my $sth_update = $dbh->prepare("UPDATE default_branch_circ_rules - SET maxissueqty = ? - WHERE branchcode = ?"); - $sth_search->execute($branch); - my $res = $sth_search->fetchrow_hashref(); - if ($res->{total}) { - $sth_update->execute($maxissueqty, $branch); - } else { - $sth_insert->execute($branch, $maxissueqty); - } - } else { - my $sth_search = $dbh->prepare("SELECT count(*) AS total - FROM branch_borrower_circ_rules - WHERE branchcode = ? - AND categorycode = ?"); - my $sth_insert = $dbh->prepare("INSERT INTO branch_borrower_circ_rules - (branchcode, categorycode, maxissueqty) - VALUES (?, ?, ?)"); - my $sth_update = $dbh->prepare("UPDATE branch_borrower_circ_rules - SET maxissueqty = ? - WHERE branchcode = ? - AND categorycode = ?"); - - $sth_search->execute($branch, $categorycode); - my $res = $sth_search->fetchrow_hashref(); - if ($res->{total}) { - $sth_update->execute($maxissueqty, $branch, $categorycode); - } else { - $sth_insert->execute($branch, $categorycode, $maxissueqty); - } - } -} -elsif ($op eq "add-branch-item") { - my $itemtype = $input->param('itemtype'); - my $holdallowed = $input->param('holdallowed'); - $holdallowed =~ s/\s//g; - $holdallowed = undef if $holdallowed !~ /^\d+/; - - if ($branch eq "*") { - if ($itemtype eq "*") { - my $sth_search = $dbh->prepare("SELECT count(*) AS total - FROM default_circ_rules"); - my $sth_insert = $dbh->prepare("INSERT INTO default_circ_rules - (holdallowed) - VALUES (?)"); - my $sth_update = $dbh->prepare("UPDATE default_circ_rules - SET holdallowed = ?"); - - $sth_search->execute(); - my $res = $sth_search->fetchrow_hashref(); - if ($res->{total}) { - $sth_update->execute($holdallowed); - } else { - $sth_insert->execute($holdallowed); - } - } else { - my $sth_search = $dbh->prepare("SELECT count(*) AS total - FROM default_branch_item_rules - WHERE itemtype = ?"); - my $sth_insert = $dbh->prepare("INSERT INTO default_branch_item_rules - (itemtype, holdallowed) - VALUES (?, ?)"); - my $sth_update = $dbh->prepare("UPDATE default_branch_item_rules - SET holdallowed = ? - WHERE itemtype = ?"); - $sth_search->execute($itemtype); - my $res = $sth_search->fetchrow_hashref(); - if ($res->{total}) { - $sth_update->execute($holdallowed, $itemtype); - } else { - $sth_insert->execute($itemtype, $holdallowed); - } - } - } elsif ($itemtype eq "*") { - my $sth_search = $dbh->prepare("SELECT count(*) AS total - FROM default_branch_circ_rules - WHERE branchcode = ?"); - my $sth_insert = $dbh->prepare("INSERT INTO default_branch_circ_rules - (branchcode, holdallowed) - VALUES (?, ?)"); - my $sth_update = $dbh->prepare("UPDATE default_branch_circ_rules - SET holdallowed = ? - WHERE branchcode = ?"); - $sth_search->execute($branch); - my $res = $sth_search->fetchrow_hashref(); - if ($res->{total}) { - $sth_update->execute($holdallowed, $branch); - } else { - $sth_insert->execute($branch, $holdallowed); - } - } else { - my $sth_search = $dbh->prepare("SELECT count(*) AS total - FROM branch_item_rules - WHERE branchcode = ? - AND itemtype = ?"); - my $sth_insert = $dbh->prepare("INSERT INTO branch_item_rules - (branchcode, itemtype, holdallowed) - VALUES (?, ?, ?)"); - my $sth_update = $dbh->prepare("UPDATE branch_item_rules - SET holdallowed = ? - WHERE branchcode = ? - AND itemtype = ?"); - - $sth_search->execute($branch, $itemtype); - my $res = $sth_search->fetchrow_hashref(); - if ($res->{total}) { - $sth_update->execute($holdallowed, $branch, $itemtype); - } else { - $sth_insert->execute($branch, $itemtype, $holdallowed); - } - } -} - +my ($template, $loggedinuser, $cookie) = get_template_and_user({ + template_name => "admin/smart-rules.tmpl", + query => $input, + type => "intranet", + authnotrequired => 0, + flagsrequired => {parameters => 1}, + debug => 1, +}); + +my $type = $input->param('type'); +my $branchcode = $input->param('branchcode') || ( C4::Branch::onlymine() ? ( C4::Branch::mybranch() || '*' ) : '*' ); +my $op = $input->param('op'); +my $confirm = $input->param('confirm'); + +# This block builds the branch list my $branches = GetBranches(); my @branchloop; -for my $thisbranch (sort { $branches->{$a}->{branchname} cmp $branches->{$b}->{branchname} } keys %$branches) { - my $selected = 1 if $thisbranch eq $branch; +for my $thisbranch (sort { $branches->{$a}->{'branchname'} cmp $branches->{$b}->{'branchname'} } keys %$branches) { + my $selected = 1 if $thisbranch eq $branchcode; my %row =(value => $thisbranch, selected => $selected, branchname => $branches->{$thisbranch}->{'branchname'}, @@ -343,177 +58,115 @@ for my $thisbranch (sort { $branches->{$a}->{branchname} cmp $branches->{$b}->{b push @branchloop, \%row; } -my $sth=$dbh->prepare("SELECT description,categorycode FROM categories ORDER BY description"); -$sth->execute; -my @category_loop; -while (my $data=$sth->fetchrow_hashref){ - push @category_loop,$data; -} - -$sth->finish; -$sth=$dbh->prepare("SELECT description,itemtype FROM itemtypes ORDER BY description"); -$sth->execute; -# $i=0; -my @row_loop; -my @itemtypes; -while (my $row=$sth->fetchrow_hashref){ - push @itemtypes,$row; -} +# Get the patron category list +my @category_loop = C4::Category->all; -my $sth2 = $dbh->prepare(" - SELECT issuingrules.*, itemtypes.description AS humanitemtype, categories.description AS humancategorycode - FROM issuingrules - LEFT JOIN itemtypes - ON (itemtypes.itemtype = issuingrules.itemtype) - LEFT JOIN categories - ON (categories.categorycode = issuingrules.categorycode) - WHERE issuingrules.branchcode = ? -"); -$sth2->execute($branch); +# Get the item types list +my @itemtypes = C4::ItemType->all; -while (my $row = $sth2->fetchrow_hashref) { - $row->{'humanitemtype'} ||= $row->{'itemtype'}; - $row->{'default_humanitemtype'} = 1 if $row->{'humanitemtype'} eq '*'; - $row->{'humancategorycode'} ||= $row->{'categorycode'}; - $row->{'default_humancategorycode'} = 1 if $row->{'humancategorycode'} eq '*'; - $row->{'fine'} = sprintf('%.2f', $row->{'fine'}); - push @row_loop, $row; +if ( $op eq 'delete' ) { + DelIssuingRule({ + branchcode => $branchcode, + categorycode => $input->param('categorycode'), + itemtype => $input->param('itemtype'), + }); } -$sth->finish; - -my @sorted_row_loop = sort by_category_and_itemtype @row_loop; +# save the values entered +elsif ( $op eq 'add' ) { -my $sth_branch_cat; -if ($branch eq "*") { - $sth_branch_cat = $dbh->prepare(" - SELECT default_borrower_circ_rules.*, categories.description AS humancategorycode - FROM default_borrower_circ_rules - JOIN categories USING (categorycode) - - "); - $sth_branch_cat->execute(); -} else { - $sth_branch_cat = $dbh->prepare(" - SELECT branch_borrower_circ_rules.*, categories.description AS humancategorycode - FROM branch_borrower_circ_rules - JOIN categories USING (categorycode) - WHERE branch_borrower_circ_rules.branchcode = ? - "); - $sth_branch_cat->execute($branch); -} + # Converts '' to undef, so we can have NULL in database fields + my $issuingrule; + for ( $input->param ) { + my $v = $input->param($_); + $issuingrule->{$_} = length $v ? $v : undef; + } -my @branch_cat_rules = (); -while (my $row = $sth_branch_cat->fetchrow_hashref) { - push @branch_cat_rules, $row; + # We don't want op to be passed to the API + delete $issuingrule->{'op'}; + + # If the (branchcode,categorycode,itemtype) combination already exists... + my @issuingrules = GetIssuingRules({ + branchcode => $issuingrule->{'branchcode'}, + categorycode => $issuingrule->{'categorycode'}, + itemtype => $issuingrule->{'itemtype'}, + }); + + # ...we modify the existing rule... + if ( @issuingrules ) { + ModIssuingRule( $issuingrule ); +# } elsif (@issuingrules){ +# $template->param(confirm=>1); +# $template->param(%$issuingrule); +# foreach (@category_loop) { +# $_->{selected}="selected" if ($_->{categorycode} eq $issuingrule->{categorycode}); +# } +# foreach (@itemtypes) { +# $_->{selected}="selected" if ($_->{itemtype} eq $issuingrule->{itemtype}); +# } + # ...else we add a new rule. + } else { + AddIssuingRule( $issuingrule ); + } } -my @sorted_branch_cat_rules = sort { $a->{'humancategorycode'} cmp $b->{'humancategorycode'} } @branch_cat_rules; -# note undef maxissueqty so that template can deal with them -foreach my $entry (@sorted_branch_cat_rules, @sorted_row_loop) { - $entry->{unlimited_maxissueqty} = 1 unless defined($entry->{maxissueqty}); -} +# Get the issuing rules list... +my @issuingrules = GetIssuingRulesByBranchCode($branchcode); -my @sorted_row_loop = sort by_category_and_itemtype @row_loop; +# ...and refine its data, row by row. +for my $rule ( @issuingrules ) { + $rule->{'humanitemtype'} ||= $rule->{'itemtype'}; + $rule->{'default_humanitemtype'} = $rule->{'humanitemtype'} eq '*'; + $rule->{'humancategorycode'} ||= $rule->{'categorycode'}; + $rule->{'default_humancategorycode'} = $rule->{'humancategorycode'} eq '*'; -my $sth_branch_item; -if ($branch eq "*") { - $sth_branch_item = $dbh->prepare(" - SELECT default_branch_item_rules.*, itemtypes.description AS humanitemtype - FROM default_branch_item_rules - JOIN itemtypes USING (itemtype) - "); - $sth_branch_item->execute(); -} else { - $sth_branch_item = $dbh->prepare(" - SELECT branch_item_rules.*, itemtypes.description AS humanitemtype - FROM branch_item_rules - JOIN itemtypes USING (itemtype) - WHERE branch_item_rules.branchcode = ? - "); - $sth_branch_item->execute($branch); + # This block is to show herited values in grey. + # We juste compare keys from our raw rule, with keys from the computed rule. + my $computedrule = GetIssuingRule($rule->{'categorycode'}, $rule->{'itemtype'}, $rule->{'branchcode'}); + for ( keys %$rule ) { + if ( not defined $rule->{$_} ) { + $rule->{$_} = $computedrule->{$_}; + $rule->{"herited_$_"} = 1; + } + } } -my @branch_item_rules = (); -while (my $row = $sth_branch_item->fetchrow_hashref) { - push @branch_item_rules, $row; -} -my @sorted_branch_item_rules = sort { $a->{'humanitemtype'} cmp $b->{'humanitemtype'} } @branch_item_rules; +# Get the issuing rules list... +my @issuingrules = GetIssuingRulesByBranchCode($branchcode); -# note undef holdallowed so that template can deal with them -foreach my $entry (@sorted_branch_item_rules) { - $entry->{holdallowed_any} = 1 if($entry->{holdallowed} == 2); - $entry->{holdallowed_same} = 1 if($entry->{holdallowed} == 1); -} +# ...and refine its data, row by row. +for my $rule ( @issuingrules ) { + $rule->{'humanitemtype'} ||= $rule->{'itemtype'}; + $rule->{'default_humanitemtype'} = $rule->{'humanitemtype'} eq '*'; + $rule->{'humancategorycode'} ||= $rule->{'categorycode'}; + $rule->{'default_humancategorycode'} = $rule->{'humancategorycode'} eq '*'; -$template->param(show_branch_cat_rule_form => 1); -$template->param(branch_item_rule_loop => \@sorted_branch_item_rules); -$template->param(branch_cat_rule_loop => \@sorted_branch_cat_rules); + # This block is to show herited values in grey. + # We juste compare keys from our raw rule, with keys from the computed rule. + my $computedrule = GetIssuingRule($rule->{'categorycode'}, $rule->{'itemtype'}, $rule->{'branchcode'}); + for ( keys %$rule ) { + if ( not defined $rule->{$_} ) { + $rule->{$_} = $computedrule->{$_}; + $rule->{"herited_$_"} = 1; + } + } -my $sth_defaults; -if ($branch eq "*") { - $sth_defaults = $dbh->prepare(" - SELECT * - FROM default_circ_rules - "); - $sth_defaults->execute(); -} else { - $sth_defaults = $dbh->prepare(" - SELECT * - FROM default_branch_circ_rules - WHERE branchcode = ? - "); - $sth_defaults->execute($branch); + $rule->{'fine'} = sprintf('%.2f', $rule->{'fine'}); } -my $defaults = $sth_defaults->fetchrow_hashref; - -if ($defaults) { - $template->param(default_holdallowed_none => 1) if($defaults->{holdallowed} == 0); - $template->param(default_holdallowed_same => 1) if($defaults->{holdallowed} == 1); - $template->param(default_holdallowed_any => 1) if($defaults->{holdallowed} == 2); - $template->param(default_maxissueqty => $defaults->{maxissueqty}); -} $template->param(default_rules => ($defaults ? 1 : 0)); -$template->param(categoryloop => \@category_loop, - itemtypeloop => \@itemtypes, - rules => \@sorted_row_loop, - branchloop => \@branchloop, - humanbranch => ($branch ne '*' ? $branches->{$branch}->{branchname} : ''), - current_branch => $branch, - definedbranch => scalar(@sorted_row_loop)>0 - ); +$template->param( + categoryloop => \@category_loop, + itemtypeloop => \@itemtypes, + rules => \@issuingrules, + branchloop => \@branchloop, + humanbranch => ($branchcode ne '*' ? $branches->{$branchcode}->{branchname} : ''), + branchcode => $branchcode, + definedbranch => scalar(@issuingrules) > 0, +); + output_html_with_http_headers $input, $cookie, $template->output; exit 0; -# sort by patron category, then item type, putting -# default entries at the bottom -sub by_category_and_itemtype { - unless (by_category($a, $b)) { - return by_itemtype($a, $b); - } -} - -sub by_category { - my ($a, $b) = @_; - if ($a->{'default_humancategorycode'}) { - return ($b->{'default_humancategorycode'} ? 0 : 1); - } elsif ($b->{'default_humancategorycode'}) { - return -1; - } else { - return $a->{'humancategorycode'} cmp $b->{'humancategorycode'}; - } -} - -sub by_itemtype { - my ($a, $b) = @_; - if ($a->{'default_humanitemtype'}) { - return ($b->{'default_humanitemtype'} ? 0 : 1); - } elsif ($b->{'default_humanitemtype'}) { - return -1; - } else { - return $a->{'humanitemtype'} cmp $b->{'humanitemtype'}; - } -} --- a/circ/branchtransfers.pl +++ a/circ/branchtransfers.pl @@ -87,7 +87,9 @@ if ( $request eq "KillWaiting" ) { } elsif ( $request eq "SetWaiting" ) { my $item = $query->param('itemnumber'); - ModReserveAffect( $item, $borrowernumber ); + my $itemhash=C4::Items::GetItem($item); + my ( $reservetype, $reserve ) = C4::Reserves::CheckReserves( undef, $itemhash->{barcode} ); + ModReserveAffect( $item, $borrowernumber,$tobranchcd ne C4::Context->userenv->{'branch'}, $reserve->{reservenumber}); $ignoreRs = 1; $setwaiting = 1; $reqmessage = 1; --- a/circ/circulation.pl +++ a/circ/circulation.pl @@ -30,27 +30,33 @@ use C4::Dates qw/format_date/; use C4::Branch; # GetBranches use C4::Koha; # GetPrinter use C4::Circulation; +use C4::Overdues qw/CheckBorrowerDebarred/; use C4::Members; use C4::Biblio; use C4::Reserves; use C4::Context; +use C4::Debug; use CGI::Session; - +use C4::Items; +use JSON; +use YAML; use Date::Calc qw( Today Add_Delta_YM Add_Delta_Days Date_to_Days ); +use List::MoreUtils qw/uniq/; # # PARAMETERS READING # my $query = new CGI; - -my $sessionID = $query->cookie("CGISESSID") ; -my $session = get_session($sessionID); +my $dbh = C4::Context->dbh; +my $remotehost = $query->remote_host(); +my $sessionID = $query->cookie("CGISESSID"); +my $session = get_session($sessionID); # branch and printer are now defined by the userenv # but first we have to check if someone has tried to change them @@ -88,9 +94,14 @@ my ( $template, $loggedinuser, $cookie ) = get_template_and_user ( my $branches = GetBranches(); -my @failedrenews = $query->param('failedrenew'); # expected to be itemnumbers +my @failedrenews = $query->param('failedrenew'); # expected to be itemnumbers +my @failedreturns = $query->param('failedreturn'); # expected to be barcodes +my @renewerrors = $query->param('renewerror'); # expected to be json +my @returnerrors = $query->param('returnerror'); # expected to be json my %renew_failed; -for (@failedrenews) { $renew_failed{$_} = 1; } +my %return_failed; +for (@failedrenews) { $renew_failed{$_} = decode_json(shift @renewerrors); } +for (@failedreturns) { $return_failed{GetItemnumberFromBarcode($_)} = decode_json(shift @returnerrors); } my $findborrower = $query->param('findborrower'); $findborrower =~ s|,| |g; @@ -125,14 +136,13 @@ my $organisation = $query->param('organisations'); my $print = $query->param('print'); my $newexpiry = $query->param('dateexpiry'); my $debt_confirmed = $query->param('debt_confirmed') || 0; # Don't show the debt error dialog twice - +$debug && warn $newexpiry; # Check if stickyduedate is turned off if ( $barcode ) { # was stickyduedate loaded from session? if ( $stickyduedate && ! $query->param("stickyduedate") ) { $session->clear( 'stickyduedate' ); $stickyduedate = $query->param('stickyduedate'); - $duedatespec = $query->param('duedatespec'); } } @@ -169,7 +179,7 @@ if($duedatespec_allow){ $datedue = $globalduedate if ($globalduedate); } -my $todaysdate = C4::Dates->new->output('iso'); +my $todaysdate = C4::Dates->new->output( 'iso' ); # check and see if we should print if ( $barcode eq '' && $print eq 'maybe' ) { @@ -231,8 +241,8 @@ if ($borrowernumber) { # Warningdate is the date that the warning starts appearing my ( $today_year, $today_month, $today_day) = Today(); - my ($warning_year, $warning_month, $warning_day) = split /-/, $borrower->{'dateexpiry'}; - my ( $enrol_year, $enrol_month, $enrol_day) = split /-/, $borrower->{'dateenrolled'}; + my ($warning_year, $warning_month, $warning_day) = split (/-/, $borrower->{'dateexpiry'}); + my ( $enrol_year, $enrol_month, $enrol_day) = split (/-/, $borrower->{'dateenrolled'}); # Renew day is calculated by adding the enrolment period to today my ( $renew_year, $renew_month, $renew_day); if ($enrol_year*$enrol_month*$enrol_day>0) { @@ -250,6 +260,7 @@ if ($borrowernumber) { flagged => "1", noissues => "1", expired => format_date($borrower->{dateexpiry}), + warning => 1, renewaldate => format_date("$renew_year-$renew_month-$renew_day") ); } @@ -260,9 +271,9 @@ if ($borrowernumber) { { # borrower card soon to expire warn librarian $template->param("warndeparture" => format_date($borrower->{dateexpiry}), - flagged => "1",); + flagged => "1", "warning" => 1); if (C4::Context->preference('ReturnBeforeExpiry')){ - $template->param("returnbeforeexpiry" => 1); + $template->param("returnbeforeexpiry" => 1, "warning => 1"); } } $template->param( @@ -270,12 +281,22 @@ if ($borrowernumber) { issuecount => $issue, finetotal => $fines ); + + my $debar = CheckBorrowerDebarred($borrowernumber); + if($debar){ + $template->param(userdebarred => 1); + $template->param(debarredcomment => $borrower->{debarredcomment}); + if( $debar ne "9999-12-31"){ + $template->param(userdebarreddate => C4::Dates::format_date($debar)); + } + } } # # STEP 3 : ISSUING # # +my $confirm_required = 0; if ($barcode) { # always check for blockers on issuing my ( $error, $question ) = @@ -321,8 +342,33 @@ if ($barcode) { # FIXME If the issue is confirmed, we launch another time GetMemberIssuesAndFines, now display the issue count after issue my ( $od, $issue, $fines ) = GetMemberIssuesAndFines( $borrowernumber ); $template->param( issuecount => $issue ); + + # Is there a circulation note? + my $itemnumber = GetItemnumberFromBarcode($barcode); + my $biblionumber = GetBiblionumberFromItemnumber($itemnumber); + my $record = GetMarcBiblio($biblionumber); + my $frameworkcode = GetFrameworkCode($biblionumber); + my $circnotefield = GetRecordValue('circnote', $record, $frameworkcode); + if (defined @$circnotefield[0]) { + $template->param(circnote => @$circnotefield[0]->{'subfield'}); + } } + +# Setting the right status if an hold has been confirmed +#This should never be used in fact +my $resbarcode = $query->param("resbarcode"); +if ($resbarcode) { + if ( my ( $reservetype, $reserve ) = C4::Reserves::CheckReserves( undef, $resbarcode ) ) { + if ( $reservetype eq "Waiting" || $reservetype eq "Reserved" ) { + my $transfer = C4::Context->userenv->{branch} ne $reserve->{branchcode}; + ModReserveAffect( $reserve->{itemnumber}, $reserve->{borrowernumber}, $transfer, $reserve->{"reservenumber"} ); + } + } +} + + + # reload the borrower info for the sake of reseting the flags..... if ($borrowernumber) { $borrower = GetMemberDetails( $borrowernumber, 0 ); @@ -360,11 +406,18 @@ if ($borrowernumber) { $getreserv{itemcallnumber} = $getiteminfo->{'itemcallnumber'}; $getreserv{biblionumber} = $getiteminfo->{'biblionumber'}; $getreserv{waitingat} = GetBranchName( $num_res->{'branchcode'} ); + my $materials = $getiteminfo->{'materials'}; + $template->param(materials => $materials); # check if we have a waiting status for reservations if ( $num_res->{'found'} eq 'W' ) { $getreserv{color} = 'reserved'; $getreserv{waiting} = 1; # genarate information displaying only waiting reserves + my @maxpickupdate = $num_res->{'waitingdate'} ? GetMaxPickupDate( $num_res->{'waitingdate'}, $borrowernumber, $num_res ) : ''; + $getreserv{'maxpickupdate'} = sprintf( "%d-%02d-%02d", @maxpickupdate ); + $getWaitingReserveInfo{'formattedwaitingdate'} = format_date( $getreserv{'maxpickupdate'} ); + $getreserv{'formattedwaitingdate'} = format_date( $getreserv{'maxpickupdate'} ); + $getWaitingReserveInfo{title} = $getiteminfo->{'title'}; $getWaitingReserveInfo{biblionumber} = $getiteminfo->{'biblionumber'}; $getWaitingReserveInfo{itemtype} = $itemtypeinfo->{'description'}; @@ -420,51 +473,97 @@ my $previssues = ''; my @todaysissues; my @previousissues; +my @borrowers_with_issues; +my @relissues; +my @relprevissues; +my $displayrelissues; +## ADDED BY JF: new itemtype issuingrules counter stuff +my $issued_itemtypes_count; +my @issued_itemtypes_count_loop; + my $totalprice = 0; -if ($borrower) { -# get each issue of the borrower & separate them in todayissues & previous issues - my ($issueslist) = GetPendingIssues($borrower->{'borrowernumber'}); - # split in 2 arrays for today & previous - foreach my $it ( @$issueslist ) { - my $itemtypeinfo = getitemtypeinfo( (C4::Context->preference('item-level_itypes')) ? $it->{'itype'} : $it->{'itemtype'} ); - # set itemtype per item-level_itype syspref - FIXME this is an ugly hack - $it->{'itemtype'} = ( C4::Context->preference( 'item-level_itypes' ) ) ? $it->{'itype'} : $it->{'itemtype'}; +sub build_issue_data { + my $issueslist = shift; + my $relatives = shift; - ($it->{'charge'}, $it->{'itemtype_charge'}) = GetIssuingCharges( - $it->{'itemnumber'}, $borrower->{'borrowernumber'} - ); - $it->{'charge'} = sprintf("%.2f", $it->{'charge'}); - my ($can_renew, $can_renew_error) = CanBookBeRenewed( - $borrower->{'borrowernumber'},$it->{'itemnumber'} - ); - $it->{"renew_error_${can_renew_error}"} = 1 if defined $can_renew_error; + # split in 2 arrays for today & previous + foreach my $it (@$issueslist) { + my $itemtypeinfo = getitemtypeinfo( ( C4::Context->preference('item-level_itypes') ) ? $it->{'itype'} : $it->{'itemtype'} ); + + # Getting borrower details + my $memberdetails = GetMemberDetails($it->{'borrowernumber'}); + $it->{'borrowername'} = $memberdetails->{'firstname'} . " " . $memberdetails->{'surname'}; + + # set itemtype per item-level_itype syspref - FIXME this is an ugly hack + $it->{'itemtype'} = ( C4::Context->preference('item-level_itypes') ) ? $it->{'itype'} : $it->{'itemtype'}; + + ( $it->{'charge'}, $it->{'itemtype_charge'} ) = GetIssuingCharges( $it->{'itemnumber'}, $borrower->{'borrowernumber'} ); + $it->{'charge'} = sprintf( "%.2f", $it->{'charge'} ); + my ( $can_renew, $can_renew_error ) = CanBookBeRenewed( $borrower->{'borrowernumber'}, $it->{'itemnumber'} ); + if ( defined $can_renew_error->{message} ) { + $it->{ "renew_error_" . $can_renew_error->{message} } = 1; + $it->{'renew_error'} = 1; + } + $it->{renewals} ||= 0; + $it->{$_} = $can_renew_error->{$_} for (qw(renewalsallowed reserves)); my ( $restype, $reserves ) = CheckReserves( $it->{'itemnumber'} ); - $it->{'can_renew'} = $can_renew; - $it->{'can_confirm'} = !$can_renew && !$restype; - $it->{'renew_error'} = $restype; - $it->{'checkoutdate'} = C4::Dates->new($it->{'issuedate'},'iso')->output('syspref'); + if ($restype) { + $it->{'reserved'} = 1; + $it->{$restype} = 1; + } + $it->{'can_renew'} = $can_renew; + $it->{'can_confirm'} = !$can_renew && !$restype; + $it->{'renew_error'} = $restype; + $it->{'checkoutdate'} = $it->{'issuedate'}; $totalprice += $it->{'replacementprice'}; - $it->{'itemtype'} = $itemtypeinfo->{'description'}; + $it->{'itemtype'} = $itemtypeinfo->{'description'}; $it->{'itemtype_image'} = $itemtypeinfo->{'imageurl'}; - $it->{'dd'} = format_date($it->{'date_due'}); - $it->{'displaydate'} = format_date($it->{'issuedate'}); - $it->{'od'} = ( $it->{'date_due'} lt $todaysdate ) ? 1 : 0 ; - ($it->{'author'} eq '') and $it->{'author'} = ' '; - $it->{'renew_failed'} = $renew_failed{$it->{'itemnumber'}}; - - if ( $todaysdate eq $it->{'issuedate'} or $todaysdate eq $it->{'lastreneweddate'} ) { - push @todaysissues, $it; - } else { - push @previousissues, $it; + $it->{'dd'} = format_date( $it->{'date_due'} ); + $it->{'displaydate'} = format_date( $it->{'issuedate'} ); + ( $it->{'author'} eq '' ) and $it->{'author'} = ' '; + if ( defined( $return_failed{ $it->{'itemnumber'} } ) ) { + $it->{ 'return_error_' . $return_failed{ $it->{'itemnumber'} }->{message} } = 1; + delete $return_failed{ $it->{'itemnumber'} }; } + if ( defined( $renew_failed{ $it->{'itemnumber'} } ) ) { + $it->{ 'renew_error_' . $renew_failed{ $it->{'itemnumber'} }->{message} } = 1; + } + $it->{'return_failed'} = defined( $return_failed{ $it->{'itemnumber'} } ); + $it->{'branchdisplay'} = GetBranchName( ( C4::Context->preference('HomeOrHoldingBranch') eq 'holdingbranch' ) ? $it->{'holdingbranch'} : $it->{'homebranch'} ); + + # ADDED BY JF: NEW ITEMTYPE COUNT DISPLAY + $issued_itemtypes_count->{ $it->{'itemtype'} }++; + if ( $todaysdate eq $it->{'checkoutdate'} or $todaysdate eq $it->{'lastreneweddate'} ) { + (!$relatives) ? push @todaysissues, $it : push @relissues, $it; + } else { + (!$relatives) ? push @previousissues, $it : push @relprevissues, $it; + } + } - if ( C4::Context->preference( "todaysIssuesDefaultSortOrder" ) eq 'asc' ) { - @todaysissues = sort { $a->{'timestamp'} cmp $b->{'timestamp'} } @todaysissues; - } - else { - @todaysissues = sort { $b->{'timestamp'} cmp $a->{'timestamp'} } @todaysissues; + +} + +if ($borrower) { + + # Getting borrower relatives + my @relborrowernumbers = GetMemberRelatives($borrower->{'borrowernumber'}); + #push @borrowernumbers, $borrower->{'borrowernumber'}; + + # get each issue of the borrower & separate them in todayissues & previous issues + my ($issueslist) = GetPendingIssues($borrower->{'borrowernumber'}); + my ($relissueslist) = GetPendingIssues(@relborrowernumbers); + + build_issue_data($issueslist, 0); + build_issue_data($relissueslist, 1); + + $displayrelissues = scalar($relissueslist); + + if ( C4::Context->preference("todaysIssuesDefaultSortOrder") eq 'asc' ) { + @todaysissues = sort { $a->{'timestamp'} cmp $b->{'timestamp'} } @todaysissues; + } else { + @todaysissues = sort { $b->{'timestamp'} cmp $a->{'timestamp'} } @todaysissues; } if ( C4::Context->preference( "previousIssuesDefaultSortOrder" ) eq 'asc' ){ @previousissues = sort { $a->{'date_due'} cmp $b->{'date_due'} } @previousissues; @@ -473,7 +572,48 @@ if ($borrower) { @previousissues = sort { $b->{'date_due'} cmp $a->{'date_due'} } @previousissues; } } +my @reserveswaiting; +foreach my $itemnumber (keys %return_failed){ + next unless $return_failed{$itemnumber}->{'reservesdata'}; + my $hashdata=$return_failed{$itemnumber}->{'reservesdata'}; + $hashdata->{circborrowernumber}=$borrowernumber; + $hashdata->{script_name}=$query->script_name(); + push @reserveswaiting, $hashdata if (%$hashdata); +} + +$template->param(reserves_waiting=>\@reserveswaiting); + +#### ADDED BY JF FOR COUNTS BY ITEMTYPE RULES +# FIXME: This should utilize all the issuingrules options rather than just the defaults +# and it should be moved to a module + +# how many of each is allowed? +my $issueqty_sth = $dbh->prepare( + 'SELECT itemtypes.description AS description,issuingrules.itemtype,maxissueqty ' . + 'FROM issuingrules LEFT JOIN itemtypes ON (itemtypes.itemtype=issuingrules.itemtype) ' . + 'WHERE categorycode=?' +); +$issueqty_sth->execute(q{*}); # This is a literal asterisk, not a wildcard. + +while ( my $data = $issueqty_sth->fetchrow_hashref() ) { + + # subtract how many of each this borrower has + $data->{'count'} = $issued_itemtypes_count->{ $data->{'description'} }; + $data->{'left'} = + ( $data->{'maxissueqty'} - + $issued_itemtypes_count->{ $data->{'description'} } ); + + # can't have a negative number of remaining + if ( $data->{'left'} < 0 ) { $data->{'left'} = '0' } + if ( $data->{maxissueqty} <= $data->{count} ) { + $data->{flag} = 1; + } + if ( $data->{maxissueqty} > 0 && $data->{itemtype} !~m/^(\*|CIRC)$/ ) { + push @issued_itemtypes_count_loop, $data; + } +} +#### / JF my @values; my %labels; @@ -503,6 +643,7 @@ if ($borrowerslist) { #title my $flags = $borrower->{'flags'}; + $debug && warn Dump($flags); foreach my $flag ( sort keys %$flags ) { $template->param( flagged=> 1); $flags->{$flag}->{'message'} =~ s#\n#
#g; @@ -512,20 +653,19 @@ foreach my $flag ( sort keys %$flags ) { noissues => 'true', ); if ( $flag eq 'GNA' ) { - $template->param( gna => 'true' ); + $template->param( gna => 'true', gonenoaddresscomment => $borrower->{'gonenoaddresscomment'}, warning => 1 ); } elsif ( $flag eq 'LOST' ) { - $template->param( lost => 'true' ); - } - elsif ( $flag eq 'DBARRED' ) { - $template->param( dbarred => 'true' ); - } - elsif ( $flag eq 'CHARGES' ) { + $template->param( lost => 'true', lostcomment => $borrower->{'lostcomment'}, warning => 1 ); + } elsif ( $flag eq 'DEBARRED' ) { + $template->param( userdebarred => 'true', warning => 1, userdebarreddate=> format_date($flags->{DEBARRED}->{dateend}),debardebarredcomment=>$borrower->{'debarredcomment'}); + } elsif ( $flag eq 'CHARGES' ) { $template->param( charges => 'true', chargesmsg => $flags->{'CHARGES'}->{'message'}, chargesamount => $flags->{'CHARGES'}->{'amount'}, - charges_is_blocker => 1 + charges_is_blocker => 1, + warning => 1 ); } elsif ( $flag eq 'CREDITS' ) { @@ -533,6 +673,7 @@ foreach my $flag ( sort keys %$flags ) { credits => 'true', creditsmsg => $flags->{'CREDITS'}->{'message'}, creditsamount => sprintf("%.02f", -($flags->{'CREDITS'}->{'amount'})), # from patron's pov + warning => 1 ); } } @@ -543,6 +684,7 @@ foreach my $flag ( sort keys %$flags ) { flagged => 1, chargesmsg => $flags->{'CHARGES'}->{'message'}, chargesamount => $flags->{'CHARGES'}->{'amount'}, + warning => 1 ); } elsif ( $flag eq 'CREDITS' ) { @@ -550,13 +692,15 @@ foreach my $flag ( sort keys %$flags ) { credits => 'true', creditsmsg => $flags->{'CREDITS'}->{'message'}, creditsamount => sprintf("%.02f", -($flags->{'CREDITS'}->{'amount'})), # from patron's pov + warning => 1 ); } elsif ( $flag eq 'ODUES' ) { $template->param( odues => 'true', flagged => 1, - oduesmsg => $flags->{'ODUES'}->{'message'} + oduesmsg => $flags->{'ODUES'}->{'message'}, + warning => 1 ); my $items = $flags->{$flag}->{'itemlist'}; @@ -611,9 +755,16 @@ if($lib_messages_loop){ $template->param(flagged => 1 ); } my $bor_messages_loop = GetMessages( $borrowernumber, 'B', $branch ); if($bor_messages_loop){ $template->param(flagged => 1 ); } + # Computes full borrower address my (undef, $roadttype_hashref) = &GetRoadTypes(); -my $address = $borrower->{'streetnumber'}.' '.$roadttype_hashref->{$borrower->{'streettype'}}.' '.$borrower->{'address'}; +my $address = $borrower->{'streetnumber'}.' ' if ($borrower->{'streetnumber'}); +$address .= $roadttype_hashref->{$borrower->{'streettype'}}.' ' if ( $roadttype_hashref->{$borrower->{'streettype'}}); +$address .= $borrower->{'address'}; + + +$duedatespec = "" if not ($stickyduedate or scalar $confirm_required); +my @categories = C4::Category->all; my $fast_cataloging = 0; if (defined getframeworkinfo('FA')) { @@ -633,9 +784,9 @@ $template->param( printername => $printer, firstname => $borrower->{'firstname'}, surname => $borrower->{'surname'}, - dateexpiry => format_date($newexpiry), + printer => $printer, + printername => $printer, expiry => format_date($borrower->{'dateexpiry'}), - categorycode => $borrower->{'categorycode'}, categoryname => $borrower->{description}, address => $address, address2 => $borrower->{'address2'}, @@ -646,7 +797,6 @@ $template->param( zipcode => $borrower->{'zipcode'}, country => $borrower->{'country'}, phone => $borrower->{'phone'} || $borrower->{'mobile'}, - cardnumber => $borrower->{'cardnumber'}, amountold => $amountold, barcode => $barcode, stickyduedate => $stickyduedate, @@ -657,6 +807,9 @@ $template->param( totaldue => sprintf('%.2f', $total), todayissues => \@todaysissues, previssues => \@previousissues, + relissues => \@relissues, + relprevissues => \@relprevissues, + displayrelissues => $displayrelissues, inprocess => $inprocess, memberofinstution => $member_of_institution, CGIorganisations => $CGIorganisations, @@ -664,8 +817,13 @@ $template->param( circview => 1, soundon => C4::Context->preference("SoundOn"), fast_cataloging => $fast_cataloging, + categoryloop => \@categories, ); +$template->param(newexpiry => format_date($newexpiry)) if (defined $newexpiry); + +SetMemberInfosInTemplate($borrowernumber, $template); + # save stickyduedate to session if ($stickyduedate) { $session->param( 'stickyduedate', $duedatespec ); --- a/circ/returns.pl +++ a/circ/returns.pl @@ -36,16 +36,20 @@ use C4::Circulation; use C4::Dates qw/format_date/; use Date::Calc qw/Add_Delta_Days/; use C4::Calendar; +use C4::Budgets qw/GetCurrency/; use C4::Print; use C4::Reserves; use C4::Biblio; use C4::Items; use C4::Members; +use C4::Overdues qw/CheckBorrowerDebarred/; use C4::Branch; # GetBranches GetBranchName use C4::Koha; # FIXME : is it still useful ? use C4::RotatingCollections; +use C4::Debug; my $query = new CGI; +my $remotehost = $query->remote_host(); if (!C4::Context->userenv){ my $sessionID = $query->cookie("CGISESSID"); @@ -133,6 +137,7 @@ if ( $query->param('resbarcode') ) { my $item = $query->param('itemnumber'); my $borrowernumber = $query->param('borrowernumber'); my $resbarcode = $query->param('resbarcode'); + my $reservenumber = $query->param('reservenumber'); my $diffBranchReturned = $query->param('diffBranch'); my $iteminfo = GetBiblioFromItemNumber($item); # fix up item type for display @@ -140,18 +145,19 @@ if ( $query->param('resbarcode') ) { my $diffBranchSend = ($userenv_branch ne $diffBranchReturned) ? $diffBranchReturned : undef; # diffBranchSend tells ModReserveAffect whether document is expected in this library or not, # i.e., whether to apply waiting status - ModReserveAffect( $item, $borrowernumber, $diffBranchSend); + ModReserveAffect( $item, $borrowernumber, $diffBranchSend, $reservenumber ); + # check if we have other reserves for this document, if we have a return send the message of transfer - my ( $messages, $nextreservinfo ) = GetOtherReserves($item); + my ( $reservemessages, $nextreservinfo ) = GetOtherReserves($item); my ($borr) = GetMemberDetails( $nextreservinfo, 0 ); my $name = $borr->{'surname'} . ", " . $borr->{'title'} . " " . $borr->{'firstname'}; - if ( $messages->{'transfert'} ) { + if ( $reservemessages->{'transfert'} ) { $template->param( itemtitle => $iteminfo->{'title'}, itembiblionumber => $iteminfo->{'biblionumber'}, iteminfo => $iteminfo->{'author'}, - tobranchname => GetBranchName($messages->{'transfert'}), + tobranchname => GetBranchName($reservemessages->{'transfert'}), name => $name, borrowernumber => $borrowernumber, borcnum => $borr->{'cardnumber'}, @@ -167,10 +173,12 @@ my $returned = 0; my $messages; my $issueinformation; my $itemnumber; +my $biblio; my $barcode = $query->param('barcode'); my $exemptfine = $query->param('exemptfine'); my $dropboxmode = $query->param('dropboxmode'); my $dotransfer = $query->param('dotransfer'); +my $override = $query->param('override'); my $calendar = C4::Calendar->new( branchcode => $userenv_branch ); #dropbox: get last open day (today - 1) my $today = C4::Dates->new(); @@ -184,7 +192,7 @@ if ($dotransfer){ } # actually return book and prepare item table..... -if ($barcode) { +if ($barcode and not $query->param('cancel')) { $barcode =~ s/^\s*|\s*$//g; # remove leading/trailing whitespace $barcode = barcodedecode($barcode) if C4::Context->preference('itemBarcodeInputFilter'); $itemnumber = GetItemnumberFromBarcode($barcode); @@ -207,11 +215,19 @@ if ($barcode) { # save the return # ( $returned, $messages, $issueinformation, $borrower ) = - AddReturn( $barcode, $userenv_branch, $exemptfine, $dropboxmode); # do the return + AddReturn( $barcode, $userenv_branch, $exemptfine, $dropboxmode,$override); # do the return my $homeorholdingbranchreturn = C4::Context->preference('HomeOrHoldingBranchReturn') or 'homebranch'; + my @ips=split /,|\|/, C4::Context->preference("CI-3M:AuthorizedIPs"); + #my $pid=fork(); + #unless($pid && $remotehost=~qr(^$ips$)){ + #if (!$pid && any{ $remotehost eq $_ }@ips ){ + if (any{ $remotehost eq $_ }@ips ){ + system("../services/magnetise.pl $remotehost in"); + #die 0; + } # get biblio description - my $biblio = GetBiblioFromItemNumber($itemnumber); + $biblio = GetBiblioFromItemNumber($itemnumber); # fix up item type for display $biblio->{'itemtype'} = C4::Context->preference('item-level_itypes') ? $biblio->{'itype'} : $biblio->{'itemtype'}; @@ -249,7 +265,7 @@ if ($barcode) { } } - elsif ( !$messages->{'BadBarcode'} ) { + elsif ( !$messages->{'BadBarcode'} and ! $messages->{'Wrongbranch'} ) { $input{duedate} = 0; $returneditems{0} = $barcode; $riduedate{0} = 0; @@ -267,6 +283,18 @@ if ($barcode) { } $template->param( inputloop => \@inputloop ); + +# Is there a circulation note? +my $itemnumber = GetItemnumberFromBarcode($barcode); +my $biblionumber = GetBiblionumberFromItemnumber($itemnumber); +my $record = GetMarcBiblio($biblionumber); +my $frameworkcode = GetFrameworkCode($biblionumber); +my $circnotefield = GetRecordValue('circnote', $record, $frameworkcode); +if (defined @$circnotefield[0]) { + $template->param(circnote => @$circnotefield[0]->{'subfield'}); +} + + my $found = 0; my $waiting = 0; my $reserved = 0; @@ -290,9 +318,13 @@ if ( $messages->{'NeedsTransfer'} ){ } if ( $messages->{'Wrongbranch'} ){ - $template->param( - wrongbranch => 1, - ); + $template->param( + wrongbranch => $branches->{$messages->{'Wrongbranch'}->{'Wrongbranch'}}->{'branchname'}, + rightbranch => $branches->{$messages->{'Wrongbranch'}->{'Rightbranch'}}->{'branchname'}, + barcode => $barcode, + exemptfine => $exemptfine, + dropboxmode => $dropboxmode, + ); } # case of wrong transfert, if the document wasn't transfered to the right library (according to branchtransfer (tobranch) BDD) @@ -347,7 +379,7 @@ if ( $messages->{'ResFound'}) { reserved => 1, ); } - + my $debarred = CheckBorrowerDebarred($reserve->{borrowernumber}); # same params for Waiting or Reserved $template->param( found => 1, @@ -364,17 +396,18 @@ if ( $messages->{'ResFound'}) { borcity => $borr->{'city'}, borzip => $borr->{'zipcode'}, borcnum => $borr->{'cardnumber'}, - debarred => $borr->{'debarred'}, + debarred => $debarred, gonenoaddress => $borr->{'gonenoaddress'}, barcode => $barcode, - destbranch => $reserve->{'branchcode'}, + reservenumber => $reserve->{'reservenumber'}, + destbranch => $reserve->{'branchcode'}, borrowernumber => $reserve->{'borrowernumber'}, itemnumber => $reserve->{'itemnumber'}, reservenotes => $reserve->{'reservenotes'}, ); } # else { ; } # error? } - +$borrower->{'flags'}= C4::Members::patronflags($borrower); # Error Messages my @errmsgloop; foreach my $code ( keys %$messages ) { @@ -400,6 +433,35 @@ foreach my $code ( keys %$messages ) { elsif ( $code eq 'WasTransfered' ) { ; # FIXME... anything to do here? } + elsif ( $code eq 'NotForLoan' ) { + my $fw = GetFrameworkCode($biblio->{'biblionumber'}); + my $category = GetAuthValCode('items.notforloan',$fw); + my $authorizedvalues = GetAuthorisedValues($category, $messages->{$code}); + + foreach my $authvalue (@$authorizedvalues){ + $err{notforloan} = $authvalue->{lib} if $authvalue->{'authorised_value'} eq $messages->{$code}; + } + } + elsif ( $code eq 'Damaged' ) { + my $fw = GetFrameworkCode($biblio->{'biblionumber'}); + my $category = GetAuthValCode('items.damaged',$fw); + my $authorizedvalues = GetAuthorisedValues($category, $messages->{$code}); + + foreach my $authvalue (@$authorizedvalues){ + $err{damaged} = $authvalue->{lib} if $authvalue->{'authorised_value'} eq $messages->{$code}; + } + } + elsif( $code eq 'Debarred' ){ + $err{debarred} = format_date($messages->{'Debarred'}); + $err{debarcardnumber} = $borrower->{cardnumber}; + $err{debarborrowernumber} = $borrower->{borrowernumber}; + $err{debarname} = "$borrower->{firstname} $borrower->{surname}"; + } + elsif( $code eq 'HaveFines' ){ + $err{havefines} = $borrower->{'flags'}->{'CHARGES'}->{'amount'} . %{GetCurrency()}->{symbol}; + $err{finecardnumber} = $borrower->{cardnumber}; + $err{finename} = "$borrower->{firstname} $borrower->{surname}"; + } elsif ( $code eq 'wthdrawn' ) { $err{withdrawn} = 1; $exit_required_p = 1; @@ -421,7 +483,6 @@ foreach my $code ( keys %$messages ) { } elsif ( $code eq 'Wrongbranch' ) { } - else { die "Unknown error code $code"; # note we need all the (empty) elsif's above, or we die. # This forces the issue of staying in sync w/ Circulation.pm @@ -435,14 +496,10 @@ $template->param( errmsgloop => \@errmsgloop ); # patrontable .... if ($borrower) { - my $flags = $borrower->{'flags'}; my @flagloop; - my $flagset; + my $flags=$borrower->{flags}; foreach my $flag ( sort keys %$flags ) { my %flaginfo; - unless ($flagset) { $flagset = 1; } - $flaginfo{redfont} = ( $flags->{$flag}->{'noissues'} ); - $flaginfo{flag} = $flag; if ( $flag eq 'CHARGES' ) { $flaginfo{msg} = $flag; $flaginfo{charges} = 1; @@ -457,14 +514,29 @@ if ($borrower) { foreach my $item (@$items) { my $biblio = GetBiblioFromItemNumber( $item->{'itemnumber'}); push @waitingitemloop, { - biblionum => $biblio->{'biblionumber'}, - barcode => $biblio->{'barcode'}, - title => $biblio->{'title'}, - brname => $branches->{ $biblio->{'holdingbranch'} }->{'branchname'}, + biblionumber=> $biblio->{'biblionumber'}, + author => $biblio->{'author'}, + itemtype => $item->{'ccode'}, + reservedate => format_date($item->{'reservedate'}), + waitingdate => format_date($item->{'waitingdate'}), + barcode => $item->{'barcode'}, + maxpickupdate=> format_date( sprintf("%d-%02d-%02d",GetMaxPickupDate($item->{'waitingdate'}, $borrower->{borrowernumber},$item ))), + title => $biblio->{'title'}, + brname => $branches->{ $item->{'branchcode'} }->{'branchname'}, + waitinghere => ($item->{'branchcode'} eq $userenv_branch) }; } $flaginfo{itemloop} = \@waitingitemloop; } + elsif ( $flag =~ /DEBARRED|LOST|GNA/ ) { + if ($flag ne "GNA"){ + $flag=lc($flag); + } + else{ + $flag="gonenoaddress"; + } + %flaginfo=( $flag =>1, $flag."comment"=>$borrower->{$flag."comment"},dateend=>$flags->{uc($flag)}->{dateend}); + } elsif ( $flag eq 'ODUES' ) { my $items = $flags->{$flag}->{'itemlist'}; my @itemloop; @@ -490,7 +562,6 @@ if ($borrower) { push( @flagloop, \%flaginfo ); } $template->param( - flagset => $flagset, flagloop => \@flagloop, riborrowernumber => $borrower->{'borrowernumber'}, riborcnum => $borrower->{'cardnumber'}, @@ -501,7 +572,7 @@ if ($borrower) { } #set up so only the last 8 returned items display (make for faster loading pages) -my $returned_counter = ( C4::Context->preference('numReturnedItemsToShow') ) ? C4::Context->preference('numReturnedItemsToShow') : 8; +my $returned_counter = ( C4::Context->preference('numReturnedItemsToShow') ) ? C4::Context->preference('numReturnedItemsToShow') : 10; my $count = 0; my @riloop; foreach ( sort { $a <=> $b } keys %returneditems ) { @@ -528,19 +599,25 @@ foreach ( sort { $a <=> $b } keys %returneditems ) { else { $ri{borrowernumber} = $riborrowernumber{$_}; } - # my %ri; my $biblio = GetBiblioFromItemNumber(GetItemnumberFromBarcode($bar_code)); + my $item = GetItem(GetItemnumberFromBarcode($bar_code)); # fix up item type for display $biblio->{'itemtype'} = C4::Context->preference('item-level_itypes') ? $biblio->{'itype'} : $biblio->{'itemtype'}; $ri{itembiblionumber} = $biblio->{'biblionumber'}; $ri{itemtitle} = $biblio->{'title'}; $ri{itemauthor} = $biblio->{'author'}; - $ri{itemtype} = $biblio->{'itemtype'}; $ri{itemnote} = $biblio->{'itemnotes'}; - $ri{ccode} = $biblio->{'ccode'}; $ri{itemnumber} = $biblio->{'itemnumber'}; + $ri{itemtype} = $item->{'itype'}; + $ri{ccode} = $item->{'ccode'}; + $ri{itemcallnumber} = $item->{'itemcallnumber'}; + $ri{homebranch} = $item->{'homebranch'}; + $ri{holdingbranch} = $item->{'holdingbranch'}; $ri{barcode} = $bar_code; + my $shelflocations = GetKohaAuthorisedValues('items.location',''); + $ri{itemlocation} = $shelflocations->{$biblio->{'location'}}; + } else { last; --- a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/branch_transfer_limits.tmpl +++ a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/branch_transfer_limits.tmpl @@ -50,6 +50,7 @@ return false; }); + $('#selectlibrary input[type=submit]').hide(); }); @@ -70,13 +71,18 @@
- +

Check the boxes for the libraries you accept to checkin items from.

For all s: Check All | Uncheck All
@@ -126,11 +132,11 @@ - + - - row" name="_" checked="checked" /> + + row" name="_" checked="checked" /> @@ -148,6 +154,7 @@ +
--- a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/clone-rules.tmpl +++ a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/clone-rules.tmpl @@ -24,7 +24,8 @@
Cloning of issuing rules failed!
-

The rules have been cloned.

+

The rules have ">been cloned.

+ Return to Issuing rules --- a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/smart-rules.tmpl +++ a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/smart-rules.tmpl @@ -6,9 +6,145 @@ //tbody>tr>td.editable').click(function() { + if ( ! editor ) { + var td = $(this); + var tdvalue = td.text().trim(); + td.text(''); + + var type; + if ( $.inArray('boolean', td.attr('class').split(' ')) != -1 ) { // If the td is boolean, we display a checkbox + editor = $(''); + editor.attr('checked', tdvalue == 'Yes' ? 'checked' : ''); + type = 'boolean'; + } else if ( $.inArray('triple', td.attr('class').split(' ')) != -1 ) { // else if it is a triple, we display a listbox + var selectedn = tdvalue == '' ? 'selected="selected"' : ''; + var selected1 = tdvalue == 'Yes' ? 'selected="selected"' : ''; + var selected0 = tdvalue == 'No' ? 'selected="selected"' : ''; + var str = ''; + editor = $(str); + type = 'triple'; + } else { // else we display a textbox + editor = $(''); + editor.attr('value', tdvalue); + } + + editor.focus(); + + editor.keyup(function(e) { // on press + if ( e.keyCode == 13 ) { // enter + + // let's build the atomic rule + var branchcode = $('#branchcode').val(); + var categorycode = td.parent().children('td.categorycode').children('span').text(); + var itemtype = td.parent().children('td.itemtype').children('span').text(); + var varname = td.attr('class').split(' ')[0]; + var inputvalue; + switch ( type ) { + case 'boolean': + inputvalue = editor.attr('checked') ? 1 : 0; + break; + default: + inputvalue = editor.val(); + break; + } + + // post it to the server + $.ajax({ + url: '/cgi-bin/koha/admin/smart-rules-service.pl', + type: "POST", + async: false, + data: "branchcode="+branchcode+"&categorycode="+categorycode+"&itemtype="+itemtype+"&varname="+varname+"&inputvalue="+inputvalue, + success: function(result) { + + // repopulate the td with data + switch ( type ) { + case 'boolean': + td.text(result == '1' ? 'Yes' : 'No'); + break; + case 'triple': + switch ( result ) { + case '' : td.text(''); break; + case '1': td.text('Yes'); break; + case '0': td.text('No'); break; + } + break; + default: + td.text(result); + break; + } + + editor.remove(); + editor = false; + } + }); + } + }); + + td.append(editor); + } + }); }); //]]> @@ -32,25 +168,11 @@ $(document).ready(function() { Defining circulation and fine rules for all libraries -
-

The rules are applied from most specific to less specific, using the first found in this order:

- -

To modify a rule, create a new one with the same patron type and item type.

-
-
+
Select a library : - + @@ -60,272 +182,223 @@ $(document).ready(function() { for="tobranch">Clone these rules to: " />
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Patron CategoryItem TypeCurrent Checkouts AllowedLoan Period (day)Fine AmountFine Charging IntervalFine Grace period (day)Suspension in Days (day)Renewals Allowed (count)Holds Allowed (count)Rental Discount (%) 
- All - - - - - All - - - - - Unlimited - - - - - &categorycode=&branch=">Delete -
- - - - "/>
-
-
-
-

Default checkout and hold policy for all libraries

-

You can set a default maximum number of checkouts and hold policy that will be used if none is defined below for a particular item type or category.

-
- - "/> - - - - - - - - - - - - - - - -
 Total Current Checkouts AllowedHold Policy  
Defaults (not set)"/> - - - ">Unset -
-
-
- -
-

Checkout limit by patron category for all libraries

-

For this library, you can specify the maximum number of loans that - a patron of a given category can make, regardless of the item type. -

-

If the total amount loanable for a given patron category is left blank, - no limit applies, except possibly for a limit you define for a specific item type. -

-
- - "/> - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Patron CategoryTotal Current Checkouts Allowed 
- Default +
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - -
Patron categoryItem typeCurrent checkouts allowedLoan period (day)Fine amountFine charging intervalGrace period (day)Suspension duration (day)Renewals allowed (count)Renewals period (days)Holds allowed (count)Holds pickup delay (day)Allow on shelf holdsHolds restricted to libraryRental Discount (%) 
- Unlimited - - - - - &branch=">Delete -
- -
- -
- -
-

Holds policy by item type for all libraries

-

- For this library, you can edit rules for given itemtypes, regardless - of the patron's category. -

-

- Currently, this means hold policies. - The various policies have the following effects: -

-
    -
  • From Any Library: Patrons from any library may put this item on hold. (default if none is defined)
  • -
  • From Home Library: Only patrons from the item's home library may put this book on hold.
  • -
  • No Holds Allowed: No patron may put this book on hold.
  • -
-

- Note that if the system preference - AllowHoldPolicyOverride is enabled, these policies can - be overridden by your circulation staff. Also, these policies are - based on the patron's home library, not the library where the hold is being placed.. -

- -
- - "/> - - - - - - - - - - - - - - - - - - - - - - -
Item TypeHold Policy 
- Default - - - - - From Any Library - - From Home Library - - No Holds Allowed - - - &branch=">Delete -
- - - -
-
+
+ + + Default + + + + + + + Default + + + + + + + + + Unlimited + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Yes + + No + + + + + + + Yes + + No + + + + + + + + &categorycode=&branchcode=">Delete +
+ + + + "/>"/> "/>"/>"/> "/> "/>"/>"/>"/> + + "/>
+
+
--- a/koha-tmpl/intranet-tmpl/prog/en/modules/circ/circulation.tmpl +++ a/koha-tmpl/intranet-tmpl/prog/en/modules/circ/circulation.tmpl @@ -10,6 +10,12 @@ - - - - - - +

+ + + + + + + +

+ - - " /> " /> " /> @@ -491,9 +547,8 @@ No patron matched
-

- Cannot check out! - Attention:

+

Cannot Check Out! + Attention:

@@ -814,7 +947,7 @@ No patron matched ">, by Item - waiting at + waiting at until in transit from since --- a/koha-tmpl/intranet-tmpl/prog/en/modules/circ/returns.tmpl +++ a/koha-tmpl/intranet-tmpl/prog/en/modules/circ/returns.tmpl @@ -1,6 +1,9 @@ Koha › Circulation › Check In <!-- TMPL_VAR Name="title" escape="html" --> +