View | Details | Raw Unified | Return to bug 25711
Collapse All | Expand All

(-)a/Koha/CirculationRules.pm (+3 lines)
Lines 82-87 our $RULE_KINDS = { Link Here
82
    cap_fine_to_replacement_price => {
82
    cap_fine_to_replacement_price => {
83
        scope => [ 'branchcode', 'categorycode', 'itemtype' ],
83
        scope => [ 'branchcode', 'categorycode', 'itemtype' ],
84
    },
84
    },
85
    expire_reserves_charge => {
86
        scope => [ 'branchcode', 'categorycode', 'itemtype' ],
87
    },
85
    chargeperiod => {
88
    chargeperiod => {
86
        scope => [ 'branchcode', 'categorycode', 'itemtype' ],
89
        scope => [ 'branchcode', 'categorycode', 'itemtype' ],
87
    },
90
    },
(-)a/Koha/Hold.pm (-1 / +18 lines)
Lines 552-558 sub cancel { Link Here
552
            C4::Reserves::_FixPriority({ biblionumber => $self->biblionumber });
552
            C4::Reserves::_FixPriority({ biblionumber => $self->biblionumber });
553
553
554
            # and, if desired, charge a cancel fee
554
            # and, if desired, charge a cancel fee
555
            my $charge = C4::Context->preference("ExpireReservesMaxPickUpDelayCharge");
555
            my $charge;
556
            if (my $item = $self->item) {
557
                my $branchcode = C4::Reserves::GetReservesControlBranch($item->unblessed, $self->borrower->unblessed);
558
559
                my $rule = Koha::CirculationRules->get_effective_rule(
560
                    {
561
                        categorycode => $self->borrower->categorycode,
562
                        itemtype     => $item->effective_itemtype,
563
                        branchcode   => $branchcode,
564
                        rule_name    => 'expire_reserves_charge',
565
                    }
566
                );
567
                my $rule_value = $rule && $rule->rule_value // '';
568
                $charge = $rule_value ne '' ? $rule_value : C4::Context->preference("ExpireReservesMaxPickUpDelayCharge");
569
            } else {
570
                $charge = C4::Context->preference("ExpireReservesMaxPickUpDelayCharge");
571
            }
572
556
            if ( $charge && $params->{'charge_cancel_fee'} ) {
573
            if ( $charge && $params->{'charge_cancel_fee'} ) {
557
                my $account =
574
                my $account =
558
                  Koha::Account->new( { patron_id => $self->borrowernumber } );
575
                  Koha::Account->new( { patron_id => $self->borrowernumber } );
(-)a/admin/smart-rules.pl (+3 lines)
Lines 106-111 if ($op eq 'delete') { Link Here
106
                opacitemholds                    => undef,
106
                opacitemholds                    => undef,
107
                overduefinescap                  => undef,
107
                overduefinescap                  => undef,
108
                cap_fine_to_replacement_price    => undef,
108
                cap_fine_to_replacement_price    => undef,
109
                expire_reserves_charge           => undef,
109
                article_requests                 => undef,
110
                article_requests                 => undef,
110
                note                             => undef,
111
                note                             => undef,
111
            }
112
            }
Lines 284-289 elsif ($op eq 'add') { Link Here
284
    my $article_requests = $input->param('article_requests') || 'no';
285
    my $article_requests = $input->param('article_requests') || 'no';
285
    my $overduefinescap = $input->param('overduefinescap') || '';
286
    my $overduefinescap = $input->param('overduefinescap') || '';
286
    my $cap_fine_to_replacement_price = ($input->param('cap_fine_to_replacement_price') || '') eq 'on';
287
    my $cap_fine_to_replacement_price = ($input->param('cap_fine_to_replacement_price') || '') eq 'on';
288
    my $expire_reserves_charge = $input->param('expire_reserves_charge') // '';
287
    my $note = $input->param('note');
289
    my $note = $input->param('note');
288
    my $decreaseloanholds = $input->param('decreaseloanholds') || undef;
290
    my $decreaseloanholds = $input->param('decreaseloanholds') || undef;
289
291
Lines 317-322 elsif ($op eq 'add') { Link Here
317
        opacitemholds                 => $opacitemholds,
319
        opacitemholds                 => $opacitemholds,
318
        overduefinescap               => $overduefinescap,
320
        overduefinescap               => $overduefinescap,
319
        cap_fine_to_replacement_price => $cap_fine_to_replacement_price,
321
        cap_fine_to_replacement_price => $cap_fine_to_replacement_price,
322
        expire_reserves_charge        => $expire_reserves_charge,
320
        article_requests              => $article_requests,
323
        article_requests              => $article_requests,
321
        note                          => $note,
324
        note                          => $note,
322
        decreaseloanholds             => $decreaseloanholds,
325
        decreaseloanholds             => $decreaseloanholds,
(-)a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/smart-rules.tt (-2 / +14 lines)
Lines 123-128 Link Here
123
                <th>Fine grace period</th>
123
                <th>Fine grace period</th>
124
                <th>Overdue fines cap (amount)</th>
124
                <th>Overdue fines cap (amount)</th>
125
                <th>Cap fine at replacement price</th>
125
                <th>Cap fine at replacement price</th>
126
                <th>Expire reserve charge</th>
126
                <th>Suspension in days (day)</th>
127
                <th>Suspension in days (day)</th>
127
                <th>Max. suspension duration (day)</th>
128
                <th>Max. suspension duration (day)</th>
128
                <th>Suspension charging interval</th>
129
                <th>Suspension charging interval</th>
Lines 165-170 Link Here
165
                        [% SET firstremind = all_rules.$c.$i.firstremind %]
166
                        [% SET firstremind = all_rules.$c.$i.firstremind %]
166
                        [% SET overduefinescap = all_rules.$c.$i.overduefinescap %]
167
                        [% SET overduefinescap = all_rules.$c.$i.overduefinescap %]
167
                        [% SET cap_fine_to_replacement_price = all_rules.$c.$i.cap_fine_to_replacement_price %]
168
                        [% SET cap_fine_to_replacement_price = all_rules.$c.$i.cap_fine_to_replacement_price %]
169
                        [% SET expire_reserves_charge = all_rules.$c.$i.expire_reserves_charge %]
168
                        [% SET finedays = all_rules.$c.$i.finedays %]
170
                        [% SET finedays = all_rules.$c.$i.finedays %]
169
                        [% SET maxsuspensiondays = all_rules.$c.$i.maxsuspensiondays %]
171
                        [% SET maxsuspensiondays = all_rules.$c.$i.maxsuspensiondays %]
170
                        [% SET suspension_chargeperiod = all_rules.$c.$i.suspension_chargeperiod %]
172
                        [% SET suspension_chargeperiod = all_rules.$c.$i.suspension_chargeperiod %]
Lines 184-190 Link Here
184
                        [% SET rentaldiscount = all_rules.$c.$i.rentaldiscount %]
186
                        [% SET rentaldiscount = all_rules.$c.$i.rentaldiscount %]
185
                        [% SET decreaseloanholds = all_rules.$c.$i.decreaseloanholds %]
187
                        [% SET decreaseloanholds = all_rules.$c.$i.decreaseloanholds %]
186
188
187
                        [% SET show_rule = note || maxissueqty || maxonsiteissueqty || issuelength || daysmode || lengthunit || hardduedate || hardduedatecompare || fine || chargeperiod || chargeperiod_charge_at || firstremind || overduefinescap || cap_fine_to_replacement_price || finedays || maxsuspensiondays || suspension_chargeperiod || renewalsallowed || unseenrenewalsallowed || renewalperiod || norenewalbefore || auto_renew || no_auto_renewal_after || no_auto_renewal_after_hard_limit || reservesallowed || holds_per_day || holds_per_record || onshelfholds || opacitemholds || article_requests || rentaldiscount || decreaseloanholds %]
189
                        [% SET show_rule = note || maxissueqty || maxonsiteissueqty || issuelength || daysmode || lengthunit || hardduedate || hardduedatecompare || fine || chargeperiod || chargeperiod_charge_at || firstremind || overduefinescap || cap_fine_to_replacement_price || expire_reserves_charge || finedays || maxsuspensiondays || suspension_chargeperiod || renewalsallowed || unseenrenewalsallowed || renewalperiod || norenewalbefore || auto_renew || no_auto_renewal_after || no_auto_renewal_after_hard_limit || reservesallowed || holds_per_day || holds_per_record || onshelfholds || opacitemholds || article_requests || rentaldiscount || decreaseloanholds %]
188
                        [% IF show_rule %]
190
                        [% IF show_rule %]
189
                            [% SET row_count = row_count + 1 %]
191
                            [% SET row_count = row_count + 1 %]
190
                            <tr row_countd="row_[% row_count | html %]">
192
                            <tr row_countd="row_[% row_count | html %]">
Lines 275-280 Link Here
275
                                            <input type="checkbox" disabled="disabled" />
277
                                            <input type="checkbox" disabled="disabled" />
276
                                        [% END %]
278
                                        [% END %]
277
                                    </td>
279
                                    </td>
280
                                    <td>
281
                                        [% IF expire_reserves_charge.defined && expire_reserves_charge != '' %]
282
                                            [% expire_reserves_charge | html %]
283
                                        [% ELSE %]
284
                                            <a href="/cgi-bin/koha/admin/preferences.pl?op=search&amp;searchfield=ExpireReservesMaxPickUpDelayCharge">Use global</a>
285
                                        [% END %]
286
                                    </td>
278
                                    <td>[% finedays | html %]</td>
287
                                    <td>[% finedays | html %]</td>
279
                                    <td>[% maxsuspensiondays | html %]</td>
288
                                    <td>[% maxsuspensiondays | html %]</td>
280
                                    <td>[% suspension_chargeperiod | html %]</td>
289
                                    <td>[% suspension_chargeperiod | html %]</td>
Lines 427-432 Link Here
427
                    <td><input type="text" name="firstremind" id="firstremind" size="2" /> </td>
436
                    <td><input type="text" name="firstremind" id="firstremind" size="2" /> </td>
428
                    <td><input type="text" name="overduefinescap" id="overduefinescap" size="6" /> </td>
437
                    <td><input type="text" name="overduefinescap" id="overduefinescap" size="6" /> </td>
429
                    <td><input type="checkbox" name="cap_fine_to_replacement_price" id="cap_fine_to_replacement_price" /></td>
438
                    <td><input type="checkbox" name="cap_fine_to_replacement_price" id="cap_fine_to_replacement_price" /></td>
439
                    <td><input type="text" name="expire_reserves_charge" id="expire_reserves_charge" size="6"/></td>
430
                    <td><input type="text" name="finedays" id="fined" size="3" /> </td>
440
                    <td><input type="text" name="finedays" id="fined" size="3" /> </td>
431
                    <td><input type="text" name="maxsuspensiondays" id="maxsuspensiondays" size="3" /> </td>
441
                    <td><input type="text" name="maxsuspensiondays" id="maxsuspensiondays" size="3" /> </td>
432
                    <td><input type="text" name="suspension_chargeperiod" id="suspension_chargeperiod" size="3" /> </td>
442
                    <td><input type="text" name="suspension_chargeperiod" id="suspension_chargeperiod" size="3" /> </td>
Lines 500-505 Link Here
500
                      <th>Fine grace period</th>
510
                      <th>Fine grace period</th>
501
                      <th>Overdue fines cap (amount)</th>
511
                      <th>Overdue fines cap (amount)</th>
502
                      <th>Cap fine at replacement price</th>
512
                      <th>Cap fine at replacement price</th>
513
                      <th>Expire reserve charge</th>
503
                      <th>Suspension in days (day)</th>
514
                      <th>Suspension in days (day)</th>
504
                      <th>Max. suspension duration (day)</th>
515
                      <th>Max. suspension duration (day)</th>
505
                      <th>Suspension charging interval</th>
516
                      <th>Suspension charging interval</th>
Lines 1149-1158 Link Here
1149
                            // Remove potential previous input added
1160
                            // Remove potential previous input added
1150
                            $(current_column).find("input").remove();
1161
                            $(current_column).find("input").remove();
1151
                            $(current_column).append("<input type='hidden' name='"+name+"' value='"+val+"' />");
1162
                            $(current_column).append("<input type='hidden' name='"+name+"' value='"+val+"' />");
1152
                        } else if ( i == 5 || i == 6 || i == 26 || i == 27 || i == 28 ) {
1163
                        } else if ( i == 5 || i == 6 || i == 17 || i == 27 || i == 28 || i == 29 ) {
1153
                            // If the value is not an integer for
1164
                            // If the value is not an integer for
1154
                            //     - "Current checkouts allowed"
1165
                            //     - "Current checkouts allowed"
1155
                            //     - "Current on-site checkouts allowed"
1166
                            //     - "Current on-site checkouts allowed"
1167
                            //     - "Expire reserve charge"
1156
                            //     - "Holds allowed (total)"
1168
                            //     - "Holds allowed (total)"
1157
                            //     - "Holds allowed (daily)"
1169
                            //     - "Holds allowed (daily)"
1158
                            //     - "Holds per record (count)"
1170
                            //     - "Holds per record (count)"
(-)a/t/db_dependent/Holds/Holdfine.t (-1 / +258 lines)
Line 0 Link Here
0
- 
1
#!/usr/bin/perl
2
3
# Copyright The National Library of Finland, University of Helsinki 2020
4
# Copyright Petro Vashchuk <stalkernoid@gmail.com> 2020
5
#
6
# This file is part of Koha.
7
#
8
# Koha is free software; you can redistribute it and/or modify it under the
9
# terms of the GNU General Public License as published by the Free Software
10
# Foundation; either version 3 of the License, or (at your option) any later
11
# version.
12
#
13
# Koha is distributed in the hope that it will be useful, but WITHOUT ANY
14
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
15
# A PARTICULAR PURPOSE. See the GNU General Public License for more details.
16
#
17
# You should have received a copy of the GNU General Public License along
18
# with Koha; if not, see <http://www.gnu.org/licenses>.
19
20
use Modern::Perl;
21
22
use C4::Context;
23
use Koha::CirculationRules;
24
25
use Test::More tests => 13;
26
27
use t::lib::TestBuilder;
28
use t::lib::Mocks;
29
use Koha::Holds;
30
31
use Koha::Account;
32
use Koha::Account::DebitTypes;
33
34
BEGIN {
35
    use_ok('C4::Reserves');
36
}
37
38
my $schema = Koha::Database->schema;
39
$schema->storage->txn_begin;
40
my $dbh = C4::Context->dbh;
41
42
my $builder = t::lib::TestBuilder->new;
43
44
my $library1 = $builder->build({
45
    source => 'Branch',
46
});
47
my $library2 = $builder->build({
48
    source => 'Branch',
49
});
50
51
my $bib_title = "Test Title";
52
53
my $borrower = $builder->build({
54
    source => 'Borrower',
55
    value => {
56
        branchcode => $library1->{branchcode},
57
    }
58
});
59
60
my $itemtype1 = $builder->build({
61
    source => 'Itemtype',
62
    value => {}
63
});
64
my $itemtype2 = $builder->build({
65
    source => 'Itemtype',
66
    value => {}
67
});
68
my $itemtype3 = $builder->build({
69
    source => 'Itemtype',
70
    value => {}
71
});
72
my $itemtype4 = $builder->build({
73
    source => 'Itemtype',
74
    value => {}
75
});
76
77
my $borrowernumber = $borrower->{borrowernumber};
78
79
my $library_A_code = $library1->{branchcode};
80
81
my $biblio = $builder->build_sample_biblio({itemtype => $itemtype1->{itemtype}});
82
my $biblionumber = $biblio->biblionumber;
83
my $item1 = $builder->build_sample_item({
84
    biblionumber => $biblionumber,
85
    itype => $itemtype1->{itemtype},
86
    homebranch => $library_A_code,
87
    holdingbranch => $library_A_code
88
});
89
my $item2 = $builder->build_sample_item({
90
    biblionumber => $biblionumber,
91
    itype => $itemtype2->{itemtype},
92
    homebranch => $library_A_code,
93
    holdingbranch => $library_A_code
94
});
95
my $item3 = $builder->build_sample_item({
96
    biblionumber => $biblionumber,
97
    itype => $itemtype3->{itemtype},
98
    homebranch => $library_A_code,
99
    holdingbranch => $library_A_code
100
});
101
102
my $library_B_code = $library2->{branchcode};
103
104
my $biblio2 = $builder->build_sample_biblio({itemtype => $itemtype4->{itemtype}});
105
my $biblionumber2 = $biblio2->biblionumber;
106
my $item4 = $builder->build_sample_item({
107
    biblionumber => $biblionumber2,
108
    itype => $itemtype4->{itemtype},
109
    homebranch => $library_B_code,
110
    holdingbranch => $library_B_code
111
});
112
113
$dbh->do("DELETE FROM circulation_rules");
114
Koha::CirculationRules->set_rules(
115
    {
116
        itemtype     => $itemtype1->{itemtype},
117
        categorycode => undef,
118
        branchcode   => undef,
119
        rules        => {
120
            expire_reserves_charge => '111'
121
        }
122
    }
123
);
124
Koha::CirculationRules->set_rules(
125
    {
126
        itemtype     => $itemtype2->{itemtype},
127
        categorycode => undef,
128
        branchcode   => undef,
129
        rules        => {
130
            expire_reserves_charge => undef
131
        }
132
    }
133
);
134
Koha::CirculationRules->set_rules(
135
    {
136
        itemtype     => undef,
137
        categorycode => undef,
138
        branchcode   => $library_B_code,
139
        rules        => {
140
            expire_reserves_charge => '444'
141
        }
142
    }
143
);
144
145
t::lib::Mocks::mock_preference('ReservesControlBranch', 'ItemHomeLibrary');
146
147
my $reserve_id;
148
my $account;
149
my $status;
150
my $start_balance;
151
152
# TEST: Hold itemtype1 item
153
$reserve_id = AddReserve(
154
    {
155
        branchcode       => $library_A_code,
156
        borrowernumber   => $borrowernumber,
157
        biblionumber     => $biblionumber,
158
        priority         => 1,
159
        itemnumber       => $item1->itemnumber,
160
    }
161
);
162
163
$account = Koha::Account->new({ patron_id => $borrowernumber });
164
165
( $status ) = CheckReserves($item1->id);
166
is( $status, 'Reserved', "Hold for the itemtype1 created" );
167
168
$start_balance = $account->balance();
169
170
Koha::Holds->find( $reserve_id )->cancel({ charge_cancel_fee => 1 });
171
172
( $status ) = CheckReserves($item1->id);
173
is( $status, '', "Hold for the itemtype1 cancelled" );
174
175
is( $account->balance() - $start_balance, 111, "Used circulation rule for itemtype1" );
176
177
# TEST: circulation rule for itemtype2 has 'expire_reserves_charge' set undef, so it should use ExpireReservesMaxPickUpDelayCharge preference
178
t::lib::Mocks::mock_preference('ExpireReservesMaxPickUpDelayCharge', 222);
179
180
$reserve_id = AddReserve(
181
    {
182
        branchcode       => $library_A_code,
183
        borrowernumber   => $borrowernumber,
184
        biblionumber     => $biblionumber,
185
        priority         => 1,
186
        itemnumber       => $item2->itemnumber,
187
    }
188
);
189
190
$account = Koha::Account->new({ patron_id => $borrowernumber });
191
192
( $status ) = CheckReserves($item2->id);
193
is( $status, 'Reserved', "Hold for the itemtype2 created" );
194
195
$start_balance = $account->balance();
196
197
Koha::Holds->find( $reserve_id )->cancel({ charge_cancel_fee => 1 });
198
199
( $status ) = CheckReserves($item2->id);
200
is( $status, '', "Hold for the itemtype2 cancelled" );
201
202
is( $account->balance() - $start_balance, 222, "Used ExpireReservesMaxPickUpDelayCharge preference as expire_reserves_charge set to undef" );
203
204
# TEST: no circulation rules for itemtype3, it should use ExpireReservesMaxPickUpDelayCharge preference
205
t::lib::Mocks::mock_preference('ExpireReservesMaxPickUpDelayCharge', 333);
206
207
$reserve_id = AddReserve(
208
    {
209
        branchcode       => $library_A_code,
210
        borrowernumber   => $borrowernumber,
211
        biblionumber     => $biblionumber,
212
        priority         => 1,
213
        itemnumber       => $item3->itemnumber,
214
    }
215
);
216
217
$account = Koha::Account->new({ patron_id => $borrowernumber });
218
219
( $status ) = CheckReserves($item3->id);
220
is( $status, 'Reserved', "Hold for the itemtype3 created" );
221
222
$start_balance = $account->balance();
223
224
Koha::Holds->find( $reserve_id )->cancel({ charge_cancel_fee => 1 });
225
226
( $status ) = CheckReserves($item3->id);
227
is( $status, '', "Hold for the itemtype3 cancelled" );
228
229
is( $account->balance() - $start_balance, 333, "Used ExpireReservesMaxPickUpDelayCharge preference as there's no circulation rules for itemtype3" );
230
231
# TEST: circulation rule for itemtype4 with library_B_code
232
t::lib::Mocks::mock_preference('ExpireReservesMaxPickUpDelayCharge', 555);
233
234
$reserve_id = AddReserve(
235
    {
236
        branchcode       => $library_B_code,
237
        borrowernumber   => $borrowernumber,
238
        biblionumber     => $biblionumber2,
239
        priority         => 1,
240
        itemnumber       => $item4->itemnumber,
241
    }
242
);
243
244
$account = Koha::Account->new({ patron_id => $borrowernumber });
245
246
( $status ) = CheckReserves($item4->id);
247
is( $status, 'Reserved', "Hold for the itemtype4 created" );
248
249
$start_balance = $account->balance();
250
251
Koha::Holds->find( $reserve_id )->cancel({ charge_cancel_fee => 1 });
252
253
( $status ) = CheckReserves($item4->id);
254
is( $status, '', "Hold for the itemtype4 cancelled" );
255
256
is( $account->balance() - $start_balance, 444, "Used circulation rule for itemtype4 with library_B_code" );
257
258
$schema->storage->txn_rollback;

Return to bug 25711