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

(-)a/C4/Circulation.pm (-2 / +5 lines)
Lines 1042-1049 sub CanBookBeIssued { Link Here
1042
    #
1042
    #
1043
    # CHECKPREVCHECKOUT: CHECK IF ITEM HAS EVER BEEN LENT TO PATRON
1043
    # CHECKPREVCHECKOUT: CHECK IF ITEM HAS EVER BEEN LENT TO PATRON
1044
    #
1044
    #
1045
    $patron = Koha::Patrons->find( $patron->borrowernumber ); # FIXME Refetch just in case, to avoid regressions. But must not be needed
1045
    $patron = Koha::Patrons->find( $patron->borrowernumber )
1046
    if ( $patron->wants_check_for_previous_checkout && $patron->do_check_for_previous_checkout($item_unblessed) ) {
1046
        ;    # FIXME Refetch just in case, to avoid regressions. But must not be needed
1047
    if (   $patron->wants_check_for_previous_checkout($item_object)
1048
        && $patron->do_check_for_previous_checkout($item_unblessed) )
1049
    {
1047
        $needsconfirmation{PREVISSUE} = 1;
1050
        $needsconfirmation{PREVISSUE} = 1;
1048
    }
1051
    }
1049
1052
(-)a/Koha/Patron.pm (-8 / +13 lines)
Lines 45-50 use Koha::Encryption; Link Here
45
use Koha::Exceptions;
45
use Koha::Exceptions;
46
use Koha::Exceptions::Password;
46
use Koha::Exceptions::Password;
47
use Koha::Holds;
47
use Koha::Holds;
48
use Koha::ItemTypes;
48
use Koha::Old::Checkouts;
49
use Koha::Old::Checkouts;
49
use Koha::OverdueRules;
50
use Koha::OverdueRules;
50
use Koha::Patron::Attributes;
51
use Koha::Patron::Attributes;
Lines 798-825 Return 1 if Koha needs to perform PrevIssue checking, else 0. Link Here
798
=cut
799
=cut
799
800
800
sub wants_check_for_previous_checkout {
801
sub wants_check_for_previous_checkout {
801
    my ( $self ) = @_;
802
    my ( $self, $item ) = @_;
802
    my $syspref = C4::Context->preference("checkPrevCheckout");
803
    my $syspref = C4::Context->preference("checkPrevCheckout");
803
804
804
    # Simple cases
805
    # Simple cases
805
    ## Hard syspref trumps all
806
    ## Hard syspref trumps all
806
    return 1 if ($syspref eq 'hardyes');
807
    return 1 if ($syspref eq 'hardyes');
807
    return 0 if ($syspref eq 'hardno');
808
    return 0 if ($syspref eq 'hardno');
808
    ## Now, patron pref trumps all
809
810
    # Now, item pref trumps all
811
    if ($item) {
812
        my $itype = Koha::ItemTypes->find( $item->effective_itemtype );
813
        return 1 if ($itype->checkprevcheckout eq 'yes');
814
        return 0 if ($itype->checkprevcheckout eq 'no');
815
    }
816
817
    # Now, item type inherits -> determine patron preference
809
    return 1 if ($self->checkprevcheckout eq 'yes');
818
    return 1 if ($self->checkprevcheckout eq 'yes');
810
    return 0 if ($self->checkprevcheckout eq 'no');
819
    return 0 if ($self->checkprevcheckout eq 'no');
811
820
812
    # More complex: patron inherits -> determine category preference
821
    # More complex: item type inherit and patron inherits -> determine category preference
813
    my $checkPrevCheckoutByCat = $self->category->checkprevcheckout;
822
    my $checkPrevCheckoutByCat = $self->category->checkprevcheckout;
814
    return 1 if ($checkPrevCheckoutByCat eq 'yes');
823
    return 1 if ($checkPrevCheckoutByCat eq 'yes');
815
    return 0 if ($checkPrevCheckoutByCat eq 'no');
824
    return 0 if ($checkPrevCheckoutByCat eq 'no');
816
825
817
    # Finally: category preference is inherit, default to 0
826
    # Finally: category preference is inherit, default to 0
818
    if ($syspref eq 'softyes') {
827
    return $syspref eq 'softyes' ? 1 : 0;
819
        return 1;
820
    } else {
821
        return 0;
822
    }
823
}
828
}
824
829
825
=head3 do_check_for_previous_checkout
830
=head3 do_check_for_previous_checkout
(-)a/admin/itemtypes.pl (+4 lines)
Lines 99-104 if ( $op eq 'add_form' ) { Link Here
99
    my $rentalcharge_hourly_calendar = $input->param('rentalcharge_hourly_calendar') // 0;
99
    my $rentalcharge_hourly_calendar = $input->param('rentalcharge_hourly_calendar') // 0;
100
    my $automatic_checkin            = $input->param('automatic_checkin')            // 0;
100
    my $automatic_checkin            = $input->param('automatic_checkin')            // 0;
101
    my $bookable                     = $input->param('bookable')                     // 0;
101
    my $bookable                     = $input->param('bookable')                     // 0;
102
    my $automatic_checkin = $input->param('automatic_checkin') // 0;
103
    my $checkprevcheckout            = $input->param('checkprevcheckout');
102
104
103
    if ( $itemtype and $is_a_modif ) {    # it's a modification
105
    if ( $itemtype and $is_a_modif ) {    # it's a modification
104
        $itemtype->description($description);
106
        $itemtype->description($description);
Lines 120-125 if ( $op eq 'add_form' ) { Link Here
120
        $itemtype->rentalcharge_hourly_calendar($rentalcharge_hourly_calendar);
122
        $itemtype->rentalcharge_hourly_calendar($rentalcharge_hourly_calendar);
121
        $itemtype->automatic_checkin($automatic_checkin);
123
        $itemtype->automatic_checkin($automatic_checkin);
122
        $itemtype->bookable($bookable);
124
        $itemtype->bookable($bookable);
125
        $itemtype->checkprevcheckout($checkprevcheckout);
123
126
124
        eval {
127
        eval {
125
            $itemtype->store;
128
            $itemtype->store;
Lines 154-159 if ( $op eq 'add_form' ) { Link Here
154
                rentalcharge_hourly_calendar => $rentalcharge_hourly_calendar,
157
                rentalcharge_hourly_calendar => $rentalcharge_hourly_calendar,
155
                automatic_checkin            => $automatic_checkin,
158
                automatic_checkin            => $automatic_checkin,
156
                bookable                     => $bookable,
159
                bookable                     => $bookable,
160
                checkprevcheckout            => $checkprevcheckout,
157
            }
161
            }
158
        );
162
        );
159
        eval {
163
        eval {
(-)a/installer/data/mysql/atomicupdate/bug_20644-add_checkprevcheckout_to_items.pl (+22 lines)
Line 0 Link Here
1
use Modern::Perl;
2
3
return {
4
    bug_number  => "20644",
5
    description => "Add the column checkprevcheckout to itemtypes table",
6
    up          => sub {
7
        my ($args) = @_;
8
        my ( $dbh, $out ) = @$args{qw(dbh out)};
9
10
        if( !column_exists( 'itemtypes', 'checkprevcheckout' ) ) {
11
            $dbh->do(
12
                q{
13
                ALTER TABLE itemtypes
14
                ADD IF NOT EXISTS checkprevcheckout varchar(7) NOT NULL DEFAULT 'inherit' COMMENT 'produce a warning for a patron if a item of this type has previously been checked out to the same patron if ''yes'', not if ''no'', defer to category setting if ''inherit''.'
15
                AFTER automatic_checkin;
16
            }
17
            );
18
        }
19
20
        say $out "Added column 'itemtypes.checkprevcheckout'";
21
    },
22
};
(-)a/installer/data/mysql/kohastructure.sql (+1 lines)
Lines 4197-4202 CREATE TABLE `itemtypes` ( Link Here
4197
  `searchcategory` varchar(80) DEFAULT NULL COMMENT 'Group this item type with others with the same value on OPAC search options',
4197
  `searchcategory` varchar(80) DEFAULT NULL COMMENT 'Group this item type with others with the same value on OPAC search options',
4198
  `automatic_checkin` tinyint(1) NOT NULL DEFAULT 0 COMMENT 'If automatic checkin is enabled for items of this type',
4198
  `automatic_checkin` tinyint(1) NOT NULL DEFAULT 0 COMMENT 'If automatic checkin is enabled for items of this type',
4199
  `bookable` tinyint(1) NOT NULL DEFAULT 0 COMMENT 'Activate bookable feature for items related to this item type',
4199
  `bookable` tinyint(1) NOT NULL DEFAULT 0 COMMENT 'Activate bookable feature for items related to this item type',
4200
  `checkprevcheckout` varchar(7) NOT NULL DEFAULT 'inherit' COMMENT 'produce a warning for a patron if a item of this type has previously been checked out to the same patron if ''yes'', not if ''no'', defer to category setting if ''inherit''.',
4200
  PRIMARY KEY (`itemtype`),
4201
  PRIMARY KEY (`itemtype`),
4201
  UNIQUE KEY `itemtype` (`itemtype`),
4202
  UNIQUE KEY `itemtype` (`itemtype`),
4202
  KEY `itemtypes_ibfk_1` (`parent_type`),
4203
  KEY `itemtypes_ibfk_1` (`parent_type`),
(-)a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/itemtypes.tt (+20 lines)
Lines 353-358 Link Here
353
                    <div class="hint">Enter a summary that will overwrite the default one in search results lists. Example, for a website itemtype : </div>
353
                    <div class="hint">Enter a summary that will overwrite the default one in search results lists. Example, for a website itemtype : </div>
354
                    <div class="hint"><strong>&lt;a href="[856u]"&gt;open site&lt;/a&gt;</strong> will show the link just below the title</div>
354
                    <div class="hint"><strong>&lt;a href="[856u]"&gt;open site&lt;/a&gt;</strong> will show the link just below the title</div>
355
                </li>
355
                </li>
356
                [% IF Koha.Preference('CheckPrevCheckout') == 'softyes' || Koha.Preference('CheckPrevCheckout') == 'softno' %]
357
                    <li>
358
                        <label for="checkprevcheckout">Check for previous checkouts: </label>
359
                        <select name="checkprevcheckout" id="checkprevcheckout">
360
                            [% IF itemtype.checkprevcheckout == 'yes' %]
361
                            <option value="yes" selected="selected">Yes and override system preferences</option>
362
                            <option value="no">No and override system preferences</option>
363
                            <option value="inherit">Inherit from system preferences</option>
364
                            [% ELSIF itemtype.checkprevcheckout == 'no' %]
365
                            <option value="yes">Yes and override system preferences</option>
366
                            <option value="no" selected="selected">No and override system preferences</option>
367
                            <option value="inherit">Inherit from system preferences</option>
368
                            [% ELSE %]
369
                            <option value="yes">Yes and override system preferences</option>
370
                            <option value="no">No and override system preferences</option>
371
                            <option value="inherit" selected="selected">Inherit from system preferences</option>
372
                            [% END %]
373
                        </select>
374
                    </li>
375
                [% END %]
356
            </ol>
376
            </ol>
357
        </fieldset>
377
        </fieldset>
358
378
(-)a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/patrons.pref (-2 / +2 lines)
Lines 5-12 Patrons: Link Here
5
           default: no
5
           default: no
6
           choices:
6
           choices:
7
               hardyes: "Do"
7
               hardyes: "Do"
8
               softyes: "Unless overridden by patron category, do"
8
               softyes: "Unless overridden by patron category or by item type, do"
9
               softno: "Unless overridden by patron category, do not"
9
               softno: "Unless overridden by patron category or by item type, do not"
10
               hardno: "Do not"
10
               hardno: "Do not"
11
         - " check borrower checkout history to see if the current item has been checked out before."
11
         - " check borrower checkout history to see if the current item has been checked out before."
12
     -
12
     -
(-)a/t/db_dependent/Koha/ItemTypes.t (-3 / +33 lines)
Lines 19-25 Link Here
19
19
20
use Modern::Perl;
20
use Modern::Perl;
21
21
22
use Test::More tests => 15;
22
use Test::More tests => 18;
23
23
24
use t::lib::Mocks;
24
use t::lib::Mocks;
25
use t::lib::TestBuilder;
25
use t::lib::TestBuilder;
Lines 41-47 my $schema = $database->schema(); Link Here
41
$schema->txn_begin;
41
$schema->txn_begin;
42
42
43
my $builder     = t::lib::TestBuilder->new;
43
my $builder     = t::lib::TestBuilder->new;
44
my $initial_count = Koha::ItemTypes->search->count;
44
my $initial_count1 = Koha::ItemTypes->search->count;
45
45
46
my $parent1 = $builder->build_object({ class => 'Koha::ItemTypes', value => { description => 'description' } });
46
my $parent1 = $builder->build_object({ class => 'Koha::ItemTypes', value => { description => 'description' } });
47
my $child1  = $builder->build_object({
47
my $child1  = $builder->build_object({
Lines 103-109 is_deeply( $type->unblessed, $child2->unblessed, "We got back the same object" ) Link Here
103
t::lib::Mocks::mock_preference('language', 'en');
103
t::lib::Mocks::mock_preference('language', 'en');
104
t::lib::Mocks::mock_preference('OPACLanguages', 'en');
104
t::lib::Mocks::mock_preference('OPACLanguages', 'en');
105
my $itemtypes = Koha::ItemTypes->search_with_localization;
105
my $itemtypes = Koha::ItemTypes->search_with_localization;
106
is( $itemtypes->count, $initial_count + 4, 'We added 4 item types' );
106
is( $itemtypes->count, $initial_count1 + 4, 'We added 4 item types' );
107
my $first_itemtype = $itemtypes->next;
107
my $first_itemtype = $itemtypes->next;
108
is(
108
is(
109
    $first_itemtype->translated_description,
109
    $first_itemtype->translated_description,
Lines 150-153 subtest 'image_location' => sub { Link Here
150
    );
150
    );
151
};
151
};
152
152
153
# test for checkprevcheckout
154
my $initial_count2 = Koha::ItemTypes->search->count;
155
156
my @itypes = (
157
    Koha::ItemType->new(
158
        {
159
            itemtype => 'ITYPE1',
160
        }
161
    )->store,
162
    Koha::ItemType->new(
163
        {
164
            itemtype          => 'ITYPE2',
165
            checkprevcheckout => undef,
166
        }
167
    )->store,
168
);
169
170
for my $itype (@itypes) {
171
    my $retrived_itype = Koha::ItemTypes->find( $itype->itemtype );
172
    is(
173
        $retrived_itype->checkprevcheckout, 'inherit',
174
        'Koha::ItemType->store should default checkprevcheckout to inherit'
175
    );
176
}
177
178
is(
179
    Koha::ItemTypes->search->count, $initial_count2 + 2,
180
    'We added two item types with checkprevcheckout set to inherit'
181
);
182
153
$schema->txn_rollback;
183
$schema->txn_rollback;
(-)a/t/db_dependent/Patron/Borrower_PrevCheckout.t (-2 / +125 lines)
Lines 7-13 use Koha::Database; Link Here
7
use Koha::DateUtils qw( dt_from_string );
7
use Koha::DateUtils qw( dt_from_string );
8
use Koha::Patrons;
8
use Koha::Patrons;
9
9
10
use Test::More tests => 61;
10
use Test::More tests => 169;
11
11
12
use_ok('Koha::Patron');
12
use_ok('Koha::Patron');
13
13
Lines 42-47 my $inheritCatCode = $builder->build({ Link Here
42
    },
42
    },
43
});
43
});
44
44
45
my $yesItypeCode = $builder->build_object({
46
    class => 'Koha::ItemTypes',
47
    value => {
48
        checkprevcheckout => 'yes',
49
    }
50
});
51
52
my $noItypeCode = $builder->build_object({
53
    class => 'Koha::ItemTypes',
54
    value  => {
55
        checkprevcheckout => 'no',
56
    }
57
});
58
59
my $inheritItypeCode = $builder->build_object({
60
    class => 'Koha::ItemTypes',
61
    value  => {
62
        checkprevcheckout => 'inherit',
63
    }
64
});
65
45
# Create context for some tests late on in the file.
66
# Create context for some tests late on in the file.
46
my $library = $builder->build({ source => 'Branch' });
67
my $library = $builder->build({ source => 'Branch' });
47
my $staff = $builder->build({source => 'Borrower'});
68
my $staff = $builder->build({source => 'Borrower'});
Lines 50-55 t::lib::Mocks::mock_userenv({ branchcode => $library->{branchcode} }); Link Here
50
71
51
# wants_check_for_previous_checkout
72
# wants_check_for_previous_checkout
52
73
74
# We want to test the subroutine without passing the $item parameter
53
# We expect the following result matrix:
75
# We expect the following result matrix:
54
#
76
#
55
# (1/0 indicates the return value of WantsCheckPrevCheckout; i.e. 1 says we
77
# (1/0 indicates the return value of WantsCheckPrevCheckout; i.e. 1 says we
Lines 205-210 map { Link Here
205
    } @{$_->{categories}};
227
    } @{$_->{categories}};
206
} @{$mappings};
228
} @{$mappings};
207
229
230
231
# wants_check_for_previous_checkout
232
233
# We want to test the subroutine by passing the $item parameter
234
# We expect the following result matrix:
235
#
236
# (1/0 indicates the return value of WantsCheckPrevCheckout; i.e. 1 says we
237
# should check whether the item was previously issued)
238
#
239
# | System Preference | hardyes                                                                                                   |
240
# |-------------------+-----------------------------------------------------------------------------------------------------------|
241
# | Item Type Setting | yes                               | no                                | inherit                           |
242
# |-------------------+-----------------------------------+-----------------------------------+-----------------------------------|
243
# | Category Setting  | yes       | no        | inherit   | yes       | no        | inherit   | yes       | no        | inherit   |
244
# |-------------------+-----------+-----------+-----------+-----------+-----------+-----------+-----------+-----------+-----------|
245
# | Patron Setting    | y | n | i | y | n | i | y | n | i | y | n | i | y | n | i | y | n | i | y | n | i | y | n | i | y | n | i |
246
# |-------------------+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---|
247
# | Expected Result   | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 |
248
#
249
#
250
# | System Preference | softyes                                                                                                   |
251
# |-------------------+-----------------------------------------------------------------------------------------------------------|
252
# | Item Type Setting | yes                               | no                                | inherit                           |
253
# |-------------------+-----------------------------------+-----------------------------------+-----------------------------------|
254
# | Category Setting  | yes       | no        | inherit   | yes       | no        | inherit   | yes       | no        | inherit   |
255
# |-------------------+-----------+-----------+-----------+-----------+-----------+-----------+-----------+-----------+-----------|
256
# | Patron Setting    | y | n | i | y | n | i | y | n | i | y | n | i | y | n | i | y | n | i | y | n | i | y | n | i | y | n | i |
257
# |-------------------+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---|
258
# | Expected Result   | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 1 | 1 | 0 | 0 | 1 | 0 | 1 |
259
#
260
#
261
# | System Preference | softno                                                                                                    |
262
# |-------------------+-----------------------------------------------------------------------------------------------------------|
263
# | Item Type Setting | yes                               | no                                | inherit                           |
264
# |-------------------+-----------------------------------+-----------------------------------+-----------------------------------|
265
# | Category Setting  | yes       | no        | inherit   | yes       | no        | inherit   | yes       | no        | inherit   |
266
# |-------------------+-----------+-----------+-----------+-----------+-----------+-----------+-----------+-----------+-----------|
267
# | Patron Setting    | y | n | i | y | n | i | y | n | i | y | n | i | y | n | i | y | n | i | y | n | i | y | n | i | y | n | i |
268
# |-------------------+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---|
269
# | Expected Result   | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 1 | 1 | 0 | 0 | 1 | 0 | 0 |
270
#
271
#
272
# | System Preference | hardno                                                                                                    |
273
# |-------------------+-----------------------------------------------------------------------------------------------------------|
274
# | Item Type Setting | yes                               | no                                | inherit                           |
275
# |-------------------+-----------------------------------+-----------------------------------+-----------------------------------|
276
# | Category Setting  | yes       | no        | inherit   | yes       | no        | inherit   | yes       | no        | inherit   |
277
# |-------------------+-----------+-----------+-----------+-----------+-----------+-----------+-----------+-----------+-----------|
278
# | Patron Setting    | y | n | i | y | n | i | y | n | i | y | n | i | y | n | i | y | n | i | y | n | i | y | n | i | y | n | i |
279
# |-------------------+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---|
280
# | Expected Result   | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
281
282
my $itypeCode = {
283
    'yes' => $yesItypeCode->itemtype,
284
    'no' => $noItypeCode->itemtype,
285
    'inherit' => $inheritItypeCode->itemtype,
286
};
287
288
foreach my $syspref ('hardyes','softyes','softno','hardno'){
289
    t::lib::Mocks::mock_preference( 'checkprevcheckout', $syspref );
290
    foreach my $itemtype_setting ('yes','no','inherit'){ #itemtype Setting
291
        my $item      = $builder->build_sample_item( { itype => $itypeCode->{$itemtype_setting} } );
292
        foreach my $categorie_settings('yes','no','inherit'){
293
            my $catCode = $categorie_settings . 'Cat';
294
            foreach my $patron_setting('yes','no','inherit'){
295
                my $result = undef;
296
                $result = 1 if($syspref eq 'hardyes');
297
                $result = 0 if($syspref eq 'hardno');
298
                $result = 1 if(!defined $result && $itemtype_setting eq 'yes');
299
                $result = 0 if(!defined $result && $itemtype_setting eq 'no');
300
                $result = 1 if(!defined $result && $patron_setting eq 'yes');
301
                $result = 0 if(!defined $result && $patron_setting eq 'no');
302
                $result = 1 if(!defined $result && $categorie_settings eq 'yes');
303
                $result = 0 if(!defined $result && $categorie_settings eq 'no');
304
                $result = 1 if(!defined $result && $syspref eq 'softyes');
305
                $result = 0 if(!defined $result && $syspref eq 'softno');
306
                my $kpatron = $builder->build(
307
                    {
308
                        source => 'Borrower',
309
                        value  => {
310
                            checkprevcheckout => $patron_setting,
311
                            categorycode      => $catCode,
312
                        },
313
                    }
314
                );
315
                my $patron = Koha::Patrons->find( $kpatron->{borrowernumber} );
316
                is(
317
                    $patron->wants_check_for_previous_checkout($item), $result,
318
                    "Predicate with syspref "
319
                        . $syspref
320
                        . ", cat "
321
                        . $catCode
322
                        . ", patron "
323
                        . $patron_setting
324
                        . ", item type "
325
                        . $itypeCode->{$itemtype_setting}
326
                );
327
            }
328
        }
329
    }
330
}
331
208
# do_check_for_previous_checkout
332
# do_check_for_previous_checkout
209
333
210
# We want to test:
334
# We want to test:
211
- 

Return to bug 20644