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

(-)a/Koha/CirculationRules.pm (+3 lines)
Lines 96-101 our $RULE_KINDS = { Link Here
96
    cap_fine_to_replacement_price => {
96
    cap_fine_to_replacement_price => {
97
        scope => [ 'branchcode', 'categorycode', 'itemtype' ],
97
        scope => [ 'branchcode', 'categorycode', 'itemtype' ],
98
    },
98
    },
99
    expire_reserves_charge => {
100
        scope => [ 'branchcode', 'categorycode', 'itemtype' ],
101
    },
99
    chargeperiod => {
102
    chargeperiod => {
100
        scope => [ 'branchcode', 'categorycode', 'itemtype' ],
103
        scope => [ 'branchcode', 'categorycode', 'itemtype' ],
101
    },
104
    },
(-)a/Koha/Hold.pm (-1 / +18 lines)
Lines 739-745 sub cancel { Link Here
739
            C4::Reserves::_FixPriority({ biblionumber => $self->biblionumber });
739
            C4::Reserves::_FixPriority({ biblionumber => $self->biblionumber });
740
740
741
            # and, if desired, charge a cancel fee
741
            # and, if desired, charge a cancel fee
742
            my $charge = C4::Context->preference("ExpireReservesMaxPickUpDelayCharge");
742
            my $charge;
743
            if (my $item = $self->item) {
744
                my $branchcode = C4::Reserves::GetReservesControlBranch($item->unblessed, $self->borrower->unblessed);
745
746
                my $rule = Koha::CirculationRules->get_effective_rule(
747
                    {
748
                        categorycode => $self->borrower->categorycode,
749
                        itemtype     => $item->effective_itemtype,
750
                        branchcode   => $branchcode,
751
                        rule_name    => 'expire_reserves_charge',
752
                    }
753
                );
754
                my $rule_value = $rule && $rule->rule_value // '';
755
                $charge = $rule_value ne '' ? $rule_value : C4::Context->preference("ExpireReservesMaxPickUpDelayCharge");
756
            } else {
757
                $charge = C4::Context->preference("ExpireReservesMaxPickUpDelayCharge");
758
            }
759
743
            if ( $charge && $params->{'charge_cancel_fee'} ) {
760
            if ( $charge && $params->{'charge_cancel_fee'} ) {
744
                my $account =
761
                my $account =
745
                  Koha::Account->new( { patron_id => $self->borrowernumber } );
762
                  Koha::Account->new( { patron_id => $self->borrowernumber } );
(-)a/admin/smart-rules.pl (+3 lines)
Lines 107-112 if ($op eq 'delete') { Link Here
107
                opacitemholds                    => undef,
107
                opacitemholds                    => undef,
108
                overduefinescap                  => undef,
108
                overduefinescap                  => undef,
109
                cap_fine_to_replacement_price    => undef,
109
                cap_fine_to_replacement_price    => undef,
110
                expire_reserves_charge           => undef,
110
                article_requests                 => undef,
111
                article_requests                 => undef,
111
                note                             => undef,
112
                note                             => undef,
112
                recalls_allowed                  => undef,
113
                recalls_allowed                  => undef,
Lines 290-295 elsif ($op eq 'add') { Link Here
290
    my $overduefinescap = $input->param('overduefinescap')
291
    my $overduefinescap = $input->param('overduefinescap')
291
        && ( $input->param('overduefinescap') + 0 ) > 0 ? sprintf( "%.02f", $input->param('overduefinescap') ) : q{};
292
        && ( $input->param('overduefinescap') + 0 ) > 0 ? sprintf( "%.02f", $input->param('overduefinescap') ) : q{};
292
    my $cap_fine_to_replacement_price = ($input->param('cap_fine_to_replacement_price') || q{}) eq 'on';
293
    my $cap_fine_to_replacement_price = ($input->param('cap_fine_to_replacement_price') || q{}) eq 'on';
294
    my $expire_reserves_charge = $input->param('expire_reserves_charge') // q{};
293
    my $note = $input->param('note');
295
    my $note = $input->param('note');
294
    my $decreaseloanholds = $input->param('decreaseloanholds') || q{};
296
    my $decreaseloanholds = $input->param('decreaseloanholds') || q{};
295
    my $recalls_allowed = $input->param('recalls_allowed');
297
    my $recalls_allowed = $input->param('recalls_allowed');
Lines 330-335 elsif ($op eq 'add') { Link Here
330
        opacitemholds                    => $opacitemholds,
332
        opacitemholds                    => $opacitemholds,
331
        overduefinescap                  => $overduefinescap,
333
        overduefinescap                  => $overduefinescap,
332
        cap_fine_to_replacement_price    => $cap_fine_to_replacement_price,
334
        cap_fine_to_replacement_price    => $cap_fine_to_replacement_price,
335
        expire_reserves_charge           => $expire_reserves_charge,
333
        article_requests                 => $article_requests,
336
        article_requests                 => $article_requests,
334
        note                             => $note,
337
        note                             => $note,
335
        decreaseloanholds                => $decreaseloanholds,
338
        decreaseloanholds                => $decreaseloanholds,
(-)a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/smart-rules.tt (-2 / +14 lines)
Lines 128-133 Link Here
128
                            <th>Fine/suspension grace period</th>
128
                            <th>Fine/suspension grace period</th>
129
                            <th>Overdue fines cap (amount)</th>
129
                            <th>Overdue fines cap (amount)</th>
130
                            <th>Cap fine at replacement price</th>
130
                            <th>Cap fine at replacement price</th>
131
                            <th>Expire reserve charge</th>
131
                            <th>Suspension in days (day)</th>
132
                            <th>Suspension in days (day)</th>
132
                            <th>Max. suspension duration (day)</th>
133
                            <th>Max. suspension duration (day)</th>
133
                            <th>Suspension charging interval</th>
134
                            <th>Suspension charging interval</th>
Lines 181-186 Link Here
181
                                    [% SET firstremind = all_rules.$c.$i.firstremind %]
182
                                    [% SET firstremind = all_rules.$c.$i.firstremind %]
182
                                    [% SET overduefinescap = all_rules.$c.$i.overduefinescap %]
183
                                    [% SET overduefinescap = all_rules.$c.$i.overduefinescap %]
183
                                    [% SET cap_fine_to_replacement_price = all_rules.$c.$i.cap_fine_to_replacement_price %]
184
                                    [% SET cap_fine_to_replacement_price = all_rules.$c.$i.cap_fine_to_replacement_price %]
185
                                    [% SET expire_reserves_charge = all_rules.$c.$i.expire_reserves_charge %]
184
                                    [% SET finedays = all_rules.$c.$i.finedays %]
186
                                    [% SET finedays = all_rules.$c.$i.finedays %]
185
                                    [% SET maxsuspensiondays = all_rules.$c.$i.maxsuspensiondays %]
187
                                    [% SET maxsuspensiondays = all_rules.$c.$i.maxsuspensiondays %]
186
                                    [% SET suspension_chargeperiod = all_rules.$c.$i.suspension_chargeperiod %]
188
                                    [% SET suspension_chargeperiod = all_rules.$c.$i.suspension_chargeperiod %]
Lines 207-213 Link Here
207
                                    [% SET recall_overdue_fine = all_rules.$c.$i.recall_overdue_fine %]
209
                                    [% SET recall_overdue_fine = all_rules.$c.$i.recall_overdue_fine %]
208
                                    [% SET recall_shelf_time = all_rules.$c.$i.recall_shelf_time %]
210
                                    [% SET recall_shelf_time = all_rules.$c.$i.recall_shelf_time %]
209
211
210
                                    [% 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 || noautorenewalbefore || 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 || recalls_allowed || recalls_per_record || on_shelf_recalls || recall_due_date_interval || recall_overdue_fine || recall_shelf_time %]
212
                                    [% 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 || noautorenewalbefore || 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 || recalls_allowed || recalls_per_record || on_shelf_recalls || recall_due_date_interval || recall_overdue_fine || recall_shelf_time %]
211
                                    [% IF show_rule %]
213
                                    [% IF show_rule %]
212
                                        [% SET row_count = row_count + 1 %]
214
                                        [% SET row_count = row_count + 1 %]
213
                                        <tr row_countd="row_[% row_count | html %]">
215
                                        <tr row_countd="row_[% row_count | html %]">
Lines 306-311 Link Here
306
                                                        <input type="checkbox" disabled="disabled" />
308
                                                        <input type="checkbox" disabled="disabled" />
307
                                                    [% END %]
309
                                                    [% END %]
308
                                                </td>
310
                                                </td>
311
                                                <td>
312
                                                    [% IF expire_reserves_charge.defined && expire_reserves_charge != '' %]
313
                                                        [% expire_reserves_charge | html %]
314
                                                    [% ELSE %]
315
                                                        <a href="/cgi-bin/koha/admin/preferences.pl?op=search&amp;searchfield=ExpireReservesMaxPickUpDelayCharge">Use global</a>
316
                                                    [% END %]
317
                                                </td>
309
                                                <td>[% finedays | html %]</td>
318
                                                <td>[% finedays | html %]</td>
310
                                                <td>[% maxsuspensiondays | html %]</td>
319
                                                <td>[% maxsuspensiondays | html %]</td>
311
                                                <td>[% suspension_chargeperiod | html %]</td>
320
                                                <td>[% suspension_chargeperiod | html %]</td>
Lines 481-486 Link Here
481
                                <td><input type="text" name="firstremind" id="firstremind" size="2" /> </td>
490
                                <td><input type="text" name="firstremind" id="firstremind" size="2" /> </td>
482
                                <td><input type="text" name="overduefinescap" id="overduefinescap" size="6" inputmode="decimal" pattern="^\d+(\.\d{2})?$" /> </td>
491
                                <td><input type="text" name="overduefinescap" id="overduefinescap" size="6" inputmode="decimal" pattern="^\d+(\.\d{2})?$" /> </td>
483
                                <td><input type="checkbox" name="cap_fine_to_replacement_price" id="cap_fine_to_replacement_price" /></td>
492
                                <td><input type="checkbox" name="cap_fine_to_replacement_price" id="cap_fine_to_replacement_price" /></td>
493
                                <td><input type="text" name="expire_reserves_charge" id="expire_reserves_charge" size="6"/></td>
484
                                <td><input type="text" name="finedays" id="fined" size="3" /> </td>
494
                                <td><input type="text" name="finedays" id="fined" size="3" /> </td>
485
                                <td><input type="text" name="maxsuspensiondays" id="maxsuspensiondays" size="3" /> </td>
495
                                <td><input type="text" name="maxsuspensiondays" id="maxsuspensiondays" size="3" /> </td>
486
                                <td><input type="text" name="suspension_chargeperiod" id="suspension_chargeperiod" size="3" /> </td>
496
                                <td><input type="text" name="suspension_chargeperiod" id="suspension_chargeperiod" size="3" /> </td>
Lines 571-576 Link Here
571
                                  <th>Fine/suspension grace period</th>
581
                                  <th>Fine/suspension grace period</th>
572
                                  <th>Overdue fines cap (amount)</th>
582
                                  <th>Overdue fines cap (amount)</th>
573
                                  <th>Cap fine at replacement price</th>
583
                                  <th>Cap fine at replacement price</th>
584
                                  <th>Expire reserve charge</th>
574
                                  <th>Suspension in days (day)</th>
585
                                  <th>Suspension in days (day)</th>
575
                                  <th>Max. suspension duration (day)</th>
586
                                  <th>Max. suspension duration (day)</th>
576
                                  <th>Suspension charging interval</th>
587
                                  <th>Suspension charging interval</th>
Lines 1530-1539 Link Here
1530
                            // Remove potential previous input added
1541
                            // Remove potential previous input added
1531
                            $(current_column).find("input").remove();
1542
                            $(current_column).find("input").remove();
1532
                            $(current_column).append("<input type='hidden' name='"+name+"' value='"+val+"' />");
1543
                            $(current_column).append("<input type='hidden' name='"+name+"' value='"+val+"' />");
1533
                        } else if ( i == 5 || i == 6 || i == 26 || i == 27 || i == 28 ) {
1544
                        } else if ( i == 5 || i == 6 || i == 17 || i == 27 || i == 28 || i == 29 ) {
1534
                            // If the value is not an integer for
1545
                            // If the value is not an integer for
1535
                            //     - "Current checkouts allowed"
1546
                            //     - "Current checkouts allowed"
1536
                            //     - "Current on-site checkouts allowed"
1547
                            //     - "Current on-site checkouts allowed"
1548
                            //     - "Expire reserve charge"
1537
                            //     - "Holds allowed (total)"
1549
                            //     - "Holds allowed (total)"
1538
                            //     - "Holds allowed (daily)"
1550
                            //     - "Holds allowed (daily)"
1539
                            //     - "Holds per record (count)"
1551
                            //     - "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