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