Bug 26017: (follow-up) Serverside permission check on registers page
[koha.git] / admin / smart-rules.pl
1 #!/usr/bin/perl
2 # Copyright 2000-2002 Katipo Communications
3 # copyright 2010 BibLibre
4 #
5 # This file is part of Koha.
6 #
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.
11 #
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.
16 #
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>.
19
20 use Modern::Perl;
21 use CGI qw ( -utf8 );
22 use C4::Context;
23 use C4::Output;
24 use C4::Auth;
25 use C4::Koha;
26 use C4::Debug;
27 use Koha::DateUtils;
28 use Koha::Database;
29 use Koha::Logger;
30 use Koha::RefundLostItemFeeRules;
31 use Koha::Libraries;
32 use Koha::CirculationRules;
33 use Koha::Patron::Categories;
34 use Koha::Caches;
35 use Koha::Patrons;
36
37 my $input = CGI->new;
38 my $dbh = C4::Context->dbh;
39
40 # my $flagsrequired;
41 # $flagsrequired->{circulation}=1;
42 my ($template, $loggedinuser, $cookie)
43     = get_template_and_user({template_name => "admin/smart-rules.tt",
44                             query => $input,
45                             type => "intranet",
46                             authnotrequired => 0,
47                             flagsrequired => {parameters => 'manage_circ_rules'},
48                             debug => 1,
49                             });
50
51 my $type=$input->param('type');
52
53 my $branch = $input->param('branch');
54 unless ( $branch ) {
55     if ( C4::Context->preference('DefaultToLoggedInLibraryCircRules') ) {
56         $branch = Koha::Libraries->search->count() == 1 ? undef : C4::Context::mybranch();
57     }
58     else {
59         $branch = C4::Context::only_my_library() ? ( C4::Context::mybranch() || '*' ) : '*';
60     }
61 }
62
63 my $logged_in_patron = Koha::Patrons->find( $loggedinuser );
64
65 my $can_edit_from_any_library = $logged_in_patron->has_permission( {parameters => 'manage_circ_rules_from_any_libraries' } );
66 $template->param( restricted_to_own_library => not $can_edit_from_any_library );
67 $branch = C4::Context::mybranch() unless $can_edit_from_any_library;
68
69 my $op = $input->param('op') || q{};
70 my $language = C4::Languages::getlanguage();
71
72 my $cache = Koha::Caches->get_instance;
73 $cache->clear_from_cache( Koha::CirculationRules::GUESSED_ITEMTYPES_KEY );
74
75 if ($op eq 'delete') {
76     my $itemtype     = $input->param('itemtype');
77     my $categorycode = $input->param('categorycode');
78     $debug and warn "deleting $1 $2 $branch";
79
80     Koha::CirculationRules->set_rules(
81         {
82             categorycode => $categorycode eq '*' ? undef : $categorycode,
83             branchcode   => $branch eq '*' ? undef : $branch,
84             itemtype     => $itemtype eq '*' ? undef : $itemtype,
85             rules        => {
86                 maxissueqty                      => undef,
87                 maxonsiteissueqty                => undef,
88                 rentaldiscount                   => undef,
89                 fine                             => undef,
90                 finedays                         => undef,
91                 maxsuspensiondays                => undef,
92                 suspension_chargeperiod          => undef,
93                 firstremind                      => undef,
94                 chargeperiod                     => undef,
95                 chargeperiod_charge_at           => undef,
96                 issuelength                      => undef,
97                 lengthunit                       => undef,
98                 hardduedate                      => undef,
99                 hardduedatecompare               => undef,
100                 renewalsallowed                  => undef,
101                 renewalperiod                    => undef,
102                 norenewalbefore                  => undef,
103                 auto_renew                       => undef,
104                 no_auto_renewal_after            => undef,
105                 no_auto_renewal_after_hard_limit => undef,
106                 reservesallowed                  => undef,
107                 holds_per_record                 => undef,
108                 holds_per_day                    => undef,
109                 onshelfholds                     => undef,
110                 opacitemholds                    => undef,
111                 overduefinescap                  => undef,
112                 cap_fine_to_replacement_price    => undef,
113                 article_requests                 => undef,
114                 note                             => undef,
115             }
116         }
117     );
118 }
119 elsif ($op eq 'delete-branch-cat') {
120     my $categorycode  = $input->param('categorycode');
121     if ($branch eq "*") {
122         if ($categorycode eq "*") {
123             Koha::CirculationRules->set_rules(
124                 {
125                     branchcode   => undef,
126                     categorycode => undef,
127                     rules        => {
128                         max_holds                      => undef,
129                         patron_maxissueqty             => undef,
130                         patron_maxonsiteissueqty       => undef,
131                     }
132                 }
133             );
134             Koha::CirculationRules->set_rules(
135                 {
136                     branchcode   => undef,
137                     itemtype     => undef,
138                     rules        => {
139                         holdallowed             => undef,
140                         hold_fulfillment_policy => undef,
141                         returnbranch            => undef,
142                     }
143                 }
144             );
145         } else {
146             Koha::CirculationRules->set_rules(
147                 {
148                     categorycode => $categorycode,
149                     branchcode   => undef,
150                     rules        => {
151                         max_holds                => undef,
152                         patron_maxissueqty       => undef,
153                         patron_maxonsiteissueqty => undef,
154                     }
155                 }
156             );
157         }
158     } elsif ($categorycode eq "*") {
159         Koha::CirculationRules->set_rules(
160             {
161                 branchcode   => $branch,
162                 categorycode => undef,
163                 rules        => {
164                     max_holds                => undef,
165                     patron_maxissueqty       => undef,
166                     patron_maxonsiteissueqty => undef,
167                 }
168             }
169         );
170         Koha::CirculationRules->set_rules(
171             {
172                 branchcode   => $branch,
173                 itemtype     => undef,
174                 rules        => {
175                     holdallowed             => undef,
176                     hold_fulfillment_policy => undef,
177                     returnbranch            => undef,
178                 }
179             }
180         );
181     } else {
182         Koha::CirculationRules->set_rules(
183             {
184                 categorycode => $categorycode,
185                 branchcode   => $branch,
186                 rules        => {
187                     max_holds         => undef,
188                     patron_maxissueqty       => undef,
189                     patron_maxonsiteissueqty => undef,
190                 }
191             }
192         );
193     }
194 }
195 elsif ($op eq 'delete-branch-item') {
196     my $itemtype  = $input->param('itemtype');
197     if ($branch eq "*") {
198         if ($itemtype eq "*") {
199             Koha::CirculationRules->set_rules(
200                 {
201                     branchcode   => undef,
202                     itemtype     => undef,
203                     rules        => {
204                         holdallowed             => undef,
205                         hold_fulfillment_policy => undef,
206                         returnbranch            => undef,
207                     }
208                 }
209             );
210         } else {
211             Koha::CirculationRules->set_rules(
212                 {
213                     branchcode   => undef,
214                     itemtype     => $itemtype,
215                     rules        => {
216                         holdallowed             => undef,
217                         hold_fulfillment_policy => undef,
218                         returnbranch            => undef,
219                     }
220                 }
221             );
222         }
223     } elsif ($itemtype eq "*") {
224         Koha::CirculationRules->set_rules(
225             {
226                 branchcode   => $branch,
227                 itemtype     => undef,
228                 rules        => {
229                     holdallowed             => undef,
230                     hold_fulfillment_policy => undef,
231                     returnbranch            => undef,
232                 }
233             }
234         );
235     } else {
236         Koha::CirculationRules->set_rules(
237             {
238                 branchcode   => $branch,
239                 itemtype     => $itemtype,
240                 rules        => {
241                     holdallowed             => undef,
242                     hold_fulfillment_policy => undef,
243                     returnbranch            => undef,
244                 }
245             }
246         );
247     }
248 }
249 # save the values entered
250 elsif ($op eq 'add') {
251     my $br = $branch; # branch
252     my $bor  = $input->param('categorycode'); # borrower category
253     my $itemtype  = $input->param('itemtype');     # item type
254     my $fine = $input->param('fine');
255     my $finedays     = $input->param('finedays');
256     my $maxsuspensiondays = $input->param('maxsuspensiondays') || '';
257     my $suspension_chargeperiod = $input->param('suspension_chargeperiod') || 1;
258     my $firstremind  = $input->param('firstremind');
259     my $chargeperiod = $input->param('chargeperiod');
260     my $chargeperiod_charge_at = $input->param('chargeperiod_charge_at');
261     my $maxissueqty = strip_non_numeric( scalar $input->param('maxissueqty') );
262     my $maxonsiteissueqty = strip_non_numeric( scalar $input->param('maxonsiteissueqty') );
263     my $renewalsallowed  = $input->param('renewalsallowed');
264     my $renewalperiod    = $input->param('renewalperiod');
265     my $norenewalbefore  = $input->param('norenewalbefore');
266     $norenewalbefore = '' if $norenewalbefore =~ /^\s*$/;
267     my $auto_renew = $input->param('auto_renew') eq 'yes' ? 1 : 0;
268     my $no_auto_renewal_after = $input->param('no_auto_renewal_after');
269     $no_auto_renewal_after = '' if $no_auto_renewal_after =~ /^\s*$/;
270     my $no_auto_renewal_after_hard_limit = $input->param('no_auto_renewal_after_hard_limit') || '';
271     $no_auto_renewal_after_hard_limit = eval { dt_from_string( scalar $no_auto_renewal_after_hard_limit ) } if ( $no_auto_renewal_after_hard_limit );
272     $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 );
273     my $reservesallowed  = strip_non_numeric( scalar $input->param('reservesallowed') );
274     my $holds_per_record = strip_non_numeric( scalar $input->param('holds_per_record') );
275     my $holds_per_day    = strip_non_numeric( scalar $input->param('holds_per_day') );
276     my $onshelfholds     = $input->param('onshelfholds') || 0;
277     my $issuelength  = $input->param('issuelength');
278     $issuelength = $issuelength eq q{} ? undef : $issuelength;
279     my $lengthunit  = $input->param('lengthunit');
280     my $hardduedate = $input->param('hardduedate') || undef;
281     $hardduedate = eval { dt_from_string( scalar $hardduedate ) } if ( $hardduedate );
282     $hardduedate = output_pref( { dt => $hardduedate, dateonly => 1, dateformat => 'iso' } ) if ( $hardduedate );
283     my $hardduedatecompare = $input->param('hardduedatecompare');
284     my $rentaldiscount = $input->param('rentaldiscount');
285     my $opacitemholds = $input->param('opacitemholds') || 0;
286     my $article_requests = $input->param('article_requests') || 'no';
287     my $overduefinescap = $input->param('overduefinescap') || '';
288     my $cap_fine_to_replacement_price = ($input->param('cap_fine_to_replacement_price') || '') eq 'on';
289     my $note = $input->param('note');
290     $debug and warn "Adding $br, $bor, $itemtype, $fine, $maxissueqty, $maxonsiteissueqty, $cap_fine_to_replacement_price";
291
292     my $rules = {
293         maxissueqty                   => $maxissueqty,
294         maxonsiteissueqty             => $maxonsiteissueqty,
295         rentaldiscount                => $rentaldiscount,
296         fine                          => $fine,
297         finedays                      => $finedays,
298         maxsuspensiondays             => $maxsuspensiondays,
299         suspension_chargeperiod       => $suspension_chargeperiod,
300         firstremind                   => $firstremind,
301         chargeperiod                  => $chargeperiod,
302         chargeperiod_charge_at        => $chargeperiod_charge_at,
303         issuelength                   => $issuelength,
304         lengthunit                    => $lengthunit,
305         hardduedate                   => $hardduedate,
306         hardduedatecompare            => $hardduedatecompare,
307         renewalsallowed               => $renewalsallowed,
308         renewalperiod                 => $renewalperiod,
309         norenewalbefore               => $norenewalbefore,
310         auto_renew                    => $auto_renew,
311         no_auto_renewal_after         => $no_auto_renewal_after,
312         no_auto_renewal_after_hard_limit => $no_auto_renewal_after_hard_limit,
313         reservesallowed               => $reservesallowed,
314         holds_per_record              => $holds_per_record,
315         holds_per_day                 => $holds_per_day,
316         onshelfholds                  => $onshelfholds,
317         opacitemholds                 => $opacitemholds,
318         overduefinescap               => $overduefinescap,
319         cap_fine_to_replacement_price => $cap_fine_to_replacement_price,
320         article_requests              => $article_requests,
321         note                          => $note,
322     };
323
324     Koha::CirculationRules->set_rules(
325         {
326             categorycode => $bor eq '*' ? undef : $bor,
327             itemtype     => $itemtype eq '*' ? undef : $itemtype,
328             branchcode   => $br eq '*' ? undef : $br,
329             rules        => $rules,
330         }
331     );
332
333 }
334 elsif ($op eq "set-branch-defaults") {
335     my $categorycode  = $input->param('categorycode');
336     my $patron_maxissueqty = strip_non_numeric( scalar $input->param('patron_maxissueqty') );
337     my $patron_maxonsiteissueqty = $input->param('patron_maxonsiteissueqty');
338     $patron_maxonsiteissueqty = strip_non_numeric($patron_maxonsiteissueqty);
339     my $holdallowed   = $input->param('holdallowed');
340     my $hold_fulfillment_policy = $input->param('hold_fulfillment_policy');
341     my $returnbranch  = $input->param('returnbranch');
342     my $max_holds = strip_non_numeric( scalar $input->param('max_holds') );
343     $holdallowed =~ s/\s//g;
344     $holdallowed = undef if $holdallowed !~ /^\d+/;
345
346     if ($branch eq "*") {
347         Koha::CirculationRules->set_rules(
348             {
349                 itemtype     => undef,
350                 branchcode   => undef,
351                 rules        => {
352                     holdallowed             => $holdallowed,
353                     hold_fulfillment_policy => $hold_fulfillment_policy,
354                     returnbranch            => $returnbranch,
355                 }
356             }
357         );
358         Koha::CirculationRules->set_rules(
359             {
360                 categorycode => undef,
361                 branchcode   => undef,
362                 rules        => {
363                     patron_maxissueqty             => $patron_maxissueqty,
364                     patron_maxonsiteissueqty       => $patron_maxonsiteissueqty,
365                 }
366             }
367         );
368     } else {
369         Koha::CirculationRules->set_rules(
370             {
371                 itemtype     => undef,
372                 branchcode   => $branch,
373                 rules        => {
374                     holdallowed             => $holdallowed,
375                     hold_fulfillment_policy => $hold_fulfillment_policy,
376                     returnbranch            => $returnbranch,
377                 }
378             }
379         );
380         Koha::CirculationRules->set_rules(
381             {
382                 categorycode => undef,
383                 branchcode   => $branch,
384                 rules        => {
385                     patron_maxissueqty             => $patron_maxissueqty,
386                     patron_maxonsiteissueqty       => $patron_maxonsiteissueqty,
387                 }
388             }
389         );
390     }
391     Koha::CirculationRules->set_rule(
392         {
393             branchcode   => $branch,
394             categorycode => undef,
395             rule_name    => 'max_holds',
396             rule_value   => $max_holds,
397         }
398     );
399 }
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+/;
408
409     if ($branch eq "*") {
410         if ($categorycode eq "*") {
411             Koha::CirculationRules->set_rules(
412                 {
413                     categorycode => undef,
414                     branchcode   => undef,
415                     rules        => {
416                         max_holds         => $max_holds,
417                         patron_maxissueqty       => $patron_maxissueqty,
418                         patron_maxonsiteissueqty => $patron_maxonsiteissueqty,
419                     }
420                 }
421             );
422         } else {
423             Koha::CirculationRules->set_rules(
424                 {
425                     categorycode => $categorycode,
426                     branchcode   => undef,
427                     rules        => {
428                         max_holds         => $max_holds,
429                         patron_maxissueqty       => $patron_maxissueqty,
430                         patron_maxonsiteissueqty => $patron_maxonsiteissueqty,
431                     }
432                 }
433             );
434         }
435     } elsif ($categorycode eq "*") {
436         Koha::CirculationRules->set_rules(
437             {
438                 categorycode => undef,
439                 branchcode   => $branch,
440                 rules        => {
441                     max_holds         => $max_holds,
442                     patron_maxissueqty       => $patron_maxissueqty,
443                     patron_maxonsiteissueqty => $patron_maxonsiteissueqty,
444                 }
445             }
446         );
447     } else {
448         Koha::CirculationRules->set_rules(
449             {
450                 categorycode => $categorycode,
451                 branchcode   => $branch,
452                 rules        => {
453                     max_holds         => $max_holds,
454                     patron_maxissueqty       => $patron_maxissueqty,
455                     patron_maxonsiteissueqty => $patron_maxonsiteissueqty,
456                 }
457             }
458         );
459     }
460 }
461 elsif ($op eq "add-branch-item") {
462     my $itemtype                = $input->param('itemtype');
463     my $holdallowed             = $input->param('holdallowed');
464     my $hold_fulfillment_policy = $input->param('hold_fulfillment_policy');
465     my $returnbranch            = $input->param('returnbranch');
466
467     $holdallowed =~ s/\s//g;
468     $holdallowed = undef if $holdallowed !~ /^\d+/;
469
470     if ($branch eq "*") {
471         if ($itemtype eq "*") {
472             Koha::CirculationRules->set_rules(
473                 {
474                     itemtype     => undef,
475                     branchcode   => undef,
476                     rules        => {
477                         holdallowed             => $holdallowed,
478                         hold_fulfillment_policy => $hold_fulfillment_policy,
479                         returnbranch            => $returnbranch,
480                     }
481                 }
482             );
483         } else {
484             Koha::CirculationRules->set_rules(
485                 {
486                     itemtype     => $itemtype,
487                     branchcode   => undef,
488                     rules        => {
489                         holdallowed             => $holdallowed,
490                         hold_fulfillment_policy => $hold_fulfillment_policy,
491                         returnbranch            => $returnbranch,
492                     }
493                 }
494             );
495         }
496     } elsif ($itemtype eq "*") {
497             Koha::CirculationRules->set_rules(
498                 {
499                     itemtype     => undef,
500                     branchcode   => $branch,
501                     rules        => {
502                         holdallowed             => $holdallowed,
503                         hold_fulfillment_policy => $hold_fulfillment_policy,
504                         returnbranch            => $returnbranch,
505                     }
506                 }
507             );
508     } else {
509         Koha::CirculationRules->set_rules(
510             {
511                 itemtype     => $itemtype,
512                 branchcode   => $branch,
513                 rules        => {
514                     holdallowed             => $holdallowed,
515                     hold_fulfillment_policy => $hold_fulfillment_policy,
516                     returnbranch            => $returnbranch,
517                 }
518             }
519         );
520     }
521 }
522 elsif ( $op eq 'mod-refund-lost-item-fee-rule' ) {
523
524     my $refund = $input->param('refund');
525
526     if ( $refund eq '*' ) {
527         if ( $branch ne '*' ) {
528             # only do something for $refund eq '*' if branch-specific
529             Koha::CirculationRules->set_rules(
530                 {
531                     branchcode   => $branch,
532                     rules        => {
533                         refund => undef
534                     }
535                 }
536             );
537         }
538     } else {
539         Koha::CirculationRules->set_rules(
540             {
541                 branchcode   => $branch,
542                 rules        => {
543                     refund => $refund
544                 }
545             }
546         );
547     }
548 }
549
550 my $refundLostItemFeeRule = Koha::RefundLostItemFeeRules->find({ branchcode => ($branch eq '*') ? undef : $branch });
551 $template->param(
552     refundLostItemFeeRule => $refundLostItemFeeRule,
553     defaultRefundRule     => Koha::RefundLostItemFeeRules->_default_rule
554 );
555
556 my $patron_categories = Koha::Patron::Categories->search({}, { order_by => ['description'] });
557
558 my $itemtypes = Koha::ItemTypes->search_with_localization;
559
560 my $humanbranch = ( $branch ne '*' ? $branch : undef );
561
562 my $all_rules = Koha::CirculationRules->search({ branchcode => $humanbranch });
563 my $definedbranch = $all_rules->count ? 1 : 0;
564
565 my $rules = {};
566 while ( my $r = $all_rules->next ) {
567     $r = $r->unblessed;
568     $rules->{ $r->{categorycode} // '' }->{ $r->{itemtype} // '' }->{ $r->{rule_name} } = $r->{rule_value};
569 }
570
571 $template->param(show_branch_cat_rule_form => 1);
572
573 $template->param(
574     patron_categories => $patron_categories,
575     itemtypeloop      => $itemtypes,
576     humanbranch       => $humanbranch,
577     current_branch    => $branch,
578     definedbranch     => $definedbranch,
579     all_rules         => $rules,
580 );
581 output_html_with_http_headers $input, $cookie, $template->output;
582
583 exit 0;
584
585 # sort by patron category, then item type, putting
586 # default entries at the bottom
587 sub by_category_and_itemtype {
588     unless (by_category($a, $b)) {
589         return by_itemtype($a, $b);
590     }
591 }
592
593 sub by_category {
594     my ($a, $b) = @_;
595     if ($a->{'default_humancategorycode'}) {
596         return ($b->{'default_humancategorycode'} ? 0 : 1);
597     } elsif ($b->{'default_humancategorycode'}) {
598         return -1;
599     } else {
600         return $a->{'humancategorycode'} cmp $b->{'humancategorycode'};
601     }
602 }
603
604 sub by_itemtype {
605     my ($a, $b) = @_;
606     if ($a->{default_translated_description}) {
607         return ($b->{'default_translated_description'} ? 0 : 1);
608     } elsif ($b->{'default_translated_description'}) {
609         return -1;
610     } else {
611         return lc $a->{'translated_description'} cmp lc $b->{'translated_description'};
612     }
613 }
614
615 sub strip_non_numeric {
616     my $string = shift;
617     $string =~ s/\s//g;
618     $string = '' if $string !~ /^\d+/;
619     return $string;
620 }