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 (+78 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;
26
use Koha::Biblios;
24
use Koha::Database;
27
use Koha::Database;
28
use Koha::Issues;
29
use Koha::OldIssues;
30
use Koha::Patron::Categories;
25
use Koha::Patron::Images;
31
use Koha::Patron::Images;
26
32
27
use base qw(Koha::Object);
33
use base qw(Koha::Object);
Lines 94-99 sub siblings { Link Here
94
    );
100
    );
95
}
101
}
96
102
103
=head3 wantsCheckPrevCheckout
104
105
    $wantsCheckPrevCheckout = $patron->wantsCheckPrevCheckout;
106
107
Return 1 if Koha needs to perform PrevIssue checking, else 0.
108
109
=cut
110
111
sub wantsCheckPrevCheckout {
112
    my ( $self ) = @_;
113
    my $syspref = C4::Context->preference("checkPrevCheckout");
114
115
    # Simple cases
116
    ## Hard syspref trumps all
117
    return 1 if ($syspref eq 'hardyes');
118
    return 0 if ($syspref eq 'hardno');
119
    ## Now, patron pref trumps all
120
    return 1 if ($self->checkprevcheckout eq 'yes');
121
    return 0 if ($self->checkprevcheckout eq 'no');
122
123
    # More complex: patron inherits -> determine category preference
124
    my $checkPrevCheckoutByCat = Koha::Patron::Categories
125
        ->find($self->categorycode)->checkprevcheckout;
126
    return 1 if ($checkPrevCheckoutByCat eq 'yes');
127
    return 0 if ($checkPrevCheckoutByCat eq 'no');
128
129
    # Finally: category preference is inherit, default to 0
130
    if ($syspref eq 'softyes') {
131
        return 1;
132
    } else {
133
        return 0;
134
    }
135
}
136
137
=head3 doCheckPrevCheckout
138
139
    $checkPrevCheckout = $patron->doCheckPrevCheckout($item);
140
141
Return 1 if the bib associated with $ITEM has previously been checked out to
142
$PATRON, 0 otherwise.
143
144
=cut
145
146
sub doCheckPrevCheckout {
147
    my ( $self, $item ) = @_;
148
149
    # We don't check for previous checkout if this is a serial.  A future
150
    # enhancement might be to implement comprehensive serial handling.
151
    return 0 if (Koha::Biblios->find($item->{biblionumber})->serial);
152
    # Find all items for bib and extract item numbers.
153
    my @items = Koha::Items->search({biblionumber => $item->{biblionumber}});
154
    my @item_nos;
155
    foreach my $item (@items) {
156
        push @item_nos, $item->itemnumber;
157
    }
158
159
    # Create (old)issues search criteria
160
    my $criteria = {
161
        borrowernumber => $self->borrowernumber,
162
        itemnumber => \@item_nos,
163
    };
164
165
    # Check current issues table
166
    my $issues = Koha::Issues->search($criteria);
167
    return 1 if $issues->count; # 0 || N
168
169
    # Check old issues table
170
    my $old_issues = Koha::OldIssues->search($criteria);
171
    return $old_issues->count;  # 0 || N
172
}
173
97
=head3 type
174
=head3 type
98
175
99
=cut
176
=cut
Lines 105-110 sub _type { Link Here
105
=head1 AUTHOR
182
=head1 AUTHOR
106
183
107
Kyle M Hall <kyle@bywatersolutions.com>
184
Kyle M Hall <kyle@bywatersolutions.com>
185
Alex Sassmannshausen <alex.sassmannshausen@ptfs-europe.com>
108
186
109
=cut
187
=cut
110
188
(-)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 (unless it is a serial), should we warn if the patron has checked this item out 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 (unless it is a serial), should we warn if the patron has checked this item out 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 (unless it is a serial)."
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 / +492 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::Database;
7
use Koha::Patrons;
8
use Koha::Patron;
9
10
use Test::More tests => 62;
11
12
use_ok('Koha::Patron');
13
14
use t::lib::TestBuilder;
15
use t::lib::Mocks;
16
17
my $schema = Koha::Database->new->schema;
18
$schema->storage->txn_begin;
19
20
my $builder = t::lib::TestBuilder->new;
21
my $yesCatCode = $builder->build({
22
    source => 'Category',
23
    value => {
24
        categorycode => 'yesCat',
25
        checkprevcheckout => 'yes',
26
    },
27
});
28
29
my $noCatCode = $builder->build({
30
    source => 'Category',
31
    value => {
32
        categorycode => 'noCat',
33
        checkprevcheckout => 'no',
34
    },
35
});
36
37
my $inheritCatCode = $builder->build({
38
    source => 'Category',
39
    value => {
40
        categorycode => 'inheritCat',
41
        checkprevcheckout => 'inherit',
42
    },
43
});
44
45
# Create context for some tests late on in the file.
46
my $staff = $builder->build({source => 'Borrower'});
47
my @USERENV = (
48
    $staff->{borrowernumber}, 'test', 'MASTERTEST', 'firstname', 'CPL',
49
    'CPL', 'email@example.org'
50
);
51
C4::Context->_new_userenv('DUMMY_SESSION_ID');
52
C4::Context->set_userenv(@USERENV);
53
BAIL_OUT("No userenv") unless C4::Context->userenv;
54
55
56
# wantsCheckPrevCheckout
57
58
# We expect the following result matrix:
59
#
60
# (1/0 indicates the return value of WantsCheckPrevCheckout; i.e. 1 says we
61
# should check whether the item was previously issued)
62
#
63
# | System Preference | hardyes                           | softyes                           | softno                            | hardno                            |
64
# |-------------------+-----------------------------------+-----------------------------------+-----------------------------------+-----------------------------------|
65
# | Category Setting  | yes       | no        | inherit   | yes       | no        | inherit   | yes       | no        | inherit   | yes       | no        | inherit   |
66
# |-------------------+-----------+-----------+-----------+-----------+-----------+-----------+-----------+-----------+-----------+-----------+-----------+-----------|
67
# | 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 |
68
# |-------------------+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
69
# | 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 |
70
71
my $mappings = [
72
    {
73
        syspref    => 'hardyes',
74
        categories => [
75
            {
76
                setting => 'yes',
77
                patrons => [
78
                    {setting => 'yes',     result => 1},
79
                    {setting => 'no',      result => 1},
80
                    {setting => 'inherit', result => 1},
81
                ],
82
            },
83
            {
84
                setting => 'no',
85
                patrons => [
86
                    {setting => 'yes',     result => 1},
87
                    {setting => 'no',      result => 1},
88
                    {setting => 'inherit', result => 1},
89
                ],
90
            },
91
            {
92
                setting => 'inherit',
93
                patrons => [
94
                    {setting => 'yes',     result => 1},
95
                    {setting => 'no',      result => 1},
96
                    {setting => 'inherit', result => 1},
97
                ],
98
            },
99
        ],
100
    },
101
    {
102
        syspref    => 'softyes',
103
        categories => [
104
            {
105
                setting => 'yes',
106
                patrons => [
107
                    {setting => 'yes',     result => 1},
108
                    {setting => 'no',      result => 0},
109
                    {setting => 'inherit', result => 1},
110
                ],
111
            },
112
            {
113
                setting => 'no',
114
                patrons => [
115
                    {setting => 'yes',     result => 1},
116
                    {setting => 'no',      result => 0},
117
                    {setting => 'inherit', result => 0},
118
                ],
119
            },
120
            {
121
                setting => 'inherit',
122
                patrons => [
123
                    {setting => 'yes',     result => 1},
124
                    {setting => 'no',      result => 0},
125
                    {setting => 'inherit', result => 1},
126
                ],
127
            },
128
        ],
129
    },
130
    {
131
        syspref    => 'softno',
132
        categories => [
133
            {
134
                setting => 'yes',
135
                patrons => [
136
                    {setting => 'yes',     result => 1},
137
                    {setting => 'no',      result => 0},
138
                    {setting => 'inherit', result => 1},
139
                ],
140
            },
141
            {
142
                setting => 'no',
143
                patrons => [
144
                    {setting => 'yes',     result => 1},
145
                    {setting => 'no',      result => 0},
146
                    {setting => 'inherit', result => 0},
147
                ],
148
            },
149
            {
150
                setting => 'inherit',
151
                patrons => [
152
                    {setting => 'yes',     result => 1},
153
                    {setting => 'no',      result => 0},
154
                    {setting => 'inherit', result => 0},
155
                ],
156
            },
157
        ],
158
    },
159
    {
160
        syspref    => 'hardno',
161
        categories => [
162
            {
163
                setting => 'yes',
164
                patrons => [
165
                    {setting => 'yes',     result => 0},
166
                    {setting => 'no',      result => 0},
167
                    {setting => 'inherit', result => 0},
168
                ],
169
            },
170
            {
171
                setting => 'no',
172
                patrons => [
173
                    {setting => 'yes',     result => 0},
174
                    {setting => 'no',      result => 0},
175
                    {setting => 'inherit', result => 0},
176
                ],
177
            },
178
            {
179
                setting => 'inherit',
180
                patrons => [
181
                    {setting => 'yes',     result => 0},
182
                    {setting => 'no',      result => 0},
183
                    {setting => 'inherit', result => 0},
184
                ],
185
            },
186
        ],
187
    },
188
];
189
190
map {
191
    my $syspref = $_->{syspref};
192
    t::lib::Mocks::mock_preference('checkprevcheckout', $syspref);
193
    map {
194
        my $code = $_->{setting} . 'Cat';
195
        map {
196
            my $kpatron = $builder->build({
197
                source => 'Borrower',
198
                value  => {
199
                    checkprevcheckout => $_->{setting},
200
                    categorycode => $code,
201
                },
202
            });
203
            my $patron = Koha::Patrons->find($kpatron->{borrowernumber});
204
            is(
205
                $patron->wantsCheckPrevCheckout, $_->{result},
206
                "Predicate with syspref " . $syspref . ", cat " . $code
207
                    . ", patron " . $_->{setting}
208
              );
209
        } @{$_->{patrons}};
210
    } @{$_->{categories}};
211
} @{$mappings};
212
213
# doCheckPrevCheckout
214
215
# We want to test:
216
# - DESCRIPTION [RETURNVALUE (0/1)]
217
## PreIssue (sanity checks)
218
# - Item, patron [0]
219
# - Diff item, same bib, same patron [0]
220
# - Diff item, diff bib, same patron [0]
221
# - Same item, diff patron [0]
222
# - Diff item, same bib, diff patron [0]
223
# - Diff item, diff bib, diff patron [0]
224
# - Serial item, serial bib, same patron [0]
225
## PostIssue
226
# - Same item, same patron [1]
227
# - Diff item, same bib, same patron [1]
228
# - Diff item, diff bib, same patron [0]
229
# - Same item, diff patron [0]
230
# - Diff item, same bib, diff patron [0]
231
# - Diff item, diff bib, diff patron [0]
232
# - Serial item, serial bib, same patron [0]
233
## PostReturn
234
# - Same item, same patron [1]
235
# - Diff item, same bib, same patron [1]
236
# - Diff item, diff bib, same patron [0]
237
# - Same item, diff patron [0]
238
# - Diff item, same bib, diff patron [0]
239
# - Diff item, diff bib, diff patron [0]
240
# - Serial item, serial bib, same patron [0]
241
242
# Requirements:
243
# $patron, $different_patron
244
my $patron = $builder->build({ source => 'Borrower' });
245
my $patron_d = $builder->build({ source => 'Borrower' });
246
# $nonserialbibs $items (same bib number), $different_item (different bib)
247
my $bib = $builder->build({ source => 'Biblio', value => { serial => 0 } });
248
my $item_1 = $builder->build({
249
    source => 'Item',
250
    value => { biblionumber => $bib->{biblionumber} },
251
});
252
my $item_2 = $builder->build({
253
    source => 'Item',
254
    value => { biblionumber => $item_1->{biblionumber} },
255
});
256
my $bib_d = $builder->build({ source => 'Biblio', value => { serial => 0 } });
257
my $item_d = $builder->build({
258
    source => 'Item',
259
    value => { biblionumber => $bib_d->{biblionumber} },
260
});
261
# $serial bib, $serial item
262
my $bib_s = $builder->build({ source => 'Biblio', value => { serial => 1 } });
263
my $item_s = $builder->build({
264
    source => 'Item',
265
    value => { biblionumber => $bib_s->{biblionumber} },
266
});
267
268
## Testing Sub
269
sub test_it {
270
    my ($mapping, $stage) = @_;
271
    map {
272
        my $patron = Koha::Patrons->find($_->{patron}->{borrowernumber});
273
        is(
274
            $patron->doCheckPrevCheckout($_->{item}),
275
            $_->{result}, $stage . ": " . $_->{msg}
276
        );
277
    } @{$mapping};
278
};
279
280
## Initial Mappings
281
my $cpvmappings = [
282
    {
283
        msg => "Item, patron [0]",
284
        item => $item_1,
285
        patron => $patron,
286
        result => 0,
287
    },
288
    {
289
        msg => "Diff item, same bib, same patron [0]",
290
        item => $item_2,
291
        patron => $patron,
292
        result => 0,
293
    },
294
    {
295
        msg => "Diff item, diff bib, same patron [0]",
296
        item => $item_d,
297
        patron => $patron,
298
        result => 0,
299
    },
300
    {
301
        msg => "Same item, diff patron [0]",
302
        item => $item_1,
303
        patron => $patron_d,
304
        result => 0,
305
    },
306
    {
307
        msg => "Diff item, same bib, diff patron [0]",
308
        item => $item_2,
309
        patron => $patron_d,
310
        result => 0,
311
    },
312
    {
313
        msg => "Diff item, diff bib, diff patron [0]",
314
        item => $item_d,
315
        patron => $patron_d,
316
        result => 0,
317
    },
318
    {
319
        msg => "Serial item, serial bib, same patron [0]",
320
        item => $item_s,
321
        patron => $patron,
322
        result => 0,
323
    },
324
];
325
326
test_it($cpvmappings, "PreIssue");
327
328
# Issue item_1, item_s to $patron:
329
my $patron_get_mem =
330
    GetMember(%{{borrowernumber => $patron->{borrowernumber}}});
331
BAIL_OUT("Issue failed")
332
    unless AddIssue($patron_get_mem, $item_1->{barcode});
333
BAIL_OUT("Serial Issue failed")
334
    unless AddIssue($patron_get_mem, $item_s->{barcode});
335
336
# Then test:
337
my $cpvPmappings = [
338
    {
339
        msg => "Same item, same patron [1]",
340
        item => $item_1,
341
        patron => $patron,
342
        result => 1,
343
    },
344
    {
345
        msg => "Diff item, same bib, same patron [1]",
346
        item => $item_2,
347
        patron => $patron,
348
        result => 1,
349
    },
350
    {
351
        msg => "Diff item, diff bib, same patron [0]",
352
        item => $item_d,
353
        patron => $patron,
354
        result => 0,
355
    },
356
    {
357
        msg => "Same item, diff patron [0]",
358
        item => $item_1,
359
        patron => $patron_d,
360
        result => 0,
361
    },
362
    {
363
        msg => "Diff item, same bib, diff patron [0]",
364
        item => $item_2,
365
        patron => $patron_d,
366
        result => 0,
367
    },
368
    {
369
        msg => "Diff item, diff bib, diff patron [0]",
370
        item => $item_d,
371
        patron => $patron_d,
372
        result => 0,
373
    },
374
    {
375
        msg => "Serial item, serial bib, same patron [0]",
376
        item => $item_s,
377
        patron => $patron,
378
        result => 0,
379
    },
380
];
381
382
test_it($cpvPmappings, "PostIssue");
383
384
# Return item_1, item_s from patron:
385
BAIL_OUT("Return Failed")
386
    unless AddReturn($item_1->{barcode}, $patron->{branchcode});
387
BAIL_OUT("Serial Return Failed")
388
    unless AddReturn($item_s->{barcode}, $patron->{branchcode});
389
390
# Then:
391
test_it($cpvPmappings, "PostReturn");
392
393
# Finally test C4::Circulation::CanBookBeIssued
394
395
# We have already tested ->wantsCheckPrevCheckout and ->doCheckPrevCheckout,
396
# so all that remains to be tested is whetherthe different combinational
397
# outcomes of the above return values in CanBookBeIssued result in the
398
# approriate $needsconfirmation.
399
400
# We want to test:
401
# - DESCRIPTION [RETURNVALUE (0/1)]
402
# - patron, !wantsCheckPrevCheckout, !doCheckPrevCheckout
403
#   [!$issuingimpossible,!$needsconfirmation->{PREVISSUE}]
404
# - patron, wantsCheckPrevCheckout, !doCheckPrevCheckout
405
#   [!$issuingimpossible,!$needsconfirmation->{PREVISSUE}]
406
# - patron, !wantsCheckPrevCheckout, doCheckPrevCheckout
407
#   [!$issuingimpossible,!$needsconfirmation->{PREVISSUE}]
408
# - patron, wantsCheckPrevCheckout, doCheckPrevCheckout
409
#   [!$issuingimpossible,$needsconfirmation->{PREVISSUE}]
410
411
# Needs:
412
# - $patron_from_GetMember
413
# - $item objects (one not issued, another prevIssued)
414
# - $checkprevcheckout pref (first hardno, then hardyes)
415
416
# Our Patron
417
my $CBBI_patron = $builder->build({source => 'Borrower'});
418
my $p_from_GetMember =
419
    GetMember(%{{borrowernumber => $CBBI_patron->{borrowernumber}}});
420
# Our Items
421
my $new_bib = $builder->build({
422
    source => 'Biblio',
423
    value => { serial => 0 }
424
});
425
my $new_item = $builder->build({
426
    source => 'Item',
427
    value => {
428
        notforloan => 0,
429
        withdrawn  => 0,
430
        itemlost   => 0,
431
        biblionumber => $new_bib->{biblionumber},
432
    },
433
});
434
my $prev_bib = $builder->build({
435
    source => 'Biblio',
436
    value => { serial => 0 }
437
});
438
my $prev_item = $builder->build({
439
    source => 'Item',
440
    value => {
441
        notforloan => 0,
442
        withdrawn  => 0,
443
        itemlost   => 0,
444
        biblionumber => $prev_bib->{biblionumber},
445
    },
446
});
447
# Second is Checked Out
448
BAIL_OUT("CanBookBeIssued Issue failed")
449
    unless AddIssue($p_from_GetMember, $prev_item->{barcode});
450
451
# Mappings
452
my $CBBI_mappings = [
453
    {
454
        syspref => 'hardno',
455
        item    => $new_item,
456
        result  => undef,
457
        msg     => "patron, !wantsCheckPrevCheckout, !doCheckPrevCheckout"
458
459
    },
460
    {
461
        syspref => 'hardyes',
462
        item    => $new_item,
463
        result  => undef,
464
        msg     => "patron, wantsCheckPrevCheckout, !doCheckPrevCheckout"
465
    },
466
    {
467
        syspref => 'hardno',
468
        item    => $prev_item,
469
        result  => undef,
470
        msg     => "patron, !wantsCheckPrevCheckout, doCheckPrevCheckout"
471
    },
472
    {
473
        syspref => 'hardyes',
474
        item    => $prev_item,
475
        result  => 1,
476
        msg     => "patron, wantsCheckPrevCheckout, doCheckPrevCheckout"
477
    },
478
];
479
480
# Tests
481
map {
482
    t::lib::Mocks::mock_preference('checkprevcheckout', $_->{syspref});
483
    my ( $issuingimpossible, $needsconfirmation ) =
484
        C4::Circulation::CanBookBeIssued(
485
            $p_from_GetMember, $_->{item}->{barcode}
486
        );
487
    is($needsconfirmation->{PREVISSUE}, $_->{result}, $_->{msg});
488
} @{$CBBI_mappings};
489
490
$schema->storage->txn_rollback;
491
492
1;

Return to bug 6906