Bug 28530: Implement float limits
Signed-off-by: Kris Becker <kbecker@jcls.org> Signed-off-by: Martin Renvoize <martin.renvoize@openfifth.co.uk> Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
This commit is contained in:
parent
83e6aecd07
commit
1f63d8c13f
|
|
@ -2346,6 +2346,19 @@ sub AddReturn {
|
|||
# if $hbr was "noreturn" or any other non-item table value, then it should 'float' (i.e. stay at this branch)
|
||||
my $transfer_trigger = $hbr eq 'homebranch' ? 'ReturnToHome' : $hbr eq 'holdingbranch' ? 'ReturnToHolding' : undef;
|
||||
|
||||
# check library float limits if enabled if the item isn't being transferred away
|
||||
if ( ( $returnbranch eq $branch ) && C4::Context->preference('UseLibraryFloatLimits') ) {
|
||||
my $effective_itemtype = $item->effective_itemtype;
|
||||
my $limit = Koha::Library::FloatLimits->find( { itemtype => $effective_itemtype, branchcode => $branch } );
|
||||
if ($limit) {
|
||||
my $transfer_library = Koha::Library::FloatLimits->lowest_ratio_library( $item, $branch );
|
||||
if ( $transfer_library && $transfer_library->branchcode ne $branch ) {
|
||||
$returnbranch = $transfer_library->branchcode;
|
||||
$transfer_trigger = 'LibraryFloatLimit';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
my $borrowernumber = $patron ? $patron->borrowernumber : undef; # we don't know if we had a borrower or not
|
||||
my $patron_unblessed = $patron ? $patron->unblessed : {};
|
||||
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ use Modern::Perl;
|
|||
use Koha::Database;
|
||||
|
||||
use Koha::Library::FloatLimit;
|
||||
use Koha::Libraries;
|
||||
use C4::Circulation qw(IsBranchTransferAllowed);
|
||||
|
||||
use base qw(Koha::Objects);
|
||||
|
|
@ -41,40 +42,141 @@ Koha::Library::FloatLimits - Koha Library::FloatLimit object set class
|
|||
sub lowest_ratio_library {
|
||||
my ( $self, $item, $branchcode ) = @_;
|
||||
|
||||
my $field = C4::Context->preference('item-level_itypes') ? 'items.itype' : 'biblioitems.itemtype';
|
||||
my $query = qq{
|
||||
SELECT branchcode
|
||||
FROM library_float_limits
|
||||
LEFT JOIN items ON ( items.itype = library_float_limits.itemtype AND items.holdingbranch = library_float_limits.branchcode )
|
||||
LEFT JOIN biblioitems ON ( items.biblioitemnumber = biblioitems.biblioitemnumber )
|
||||
WHERE library_float_limits.itemtype = ?
|
||||
AND ( $field = ? OR $field IS NULL )
|
||||
AND library_float_limits.float_limit != 0
|
||||
GROUP BY branchcode
|
||||
ORDER BY COUNT(items.holdingbranch)/library_float_limits.float_limit ASC, library_float_limits.float_limit DESC;
|
||||
};
|
||||
my $schema = Koha::Database->new->schema;
|
||||
|
||||
my $results = C4::Context->dbh->selectall_arrayref(
|
||||
$query, { Slice => {} }, $item->effective_itemtype,
|
||||
$item->effective_itemtype
|
||||
);
|
||||
my @float_limits = $schema->resultset('LibraryFloatLimit')->search(
|
||||
{
|
||||
itemtype => $item->effective_itemtype,
|
||||
float_limit => { '!=' => 0 }
|
||||
}
|
||||
)->all;
|
||||
|
||||
return unless @float_limits;
|
||||
|
||||
my @candidates;
|
||||
my $use_item_level = C4::Context->preference('item-level_itypes');
|
||||
|
||||
foreach my $limit (@float_limits) {
|
||||
my $branch = $limit->get_column('branchcode');
|
||||
my $float_limit_val = $limit->get_column('float_limit');
|
||||
|
||||
my $item_count;
|
||||
|
||||
if ($use_item_level) {
|
||||
my $at_branch_count = Koha::Items->search(
|
||||
{
|
||||
itype => $item->effective_itemtype,
|
||||
holdingbranch => $branch,
|
||||
onloan => undef
|
||||
},
|
||||
)->count;
|
||||
|
||||
# Count items in transit TO this branch
|
||||
my $in_transit_to_count = Koha::Items->search(
|
||||
{
|
||||
itype => $item->effective_itemtype,
|
||||
|
||||
# Join with active transfers where this branch is the destination
|
||||
},
|
||||
{
|
||||
join => 'branchtransfers',
|
||||
where => {
|
||||
'branchtransfers.tobranch' => $branch,
|
||||
'branchtransfers.datearrived' => undef, # Still in transit
|
||||
'branchtransfers.datecancelled' => undef, #Not cancelled
|
||||
},
|
||||
distinct => 1
|
||||
}
|
||||
)->count;
|
||||
|
||||
my $in_transit_from_count = Koha::Items->search(
|
||||
{ itype => $item->effective_itemtype },
|
||||
{
|
||||
join => 'branchtransfers',
|
||||
where => {
|
||||
'branchtransfers.frombranch' => $branch,
|
||||
'branchtransfers.datearrived' => undef, # Still in transit
|
||||
'branchtransfers.datecancelled' => undef, #Not cancelled
|
||||
},
|
||||
distinct => 1
|
||||
}
|
||||
)->count;
|
||||
$item_count = $at_branch_count + $in_transit_to_count - $in_transit_from_count;
|
||||
} else {
|
||||
my $at_branch_count = Koha::Items->search(
|
||||
{
|
||||
holdingbranch => $branch,
|
||||
'biblioitem.itemtype' => $item->effective_itemtype,
|
||||
onloan => undef
|
||||
},
|
||||
)->count;
|
||||
|
||||
# Count items in transit TO this branch
|
||||
my $in_transit_to_count = Koha::Items->search(
|
||||
{
|
||||
itype => $item->effective_itemtype,
|
||||
},
|
||||
{
|
||||
join => 'branchtransfers',
|
||||
where => {
|
||||
'branchtransfers.tobranch' => $branch,
|
||||
'branchtransfers.datearrived' => undef, # Still in transit
|
||||
'branchtransfers.datecancelled' => undef, #Not cancelled
|
||||
},
|
||||
distinct => 1
|
||||
}
|
||||
)->count;
|
||||
|
||||
my $in_transit_from_count = Koha::Items->search(
|
||||
{
|
||||
itype => $item->effective_itemtype,
|
||||
},
|
||||
{
|
||||
join => 'branchtransfers',
|
||||
where => {
|
||||
'branchtransfers.frombranch' => $branch,
|
||||
'branchtransfers.datearrived' => undef, # Still in transit
|
||||
'branchtransfers.datecancelled' => undef, #Not cancelled
|
||||
},
|
||||
distinct => 1
|
||||
}
|
||||
)->count;
|
||||
$item_count = $at_branch_count + $in_transit_to_count - $in_transit_from_count;
|
||||
}
|
||||
|
||||
my $ratio = $item_count / $float_limit_val;
|
||||
|
||||
push @candidates, {
|
||||
branchcode => $branch,
|
||||
ratio => $ratio,
|
||||
float_limit => $float_limit_val
|
||||
};
|
||||
}
|
||||
|
||||
# sort the branches by lowest ratio in the event of a tie choose a random branch
|
||||
@candidates = sort { $a->{ratio} <=> $b->{ratio} || rand() <=> rand() } @candidates;
|
||||
|
||||
my $UseBranchTransferLimits = C4::Context->preference("UseBranchTransferLimits");
|
||||
my $BranchTransferLimitsType =
|
||||
C4::Context->preference("BranchTransferLimitsType") eq 'itemtype' ? 'effective_itemtype' : 'ccode';
|
||||
|
||||
foreach my $r (@$results) {
|
||||
my $transfer_branch;
|
||||
for my $candidate (@candidates) {
|
||||
if ($UseBranchTransferLimits) {
|
||||
my $allowed = C4::Circulation::IsBranchTransferAllowed(
|
||||
$r->{branchcode}, # to
|
||||
$branchcode, # from
|
||||
$candidate->{branchcode},
|
||||
$branchcode,
|
||||
$item->$BranchTransferLimitsType
|
||||
);
|
||||
return $r->{branchcode} if $allowed;
|
||||
$transfer_branch = Koha::Libraries->find( $candidate->{branchcode} ) if $allowed;
|
||||
last;
|
||||
} else {
|
||||
return $r->{branchcode};
|
||||
$transfer_branch = Koha::Libraries->find( $candidate->{branchcode} );
|
||||
last;
|
||||
}
|
||||
}
|
||||
|
||||
return $transfer_branch;
|
||||
}
|
||||
|
||||
=head3 _type
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ my ( $template, $loggedinuser, $cookie ) = get_template_and_user(
|
|||
|
||||
my $op = $input->param('op');
|
||||
|
||||
if ( $op eq 'set_float_limits' ) {
|
||||
if ( $op eq 'cud-set_float_limits' ) {
|
||||
my $schema = Koha::Database->new()->schema();
|
||||
my @branches = Koha::Libraries->search()->as_list;
|
||||
my @itemtypes = Koha::ItemTypes->search()->as_list;
|
||||
|
|
|
|||
|
|
@ -313,7 +313,6 @@ if ( $barcode && ( $op eq 'cud-checkin' || $op eq 'cud-affect_reserve' ) ) {
|
|||
my $biblio = $item->biblio;
|
||||
$template->param(
|
||||
title => $biblio->title,
|
||||
returnbranch => $returnbranch,
|
||||
author => $biblio->author,
|
||||
itembiblionumber => $biblio->biblionumber,
|
||||
biblionumber => $biblio->biblionumber,
|
||||
|
|
@ -428,6 +427,8 @@ if ( $barcode && ( $op eq 'cud-checkin' || $op eq 'cud-affect_reserve' ) ) {
|
|||
);
|
||||
}
|
||||
|
||||
$returnbranch = $messages->{NeedsTransfer} if $messages->{NeedsTransfer};
|
||||
|
||||
# Mark missing bundle items as lost and report unexpected items
|
||||
if ( $item
|
||||
&& $item->is_bundle
|
||||
|
|
@ -524,6 +525,13 @@ my $recalled = 0;
|
|||
# new op dev : we check if the document must be returned to his homebranch directly,
|
||||
# if the document is transferred, we have warning message .
|
||||
|
||||
if ( $messages->{'LibraryFloatLimitTransferRequest'} ) {
|
||||
$template->param(
|
||||
LibraryFloatLimitTransferRequest => 1,
|
||||
itemnumber => $itemnumber,
|
||||
);
|
||||
}
|
||||
|
||||
if ( $messages->{'WasTransfered'} ) {
|
||||
$template->param(
|
||||
found => 1,
|
||||
|
|
@ -756,6 +764,8 @@ foreach my $code ( keys %$messages ) {
|
|||
;
|
||||
} elsif ( $code eq 'TransferredRecall' ) {
|
||||
;
|
||||
} elsif ( $code eq 'LibraryFloatLimitTransferRequest' ) {
|
||||
;
|
||||
} elsif ( $code eq 'InBundle' ) {
|
||||
$template->param( InBundle => $messages->{InBundle} );
|
||||
} elsif ( $code eq 'UpdateLastSeenError' ) {
|
||||
|
|
@ -834,7 +844,10 @@ if ($barcode) {
|
|||
}
|
||||
}
|
||||
|
||||
$template->param( itemnumber => $itemnumber );
|
||||
$template->param(
|
||||
itemnumber => $itemnumber,
|
||||
returnbranch => $returnbranch,
|
||||
);
|
||||
|
||||
# Checking if there is a Fast Cataloging Framework
|
||||
$template->param( fast_cataloging => 1 ) if Koha::BiblioFrameworks->find('FA');
|
||||
|
|
|
|||
|
|
@ -24,40 +24,40 @@
|
|||
</ul>
|
||||
[% END %]
|
||||
|
||||
[% IF ( CAN_user_parameters_manage_patron_categories || CAN_user_parameters_manage_circ_rules || CAN_user_parameters_manage_patron_attributes || CAN_user_parameters_manage_transfers || CAN_user_parameters_manage_item_circ_alerts || CAN_user_parameters_manage_cities || CAN_user_parameters_manage_curbside_pickups || CAN_user_parameters_manage_patron_restrictions ) %]
|
||||
<h5>Patrons and circulation</h5>
|
||||
<ul>
|
||||
[% IF ( CAN_user_parameters_manage_patron_categories ) %]
|
||||
<li><a href="/cgi-bin/koha/admin/categories.pl">Patron categories</a></li>
|
||||
[% END %]
|
||||
[% IF ( CAN_user_parameters_manage_circ_rules ) %]
|
||||
<li><a href="/cgi-bin/koha/admin/smart-rules.pl">Circulation and fine rules</a></li>
|
||||
[% END %]
|
||||
[% IF ( CAN_user_parameters_manage_patron_attributes ) %]
|
||||
<li><a href="/cgi-bin/koha/admin/patron-attr-types.pl">Patron attribute types</a></li>
|
||||
[% END %]
|
||||
[% IF ( CAN_user_parameters_manage_transfers ) %]
|
||||
<li><a href="/cgi-bin/koha/admin/branch_transfer_limits.pl">Library transfer limits</a></li>
|
||||
<li><a href="/cgi-bin/koha/admin/transport-cost-matrix.pl">Transport cost matrix</a></li>
|
||||
<li><a href="/cgi-bin/koha/admin/float_limits.pl">Library float limits</a></li>
|
||||
[% END %]
|
||||
[% IF ( CAN_user_parameters_manage_item_circ_alerts ) %]
|
||||
<li><a href="/cgi-bin/koha/admin/item_circulation_alerts.pl">Item circulation alerts</a></li>
|
||||
[% END %]
|
||||
[% IF ( Koha.Preference('UseCirculationDesks') && CAN_user_parameters_manage_libraries ) %]
|
||||
<li><a href="/cgi-bin/koha/admin/desks.pl">Desks</a></li>
|
||||
[% END %]
|
||||
[% IF ( CAN_user_parameters_manage_cities ) %]
|
||||
<li><a href="/cgi-bin/koha/admin/cities.pl">Cities and towns</a></li>
|
||||
[% END %]
|
||||
[% IF ( CAN_user_parameters_manage_curbside_pickups ) %]
|
||||
<li><a href="/cgi-bin/koha/admin/curbside_pickup.pl">Curbside pickup</a></li>
|
||||
[% END %]
|
||||
[% IF ( CAN_user_parameters_manage_patron_restrictions ) %]
|
||||
<li><a href="/cgi-bin/koha/admin/restrictions.pl">Patron restriction types</a></li>
|
||||
[% END %]
|
||||
</ul>
|
||||
[% END %]
|
||||
[% IF ( CAN_user_parameters_manage_patron_categories || CAN_user_parameters_manage_circ_rules || CAN_user_parameters_manage_patron_attributes || CAN_user_parameters_manage_transfers || CAN_user_parameters_manage_item_circ_alerts || CAN_user_parameters_manage_cities || CAN_user_parameters_manage_curbside_pickups || CAN_user_parameters_manage_patron_restrictions ) %]
|
||||
<h5>Patrons and circulation</h5>
|
||||
<ul>
|
||||
[% IF ( CAN_user_parameters_manage_patron_categories ) %]
|
||||
<li><a href="/cgi-bin/koha/admin/categories.pl">Patron categories</a></li>
|
||||
[% END %]
|
||||
[% IF ( CAN_user_parameters_manage_circ_rules ) %]
|
||||
<li><a href="/cgi-bin/koha/admin/smart-rules.pl">Circulation and fine rules</a></li>
|
||||
[% END %]
|
||||
[% IF ( CAN_user_parameters_manage_patron_attributes ) %]
|
||||
<li><a href="/cgi-bin/koha/admin/patron-attr-types.pl">Patron attribute types</a></li>
|
||||
[% END %]
|
||||
[% IF ( CAN_user_parameters_manage_transfers ) %]
|
||||
<li><a href="/cgi-bin/koha/admin/branch_transfer_limits.pl">Library transfer limits</a></li>
|
||||
<li><a href="/cgi-bin/koha/admin/transport-cost-matrix.pl">Transport cost matrix</a></li>
|
||||
<li><a href="/cgi-bin/koha/admin/float_limits.pl">Library float limits</a></li>
|
||||
[% END %]
|
||||
[% IF ( CAN_user_parameters_manage_item_circ_alerts ) %]
|
||||
<li><a href="/cgi-bin/koha/admin/item_circulation_alerts.pl">Item circulation alerts</a></li>
|
||||
[% END %]
|
||||
[% IF ( Koha.Preference('UseCirculationDesks') && CAN_user_parameters_manage_libraries ) %]
|
||||
<li><a href="/cgi-bin/koha/admin/desks.pl">Desks</a></li>
|
||||
[% END %]
|
||||
[% IF ( CAN_user_parameters_manage_cities ) %]
|
||||
<li><a href="/cgi-bin/koha/admin/cities.pl">Cities and towns</a></li>
|
||||
[% END %]
|
||||
[% IF ( CAN_user_parameters_manage_curbside_pickups ) %]
|
||||
<li><a href="/cgi-bin/koha/admin/curbside_pickup.pl">Curbside pickup</a></li>
|
||||
[% END %]
|
||||
[% IF ( CAN_user_parameters_manage_patron_restrictions ) %]
|
||||
<li><a href="/cgi-bin/koha/admin/restrictions.pl">Patron restriction types</a></li>
|
||||
[% END %]
|
||||
</ul>
|
||||
[% END %]
|
||||
|
||||
[% IF ( CAN_user_parameters_manage_accounts || ( Koha.Preference('UseCashRegisters') && CAN_user_parameters_manage_cash_registers ) ) %]
|
||||
<h5>Accounting</h5>
|
||||
|
|
|
|||
|
|
@ -108,6 +108,8 @@
|
|||
>
|
||||
<dt><a href="/cgi-bin/koha/admin/transport-cost-matrix.pl">Transport cost matrix</a></dt>
|
||||
<dd>Define transport costs between branches</dd>
|
||||
<dt><a href="/cgi-bin/koha/admin/float_limits.pl">Library float limits</a></dt>
|
||||
<dd>Define when items should be transferred to other libraries on checkin to balance floating collections</dd>
|
||||
[% END %]
|
||||
[% IF ( CAN_user_sip2 ) %]
|
||||
<dt><a href="/cgi-bin/koha/sip2/sip2.pl">Self-service circulation (SIP2)</a></dt>
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@
|
|||
|
||||
<div class="main container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-sm-10 col-sm-push-2">
|
||||
<div class="col-sm-10 order-sm-1 col-sm-push-2">
|
||||
<main>
|
||||
<h1 class="parameters"> Library float limits </h1>
|
||||
|
||||
|
|
@ -53,7 +53,8 @@
|
|||
[% END %]
|
||||
|
||||
<form method="post" action="/cgi-bin/koha/admin/float_limits.pl" id="float_limit_form">
|
||||
<input type="hidden" name="op" value="set_float_limits" />
|
||||
[% INCLUDE 'csrf-token.inc' %]
|
||||
<input type="hidden" name="op" value="cud-set_float_limits" />
|
||||
<fieldset id="float_limits">
|
||||
<div class="help">
|
||||
<p>Specify the total number of items per itemtype that are allowed to float at each library.</p>
|
||||
|
|
|
|||
|
|
@ -120,6 +120,8 @@
|
|||
<p>
|
||||
[%- SWITCH trigger -%]
|
||||
|
||||
[%- CASE 'LibraryFloatLimit' -%]
|
||||
<span>Library has exceeded float limit for this item type</span>
|
||||
[%- CASE 'Manual' -%]
|
||||
<span>Manual</span>
|
||||
[%- CASE 'StockrotationAdvance' -%]
|
||||
|
|
|
|||
Loading…
Reference in New Issue