use strict;
-require Exporter;
+#use warnings; # soon!
use C4::Context;
use C4::Stats;
use C4::Reserves;
use C4::Dates;
use C4::Calendar;
use C4::Accounts;
+use C4::ItemCirculationAlertPreference;
+use C4::Message;
use Date::Calc qw(
Today
Today_and_Now
use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);
BEGIN {
- # set the version for version checking
- $VERSION = 3.01;
+ require Exporter;
+ $VERSION = 3.02; # for version checking
@ISA = qw(Exporter);
# FIXME subs that should probably be elsewhere
&AddRenewal
&GetRenewCount
&GetItemIssue
+ &GetOpenIssue
&GetItemIssues
&GetBorrowerIssues
&GetIssuingCharges
&GetIssuingRule
&GetBranchBorrowerCircRule
+ &GetBranchItemRule
&GetBiblioIssues
&AnonymiseIssueHistory
);
&GetTransfersFromTo
&updateWrongTransfer
&DeleteTransfer
+ &IsBranchTransferAllowed
+ &CreateBranchTransferLimit
+ &DeleteBranchTransferLimits
);
}
=head2 barcodedecode
-=head3 $str = &barcodedecode($barcode);
+=head3 $str = &barcodedecode($barcode, [$filter]);
=over 4
For proper functioning of this filter, calling the function on the
correct barcode string (items.barcode) should return an unaltered barcode.
+The optional $filter argument is to allow for testing or explicit
+behavior that ignores the System Pref. Valid values are the same as the
+System Pref options.
+
=back
=cut
# FIXME -- these plugins should be moved out of Circulation.pm
#
sub barcodedecode {
- my ($barcode) = @_;
- my $filter = C4::Context->preference('itemBarcodeInputFilter');
- if($filter eq 'whitespace') {
+ my ($barcode, $filter) = @_;
+ $filter = C4::Context->preference('itemBarcodeInputFilter') unless $filter;
+ $filter or return $barcode; # ensure filter is defined, else return untouched barcode
+ if ($filter eq 'whitespace') {
$barcode =~ s/\s//g;
- return $barcode;
- } elsif($filter eq 'cuecat') {
+ } elsif ($filter eq 'cuecat') {
chomp($barcode);
my @fields = split( /\./, $barcode );
my @results = map( decode($_), @fields[ 1 .. $#fields ] );
- if ( $#results == 2 ) {
- return $results[2];
- }
- else {
- return $barcode;
- }
- } elsif($filter eq 'T-prefix') {
- if ( $barcode =~ /^[Tt]/) {
- if (substr($barcode,1,1) eq '0') {
- return $barcode;
- } else {
- $barcode = substr($barcode,2) + 0 ;
- }
+ ($#results == 2) and return $results[2];
+ } elsif ($filter eq 'T-prefix') {
+ if ($barcode =~ /^[Tt](\d)/) {
+ (defined($1) and $1 eq '0') and return $barcode;
+ $barcode = substr($barcode, 2) + 0; # FIXME: probably should be substr($barcode, 1)
}
- return sprintf( "T%07d",$barcode);
+ return sprintf("T%07d", $barcode);
+ # FIXME: $barcode could be "T1", causing warning: substr outside of string
+ # Why drop the nonzero digit after the T?
+ # Why pass non-digits (or empty string) to "T%07d"?
}
+ return $barcode; # return barcode, modified or not
}
=head2 decode
=item Decodes a segment of a string emitted by a CueCat barcode scanner and
returns it.
+FIXME: Should be replaced with Barcode::Cuecat from CPAN
+or Javascript based decoding on the client side.
+
=back
=cut
my $l = ( $#s + 1 ) % 4;
if ($l) {
if ( $l == 1 ) {
- warn "Error!";
+ # warn "Error: Cuecat decode parsing failed!";
return;
}
$l = 4 - $l;
my $hbr = $biblio->{'homebranch'};
my $fbr = $biblio->{'holdingbranch'};
+ # if using Branch Transfer Limits
+ if ( C4::Context->preference("UseBranchTransferLimits") == 1 ) {
+ if ( C4::Context->preference("item-level_itypes") && C4::Context->preference("BranchTransferLimitsType") eq 'itemtype' ) {
+ if ( ! IsBranchTransferAllowed( $tbr, $fbr, $biblio->{'itype'} ) ) {
+ $messages->{'NotAllowed'} = $tbr . "::" . $biblio->{'itype'};
+ $dotransfer = 0;
+ }
+ } elsif ( ! IsBranchTransferAllowed( $tbr, $fbr, $biblio->{ C4::Context->preference("BranchTransferLimitsType") } ) ) {
+ $messages->{'NotAllowed'} = $tbr . "::" . $biblio->{ C4::Context->preference("BranchTransferLimitsType") };
+ $dotransfer = 0;
+ }
+ }
+
# if is permanent...
if ( $hbr && $branches->{$hbr}->{'PE'} ) {
$messages->{'IsPermanent'} = $hbr;
+ $dotransfer = 0;
}
# can't transfer book if is already there....
return ( $dotransfer, $messages, $biblio );
}
-=head2 CanBookBeIssued
-
-Check if a book can be issued.
-
-my ($issuingimpossible,$needsconfirmation) = CanBookBeIssued($borrower,$barcode,$year,$month,$day);
-
-=over 4
-
-=item C<$borrower> hash with borrower informations (from GetMemberDetails)
-
-=item C<$barcode> is the bar code of the book being issued.
-
-=item C<$year> C<$month> C<$day> contains the date of the return (in case it's forced by "stickyduedate".
-
-=back
-
-Returns :
-
-=over 4
-
-=item C<$issuingimpossible> a reference to a hash. It contains reasons why issuing is impossible.
-Possible values are :
-
-=back
-
-=head3 INVALID_DATE
-
-sticky due date is invalid
-
-=head3 GNA
-
-borrower gone with no address
-
-=head3 CARD_LOST
-
-borrower declared it's card lost
-
-=head3 DEBARRED
-
-borrower debarred
-
-=head3 UNKNOWN_BARCODE
-
-barcode unknown
-
-=head3 NOT_FOR_LOAN
-
-item is not for loan
-
-=head3 WTHDRAWN
-
-item withdrawn.
-
-=head3 RESTRICTED
-
-item is restricted (set by ??)
-
-C<$issuingimpossible> a reference to a hash. It contains reasons why issuing is impossible.
-Possible values are :
-
-=head3 DEBT
-
-borrower has debts.
-
-=head3 RENEW_ISSUE
-
-renewing, not issuing
-
-=head3 ISSUED_TO_ANOTHER
-
-issued to someone else.
-
-=head3 RESERVED
-
-reserved for someone else.
-
-=head3 INVALID_DATE
-
-sticky due date is invalid
-
-=head3 TOO_MANY
-
-if the borrower borrows to much things
-
-=cut
-
-# check if a book can be issued.
-
sub TooMany {
my $borrower = shift;
=head2 CanBookBeIssued
-( $issuingimpossible, $needsconfirmation ) =
- CanBookBeIssued( $borrower, $barcode, $duedatespec, $inprocess );
-C<$duedatespec> is a C4::Dates object.
+Check if a book can be issued.
+
+( $issuingimpossible, $needsconfirmation ) = CanBookBeIssued( $borrower, $barcode, $duedatespec, $inprocess );
+
C<$issuingimpossible> and C<$needsconfirmation> are some hashref.
+=over 4
+
+=item C<$borrower> hash with borrower informations (from GetMemberDetails)
+
+=item C<$barcode> is the bar code of the book being issued.
+
+=item C<$duedatespec> is a C4::Dates object.
+
+=item C<$inprocess>
+
+=back
+
+Returns :
+
+=over 4
+
+=item C<$issuingimpossible> a reference to a hash. It contains reasons why issuing is impossible.
+Possible values are :
+
+=back
+
+=head3 INVALID_DATE
+
+sticky due date is invalid
+
+=head3 GNA
+
+borrower gone with no address
+
+=head3 CARD_LOST
+
+borrower declared it's card lost
+
+=head3 DEBARRED
+
+borrower debarred
+
+=head3 UNKNOWN_BARCODE
+
+barcode unknown
+
+=head3 NOT_FOR_LOAN
+
+item is not for loan
+
+=head3 WTHDRAWN
+
+item withdrawn.
+
+=head3 RESTRICTED
+
+item is restricted (set by ??)
+
+C<$issuingimpossible> a reference to a hash. It contains reasons why issuing is impossible.
+Possible values are :
+
+=head3 DEBT
+
+borrower has debts.
+
+=head3 RENEW_ISSUE
+
+renewing, not issuing
+
+=head3 ISSUED_TO_ANOTHER
+
+issued to someone else.
+
+=head3 RESERVED
+
+reserved for someone else.
+
+=head3 INVALID_DATE
+
+sticky due date is invalid
+
+=head3 TOO_MANY
+
+if the borrower borrows to much things
+
=cut
sub CanBookBeIssued {
$item->{'itemtype'}=$item->{'itype'};
my $dbh = C4::Context->dbh;
+ # MANDATORY CHECKS - unless item exists, nothing else matters
+ unless ( $item->{barcode} ) {
+ $issuingimpossible{UNKNOWN_BARCODE} = 1;
+ }
+ return ( \%issuingimpossible, \%needsconfirmation ) if %issuingimpossible;
+
#
# DUE DATE is OK ? -- should already have checked.
#
- #$issuingimpossible{INVALID_DATE} = 1 unless ($duedate);
+ unless ( $duedate ) {
+ my $issuedate = strftime( "%Y-%m-%d", localtime );
+ my $branch = (C4::Context->preference('CircControl') eq 'PickupLibrary') ? C4::Context->userenv->{'branch'} :
+ (C4::Context->preference('CircControl') eq 'PatronLibrary') ? $borrower->{'branchcode'} :
+ $item->{'homebranch'}; # fallback to item's homebranch
+ 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 );
+
+ # Offline circ calls AddIssue directly, doesn't run through here
+ # So issuingimpossible should be ok.
+ }
+ $issuingimpossible{INVALID_DATE} = $duedate->output('syspref') unless ( $duedate && $duedate->output('iso') ge C4::Dates->today('iso') );
#
# BORROWER STATUS
if ( $amount > $amountlimit && !$inprocess ) {
$issuingimpossible{DEBT} = sprintf( "%.2f", $amount );
}
- elsif ( $amount <= $amountlimit && !$inprocess ) {
+ elsif ( $amount > 0 && $amount <= $amountlimit && !$inprocess ) {
$needsconfirmation{DEBT} = sprintf( "%.2f", $amount );
}
}
else {
if ( $amount > 0 ) {
- $needsconfirmation{DEBT} = $amount;
+ $needsconfirmation{DEBT} = sprintf( "%.2f", $amount );
}
}
# JB34 CHECKS IF BORROWERS DONT HAVE ISSUE TOO MANY BOOKS
#
my $toomany = TooMany( $borrower, $item->{biblionumber}, $item );
- $needsconfirmation{TOO_MANY} = $toomany if $toomany;
+ # if TooMany return / 0, then the user has no permission to check out this book
+ if ($toomany =~ /\/ 0/) {
+ $needsconfirmation{PATRON_CANT} = 1;
+ } else {
+ $needsconfirmation{TOO_MANY} = $toomany if $toomany;
+ }
#
# ITEM CHECKING
#
- unless ( $item->{barcode} ) {
- $issuingimpossible{UNKNOWN_BARCODE} = 1;
- }
if ( $item->{'notforloan'}
&& $item->{'notforloan'} > 0 )
{
- $issuingimpossible{NOT_FOR_LOAN} = 1;
+ if(!C4::Context->preference("AllowNotForLoanOverride")){
+ $issuingimpossible{NOT_FOR_LOAN} = 1;
+ }else{
+ $needsconfirmation{NOT_FOR_LOAN_FORCING} = 1;
+ }
+ }
+ elsif ( !$item->{'notforloan'} ){
+ # we have to check itemtypes.notforloan also
+ if (C4::Context->preference('item-level_itypes')){
+ # this should probably be a subroutine
+ my $sth = $dbh->prepare("SELECT notforloan FROM itemtypes WHERE itemtype = ?");
+ $sth->execute($item->{'itemtype'});
+ my $notforloan=$sth->fetchrow_hashref();
+ $sth->finish();
+ if ($notforloan->{'notforloan'}) {
+ if (!C4::Context->preference("AllowNotForLoanOverride")) {
+ $issuingimpossible{NOT_FOR_LOAN} = 1;
+ } else {
+ $needsconfirmation{NOT_FOR_LOAN_FORCING} = 1;
+ }
+ }
+ }
+ elsif ($biblioitem->{'notforloan'} == 1){
+ if (!C4::Context->preference("AllowNotForLoanOverride")) {
+ $issuingimpossible{NOT_FOR_LOAN} = 1;
+ } else {
+ $needsconfirmation{NOT_FOR_LOAN_FORCING} = 1;
+ }
+ }
}
- elsif ( !$item->{'notforloan'} ){
- # we have to check itemtypes.notforloan also
- if (C4::Context->preference('item-level_itypes')){
- # this should probably be a subroutine
- my $sth = $dbh->prepare("SELECT notforloan FROM itemtypes WHERE itemtype = ?");
- $sth->execute($item->{'itemtype'});
- my $notforloan=$sth->fetchrow_hashref();
- $sth->finish();
- if ($notforloan->{'notforloan'} == 1){
- $issuingimpossible{NOT_FOR_LOAN} = 1;
- }
- }
- elsif ($biblioitem->{'notforloan'} == 1){
- $issuingimpossible{NOT_FOR_LOAN} = 1;
- }
- }
if ( $item->{'wthdrawn'} && $item->{'wthdrawn'} == 1 )
{
$issuingimpossible{WTHDRAWN} = 1;
}
if ( C4::Context->preference("IndependantBranches") ) {
my $userenv = C4::Context->userenv;
- if ( ($userenv) && ( $userenv->{flags} != 1 ) ) {
+ if ( ($userenv) && ( $userenv->{flags} % 2 != 1 ) ) {
$issuingimpossible{NOTSAMEBRANCH} = 1
if ( $item->{C4::Context->preference("HomeOrHoldingBranch")} ne $userenv->{branch} );
}
elsif ($issue->{borrowernumber}) {
# issued to someone else
- my $currborinfo = GetMemberDetails( $issue->{borrowernumber} );
+ my $currborinfo = C4::Members::GetMemberDetails( $issue->{borrowernumber} );
# warn "=>.$currborinfo->{'firstname'} $currborinfo->{'surname'} ($currborinfo->{'cardnumber'})";
$needsconfirmation{ISSUED_TO_ANOTHER} =
my ( $restype, $res ) = C4::Reserves::CheckReserves( $item->{'itemnumber'} );
if ($restype) {
my $resbor = $res->{'borrowernumber'};
- my ( $resborrower, $flags ) = GetMemberDetails( $resbor, 0 );
+ my ( $resborrower ) = C4::Members::GetMemberDetails( $resbor, 0 );
my $branches = GetBranches();
my $branchname = $branches->{ $res->{'branchcode'} }->{'branchname'};
if ( $resbor ne $borrower->{'borrowernumber'} && $restype eq "Waiting" )
"$res->{'reservedate'} : $resborrower->{'firstname'} $resborrower->{'surname'} ($resborrower->{'cardnumber'})";
}
}
- if ( C4::Context->preference("LibraryName") eq "Horowhenua Library Trust" ) {
- if ( $borrower->{'categorycode'} eq 'W' ) {
- my %emptyhash;
- return ( \%emptyhash, \%needsconfirmation );
- }
- }
return ( \%issuingimpossible, \%needsconfirmation );
}
Issue a book. Does no check, they are done in CanBookBeIssued. If we reach this sub, it means the user confirmed if needed.
-&AddIssue($borrower,$barcode,$date)
+&AddIssue($borrower, $barcode, [$datedue], [$cancelreserve], [$issuedate])
=over 4
-=item C<$borrower> hash with borrower informations (from GetMemberDetails)
+=item C<$borrower> is a hash with borrower informations (from GetMemberDetails).
-=item C<$barcode> is the bar code of the book being issued.
+=item C<$barcode> is the barcode of the item being issued.
+
+=item C<$datedue> is a C4::Dates object for the max date of return, i.e. the date due (optional).
+Calculated if empty.
+
+=item C<$cancelreserve> is 1 to override and cancel any pending reserves for the item (optional).
-=item C<$date> contains the max date of return. calculated if empty.
+=item C<$issuedate> is the date to issue the item in iso (YYYY-MM-DD) format (optional).
+Defaults to today. Unlike C<$datedue>, NOT a C4::Dates object, unfortunately.
AddIssue does the following things :
-- step 01: check that there is a borrowernumber & a barcode provided
-- check for RENEWAL (book issued & being issued to the same patron)
- - renewal YES = Calculate Charge & renew
- - renewal NO =
- * BOOK ACTUALLY ISSUED ? do a return if book is actually issued (but to someone else)
- * RESERVE PLACED ?
- - fill reserve if reserve to this patron
- - cancel reserve or not, otherwise
- * TRANSFERT PENDING ?
- - complete the transfert
- * ISSUE THE BOOK
+
+ - step 01: check that there is a borrowernumber & a barcode provided
+ - check for RENEWAL (book issued & being issued to the same patron)
+ - renewal YES = Calculate Charge & renew
+ - renewal NO =
+ * BOOK ACTUALLY ISSUED ? do a return if book is actually issued (but to someone else)
+ * RESERVE PLACED ?
+ - fill reserve if reserve to this patron
+ - cancel reserve or not, otherwise
+ * TRANSFERT PENDING ?
+ - complete the transfert
+ * ISSUE THE BOOK
=back
=cut
sub AddIssue {
- my ( $borrower, $barcode, $date, $cancelreserve ) = @_;
+ my ( $borrower, $barcode, $datedue, $cancelreserve, $issuedate, $sipmode) = @_;
my $dbh = C4::Context->dbh;
my $barcodecheck=CheckValidBarcode($barcode);
+
+ # $issuedate defaults to today.
+ if ( ! defined $issuedate ) {
+ $issuedate = strftime( "%Y-%m-%d", localtime );
+ # TODO: for hourly circ, this will need to be a C4::Dates object
+ # and all calls to AddIssue including issuedate will need to pass a Dates object.
+ }
if ($borrower and $barcode and $barcodecheck ne '0'){
# find which item we issue
- my $item = GetItem('', $barcode);
- my $datedue;
-
- my $branch;
- # Get which branchcode we need
- if (C4::Context->preference('CircControl') eq 'PickupLibrary'){
- $branch = C4::Context->userenv->{'branch'};
- }
- elsif (C4::Context->preference('CircControl') eq 'PatronLibrary'){
- $branch = $borrower->{'branchcode'};
- }
- else {
- # items home library
- $branch = $item->{'homebranch'};
- }
+ my $item = GetItem('', $barcode) or return undef; # if we don't get an Item, abort.
+ my $branch = (C4::Context->preference('CircControl') eq 'PickupLibrary') ? C4::Context->userenv->{'branch'} :
+ (C4::Context->preference('CircControl') eq 'PatronLibrary') ? $borrower->{'branchcode'} :
+ $item->{'homebranch'}; # fallback to item's homebranch
# get actual issuing if there is one
my $actualissue = GetItemIssue( $item->{itemnumber});
#
# check if we just renew the issue.
#
- if ( $actualissue->{borrowernumber} eq $borrower->{'borrowernumber'} ) {
- AddRenewal(
+ if ($actualissue->{borrowernumber} eq $borrower->{'borrowernumber'}) {
+ $datedue = AddRenewal(
$borrower->{'borrowernumber'},
$item->{'itemnumber'},
$branch,
- $date
+ $datedue,
+ $issuedate, # here interpreted as the renewal date
);
-
}
else {
# it's NOT a renewal
if ($restype) {
my $resbor = $res->{'borrowernumber'};
if ( $resbor eq $borrower->{'borrowernumber'} ) {
-
# The item is reserved by the current patron
ModReserveFill($res);
}
elsif ( $restype eq "Waiting" ) {
-
# warn "Waiting";
# The item is on reserve and waiting, but has been
# reserved by some other patron.
- my ( $resborrower, $flags ) = GetMemberDetails( $resbor, 0 );
- my $branches = GetBranches();
- my $branchname =
- $branches->{ $res->{'branchcode'} }->{'branchname'};
}
elsif ( $restype eq "Reserved" ) {
-
# warn "Reserved";
# The item is reserved by someone else.
- my ( $resborrower, $flags ) =
- GetMemberDetails( $resbor, 0 );
- my $branches = GetBranches();
- my $branchname = $branches->{ $res->{'branchcode'} }->{'branchname'};
if ($cancelreserve) { # cancel reserves on this item
- CancelReserve( 0, $res->{'itemnumber'},
- $res->{'borrowernumber'} );
+ CancelReserve(0, $res->{'itemnumber'}, $res->{'borrowernumber'});
}
}
if ($cancelreserve) {
- CancelReserve( $res->{'biblionumber'}, 0,
- $res->{'borrowernumber'} );
+ CancelReserve($res->{'biblionumber'}, 0, $res->{'borrowernumber'});
}
else {
# set waiting reserve to first in reserve queue as book isn't waiting now
# Starting process for transfer job (checking transfert and validate it if we have one)
my ($datesent) = GetTransfers($item->{'itemnumber'});
if ($datesent) {
- # updating line of branchtranfert to finish it, and changing the to branch value, implement a comment for lisibility of this case (maybe for stats ....)
- my $sth =
+ # updating line of branchtranfert to finish it, and changing the to branch value, implement a comment for visibility of this case (maybe for stats ....)
+ my $sth =
$dbh->prepare(
"UPDATE branchtransfers
SET datearrived = now(),
comments = 'Forced branchtransfer'
WHERE itemnumber= ? AND datearrived IS NULL"
);
- $sth->execute(C4::Context->userenv->{'branch'},$item->{'itemnumber'});
- $sth->finish;
+ $sth->execute(C4::Context->userenv->{'branch'},$item->{'itemnumber'});
}
# Record in the database the fact that the book was issued.
(borrowernumber, itemnumber,issuedate, date_due, branchcode)
VALUES (?,?,?,?,?)"
);
- my $dateduef;
- if ($date) {
- $dateduef = $date;
- } else {
- my $itype=(C4::Context->preference('item-level_itypes')) ? $biblio->{'itype'} : $biblio->{'itemtype'} ;
- my $loanlength = GetLoanLength(
- $borrower->{'categorycode'},
- $itype,
- $branch
- );
- $dateduef = CalcDateDue(C4::Dates->new(),$loanlength,$branch);
- # if ReturnBeforeExpiry ON the datedue can't be after borrower expirydate
- if ( C4::Context->preference('ReturnBeforeExpiry') && $dateduef->output('iso') gt $borrower->{dateexpiry} ) {
- $dateduef = C4::Dates->new($borrower->{dateexpiry},'iso');
- }
- };
- $sth->execute(
- $borrower->{'borrowernumber'},
- $item->{'itemnumber'},
- strftime( "%Y-%m-%d", localtime ),$dateduef->output('iso'), C4::Context->userenv->{'branch'}
+ unless ($datedue) {
+ my $itype = ( C4::Context->preference('item-level_itypes') ) ? $biblio->{'itype'} : $biblio->{'itemtype'};
+ my $loanlength = GetLoanLength( $borrower->{'categorycode'}, $itype, $branch );
+ $datedue = CalcDateDue( C4::Dates->new( $issuedate, 'iso' ), $loanlength, $branch, $borrower );
+
+ }
+ $sth->execute(
+ $borrower->{'borrowernumber'}, # borrowernumber
+ $item->{'itemnumber'}, # itemnumber
+ $issuedate, # issuedate
+ $datedue->output('iso'), # date_due
+ C4::Context->userenv->{'branch'} # branchcode
);
$sth->finish;
$item->{'issues'}++;
holdingbranch => C4::Context->userenv->{'branch'},
itemlost => 0,
datelastborrowed => C4::Dates->new()->output('iso'),
- onloan => $dateduef->output('iso'),
+ onloan => $datedue->output('iso'),
}, $item->{'biblionumber'}, $item->{'itemnumber'});
ModDateLastSeen( $item->{'itemnumber'} );
-
+
# If it costs to borrow this book, charge it to the patron's account.
my ( $charge, $itemtype ) = GetIssuingCharges(
$item->{'itemnumber'},
# Record the fact that this book was issued.
&UpdateStats(
C4::Context->userenv->{'branch'},
- 'issue', $charge,
- '', $item->{'itemnumber'},
+ 'issue', $charge,
+ ($sipmode ? "SIP-$sipmode" : ''), $item->{'itemnumber'},
$item->{'itype'}, $borrower->{'borrowernumber'}
);
+
+ # Send a checkout slip.
+ my $circulation_alert = 'C4::ItemCirculationAlertPreference';
+ my %conditions = (
+ branchcode => $branch,
+ categorycode => $borrower->{categorycode},
+ item_type => $item->{itype},
+ notification => 'CHECKOUT',
+ );
+ if ($circulation_alert->is_enabled_for(\%conditions)) {
+ SendCirculationAlert({
+ type => 'CHECKOUT',
+ item => $item,
+ borrower => $borrower,
+ branch => $branch,
+ });
+ }
}
-
+
logaction("CIRCULATION", "ISSUE", $borrower->{'borrowernumber'}, $biblio->{'biblionumber'})
if C4::Context->preference("IssueLog");
- return ($datedue);
}
+ return ($datedue); # not necessarily the same as when it came in!
}
=head2 GetLoanLength
};
}
+=head2 GetBranchItemRule
+
+=over 4
+
+my $branch_item_rule = GetBranchItemRule($branchcode, $itemtype);
+
+=back
+
+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,
+ };
+}
+
=head2 AddReturn
($doreturn, $messages, $iteminformation, $borrower) =
Returns a book.
-C<$barcode> is the bar code of the book being returned. C<$branch> is
-the code of the branch where the book is being returned. C<$exemptfine>
-indicates that overdue charges for the item will be removed. C<$dropbox>
-indicates that the check-in date is assumed to be yesterday, or the last
-non-holiday as defined in C4::Calendar . If overdue
-charges are applied and C<$dropbox> is true, the last charge will be removed.
-This assumes that the fines accrual script has run for _today_.
+=over 4
+
+=item C<$barcode> is the bar code of the book being returned.
+
+=item C<$branch> is the code of the branch where the book is being returned.
+
+=item C<$exemptfine> indicates that overdue charges for the item will be
+removed.
+
+=item C<$dropbox> indicates that the check-in date is assumed to be
+yesterday, or the last non-holiday as defined in C4::Calendar . If
+overdue charges are applied and C<$dropbox> is true, the last charge
+will be removed. This assumes that the fines accrual script has run
+for _today_.
+
+=back
C<&AddReturn> returns a list of four items:
my $curr_iteminfo = GetItem($iteminformation->{'itemnumber'});
$iteminformation->{'homebranch'} = $curr_iteminfo->{'homebranch'};
$iteminformation->{'holdingbranch'} = $curr_iteminfo->{'holdingbranch'};
+ $iteminformation->{'itemlost'} = $curr_iteminfo->{'itemlost'};
$doreturn = 0;
}
}
MarkIssueReturned($borrower->{'borrowernumber'}, $iteminformation->{'itemnumber'},$circControlBranch);
$messages->{'WasReturned'} = 1; # FIXME is the "= 1" right?
- }
-
- # continue to deal with returns cases, but not only if we have an issue
+
- # the holdingbranch is updated if the document is returned in an other location .
- if ( $iteminformation->{'holdingbranch'} ne C4::Context->userenv->{'branch'} ) {
- UpdateHoldingbranch(C4::Context->userenv->{'branch'},$iteminformation->{'itemnumber'});
- # reload iteminformation holdingbranch with the userenv value
- $iteminformation->{'holdingbranch'} = C4::Context->userenv->{'branch'};
+ # continue to deal with returns cases, but not only if we have an issue
+
+ # the holdingbranch is updated if the document is returned in an other location .
+ if ( $iteminformation->{'holdingbranch'} ne C4::Context->userenv->{'branch'} ) {
+ UpdateHoldingbranch(C4::Context->userenv->{'branch'},$iteminformation->{'itemnumber'});
+ # reload iteminformation holdingbranch with the userenv value
+ $iteminformation->{'holdingbranch'} = C4::Context->userenv->{'branch'};
+ }
+ ModDateLastSeen( $iteminformation->{'itemnumber'} );
+ ModItem({ onloan => undef }, $biblio->{'biblionumber'}, $iteminformation->{'itemnumber'});
+
+ if ($iteminformation->{borrowernumber}){
+ ($borrower) = C4::Members::GetMemberDetails( $iteminformation->{borrowernumber}, 0 );
+ }
}
- ModDateLastSeen( $iteminformation->{'itemnumber'} );
- ModItem({ onloan => undef }, $biblio->{'biblionumber'}, $iteminformation->{'itemnumber'});
-
- if ($iteminformation->{borrowernumber}){
- ($borrower) = C4::Members::GetMemberDetails( $iteminformation->{borrowernumber}, 0 );
- }
# fix up the accounts.....
if ( $iteminformation->{'itemlost'} ) {
$messages->{'WasLost'} = 1;
$biblio->{'itemtype'},
$borrower->{'borrowernumber'}
);
+
+ # Send a check-in slip.
+ my $circulation_alert = 'C4::ItemCirculationAlertPreference';
+ my %conditions = (
+ branchcode => $branch,
+ categorycode => $borrower->{categorycode},
+ item_type => $iteminformation->{itype},
+ notification => 'CHECKIN',
+ );
+ if ($doreturn && $circulation_alert->is_enabled_for(\%conditions)) {
+ SendCirculationAlert({
+ type => 'CHECKIN',
+ item => $iteminformation,
+ borrower => $borrower,
+ branch => $branch,
+ });
+ }
logaction("CIRCULATION", "RETURN", $iteminformation->{borrowernumber}, $iteminformation->{'biblionumber'})
if C4::Context->preference("ReturnLog");
#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 ( ($iteminformation->{'holdingbranch'} ne $iteminformation->{'homebranch'}) and not $messages->{'WrongTransfer'} and ($validTransfert ne 1) and ($reserveDone ne 1) ){
+ if ($doreturn and ($branch ne $iteminformation->{'homebranch'}) and not $messages->{'WrongTransfer'} and ($validTransfert ne 1) and ($reserveDone ne 1) ){
if (C4::Context->preference("AutomaticItemReturn") == 1) {
ModItemTransfer($iteminformation->{'itemnumber'}, C4::Context->userenv->{'branch'}, $iteminformation->{'homebranch'});
$messages->{'WasTransfered'} = 1;
+ } elsif ( C4::Context->preference("UseBranchTransferLimits") == 1
+ && ! IsBranchTransferAllowed( $branch, $iteminformation->{'homebranch'}, $iteminformation->{ C4::Context->preference("BranchTransferLimitsType") } )
+ ) {
+ ModItemTransfer($iteminformation->{'itemnumber'}, C4::Context->userenv->{'branch'}, $iteminformation->{'homebranch'});
+ $messages->{'WasTransfered'} = 1;
}
else {
$messages->{'NeedsTransfer'} = 1;
=over 4
-MarkIssueReturned($borrowernumber, $itemnumber, $dropbox_branch);
+MarkIssueReturned($borrowernumber, $itemnumber, $dropbox_branch, $returndate);
=back
moving the C<issues> row to C<old_issues> and
setting C<returndate> to the current date, or
the last non-holiday date of the branccode specified in
-C<dropbox> . Assumes you've already checked that
+C<dropbox_branch> . Assumes you've already checked that
it's safe to do this, i.e. last non-holiday > issuedate.
+if C<$returndate> is specified (in iso format), it is used as the date
+of the return. It is ignored when a dropbox_branch is passed in.
+
Ideally, this function would be internal to C<C4::Circulation>,
not exported, but it is currently needed by one
routine in C<C4::Accounts>.
=cut
sub MarkIssueReturned {
- my ($borrowernumber, $itemnumber, $dropbox_branch ) = @_;
- my $dbh = C4::Context->dbh;
- my $query = "UPDATE issues SET returndate=";
- my @bind = ($borrowernumber,$itemnumber);
- if($dropbox_branch) {
- my $calendar = C4::Calendar->new( branchcode => $dropbox_branch );
- my $dropboxdate = $calendar->addDate(C4::Dates->new(), -1 );
- unshift @bind, $dropboxdate->output('iso') ;
- $query .= " ? "
- } else {
- $query .= " now() ";
- }
- $query .= " WHERE borrowernumber = ? AND itemnumber = ?";
+ my ( $borrowernumber, $itemnumber, $dropbox_branch, $returndate ) = @_;
+ my $dbh = C4::Context->dbh;
+ my $query = "UPDATE issues SET returndate=";
+ my @bind;
+ if ($dropbox_branch) {
+ my $calendar = C4::Calendar->new( branchcode => $dropbox_branch );
+ my $dropboxdate = $calendar->addDate( C4::Dates->new(), -1 );
+ $query .= " ? ";
+ push @bind, $dropboxdate->output('iso');
+ } elsif ($returndate) {
+ $query .= " ? ";
+ push @bind, $returndate;
+ } else {
+ $query .= " now() ";
+ }
+ $query .= " WHERE borrowernumber = ? AND itemnumber = ?";
+ push @bind, $borrowernumber, $itemnumber;
# FIXME transaction
my $sth_upd = $dbh->prepare($query);
$sth_upd->execute(@bind);
Returns an array of hashes
+FIXME: Though the above says that this function returns nothing if the
+item is not issued, this actually returns a hasref that looks like
+this:
+ {
+ itemnumber => 1,
+ overdue => 1
+ }
+
+
=cut
sub GetItemIssue {
return ($data);
}
+=head2 GetOpenIssue
+
+$issue = GetOpenIssue( $itemnumber );
+
+Returns the row from the issues table if the item is currently issued, undef if the item is not currently issued
+
+C<$itemnumber> is the item's itemnumber
+
+Returns a hashref
+
+=cut
+
+sub GetOpenIssue {
+ my ( $itemnumber ) = @_;
+
+ my $dbh = C4::Context->dbh;
+ my $sth = $dbh->prepare( "SELECT * FROM issues WHERE itemnumber = ? AND returndate IS NULL" );
+ $sth->execute( $itemnumber );
+ my $issue = $sth->fetchrow_hashref();
+ return $issue;
+}
+
=head2 GetItemIssues
$issues = &GetItemIssues($itemnumber, $history);
LEFT JOIN borrowers ON borrowers.borrowernumber = issues.borrowernumber
LEFT JOIN items ON issues.itemnumber = items.itemnumber
LEFT JOIN biblioitems ON items.itemnumber = biblioitems.biblioitemnumber
- LEFT JOIN biblio ON biblio.biblionumber = items.biblioitemnumber
+ LEFT JOIN biblio ON biblio.biblionumber = items.biblionumber
WHERE biblio.biblionumber = ?
UNION ALL
SELECT old_issues.*,items.barcode,biblio.biblionumber,biblio.title, biblio.author,borrowers.cardnumber,borrowers.surname,borrowers.firstname
LEFT JOIN borrowers ON borrowers.borrowernumber = old_issues.borrowernumber
LEFT JOIN items ON old_issues.itemnumber = items.itemnumber
LEFT JOIN biblioitems ON items.itemnumber = biblioitems.biblioitemnumber
- LEFT JOIN biblio ON biblio.biblionumber = items.biblioitemnumber
+ LEFT JOIN biblio ON biblio.biblionumber = items.biblionumber
WHERE biblio.biblionumber = ?
ORDER BY timestamp
";
return \@issues;
}
+=head2 GetUpcomingDueIssues
+
+=over 4
+
+my $upcoming_dues = GetUpcomingDueIssues( { days_in_advance => 4 } );
+
+=back
+
+=cut
+
+sub GetUpcomingDueIssues {
+ my $params = shift;
+
+ $params->{'days_in_advance'} = 7 unless exists $params->{'days_in_advance'};
+ my $dbh = C4::Context->dbh;
+
+ my $statement = <<END_SQL;
+SELECT issues.*, items.itype as itemtype, items.homebranch, TO_DAYS( date_due )-TO_DAYS( NOW() ) as days_until_due
+FROM issues
+LEFT JOIN items USING (itemnumber)
+WhERE returndate is NULL
+AND ( TO_DAYS( NOW() )-TO_DAYS( date_due ) ) < ?
+END_SQL
+
+ my @bind_parameters = ( $params->{'days_in_advance'} );
+
+ my $sth = $dbh->prepare( $statement );
+ $sth->execute( @bind_parameters );
+ my $upcoming_dues = $sth->fetchall_arrayref({});
+ $sth->finish;
+
+ return $upcoming_dues;
+}
+
=head2 CanBookBeRenewed
-($ok,$error) = &CanBookBeRenewed($borrowernumber, $itemnumber);
+($ok,$error) = &CanBookBeRenewed($borrowernumber, $itemnumber[, $override_limit]);
Find out whether a borrowed item may be renewed.
C<$itemnumber> is the number of the item to renew.
+C<$override_limit>, if supplied with a true value, causes
+the limit on the number of times that the loan can be renewed
+(as controlled by the item type) to be ignored.
+
C<$CanBookBeRenewed> returns a true value iff the item may be renewed. The
item must currently be on loan to the specified borrower; renewals
must be allowed for the item's type; and the borrower must not have
sub CanBookBeRenewed {
# check renewal status
- my ( $borrowernumber, $itemnumber ) = @_;
+ my ( $borrowernumber, $itemnumber, $override_limit ) = @_;
my $dbh = C4::Context->dbh;
my $renews = 1;
my $renewokay = 0;
if ( my $data2 = $sth2->fetchrow_hashref ) {
$renews = $data2->{'renewalsallowed'};
}
- if ( $renews && $renews > $data1->{'renewals'} ) {
+ if ( ( $renews && $renews > $data1->{'renewals'} ) || $override_limit ) {
$renewokay = 1;
}
else {
=head2 AddRenewal
-&AddRenewal($borrowernumber, $itemnumber, $branch, [$datedue]);
+&AddRenewal($borrowernumber, $itemnumber, $branch, [$datedue], [$lastreneweddate]);
Renews a loan.
C<$datedue> can be a C4::Dates object used to set the due date.
+C<$lastreneweddate> is an optional ISO-formatted date used to set issues.lastreneweddate. If
+this parameter is not supplied, lastreneweddate is set to the current date.
+
If C<$datedue> is the empty string, C<&AddRenewal> will calculate the due date automatically
from the book's item type.
my $item = GetItem($itemnumber) or return undef;
my $biblio = GetBiblioFromItemNumber($itemnumber) or return undef;
my $branch = (@_) ? shift : $item->{homebranch}; # opac-renew doesn't send branch
- my $datedue;
- # If the due date wasn't specified, calculate it by adding the
- # book's loan length to today's date.
- unless (@_ and $datedue = shift and $datedue->output('iso')) {
-
- 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'} ,
- $item->{homebranch} # item's homebranch determines loanlength OR do we want the branch specified by the AddRenewal argument?
- );
- #FIXME -- use circControl?
- $datedue = CalcDateDue(C4::Dates->new(),$loanlength,$branch); # this branch is the transactional branch.
- # The question of whether to use item's homebranch calendar is open.
+ my $datedue = shift;
+ my $lastreneweddate = shift;
+ # $lastreneweddate defaults to today.
+ unless (defined $lastreneweddate) {
+ $lastreneweddate = strftime( "%Y-%m-%d", localtime );
}
my $dbh = C4::Context->dbh;
my $issuedata = $sth->fetchrow_hashref;
$sth->finish;
+ # If the due date wasn't specified, calculate it by adding the
+ # book's loan length to today's date or the current due date
+ # based on the value of the RenewalPeriodBase syspref.
+ unless ($datedue && $datedue->output('iso')) {
+
+ 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'} ,
+ $item->{homebranch} # item's homebranch determines loanlength OR do we want the branch specified by the AddRenewal argument?
+ );
+
+ $datedue = (C4::Context->preference('RenewalPeriodBase') eq 'date_due') ?
+ C4::Dates->new($issuedata->{date_due}, 'iso') :
+ C4::Dates->new();
+ #FIXME -- use circControl?
+ $datedue = CalcDateDue($datedue,$loanlength,$branch,$borrower); # this branch is the transactional branch.
+ # The question of whether to use item's homebranch calendar is open.
+ }
+
# 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 = ?
+ $sth = $dbh->prepare("UPDATE issues SET date_due = ?, renewals = ?, lastreneweddate = ?
WHERE borrowernumber=?
AND itemnumber=?"
);
- $sth->execute( $datedue->output('iso'), $renews, $borrowernumber, $itemnumber );
+ $sth->execute( $datedue->output('iso'), $renews, $lastreneweddate, $borrowernumber, $itemnumber );
$sth->finish;
# Update the renewal count on the item, and tell zebra to reindex
$renews = $biblio->{'renewals'} + 1;
- ModItem({ renewals => $renews }, $biblio->{'biblionumber'}, $itemnumber);
+ ModItem({ renewals => $renews, onloan => $datedue->output('iso') }, $biblio->{'biblionumber'}, $itemnumber);
# Charge a new rental fee, if applicable?
my ( $charge, $type ) = GetIssuingCharges( $itemnumber, $borrowernumber );
}
# Log the renewal
UpdateStats( $branch, 'renew', $charge, '', $itemnumber, $item->{itype}, $borrowernumber);
+ return $datedue;
}
sub GetRenewCount {
return @row;
}
-
=head2 GetTransfersFromTo
@results = GetTransfersFromTo($frombranch,$tobranch);
return $rows_affected;
}
+=head2 SendCirculationAlert
+
+Send out a C<check-in> or C<checkout> alert using the messaging system.
+
+B<Parameters>:
+
+=over 4
+
+=item type
+
+Valid values for this parameter are: C<CHECKIN> and C<CHECKOUT>.
+
+=item item
+
+Hashref of information about the item being checked in or out.
+
+=item borrower
+
+Hashref of information about the borrower of the item.
+
+=item branch
+
+The branchcode from where the checkout or check-in took place.
+
+=back
+
+B<Example>:
+
+ SendCirculationAlert({
+ type => 'CHECKOUT',
+ item => $item,
+ borrower => $borrower,
+ branch => $branch,
+ });
+
+=cut
+
+sub SendCirculationAlert {
+ my ($opts) = @_;
+ my ($type, $item, $borrower, $branch) =
+ ($opts->{type}, $opts->{item}, $opts->{borrower}, $opts->{branch});
+ my %message_name = (
+ CHECKIN => 'Item Check-in',
+ CHECKOUT => 'Item Checkout',
+ );
+ my $borrower_preferences = C4::Members::Messaging::GetMessagingPreferences({
+ borrowernumber => $borrower->{borrowernumber},
+ message_name => $message_name{$type},
+ });
+ my $letter = C4::Letters::getletter('circulation', $type);
+ C4::Letters::parseletter($letter, 'biblio', $item->{biblionumber});
+ C4::Letters::parseletter($letter, 'biblioitems', $item->{biblionumber});
+ C4::Letters::parseletter($letter, 'borrowers', $borrower->{borrowernumber});
+ C4::Letters::parseletter($letter, 'branches', $branch);
+ my @transports = @{ $borrower_preferences->{transports} };
+ # warn "no transports" unless @transports;
+ for (@transports) {
+ # warn "transport: $_";
+ my $message = C4::Message->find_last_message($borrower, $type, $_);
+ if (!$message) {
+ #warn "create new message";
+ C4::Message->enqueue($letter, $borrower, $_);
+ } else {
+ #warn "append to old message";
+ $message->append($letter);
+ $message->update;
+ }
+ }
+ $letter;
+}
+
=head2 updateWrongTransfer
$items = updateWrongTransfer($itemNumber,$borrowernumber,$waitingAtLibrary,$FromLibrary);
=cut
sub CalcDateDue {
- my ($startdate,$loanlength,$branch) = @_;
+ my ($startdate,$loanlength,$branch,$borrower) = @_;
+ my $datedue;
+
if(C4::Context->preference('useDaysMode') eq 'Days') { # ignoring calendar
- my $datedue = time + ($loanlength) * 86400;
+ my $timedue = time + ($loanlength) * 86400;
#FIXME - assumes now even though we take a startdate
- my @datearr = localtime($datedue);
- return C4::Dates->new( sprintf("%04d-%02d-%02d", 1900 + $datearr[5], $datearr[4] + 1, $datearr[3]), 'iso');
+ 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 );
- my $datedue = $calendar->addDate($startdate, $loanlength);
- return $datedue;
+ $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} ) {
+ $datedue = C4::Dates->new( $borrower->{dateexpiry}, 'iso' );
+ }
+
+ # if ceilingDueDate ON the datedue can't be after the ceiling date
+ if ( C4::Context->preference('ceilingDueDate')
+ && ( C4::Context->preference('ceilingDueDate') =~ C4::Dates->regexp('syspref') )
+ && $datedue->output gt C4::Context->preference('ceilingDueDate') ) {
+ $datedue = C4::Dates->new( C4::Context->preference('ceilingDueDate') );
}
+
+ return $datedue;
}
=head2 CheckValidDatedue
return $exist;
}
-1;
+=head2 IsBranchTransferAllowed
+
+$allowed = IsBranchTransferAllowed( $toBranch, $fromBranch, $code );
+
+Code is either an itemtype or collection doe depending on the pref BranchTransferLimitsType
+
+=cut
+
+sub IsBranchTransferAllowed {
+ my ( $toBranch, $fromBranch, $code ) = @_;
+
+ if ( $toBranch eq $fromBranch ) { return 1; } ## Short circuit for speed.
+
+ my $limitType = C4::Context->preference("BranchTransferLimitsType");
+ my $dbh = C4::Context->dbh;
+
+ my $sth = $dbh->prepare("SELECT * FROM branch_transfer_limits WHERE toBranch = ? AND fromBranch = ? AND $limitType = ?");
+ $sth->execute( $toBranch, $fromBranch, $code );
+ my $limit = $sth->fetchrow_hashref();
+
+ ## If a row is found, then that combination is not allowed, if no matching row is found, then the combination *is allowed*
+ if ( $limit->{'limitId'} ) {
+ return 0;
+ } else {
+ return 1;
+ }
+}
+
+=head2 CreateBranchTransferLimit
+
+CreateBranchTransferLimit( $toBranch, $fromBranch, $code );
+
+$code is either itemtype or collection code depending on what the pref BranchTransferLimitsType is set to.
+
+=cut
+
+sub CreateBranchTransferLimit {
+ my ( $toBranch, $fromBranch, $code ) = @_;
+
+ my $limitType = C4::Context->preference("BranchTransferLimitsType");
+
+ my $dbh = C4::Context->dbh;
+
+ my $sth = $dbh->prepare("INSERT INTO branch_transfer_limits ( $limitType, toBranch, fromBranch ) VALUES ( ?, ?, ? )");
+ $sth->execute( $code, $toBranch, $fromBranch );
+}
+
+=head2 DeleteBranchTransferLimits
+
+DeleteBranchTransferLimits();
+
+=cut
+
+sub DeleteBranchTransferLimits {
+ my $dbh = C4::Context->dbh;
+ my $sth = $dbh->prepare("TRUNCATE TABLE branch_transfer_limits");
+ $sth->execute();
+}
+
+
+ 1;
__END__