2 # Copyright 2000-2002 Katipo Communications
3 # copyright 2010 BibLibre
5 # This file is part of Koha.
7 # Koha is free software; you can redistribute it and/or modify it
8 # under the terms of the GNU General Public License as published by
9 # the Free Software Foundation; either version 3 of the License, or
10 # (at your option) any later version.
12 # Koha is distributed in the hope that it will be useful, but
13 # WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 # GNU General Public License for more details.
17 # You should have received a copy of the GNU General Public License
18 # along with Koha; if not, see <http://www.gnu.org/licenses>.
23 use C4::Output qw( output_html_with_http_headers );
24 use C4::Auth qw( get_template_and_user );
25 use Koha::Exceptions::Exception;
26 use Koha::DateUtils qw( dt_from_string output_pref );
30 use Koha::CirculationRules;
31 use Koha::Patron::Categories;
36 my $dbh = C4::Context->dbh;
39 # $flagsrequired->{circulation}=1;
40 my ($template, $loggedinuser, $cookie)
41 = get_template_and_user({template_name => "admin/smart-rules.tt",
44 flagsrequired => {parameters => 'manage_circ_rules'},
47 my $type=$input->param('type');
49 my $branch = $input->param('branch');
51 if ( C4::Context->preference('DefaultToLoggedInLibraryCircRules') ) {
52 $branch = Koha::Libraries->search->count() == 1 ? undef : C4::Context::mybranch();
55 $branch = C4::Context::only_my_library() ? ( C4::Context::mybranch() || '*' ) : '*';
59 my $logged_in_patron = Koha::Patrons->find( $loggedinuser );
61 my $can_edit_from_any_library = $logged_in_patron->has_permission( {parameters => 'manage_circ_rules_from_any_libraries' } );
62 $template->param( restricted_to_own_library => not $can_edit_from_any_library );
63 $branch = C4::Context::mybranch() unless $can_edit_from_any_library;
65 my $op = $input->param('op') || q{};
66 my $language = C4::Languages::getlanguage();
68 my $cache = Koha::Caches->get_instance;
69 $cache->clear_from_cache( Koha::CirculationRules::GUESSED_ITEMTYPES_KEY );
71 if ($op eq 'delete') {
72 my $itemtype = $input->param('itemtype');
73 my $categorycode = $input->param('categorycode');
75 Koha::CirculationRules->set_rules(
77 categorycode => $categorycode eq '*' ? undef : $categorycode,
78 branchcode => $branch eq '*' ? undef : $branch,
79 itemtype => $itemtype eq '*' ? undef : $itemtype,
82 maxonsiteissueqty => undef,
83 rentaldiscount => undef,
86 maxsuspensiondays => undef,
87 suspension_chargeperiod => undef,
89 chargeperiod => undef,
90 chargeperiod_charge_at => undef,
95 hardduedatecompare => undef,
96 renewalsallowed => undef,
97 unseen_renewals_allowed => undef,
98 renewalperiod => undef,
99 norenewalbefore => undef,
101 no_auto_renewal_after => undef,
102 no_auto_renewal_after_hard_limit => undef,
103 reservesallowed => undef,
104 holds_per_record => undef,
105 holds_per_day => undef,
106 onshelfholds => undef,
107 opacitemholds => undef,
108 overduefinescap => undef,
109 cap_fine_to_replacement_price => undef,
110 article_requests => undef,
116 elsif ($op eq 'delete-branch-cat') {
117 my $categorycode = $input->param('categorycode');
118 if ($branch eq "*") {
119 if ($categorycode eq "*") {
120 Koha::CirculationRules->set_rules(
123 categorycode => undef,
126 patron_maxissueqty => undef,
127 patron_maxonsiteissueqty => undef,
131 Koha::CirculationRules->set_rules(
136 holdallowed => undef,
137 hold_fulfillment_policy => undef,
138 returnbranch => undef,
143 Koha::CirculationRules->set_rules(
145 categorycode => $categorycode,
149 patron_maxissueqty => undef,
150 patron_maxonsiteissueqty => undef,
155 } elsif ($categorycode eq "*") {
156 Koha::CirculationRules->set_rules(
158 branchcode => $branch,
159 categorycode => undef,
162 patron_maxissueqty => undef,
163 patron_maxonsiteissueqty => undef,
167 Koha::CirculationRules->set_rules(
169 branchcode => $branch,
172 holdallowed => undef,
173 hold_fulfillment_policy => undef,
174 returnbranch => undef,
179 Koha::CirculationRules->set_rules(
181 categorycode => $categorycode,
182 branchcode => $branch,
185 patron_maxissueqty => undef,
186 patron_maxonsiteissueqty => undef,
192 elsif ($op eq 'delete-branch-item') {
193 my $itemtype = $input->param('itemtype');
194 if ($branch eq "*") {
195 if ($itemtype eq "*") {
196 Koha::CirculationRules->set_rules(
201 holdallowed => undef,
202 hold_fulfillment_policy => undef,
203 returnbranch => undef,
208 Koha::CirculationRules->set_rules(
211 itemtype => $itemtype,
213 holdallowed => undef,
214 hold_fulfillment_policy => undef,
215 returnbranch => undef,
220 } elsif ($itemtype eq "*") {
221 Koha::CirculationRules->set_rules(
223 branchcode => $branch,
226 holdallowed => undef,
227 hold_fulfillment_policy => undef,
228 returnbranch => undef,
233 Koha::CirculationRules->set_rules(
235 branchcode => $branch,
236 itemtype => $itemtype,
238 holdallowed => undef,
239 hold_fulfillment_policy => undef,
240 returnbranch => undef,
246 # save the values entered
247 elsif ($op eq 'add') {
248 my $br = $branch; # branch
249 my $bor = $input->param('categorycode'); # borrower category
250 my $itemtype = $input->param('itemtype'); # item type
251 my $fine = $input->param('fine');
252 my $finedays = $input->param('finedays');
253 my $maxsuspensiondays = $input->param('maxsuspensiondays') || '';
254 my $suspension_chargeperiod = $input->param('suspension_chargeperiod') || 1;
255 my $firstremind = $input->param('firstremind');
256 my $chargeperiod = $input->param('chargeperiod');
257 my $chargeperiod_charge_at = $input->param('chargeperiod_charge_at');
258 my $maxissueqty = strip_non_numeric( scalar $input->param('maxissueqty') );
259 my $maxonsiteissueqty = strip_non_numeric( scalar $input->param('maxonsiteissueqty') );
260 my $renewalsallowed = $input->param('renewalsallowed');
261 my $unseen_renewals_allowed = $input->param('unseen_renewals_allowed');
262 my $renewalperiod = $input->param('renewalperiod');
263 my $norenewalbefore = $input->param('norenewalbefore');
264 $norenewalbefore = '' if $norenewalbefore =~ /^\s*$/;
265 my $auto_renew = $input->param('auto_renew') eq 'yes' ? 1 : 0;
266 my $no_auto_renewal_after = $input->param('no_auto_renewal_after');
267 $no_auto_renewal_after = '' if $no_auto_renewal_after =~ /^\s*$/;
268 my $no_auto_renewal_after_hard_limit = $input->param('no_auto_renewal_after_hard_limit') || '';
269 $no_auto_renewal_after_hard_limit = eval { dt_from_string( scalar $no_auto_renewal_after_hard_limit ) } if ( $no_auto_renewal_after_hard_limit );
270 $no_auto_renewal_after_hard_limit = output_pref( { dt => $no_auto_renewal_after_hard_limit, dateonly => 1, dateformat => 'iso' } ) if ( $no_auto_renewal_after_hard_limit );
271 my $reservesallowed = strip_non_numeric( scalar $input->param('reservesallowed') );
272 my $holds_per_record = strip_non_numeric( scalar $input->param('holds_per_record') );
273 my $holds_per_day = strip_non_numeric( scalar $input->param('holds_per_day') );
274 my $onshelfholds = $input->param('onshelfholds') || 0;
275 my $issuelength = $input->param('issuelength');
276 $issuelength = $issuelength eq q{} ? undef : $issuelength;
277 my $daysmode = $input->param('daysmode');
278 my $lengthunit = $input->param('lengthunit');
279 my $hardduedate = $input->param('hardduedate') || undef;
280 $hardduedate = eval { dt_from_string( scalar $hardduedate ) } if ( $hardduedate );
281 $hardduedate = output_pref( { dt => $hardduedate, dateonly => 1, dateformat => 'iso' } ) if ( $hardduedate );
282 my $hardduedatecompare = $input->param('hardduedatecompare');
283 my $rentaldiscount = $input->param('rentaldiscount');
284 my $opacitemholds = $input->param('opacitemholds') || 0;
285 my $article_requests = $input->param('article_requests') || 'no';
286 my $overduefinescap = $input->param('overduefinescap') || '';
287 my $cap_fine_to_replacement_price = ($input->param('cap_fine_to_replacement_price') || '') eq 'on';
288 my $note = $input->param('note');
289 my $decreaseloanholds = $input->param('decreaseloanholds') || undef;
292 maxissueqty => $maxissueqty,
293 maxonsiteissueqty => $maxonsiteissueqty,
294 rentaldiscount => $rentaldiscount,
296 finedays => $finedays,
297 maxsuspensiondays => $maxsuspensiondays,
298 suspension_chargeperiod => $suspension_chargeperiod,
299 firstremind => $firstremind,
300 chargeperiod => $chargeperiod,
301 chargeperiod_charge_at => $chargeperiod_charge_at,
302 issuelength => $issuelength,
303 daysmode => $daysmode,
304 lengthunit => $lengthunit,
305 hardduedate => $hardduedate,
306 hardduedatecompare => $hardduedatecompare,
307 renewalsallowed => $renewalsallowed,
308 unseen_renewals_allowed => $unseen_renewals_allowed,
309 renewalperiod => $renewalperiod,
310 norenewalbefore => $norenewalbefore,
311 auto_renew => $auto_renew,
312 no_auto_renewal_after => $no_auto_renewal_after,
313 no_auto_renewal_after_hard_limit => $no_auto_renewal_after_hard_limit,
314 reservesallowed => $reservesallowed,
315 holds_per_record => $holds_per_record,
316 holds_per_day => $holds_per_day,
317 onshelfholds => $onshelfholds,
318 opacitemholds => $opacitemholds,
319 overduefinescap => $overduefinescap,
320 cap_fine_to_replacement_price => $cap_fine_to_replacement_price,
321 article_requests => $article_requests,
323 decreaseloanholds => $decreaseloanholds,
326 Koha::CirculationRules->set_rules(
328 categorycode => $bor eq '*' ? undef : $bor,
329 itemtype => $itemtype eq '*' ? undef : $itemtype,
330 branchcode => $br eq '*' ? undef : $br,
336 elsif ($op eq "set-branch-defaults") {
337 my $categorycode = $input->param('categorycode');
338 my $patron_maxissueqty = strip_non_numeric( scalar $input->param('patron_maxissueqty') );
339 my $patron_maxonsiteissueqty = $input->param('patron_maxonsiteissueqty');
340 $patron_maxonsiteissueqty = strip_non_numeric($patron_maxonsiteissueqty);
341 my $holdallowed = $input->param('holdallowed');
342 my $hold_fulfillment_policy = $input->param('hold_fulfillment_policy');
343 my $returnbranch = $input->param('returnbranch');
344 my $max_holds = strip_non_numeric( scalar $input->param('max_holds') );
346 if ($branch eq "*") {
347 Koha::CirculationRules->set_rules(
352 holdallowed => $holdallowed,
353 hold_fulfillment_policy => $hold_fulfillment_policy,
354 returnbranch => $returnbranch,
358 Koha::CirculationRules->set_rules(
360 categorycode => undef,
363 patron_maxissueqty => $patron_maxissueqty,
364 patron_maxonsiteissueqty => $patron_maxonsiteissueqty,
369 Koha::CirculationRules->set_rules(
372 branchcode => $branch,
374 holdallowed => $holdallowed,
375 hold_fulfillment_policy => $hold_fulfillment_policy,
376 returnbranch => $returnbranch,
380 Koha::CirculationRules->set_rules(
382 categorycode => undef,
383 branchcode => $branch,
385 patron_maxissueqty => $patron_maxissueqty,
386 patron_maxonsiteissueqty => $patron_maxonsiteissueqty,
391 Koha::CirculationRules->set_rule(
393 branchcode => $branch,
394 categorycode => undef,
395 rule_name => 'max_holds',
396 rule_value => $max_holds,
400 elsif ($op eq "add-branch-cat") {
401 my $categorycode = $input->param('categorycode');
402 my $patron_maxissueqty = strip_non_numeric( scalar $input->param('patron_maxissueqty') );
403 my $patron_maxonsiteissueqty = $input->param('patron_maxonsiteissueqty');
404 $patron_maxonsiteissueqty = strip_non_numeric($patron_maxonsiteissueqty);
405 my $max_holds = $input->param('max_holds');
406 $max_holds =~ s/\s//g;
407 $max_holds = undef if $max_holds !~ /^\d+/;
409 if ($branch eq "*") {
410 if ($categorycode eq "*") {
411 Koha::CirculationRules->set_rules(
413 categorycode => undef,
416 max_holds => $max_holds,
417 patron_maxissueqty => $patron_maxissueqty,
418 patron_maxonsiteissueqty => $patron_maxonsiteissueqty,
423 Koha::CirculationRules->set_rules(
425 categorycode => $categorycode,
428 max_holds => $max_holds,
429 patron_maxissueqty => $patron_maxissueqty,
430 patron_maxonsiteissueqty => $patron_maxonsiteissueqty,
435 } elsif ($categorycode eq "*") {
436 Koha::CirculationRules->set_rules(
438 categorycode => undef,
439 branchcode => $branch,
441 max_holds => $max_holds,
442 patron_maxissueqty => $patron_maxissueqty,
443 patron_maxonsiteissueqty => $patron_maxonsiteissueqty,
448 Koha::CirculationRules->set_rules(
450 categorycode => $categorycode,
451 branchcode => $branch,
453 max_holds => $max_holds,
454 patron_maxissueqty => $patron_maxissueqty,
455 patron_maxonsiteissueqty => $patron_maxonsiteissueqty,
461 elsif ( $op eq "add-open-article-requests-limit" ) {
462 my $categorycode = $input->param('categorycode');
463 my $open_article_requests_limit = strip_non_numeric( scalar $input->param('open_article_requests_limit') );
465 Koha::Exceptions::Exception->throw("No value passed for article request limit")
466 if not defined $open_article_requests_limit # There is a JS check for that
467 || $open_article_requests_limit eq '';
469 if ( $branch eq "*" ) {
470 if ( $categorycode eq "*" ) {
471 Koha::CirculationRules->set_rules(
472 { categorycode => undef,
474 rules => { open_article_requests_limit => $open_article_requests_limit, }
478 Koha::CirculationRules->set_rules(
479 { categorycode => $categorycode,
481 rules => { open_article_requests_limit => $open_article_requests_limit, }
485 } elsif ( $categorycode eq "*" ) {
486 Koha::CirculationRules->set_rules(
487 { categorycode => undef,
488 branchcode => $branch,
489 rules => { open_article_requests_limit => $open_article_requests_limit, }
493 Koha::CirculationRules->set_rules(
494 { categorycode => $categorycode,
495 branchcode => $branch,
496 rules => { open_article_requests_limit => $open_article_requests_limit, }
500 } elsif ( $op eq 'del-open-article-requests-limit' ) {
501 my $categorycode = $input->param('categorycode');
502 if ( $branch eq "*" ) {
503 if ( $categorycode eq "*" ) {
504 Koha::CirculationRules->set_rules(
505 { branchcode => undef,
506 categorycode => undef,
507 rules => { open_article_requests_limit => undef, }
511 Koha::CirculationRules->set_rules(
512 { categorycode => $categorycode,
514 rules => { open_article_requests_limit => undef, }
518 } elsif ( $categorycode eq "*" ) {
519 Koha::CirculationRules->set_rules(
520 { branchcode => $branch,
521 categorycode => undef,
522 rules => { open_article_requests_limit => undef, }
526 Koha::CirculationRules->set_rules(
527 { categorycode => $categorycode,
528 branchcode => $branch,
529 rules => { open_article_requests_limit => undef, }
534 elsif ($op eq "add-branch-item") {
535 my $itemtype = $input->param('itemtype');
536 my $holdallowed = $input->param('holdallowed');
537 my $hold_fulfillment_policy = $input->param('hold_fulfillment_policy');
538 my $returnbranch = $input->param('returnbranch');
540 if ($branch eq "*") {
541 if ($itemtype eq "*") {
542 Koha::CirculationRules->set_rules(
547 holdallowed => $holdallowed,
548 hold_fulfillment_policy => $hold_fulfillment_policy,
549 returnbranch => $returnbranch,
554 Koha::CirculationRules->set_rules(
556 itemtype => $itemtype,
559 holdallowed => $holdallowed,
560 hold_fulfillment_policy => $hold_fulfillment_policy,
561 returnbranch => $returnbranch,
566 } elsif ($itemtype eq "*") {
567 Koha::CirculationRules->set_rules(
570 branchcode => $branch,
572 holdallowed => $holdallowed,
573 hold_fulfillment_policy => $hold_fulfillment_policy,
574 returnbranch => $returnbranch,
579 Koha::CirculationRules->set_rules(
581 itemtype => $itemtype,
582 branchcode => $branch,
584 holdallowed => $holdallowed,
585 hold_fulfillment_policy => $hold_fulfillment_policy,
586 returnbranch => $returnbranch,
592 elsif ( $op eq 'mod-refund-lost-item-fee-rule' ) {
594 my $lostreturn = $input->param('lostreturn');
596 if ( $lostreturn eq '*' ) {
597 if ( $branch ne '*' ) {
598 # only do something for $lostreturn eq '*' if branch-specific
599 Koha::CirculationRules->set_rules(
601 branchcode => $branch,
609 Koha::CirculationRules->set_rules(
611 branchcode => $branch,
613 lostreturn => $lostreturn
620 my $refundLostItemFeeRule = Koha::CirculationRules->find({ branchcode => ($branch eq '*') ? undef : $branch, rule_name => 'lostreturn' });
621 my $defaultLostItemFeeRule = Koha::CirculationRules->find({ branchcode => undef, rule_name => 'lostreturn' });
623 refundLostItemFeeRule => $refundLostItemFeeRule,
624 defaultRefundRule => $defaultLostItemFeeRule ? $defaultLostItemFeeRule->rule_value : 'refund'
627 my $patron_categories = Koha::Patron::Categories->search({}, { order_by => ['description'] });
629 my $itemtypes = Koha::ItemTypes->search_with_localization;
631 my $humanbranch = ( $branch ne '*' ? $branch : undef );
633 my $all_rules = Koha::CirculationRules->search({ branchcode => $humanbranch });
634 my $definedbranch = $all_rules->count ? 1 : 0;
637 while ( my $r = $all_rules->next ) {
639 $rules->{ $r->{categorycode} // '' }->{ $r->{itemtype} // '' }->{ $r->{rule_name} } = $r->{rule_value};
642 $template->param(show_branch_cat_rule_form => 1);
645 patron_categories => $patron_categories,
646 itemtypeloop => $itemtypes,
647 humanbranch => $humanbranch,
648 current_branch => $branch,
649 definedbranch => $definedbranch,
652 output_html_with_http_headers $input, $cookie, $template->output;
656 # sort by patron category, then item type, putting
657 # default entries at the bottom
658 sub by_category_and_itemtype {
659 unless (by_category($a, $b)) {
660 return by_itemtype($a, $b);
666 if ($a->{'default_humancategorycode'}) {
667 return ($b->{'default_humancategorycode'} ? 0 : 1);
668 } elsif ($b->{'default_humancategorycode'}) {
671 return $a->{'humancategorycode'} cmp $b->{'humancategorycode'};
677 if ($a->{default_translated_description}) {
678 return ($b->{'default_translated_description'} ? 0 : 1);
679 } elsif ($b->{'default_translated_description'}) {
682 return lc $a->{'translated_description'} cmp lc $b->{'translated_description'};
686 sub strip_non_numeric {
689 $string = '' if $string !~ /^\d+/;