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

(-)a/C4/Circulation.pm (+8 lines)
Lines 909-914 sub CanBookBeIssued { Link Here
909
    }
909
    }
910
910
911
    #
911
    #
912
    # CHECKPREVCHECKOUT: CHECK IF ITEM HAS EVER BEEN LENT TO PATRON
913
    #
914
    my $patron = Koha::Patrons->find($borrower->{borrowernumber});
915
    my $wantsCheckPrevCheckout = $patron->wantsCheckPrevCheckout;
916
    $needsconfirmation{PREVISSUE} = 1
917
        if ($wantsCheckPrevCheckout and $patron->doCheckPrevCheckout($item));
918
919
    #
912
    # ITEM CHECKING
920
    # ITEM CHECKING
913
    #
921
    #
914
    if ( $item->{'notforloan'} )
922
    if ( $item->{'notforloan'} )
(-)a/Koha/Patron.pm (+73 lines)
Lines 1-6 Link Here
1
package Koha::Patron;
1
package Koha::Patron;
2
2
3
# Copyright ByWater Solutions 2014
3
# Copyright ByWater Solutions 2014
4
# Copyright PTFS Europe 2016
4
#
5
#
5
# This file is part of Koha.
6
# This file is part of Koha.
6
#
7
#
Lines 21-27 use Modern::Perl; Link Here
21
22
22
use Carp;
23
use Carp;
23
24
25
use C4::Context;
24
use Koha::Database;
26
use Koha::Database;
27
use Koha::Issues;
28
use Koha::OldIssues;
25
use Koha::Patron::Images;
29
use Koha::Patron::Images;
26
30
27
use base qw(Koha::Object);
31
use base qw(Koha::Object);
Lines 94-99 sub siblings { Link Here
94
    );
98
    );
95
}
99
}
96
100
101
=head3 wantsCheckPrevCheckout
102
103
    $wantsCheckPrevCheckout = $patron->wantsCheckPrevCheckout;
104
105
Return 1 if Koha needs to perform PrevIssue checking, else 0.
106
107
=cut
108
109
sub wantsCheckPrevCheckout {
110
    my ( $self ) = @_;
111
    my $syspref = C4::Context->preference("checkPrevCheckout");
112
113
    # Simple cases
114
    ## Hard syspref trumps all
115
    return 1 if ($syspref eq 'hardyes');
116
    return 0 if ($syspref eq 'hardno');
117
    ## Now, patron pref trumps all
118
    return 1 if ($self->checkprevcheckout eq 'yes');
119
    return 0 if ($self->checkprevcheckout eq 'no');
120
121
    # More complex: patron inherits -> determine category preference
122
    my $checkPrevCheckoutByCat = Koha::Patron::Categories
123
        ->find($self->categorycode)->checkprevcheckout;
124
    return 1 if ($checkPrevCheckoutByCat eq 'yes');
125
    return 0 if ($checkPrevCheckoutByCat eq 'no');
126
127
    # Finally: category preference is inherit, default to 0
128
    if ($syspref eq 'softyes') {
129
        return 1;
130
    } else {
131
        return 0;
132
    }
133
}
134
135
=head3 doCheckPrevCheckout
136
137
    $checkPrevCheckout = $patron->doCheckPrevCheckout($item);
138
139
Return 1 if the bib associated with $ITEM has previously been checked out to
140
$PATRON, 0 otherwise.
141
142
=cut
143
144
sub doCheckPrevCheckout {
145
    my ( $self, $item ) = @_;
146
147
    # Find all items for bib and extract item numbers.
148
    my @items = Koha::Items->search({biblionumber => $item->{biblionumber}});
149
    my @item_nos;
150
    foreach my $item (@items) {
151
        push @item_nos, $item->itemnumber;
152
    }
153
154
    # Create (old)issues search criteria
155
    my $criteria = {
156
        borrowernumber => $self->borrowernumber,
157
        itemnumber => \@item_nos,
158
    };
159
160
    # Check current issues table
161
    my $issues = Koha::Issues->search($criteria);
162
    return 1 if $issues->count; # 0 || N
163
164
    # Check old issues table
165
    my $old_issues = Koha::OldIssues->search($criteria);
166
    return $old_issues->count;  # 0 || N
167
}
168
97
=head3 type
169
=head3 type
98
170
99
=cut
171
=cut
Lines 105-110 sub _type { Link Here
105
=head1 AUTHOR
177
=head1 AUTHOR
106
178
107
Kyle M Hall <kyle@bywatersolutions.com>
179
Kyle M Hall <kyle@bywatersolutions.com>
180
Alex Sassmannshausen <alex.sassmannshausen@ptfs-europe.com>
108
181
109
=cut
182
=cut
110
183
(-)a/admin/categories.pl (+3 lines)
Lines 90-95 elsif ( $op eq 'add_validate' ) { Link Here
90
    my $overduenoticerequired = $input->param('overduenoticerequired');
90
    my $overduenoticerequired = $input->param('overduenoticerequired');
91
    my $category_type = $input->param('category_type');
91
    my $category_type = $input->param('category_type');
92
    my $BlockExpiredPatronOpacActions = $input->param('BlockExpiredPatronOpacActions');
92
    my $BlockExpiredPatronOpacActions = $input->param('BlockExpiredPatronOpacActions');
93
    my $checkPrevCheckout = $input->param('checkprevcheckout');
93
    my $default_privacy = $input->param('default_privacy');
94
    my $default_privacy = $input->param('default_privacy');
94
    my @branches = grep { $_ ne q{} } $input->param('branches');
95
    my @branches = grep { $_ ne q{} } $input->param('branches');
95
96
Lines 113-118 elsif ( $op eq 'add_validate' ) { Link Here
113
        $category->overduenoticerequired($overduenoticerequired);
114
        $category->overduenoticerequired($overduenoticerequired);
114
        $category->category_type($category_type);
115
        $category->category_type($category_type);
115
        $category->BlockExpiredPatronOpacActions($BlockExpiredPatronOpacActions);
116
        $category->BlockExpiredPatronOpacActions($BlockExpiredPatronOpacActions);
117
        $category->checkprevcheckout($checkPrevCheckout);
116
        $category->default_privacy($default_privacy);
118
        $category->default_privacy($default_privacy);
117
        eval {
119
        eval {
118
            $category->store;
120
            $category->store;
Lines 138-143 elsif ( $op eq 'add_validate' ) { Link Here
138
            overduenoticerequired => $overduenoticerequired,
140
            overduenoticerequired => $overduenoticerequired,
139
            category_type => $category_type,
141
            category_type => $category_type,
140
            BlockExpiredPatronOpacActions => $BlockExpiredPatronOpacActions,
142
            BlockExpiredPatronOpacActions => $BlockExpiredPatronOpacActions,
143
            checkprevcheckout => $checkPrevCheckout,
141
            default_privacy => $default_privacy,
144
            default_privacy => $default_privacy,
142
        });
145
        });
143
        eval {
146
        eval {
(-)a/installer/data/mysql/atomicupdate/checkPrevCheckout.sql (+15 lines)
Line 0 Link Here
1
INSERT INTO systempreferences (variable,value,options,explanation,type)
2
VALUES('CheckPrevCheckout','hardno','hardyes|softyes|softno|hardno','By default, for every item checked out, should we warn if the patron has checked out that item in the past?','Choice');
3
4
ALTER TABLE categories
5
ADD COLUMN `checkprevcheckout` varchar(7) NOT NULL default 'inherit'
6
AFTER `default_privacy`;
7
8
ALTER TABLE borrowers
9
ADD COLUMN `checkprevcheckout` varchar(7) NOT NULL default 'inherit'
10
AFTER `privacy_guarantor_checkouts`;
11
12
ALTER TABLE deletedborrowers
13
ADD COLUMN `checkprevcheckout` varchar(7) NOT NULL default 'inherit'
14
AFTER `privacy_guarantor_checkouts`;
15
(-)a/installer/data/mysql/kohastructure.sql (+3 lines)
Lines 266-271 CREATE TABLE `borrowers` ( -- this table includes information about your patrons Link Here
266
  `sms_provider_id` int(11) DEFAULT NULL, -- the provider of the mobile phone number defined in smsalertnumber
266
  `sms_provider_id` int(11) DEFAULT NULL, -- the provider of the mobile phone number defined in smsalertnumber
267
  `privacy` integer(11) DEFAULT '1' NOT NULL, -- patron/borrower's privacy settings related to their reading history
267
  `privacy` integer(11) DEFAULT '1' NOT NULL, -- patron/borrower's privacy settings related to their reading history
268
  `privacy_guarantor_checkouts` tinyint(1) NOT NULL DEFAULT '0', -- controls if relatives can see this patron's checkouts
268
  `privacy_guarantor_checkouts` tinyint(1) NOT NULL DEFAULT '0', -- controls if relatives can see this patron's checkouts
269
  `checkprevcheckout` varchar(7) NOT NULL default 'inherit', -- produce a warning for this patron if this item has previously been checked out to this patron if 'yes', not if 'no', defer to category setting if 'inherit'.
269
  UNIQUE KEY `cardnumber` (`cardnumber`),
270
  UNIQUE KEY `cardnumber` (`cardnumber`),
270
  PRIMARY KEY `borrowernumber` (`borrowernumber`),
271
  PRIMARY KEY `borrowernumber` (`borrowernumber`),
271
  KEY `categorycode` (`categorycode`),
272
  KEY `categorycode` (`categorycode`),
Lines 505-510 CREATE TABLE `categories` ( -- this table shows information related to Koha patr Link Here
505
  `category_type` varchar(1) NOT NULL default 'A', -- type of Koha patron (Adult, Child, Professional, Organizational, Statistical, Staff)
506
  `category_type` varchar(1) NOT NULL default 'A', -- type of Koha patron (Adult, Child, Professional, Organizational, Statistical, Staff)
506
  `BlockExpiredPatronOpacActions` tinyint(1) NOT NULL default '-1', -- wheither or not a patron of this category can renew books or place holds once their card has expired. 0 means they can, 1 means they cannot, -1 means use syspref BlockExpiredPatronOpacActions
507
  `BlockExpiredPatronOpacActions` tinyint(1) NOT NULL default '-1', -- wheither or not a patron of this category can renew books or place holds once their card has expired. 0 means they can, 1 means they cannot, -1 means use syspref BlockExpiredPatronOpacActions
507
  `default_privacy` ENUM( 'default', 'never', 'forever' ) NOT NULL DEFAULT 'default', -- Default privacy setting for this patron category
508
  `default_privacy` ENUM( 'default', 'never', 'forever' ) NOT NULL DEFAULT 'default', -- Default privacy setting for this patron category
509
  `checkprevcheckout` varchar(7) NOT NULL default 'inherit', -- produce a warning for this patron category if this item has previously been checked out to this patron if 'yes', not if 'no', defer to syspref setting if 'inherit'.
508
  PRIMARY KEY  (`categorycode`),
510
  PRIMARY KEY  (`categorycode`),
509
  UNIQUE KEY `categorycode` (`categorycode`)
511
  UNIQUE KEY `categorycode` (`categorycode`)
510
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
512
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
Lines 918-923 CREATE TABLE `deletedborrowers` ( -- stores data related to the patrons/borrower Link Here
918
  `sms_provider_id` int(11) DEFAULT NULL, -- the provider of the mobile phone number defined in smsalertnumber
920
  `sms_provider_id` int(11) DEFAULT NULL, -- the provider of the mobile phone number defined in smsalertnumber
919
  `privacy` integer(11) DEFAULT '1' NOT NULL, -- patron/borrower's privacy settings related to their reading history  KEY `borrowernumber` (`borrowernumber`),
921
  `privacy` integer(11) DEFAULT '1' NOT NULL, -- patron/borrower's privacy settings related to their reading history  KEY `borrowernumber` (`borrowernumber`),
920
  `privacy_guarantor_checkouts` tinyint(1) NOT NULL DEFAULT '0', -- controls if relatives can see this patron's checkouts
922
  `privacy_guarantor_checkouts` tinyint(1) NOT NULL DEFAULT '0', -- controls if relatives can see this patron's checkouts
923
  `checkprevcheckout` varchar(7) NOT NULL default 'inherit', -- produce a warning for this patron if this item has previously been checked out to this patron if 'yes', not if 'no', defer to category setting if 'inherit'.
921
  KEY borrowernumber (borrowernumber),
924
  KEY borrowernumber (borrowernumber),
922
  KEY `cardnumber` (`cardnumber`),
925
  KEY `cardnumber` (`cardnumber`),
923
  KEY `sms_provider_id` (`sms_provider_id`)
926
  KEY `sms_provider_id` (`sms_provider_id`)
(-)a/installer/data/mysql/sysprefs.sql (+1 lines)
Lines 90-95 INSERT INTO systempreferences ( `variable`, `value`, `options`, `explanation`, ` Link Here
90
('CatalogModuleRelink','0',NULL,'If OFF the linker will never replace the authids that are set in the cataloging module.','YesNo'),
90
('CatalogModuleRelink','0',NULL,'If OFF the linker will never replace the authids that are set in the cataloging module.','YesNo'),
91
('CataloguingLog','1',NULL,'If ON, log edit/create/delete actions on bibliographic data. WARNING: this feature is very resource consuming.','YesNo'),
91
('CataloguingLog','1',NULL,'If ON, log edit/create/delete actions on bibliographic data. WARNING: this feature is very resource consuming.','YesNo'),
92
('checkdigit','none','none|katipo','If ON, enable checks on patron cardnumber: none or \"Katipo\" style checks','Choice'),
92
('checkdigit','none','none|katipo','If ON, enable checks on patron cardnumber: none or \"Katipo\" style checks','Choice'),
93
('CheckPrevCheckout','hardno','hardyes|softyes|softno|hardno','By default, for every item checked out, should we warn if the patron has borrowed that item in the past?','Choice'),
93
('CircAutocompl','1',NULL,'If ON, autocompletion is enabled for the Circulation input','YesNo'),
94
('CircAutocompl','1',NULL,'If ON, autocompletion is enabled for the Circulation input','YesNo'),
94
('CircAutoPrintQuickSlip','qslip',NULL,'Choose what should happen when an empty barcode field is submitted in circulation: Display a print quick slip window, Display a print slip window or Clear the screen.','Choice'),
95
('CircAutoPrintQuickSlip','qslip',NULL,'Choose what should happen when an empty barcode field is submitted in circulation: Display a print quick slip window, Display a print slip window or Clear the screen.','Choice'),
95
('CircControl','ItemHomeLibrary','PickupLibrary|PatronLibrary|ItemHomeLibrary','Specify the agency that controls the circulation and fines policy','Choice'),
96
('CircControl','ItemHomeLibrary','PickupLibrary|PatronLibrary|ItemHomeLibrary','Specify the agency that controls the circulation and fines policy','Choice'),
(-)a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/categories.tt (+53 lines)
Lines 276-281 Link Here
276
                        Choose whether patrons of this category be blocked from public catalog actions such as renewing and placing holds when their cards have expired.
276
                        Choose whether patrons of this category be blocked from public catalog actions such as renewing and placing holds when their cards have expired.
277
                    </span>
277
                    </span>
278
                </li>
278
                </li>
279
                [% IF ( Koha.Preference('CheckPrevCheckout') == 'softyes' || Koha.Preference('CheckPrevCheckout') == 'softno' )  %]
280
                  <li><label for="checkprevcheckout">Check for previous checkouts: </label>
281
                      <select name="checkprevcheckout" id="checkprevcheckout">
282
                          [% IF category.checkprevcheckout == 'yes' %]
283
                          <option value="yes" selected="selected">Yes and try to override system preferences</option>
284
                          <option value="no">No and try to override system preferences</option>
285
                          <option value="inherit">Inherit from system preferences</option>
286
                          [% ELSIF category.checkprevcheckout == 'no' %]
287
                          <option value="yes">Yes and try to override system preferences</option>
288
                          <option value="no" selected="selected">No and try to override system preferences</option>
289
                          <option value="inherit">Inherit from system preferences</option>
290
                          [% ELSE %]
291
                          <option value="yes">Yes and try to override system preferences</option>
292
                          <option value="no">No and try to override system preferences</option>
293
                          <option value="inherit" selected="selected">Inherit from system preferences</option>
294
                          [% END %]
295
                      </select>
296
                      <span>
297
                          Choose whether patrons of this category by default are reminded if they try to borrow an item they borrowed before.
298
                      </span>
299
                  </li>
300
                [% END %]
279
                <li>
301
                <li>
280
                    <label for="default_privacy">Default privacy: </label>
302
                    <label for="default_privacy">Default privacy: </label>
281
                    <select id="default_privacy" name="default_privacy">
303
                    <select id="default_privacy" name="default_privacy">
Lines 345-350 Link Here
345
                <tr><th scope="row">Receives overdue notices: </th><td>[% IF category. overduenoticerequired %]Yes[% ELSE %]No[% END %]</td></tr>
367
                <tr><th scope="row">Receives overdue notices: </th><td>[% IF category. overduenoticerequired %]Yes[% ELSE %]No[% END %]</td></tr>
346
                <tr><th scope="row">Lost items in staff client</th><td>[% IF category.hidelostitems %]Hidden by default[% ELSE %]Shown[% END %]</td></tr>
368
                <tr><th scope="row">Lost items in staff client</th><td>[% IF category.hidelostitems %]Hidden by default[% ELSE %]Shown[% END %]</td></tr>
347
                <tr><th scope="row">Hold fee: </th><td>[% category.reservefee | $Price %]</td></tr>
369
                <tr><th scope="row">Hold fee: </th><td>[% category.reservefee | $Price %]</td></tr>
370
371
                [% IF ( Koha.Preference('CheckPrevCheckout') == 'softyes' || Koha.Preference('CheckPrevCheckout') == 'softno' ) %]
372
                  <tr>
373
                      <th scope="row">Check previous checkouts: </th>
374
                      <td>
375
                          [% SWITCH category.checkprevcheckout %]
376
                          [% CASE 'yes' %]
377
                              Yes
378
                          [% CASE 'no' %]
379
                              No
380
                          [% CASE 'inherit' %]
381
                              Inherit
382
                          [% END %]
383
                      </td>
384
                  </tr>
385
                [% END %]
348
                <tr>
386
                <tr>
349
                    <th scope="row">Default privacy: </th>
387
                    <th scope="row">Default privacy: </th>
350
                    <td>
388
                    <td>
Lines 401-406 Link Here
401
                    <th scope="col">Messaging</th>
439
                    <th scope="col">Messaging</th>
402
                    [% END %]
440
                    [% END %]
403
                    <th scope="col">Branches limitations</th>
441
                    <th scope="col">Branches limitations</th>
442
                    [% IF ( Koha.Preference('CheckPrevCheckout') == 'softyes' || Koha.Preference('CheckPrevCheckout') == 'softno' ) %]
443
                    <th scope="col">Check previous checkout?</th>
444
                    [% END %]
404
                    <th scope="col">Default privacy</th>
445
                    <th scope="col">Default privacy</th>
405
                    <th scope="col">&nbsp; </th>
446
                    <th scope="col">&nbsp; </th>
406
                    <th scope="col">&nbsp; </th>
447
                    <th scope="col">&nbsp; </th>
Lines 478-483 Link Here
478
                                No limitation
519
                                No limitation
479
                            [% END %]
520
                            [% END %]
480
                        </td>
521
                        </td>
522
                        [% IF ( Koha.Preference('CheckPrevCheckout') == 'softyes' || Koha.Preference('CheckPrevCheckout') == 'softno' ) %]
523
                          <td>
524
                              [% SWITCH category.checkprevcheckout %]
525
                              [% CASE 'yes' %]
526
                              Yes
527
                              [% CASE 'no' %]
528
                              No
529
                              [% CASE 'inherit' %]
530
                              Inherit
531
                              [% END %]
532
                          </td>
533
                        [% END %]
481
                        <td>
534
                        <td>
482
                            [% SWITCH category.default_privacy %]
535
                            [% SWITCH category.default_privacy %]
483
                            [% CASE 'default' %]
536
                            [% CASE 'default' %]
(-)a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/patrons.pref (+9 lines)
Lines 44-49 Patrons: Link Here
44
           class: multi
44
           class: multi
45
         - (separate multiple choices with |)
45
         - (separate multiple choices with |)
46
     -
46
     -
47
         - pref: CheckPrevCheckout
48
           default: no
49
           choices:
50
               hardyes: "Do"
51
               softyes: "Unless overridden, do"
52
               softno: "Unless overridden, do not"
53
               hardno: "Do not"
54
         - " check borrower checkout history to see if the current item has been checked out before."
55
     -
47
         - pref: checkdigit
56
         - pref: checkdigit
48
           choices:
57
           choices:
49
               none: "Don't"
58
               none: "Don't"
(-)a/koha-tmpl/intranet-tmpl/prog/en/modules/circ/circulation.tt (+4 lines)
Lines 308-313 $(document).ready(function() { Link Here
308
    <li>High demand item. Loan period shortened to [% HIGHHOLDS.duration %] days (due [% HIGHHOLDS.returndate %]). Check out anyway?</li>
308
    <li>High demand item. Loan period shortened to [% HIGHHOLDS.duration %] days (due [% HIGHHOLDS.returndate %]). Check out anyway?</li>
309
[% END %]
309
[% END %]
310
310
311
[% IF PREVISSUE %]
312
    <li>This item has previously been checked out to this patron.  Check out anyway?</li>
313
[% END %]
314
311
[% IF BIBLIO_ALREADY_ISSUED %]
315
[% IF BIBLIO_ALREADY_ISSUED %]
312
  <li>
316
  <li>
313
    Patron has already checked out another item from this record.
317
    Patron has already checked out another item from this record.
(-)a/koha-tmpl/intranet-tmpl/prog/en/modules/members/memberentrygen.tt (-1 / +20 lines)
Lines 737-743 function select_user(borrowernumber, borrower) { Link Here
737
            [% END %]
737
            [% END %]
738
        </li>
738
        </li>
739
    [% END %]
739
    [% END %]
740
	</ol>
740
    [% IF ( Koha.Preference('CheckPrevCheckout') == 'softyes' || Koha.Preference('CheckPrevCheckout') == 'softno' ) %]
741
      <li><label for="checkprevcheckout">Check for previous checkouts: </label>
742
        <select name="checkprevcheckout" id="checkprevcheckout">
743
        [% IF ( checkprevcheckout == 'yes' ) %]
744
          <option value="yes" selected="selected">Yes if settings allow it</option>
745
          <option value="no">No if settings allow it</option>
746
          <option value="inherit">Inherit from settings</option>
747
        [% ELSIF ( checkprevcheckout == 'no' ) %]
748
          <option value="yes">Yes if settings allow it</option>
749
          <option value="no" selected="selected">No if settings allow it</option>
750
          <option value="inherit">Inherit from settings</option>
751
        [% ELSE %]
752
          <option value="yes">Yes if settings allow it</option>
753
          <option value="no">No if settings allow it</option>
754
          <option value="inherit" selected="selected">Inherit from settings</option>
755
        [% END %]
756
        </select>
757
       </li>
758
     [% END %]
759
   </ol>
741
  </fieldset>
760
  </fieldset>
742
    [% UNLESS nodateenrolled &&  noopacnote && noborrowernotes %]
761
    [% UNLESS nodateenrolled &&  noopacnote && noborrowernotes %]
743
	<fieldset class="rows" id="memberentry_subscription">
762
	<fieldset class="rows" id="memberentry_subscription">
(-)a/koha-tmpl/intranet-tmpl/prog/en/modules/members/moremember.tt (+11 lines)
Lines 400-405 function validate1(date) { Link Here
400
            <li><span class="label">Activate sync: </span>No</li>
400
            <li><span class="label">Activate sync: </span>No</li>
401
        [% END %]
401
        [% END %]
402
    [% END %]
402
    [% END %]
403
    [% IF ( Koha.Preference('CheckPrevCheckout') == 'softyes' || Koha.Preference('CheckPrevCheckout') == 'softno' ) %]
404
      <li><span class="label">Check previous checkouts: </span>
405
        [% IF ( checkprevcheckout == 'yes' ) %]
406
        Yes
407
        [% ELSIF ( checkprevcheckout == 'no' ) %]
408
        No
409
        [% ELSE %]
410
        Inherited
411
        [% END %]
412
      </li>
413
    [% END %]
403
	</ol>
414
	</ol>
404
	</div>
415
	</div>
405
 </div>
416
 </div>
(-)a/t/db_dependent/Patron/CheckPrevCheckout.t (-1 / +451 lines)
Line 0 Link Here
0
- 
1
#!/usr/bin/perl
2
use Modern::Perl;
3
4
use C4::Members;
5
use C4::Circulation;
6
use Koha::Patron::Categories;
7
use Koha::Patron::Category;
8
use Koha::Database;
9
use Koha::Patrons;
10
use Koha::Patron;
11
use Koha::Items;
12
use Koha::Item;
13
14
use Test::More tests => 59;
15
16
use_ok('Koha::Patron');
17
18
use t::lib::TestBuilder;
19
use t::lib::Mocks;
20
21
my $schema = Koha::Database->new->schema;
22
$schema->storage->txn_begin;
23
24
my $builder = t::lib::TestBuilder->new;
25
my $yesCatCode = $builder->build({
26
    source => 'Category',
27
    value => {
28
        categorycode => 'yesCat',
29
        checkprevcheckout => 'yes',
30
    },
31
});
32
33
my $noCatCode = $builder->build({
34
    source => 'Category',
35
    value => {
36
        categorycode => 'noCat',
37
        checkprevcheckout => 'no',
38
    },
39
});
40
41
my $inheritCatCode = $builder->build({
42
    source => 'Category',
43
    value => {
44
        categorycode => 'inheritCat',
45
        checkprevcheckout => 'inherit',
46
    },
47
});
48
49
# Create context for some tests late on in the file.
50
my $staff = $builder->build({source => 'Borrower'});
51
my @USERENV = (
52
    $staff->{borrowernumber}, 'test', 'MASTERTEST', 'firstname', 'CPL',
53
    'CPL', 'email@example.org'
54
);
55
C4::Context->_new_userenv('DUMMY_SESSION_ID');
56
C4::Context->set_userenv(@USERENV);
57
BAIL_OUT("No userenv") unless C4::Context->userenv;
58
59
60
# wantsCheckPrevCheckout
61
62
# We expect the following result matrix:
63
#
64
# (1/0 indicates the return value of WantsCheckPrevCheckout; i.e. 1 says we
65
# should check whether the item was previously issued)
66
#
67
# | System Preference | hardyes                           | softyes                           | softno                            | hardno                            |
68
# |-------------------+-----------------------------------+-----------------------------------+-----------------------------------+-----------------------------------|
69
# | Category Setting  | yes       | no        | inherit   | yes       | no        | inherit   | yes       | no        | inherit   | yes       | no        | inherit   |
70
# |-------------------+-----------+-----------+-----------+-----------+-----------+-----------+-----------+-----------+-----------+-----------+-----------+-----------|
71
# | 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 | y | n | i | y | n | i | y | n | i |
72
# |-------------------+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
73
# | Expected Result   | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 1 | 1 | 0 | 0 | 1 | 0 | 1 | 1 | 0 | 1 | 1 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
74
75
my $mappings = [
76
    {
77
        syspref    => 'hardyes',
78
        categories => [
79
            {
80
                setting => 'yes',
81
                patrons => [
82
                    {setting => 'yes',     result => 1},
83
                    {setting => 'no',      result => 1},
84
                    {setting => 'inherit', result => 1},
85
                ],
86
            },
87
            {
88
                setting => 'no',
89
                patrons => [
90
                    {setting => 'yes',     result => 1},
91
                    {setting => 'no',      result => 1},
92
                    {setting => 'inherit', result => 1},
93
                ],
94
            },
95
            {
96
                setting => 'inherit',
97
                patrons => [
98
                    {setting => 'yes',     result => 1},
99
                    {setting => 'no',      result => 1},
100
                    {setting => 'inherit', result => 1},
101
                ],
102
            },
103
        ],
104
    },
105
    {
106
        syspref    => 'softyes',
107
        categories => [
108
            {
109
                setting => 'yes',
110
                patrons => [
111
                    {setting => 'yes',     result => 1},
112
                    {setting => 'no',      result => 0},
113
                    {setting => 'inherit', result => 1},
114
                ],
115
            },
116
            {
117
                setting => 'no',
118
                patrons => [
119
                    {setting => 'yes',     result => 1},
120
                    {setting => 'no',      result => 0},
121
                    {setting => 'inherit', result => 0},
122
                ],
123
            },
124
            {
125
                setting => 'inherit',
126
                patrons => [
127
                    {setting => 'yes',     result => 1},
128
                    {setting => 'no',      result => 0},
129
                    {setting => 'inherit', result => 1},
130
                ],
131
            },
132
        ],
133
    },
134
    {
135
        syspref    => 'softno',
136
        categories => [
137
            {
138
                setting => 'yes',
139
                patrons => [
140
                    {setting => 'yes',     result => 1},
141
                    {setting => 'no',      result => 0},
142
                    {setting => 'inherit', result => 1},
143
                ],
144
            },
145
            {
146
                setting => 'no',
147
                patrons => [
148
                    {setting => 'yes',     result => 1},
149
                    {setting => 'no',      result => 0},
150
                    {setting => 'inherit', result => 0},
151
                ],
152
            },
153
            {
154
                setting => 'inherit',
155
                patrons => [
156
                    {setting => 'yes',     result => 1},
157
                    {setting => 'no',      result => 0},
158
                    {setting => 'inherit', result => 0},
159
                ],
160
            },
161
        ],
162
    },
163
    {
164
        syspref    => 'hardno',
165
        categories => [
166
            {
167
                setting => 'yes',
168
                patrons => [
169
                    {setting => 'yes',     result => 0},
170
                    {setting => 'no',      result => 0},
171
                    {setting => 'inherit', result => 0},
172
                ],
173
            },
174
            {
175
                setting => 'no',
176
                patrons => [
177
                    {setting => 'yes',     result => 0},
178
                    {setting => 'no',      result => 0},
179
                    {setting => 'inherit', result => 0},
180
                ],
181
            },
182
            {
183
                setting => 'inherit',
184
                patrons => [
185
                    {setting => 'yes',     result => 0},
186
                    {setting => 'no',      result => 0},
187
                    {setting => 'inherit', result => 0},
188
                ],
189
            },
190
        ],
191
    },
192
];
193
194
map {
195
    my $syspref = $_->{syspref};
196
    t::lib::Mocks::mock_preference('checkprevcheckout', $syspref);
197
    map {
198
        my $code = $_->{setting} . 'Cat';
199
        map {
200
            my $kpatron = $builder->build({
201
                source => 'Borrower',
202
                value  => {
203
                    checkprevcheckout => $_->{setting},
204
                    categorycode => $code,
205
                },
206
            });
207
            my $patron = Koha::Patrons->find($kpatron->{borrowernumber});
208
            is(
209
                $patron->wantsCheckPrevCheckout, $_->{result},
210
                "Predicate with syspref " . $syspref . ", cat " . $code
211
                    . ", patron " . $_->{setting}
212
              );
213
        } @{$_->{patrons}};
214
    } @{$_->{categories}};
215
} @{$mappings};
216
217
# doCheckPrevCheckout
218
219
# We want to test:
220
# - DESCRIPTION [RETURNVALUE (0/1)]
221
## PreIssue (sanity checks)
222
# - Item, patron [0]
223
# - Diff item, same bib, same patron [0]
224
# - Diff item, diff bib, same patron [0]
225
# - Same item, diff patron [0]
226
# - Diff item, same bib, diff patron [0]
227
# - Diff item, diff bib, diff patron [0]
228
## PostIssue
229
# - Same item, same patron [1]
230
# - Diff item, same bib, same patron [1]
231
# - Diff item, diff bib, same patron [0]
232
# - Same item, diff patron [0]
233
# - Diff item, same bib, diff patron [0]
234
# - Diff item, diff bib, diff patron [0]
235
## PostReturn
236
# - Same item, same patron [1]
237
# - Diff item, same bib, same patron [1]
238
# - Diff item, diff bib, same patron [0]
239
# - Same item, diff patron [0]
240
# - Diff item, same bib, diff patron [0]
241
# - Diff item, diff bib, diff patron [0]
242
243
# Requirements:
244
# $patron, $different_patron, $items (same bib number), $different_item
245
my $patron = $builder->build({source => 'Borrower'});
246
my $patron_d = $builder->build({source => 'Borrower'});
247
my $item_1 = $builder->build({source => 'Item'});
248
my $item_2 = $builder->build({
249
    source => 'Item',
250
    value => { biblionumber => $item_1->{biblionumber} },
251
});
252
my $item_d = $builder->build({source => 'Item'});
253
254
## Testing Sub
255
sub test_it {
256
    my ($mapping, $stage) = @_;
257
    map {
258
        my $patron = Koha::Patrons->find($_->{patron}->{borrowernumber});
259
        is(
260
            $patron->doCheckPrevCheckout($_->{item}),
261
            $_->{result}, $stage . ": " . $_->{msg}
262
        );
263
    } @{$mapping};
264
};
265
266
## Initial Mappings
267
my $cpvmappings = [
268
    {
269
        msg => "Item, patron [0]",
270
        item => $item_1,
271
        patron => $patron,
272
        result => 0,
273
    },
274
    {
275
        msg => "Diff item, same bib, same patron [0]",
276
        item => $item_2,
277
        patron => $patron,
278
        result => 0,
279
    },
280
    {
281
        msg => "Diff item, diff bib, same patron [0]",
282
        item => $item_d,
283
        patron => $patron,
284
        result => 0,
285
    },
286
    {
287
        msg => "Same item, diff patron [0]",
288
        item => $item_1,
289
        patron => $patron_d,
290
        result => 0,
291
    },
292
    {
293
        msg => "Diff item, same bib, diff patron [0]",
294
        item => $item_2,
295
        patron => $patron_d,
296
        result => 0,
297
    },
298
    {
299
        msg => "Diff item, diff bib, diff patron [0]",
300
        item => $item_d,
301
        patron => $patron_d,
302
        result => 0,
303
    },
304
];
305
306
test_it($cpvmappings, "PreIssue");
307
308
# Issue item_1 to $patron:
309
my $patron_get_mem =
310
    GetMember(%{{borrowernumber => $patron->{borrowernumber}}});
311
BAIL_OUT("Issue failed")
312
    unless AddIssue($patron_get_mem, $item_1->{barcode});
313
314
# Then test:
315
my $cpvPmappings = [
316
    {
317
        msg => "Same item, same patron [1]",
318
        item => $item_1,
319
        patron => $patron,
320
        result => 1,
321
    },
322
    {
323
        msg => "Diff item, same bib, same patron [1]",
324
        item => $item_2,
325
        patron => $patron,
326
        result => 1,
327
    },
328
    {
329
        msg => "Diff item, diff bib, same patron [0]",
330
        item => $item_d,
331
        patron => $patron,
332
        result => 0,
333
    },
334
    {
335
        msg => "Same item, diff patron [0]",
336
        item => $item_1,
337
        patron => $patron_d,
338
        result => 0,
339
    },
340
    {
341
        msg => "Diff item, same bib, diff patron [0]",
342
        item => $item_2,
343
        patron => $patron_d,
344
        result => 0,
345
    },
346
    {
347
        msg => "Diff item, diff bib, diff patron [0]",
348
        item => $item_d,
349
        patron => $patron_d,
350
        result => 0,
351
    },
352
];
353
354
test_it($cpvPmappings, "PostIssue");
355
356
# Return item_1 from patron:
357
BAIL_OUT("Return Failed") unless AddReturn($item_1->{barcode}, $patron->{branchcode});
358
359
# Then:
360
test_it($cpvPmappings, "PostReturn");
361
362
# Finally test C4::Circulation::CanBookBeIssued
363
364
# We have already tested ->wantsCheckPrevCheckout and ->doCheckPrevCheckout,
365
# so all that remains to be tested is whetherthe different combinational
366
# outcomes of the above return values in CanBookBeIssued result in the
367
# approriate $needsconfirmation.
368
369
# We want to test:
370
# - DESCRIPTION [RETURNVALUE (0/1)]
371
# - patron, !wantsCheckPrevCheckout, !doCheckPrevCheckout
372
#   [!$issuingimpossible,!$needsconfirmation->{PREVISSUE}]
373
# - patron, wantsCheckPrevCheckout, !doCheckPrevCheckout
374
#   [!$issuingimpossible,!$needsconfirmation->{PREVISSUE}]
375
# - patron, !wantsCheckPrevCheckout, doCheckPrevCheckout
376
#   [!$issuingimpossible,!$needsconfirmation->{PREVISSUE}]
377
# - patron, wantsCheckPrevCheckout, doCheckPrevCheckout
378
#   [!$issuingimpossible,$needsconfirmation->{PREVISSUE}]
379
380
# Needs:
381
# - $patron_from_GetMember
382
# - $item objects (one not issued, another prevIssued)
383
# - $checkprevcheckout pref (first hardno, then hardyes)
384
385
# Our Patron
386
my $CBBI_patron = $builder->build({source => 'Borrower'});
387
my $p_from_GetMember =
388
    GetMember(%{{borrowernumber => $CBBI_patron->{borrowernumber}}});
389
# Our Items
390
my $new_item = $builder->build({
391
    source => 'Item',
392
    value => {
393
        notforloan => 0,
394
        withdrawn  => 0,
395
        itemlost   => 0,
396
    },
397
});
398
my $prev_item = $builder->build({
399
    source => 'Item',
400
    value => {
401
        notforloan => 0,
402
        withdrawn  => 0,
403
        itemlost   => 0,
404
    },
405
});
406
# Second is Checked Out
407
BAIL_OUT("CanBookBeIssued Issue failed")
408
    unless AddIssue($p_from_GetMember, $prev_item->{barcode});
409
410
# Mappings
411
my $CBBI_mappings = [
412
    {
413
        syspref => 'hardno',
414
        item    => $new_item,
415
        result  => undef,
416
        msg     => "patron, !wantsCheckPrevCheckout, !doCheckPrevCheckout"
417
418
    },
419
    {
420
        syspref => 'hardyes',
421
        item    => $new_item,
422
        result  => undef,
423
        msg     => "patron, wantsCheckPrevCheckout, !doCheckPrevCheckout"
424
    },
425
    {
426
        syspref => 'hardno',
427
        item    => $prev_item,
428
        result  => undef,
429
        msg     => "patron, !wantsCheckPrevCheckout, doCheckPrevCheckout"
430
    },
431
    {
432
        syspref => 'hardyes',
433
        item    => $prev_item,
434
        result  => 1,
435
        msg     => "patron, wantsCheckPrevCheckout, doCheckPrevCheckout"
436
    },
437
];
438
439
# Tests
440
map {
441
    t::lib::Mocks::mock_preference('checkprevcheckout', $_->{syspref});
442
    my ( $issuingimpossible, $needsconfirmation ) =
443
        C4::Circulation::CanBookBeIssued(
444
            $p_from_GetMember, $_->{item}->{barcode}
445
        );
446
    is($needsconfirmation->{PREVISSUE}, $_->{result}, $_->{msg});
447
} @{$CBBI_mappings};
448
449
$schema->storage->txn_rollback;
450
451
1;

Return to bug 6906