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

(-)a/opac/opac-user.pl (-355 / +355 lines)
Lines 16-442 Link Here
16
# You should have received a copy of the GNU General Public License
16
# You should have received a copy of the GNU General Public License
17
# along with Koha; if not, see <https://www.gnu.org/licenses>.
17
# along with Koha; if not, see <https://www.gnu.org/licenses>.
18
18
19
    use Modern::Perl;
19
use Modern::Perl;
20
20
21
    use CGI qw ( -utf8 );
21
use CGI qw ( -utf8 );
22
    use URI;
22
use URI;
23
23
24
    use C4::Auth qw( get_template_and_user );
24
use C4::Auth qw( get_template_and_user );
25
    use C4::Koha qw(
25
use C4::Koha qw(
26
        getitemtypeimagelocation
26
    getitemtypeimagelocation
27
        GetNormalizedISBN
27
    GetNormalizedISBN
28
        GetNormalizedUPC
28
    GetNormalizedUPC
29
        GetNormalizedOCLCNumber
29
    GetNormalizedOCLCNumber
30
    );
30
);
31
    use C4::Circulation           qw( CanBookBeRenewed GetRenewCount GetIssuingCharges );
31
use C4::Circulation           qw( CanBookBeRenewed GetRenewCount GetIssuingCharges );
32
    use C4::External::BakerTaylor qw( image_url link_url );
32
use C4::External::BakerTaylor qw( image_url link_url );
33
    use C4::Reserves              qw( GetReserveStatus );
33
use C4::Reserves              qw( GetReserveStatus );
34
    use C4::Members;
34
use C4::Members;
35
    use C4::Output qw( output_html_with_http_headers );
35
use C4::Output qw( output_html_with_http_headers );
36
    use Koha::Account::Lines;
36
use Koha::Account::Lines;
37
    use Koha::Biblios;
37
use Koha::Biblios;
38
    use Koha::Libraries;
38
use Koha::Libraries;
39
    use Koha::DateUtils qw( output_pref );
39
use Koha::DateUtils qw( output_pref );
40
    use Koha::Holds;
40
use Koha::Holds;
41
    use Koha::Database;
41
use Koha::Database;
42
    use Koha::ItemTypes;
42
use Koha::ItemTypes;
43
    use Koha::Patron::Attribute::Types;
43
use Koha::Patron::Attribute::Types;
44
    use Koha::Patrons;
44
use Koha::Patrons;
45
    use Koha::Patron::Messages;
45
use Koha::Patron::Messages;
46
    use Koha::Patron::Discharge;
46
use Koha::Patron::Discharge;
47
    use Koha::Patrons;
47
use Koha::Patrons;
48
    use Koha::Ratings;
48
use Koha::Ratings;
49
    use Koha::Recalls;
49
use Koha::Recalls;
50
50
51
    use constant ATTRIBUTE_SHOW_BARCODE => 'SHOW_BCODE';
51
use constant ATTRIBUTE_SHOW_BARCODE => 'SHOW_BCODE';
52
52
53
    use Scalar::Util qw( looks_like_number );
53
use Scalar::Util qw( looks_like_number );
54
    use Date::Calc   qw( Date_to_Days Today );
54
use Date::Calc   qw( Date_to_Days Today );
55
55
56
    my $query = CGI->new;
56
my $query = CGI->new;
57
57
58
    my $op = $query->param('op') || q{};
58
my $op = $query->param('op') || q{};
59
59
60
# CAS single logout handling
60
# CAS single logout handling
61
# Will print header and exit
61
# Will print header and exit
62
    if ( C4::Context->preference('casAuthentication') ) {
62
if ( C4::Context->preference('casAuthentication') ) {
63
        require C4::Auth_with_cas;
63
    require C4::Auth_with_cas;
64
        C4::Auth_with_cas::logout_if_required($query);
64
    C4::Auth_with_cas::logout_if_required($query);
65
    }
65
}
66
66
67
    my ( $template, $borrowernumber, $cookie ) = get_template_and_user(
67
my ( $template, $borrowernumber, $cookie ) = get_template_and_user(
68
        {
68
    {
69
            template_name => "opac-user.tt",
69
        template_name => "opac-user.tt",
70
            query         => $query,
70
        query         => $query,
71
            type          => "opac",
71
        type          => "opac",
72
        }
72
    }
73
    );
73
);
74
74
75
    my %renewed = map { $_ => 1 } split( ':', $query->param('renewed') || '' );
75
my %renewed = map { $_ => 1 } split( ':', $query->param('renewed') || '' );
76
76
77
    my $show_priority;
77
my $show_priority;
78
    for ( C4::Context->preference("OPACShowHoldQueueDetails") ) {
78
for ( C4::Context->preference("OPACShowHoldQueueDetails") ) {
79
        m/priority/ and $show_priority = 1;
79
    m/priority/ and $show_priority = 1;
80
    }
80
}
81
81
82
    my $patronupdate = $query->param('patronupdate');
82
my $patronupdate = $query->param('patronupdate');
83
    my $canrenew     = 1;
83
my $canrenew     = 1;
84
84
85
    $template->param( shibbolethAuthentication => C4::Context->config('useshibboleth') );
85
$template->param( shibbolethAuthentication => C4::Context->config('useshibboleth') );
86
86
87
# get borrower information ....
87
# get borrower information ....
88
    my $patron = Koha::Patrons->find($borrowernumber);
88
my $patron = Koha::Patrons->find($borrowernumber);
89
90
    if ( $op eq 'cud-update_arc' && C4::Context->preference("AllowPatronToControlAutorenewal") ) {
91
        my $autorenew_checkouts = $query->param('borrower_autorenew_checkouts');
92
        $patron->autorenew_checkouts($autorenew_checkouts)->store() if defined $autorenew_checkouts;
93
    }
94
89
95
    my $borr = $patron->unblessed;
90
if ( $op eq 'cud-update_arc' && C4::Context->preference("AllowPatronToControlAutorenewal") ) {
91
    my $autorenew_checkouts = $query->param('borrower_autorenew_checkouts');
92
    $patron->autorenew_checkouts($autorenew_checkouts)->store() if defined $autorenew_checkouts;
93
}
96
94
97
    my ( $today_year, $today_month, $today_day ) = Today();
95
my $borr = $patron->unblessed;
98
    my ( $warning_year, $warning_month, $warning_day ) = split /-/, $borr->{'dateexpiry'};
99
96
100
    my $debar = Koha::Patrons->find($borrowernumber)->is_debarred;
97
my ( $today_year, $today_month, $today_day ) = Today();
101
    my $userdebarred;
98
my ( $warning_year, $warning_month, $warning_day ) = split /-/, $borr->{'dateexpiry'};
102
99
103
    if ($debar) {
100
my $debar = Koha::Patrons->find($borrowernumber)->is_debarred;
104
        $userdebarred = 1;
101
my $userdebarred;
105
        $template->param( 'userdebarred' => $userdebarred );
106
        if ( $debar ne "9999-12-31" ) {
107
            $borr->{'userdebarreddate'} = $debar;
108
        }
109
102
110
        # FIXME looks like $available is not needed
103
if ($debar) {
111
        # If a user is discharged they have a validated discharge available
104
    $userdebarred = 1;
112
        my $available = Koha::Patron::Discharge::count(
105
    $template->param( 'userdebarred' => $userdebarred );
113
            {
106
    if ( $debar ne "9999-12-31" ) {
114
                borrowernumber => $borrowernumber,
107
        $borr->{'userdebarreddate'} = $debar;
115
                validated      => 1,
116
            }
117
        );
118
        $template->param( 'discharge_available' => $available
119
                && Koha::Patron::Discharge::is_discharged( { borrowernumber => $borrowernumber } ) );
120
    }
108
    }
121
109
122
    if ( $userdebarred || $borr->{'gonenoaddress'} || $borr->{'lost'} ) {
110
    # FIXME looks like $available is not needed
123
        $borr->{'flagged'} = 1;
111
    # If a user is discharged they have a validated discharge available
124
        $canrenew = 0;
112
    my $available = Koha::Patron::Discharge::count(
125
    }
113
        {
114
            borrowernumber => $borrowernumber,
115
            validated      => 1,
116
        }
117
    );
118
    $template->param( 'discharge_available' => $available
119
            && Koha::Patron::Discharge::is_discharged( { borrowernumber => $borrowernumber } ) );
120
}
126
121
127
    my $amountoutstanding = $patron->account->balance;
122
if ( $userdebarred || $borr->{'gonenoaddress'} || $borr->{'lost'} ) {
128
    my $no_renewal_amt    = C4::Context->preference('OPACFineNoRenewals');
123
    $borr->{'flagged'} = 1;
129
    $no_renewal_amt = undef unless looks_like_number($no_renewal_amt);
124
    $canrenew = 0;
130
    my $amountoutstandingfornewal =
125
}
131
        C4::Context->preference("OPACFineNoRenewalsIncludeCredits")
132
        ? $amountoutstanding
133
        : $patron->account->outstanding_debits->total_outstanding;
134
135
    if (   C4::Context->preference('OpacRenewalAllowed')
136
        && defined($no_renewal_amt)
137
        && $amountoutstandingfornewal > $no_renewal_amt )
138
    {
139
        $borr->{'flagged'} = 1;
140
        $canrenew = 0;
141
        $template->param(
142
            renewal_blocked_fines                   => $no_renewal_amt,
143
            renewal_blocked_fines_amountoutstanding => $amountoutstandingfornewal,
144
        );
145
    }
146
126
147
    my $maxoutstanding = C4::Context->preference('maxoutstanding');
127
my $amountoutstanding = $patron->account->balance;
148
    if ( $amountoutstanding && ( $amountoutstanding > $maxoutstanding ) ) {
128
my $no_renewal_amt    = C4::Context->preference('OPACFineNoRenewals');
149
        $borr->{blockedonfines} = 1;
129
$no_renewal_amt = undef unless looks_like_number($no_renewal_amt);
150
    }
130
my $amountoutstandingfornewal =
131
    C4::Context->preference("OPACFineNoRenewalsIncludeCredits")
132
    ? $amountoutstanding
133
    : $patron->account->outstanding_debits->total_outstanding;
134
135
if (   C4::Context->preference('OpacRenewalAllowed')
136
    && defined($no_renewal_amt)
137
    && $amountoutstandingfornewal > $no_renewal_amt )
138
{
139
    $borr->{'flagged'} = 1;
140
    $canrenew = 0;
141
    $template->param(
142
        renewal_blocked_fines                   => $no_renewal_amt,
143
        renewal_blocked_fines_amountoutstanding => $amountoutstandingfornewal,
144
    );
145
}
146
147
my $maxoutstanding = C4::Context->preference('maxoutstanding');
148
if ( $amountoutstanding && ( $amountoutstanding > $maxoutstanding ) ) {
149
    $borr->{blockedonfines} = 1;
150
}
151
151
152
# Warningdate is the date that the warning starts appearing
152
# Warningdate is the date that the warning starts appearing
153
    if ( $borr->{'dateexpiry'} && C4::Context->preference('NotifyBorrowerDeparture') ) {
153
if ( $borr->{'dateexpiry'} && C4::Context->preference('NotifyBorrowerDeparture') ) {
154
        my $days_to_expiry = Date_to_Days( $warning_year, $warning_month, $warning_day ) -
154
    my $days_to_expiry = Date_to_Days( $warning_year, $warning_month, $warning_day ) -
155
            Date_to_Days( $today_year, $today_month, $today_day );
155
        Date_to_Days( $today_year, $today_month, $today_day );
156
        if ( $days_to_expiry < 0 ) {
156
    if ( $days_to_expiry < 0 ) {
157
157
158
            #borrower card has expired, warn the borrower
158
        #borrower card has expired, warn the borrower
159
            $borr->{'warnexpired'} = $borr->{'dateexpiry'};
159
        $borr->{'warnexpired'} = $borr->{'dateexpiry'};
160
        } elsif ( $days_to_expiry < C4::Context->preference('NotifyBorrowerDeparture') ) {
160
    } elsif ( $days_to_expiry < C4::Context->preference('NotifyBorrowerDeparture') ) {
161
161
162
            # borrower card soon to expire, warn the borrower
162
        # borrower card soon to expire, warn the borrower
163
            $borr->{'warndeparture'} = $borr->{dateexpiry};
163
        $borr->{'warndeparture'} = $borr->{dateexpiry};
164
            if ( C4::Context->preference('ReturnBeforeExpiry') ) {
164
        if ( C4::Context->preference('ReturnBeforeExpiry') ) {
165
                $borr->{'returnbeforeexpiry'} = 1;
165
            $borr->{'returnbeforeexpiry'} = 1;
166
            }
167
        }
166
        }
168
    }
167
    }
168
}
169
169
170
    my $saving_display = C4::Context->preference('OPACShowSavings');
170
my $saving_display = C4::Context->preference('OPACShowSavings');
171
    if ( $saving_display =~ /user/ ) {
171
if ( $saving_display =~ /user/ ) {
172
        $template->param( savings => $patron->get_savings );
172
    $template->param( savings => $patron->get_savings );
173
    }
173
}
174
174
175
# pass on any renew errors to the template for displaying
175
# pass on any renew errors to the template for displaying
176
    my $renew_error = $query->param('renew_error');
176
my $renew_error = $query->param('renew_error');
177
177
178
    $template->param(
178
$template->param(
179
        amountoutstanding => $amountoutstanding,
179
    amountoutstanding => $amountoutstanding,
180
        borrowernumber    => $borrowernumber,
180
    borrowernumber    => $borrowernumber,
181
        patron_flagged    => $borr->{flagged},
181
    patron_flagged    => $borr->{flagged},
182
        surname           => $borr->{surname},
182
    surname           => $borr->{surname},
183
        RENEW_ERROR       => $renew_error,
183
    RENEW_ERROR       => $renew_error,
184
        borrower          => $borr,
184
    borrower          => $borr,
185
    );
185
);
186
186
187
#get issued items ....
187
#get issued items ....
188
188
189
    my $count          = 0;
189
my $count          = 0;
190
    my $overdues_count = 0;
190
my $overdues_count = 0;
191
    my @overdues;
191
my @overdues;
192
    my @issuedat;
192
my @issuedat;
193
    my $itemtypes         = { map { $_->{itemtype} => $_ } @{ Koha::ItemTypes->search_with_localization->unblessed } };
193
my $itemtypes         = { map { $_->{itemtype} => $_ } @{ Koha::ItemTypes->search_with_localization->unblessed } };
194
    my $pending_checkouts = $patron->pending_checkouts->search(
194
my $pending_checkouts = $patron->pending_checkouts->search(
195
        {},
195
    {},
196
        {
196
    {
197
            order_by => [ { -desc => 'date_due' }, { -asc => 'issue_id' } ],
197
        order_by => [ { -desc => 'date_due' }, { -asc => 'issue_id' } ],
198
            prefetch => 'item'
198
        prefetch => 'item'
199
    }
200
);
201
my $are_renewable_items = 0;
202
if ( $pending_checkouts->count ) {    # Useless test
203
    while ( my $c = $pending_checkouts->next ) {
204
        my $issue = $c->unblessed_all_relateds;
205
206
        # check for reserves
207
        my $restype = GetReserveStatus( $issue->{'itemnumber'} );
208
        if ($restype) {
209
            $issue->{'reserved'} = 1;
199
        }
210
        }
200
    );
201
    my $are_renewable_items = 0;
202
    if ( $pending_checkouts->count ) {    # Useless test
203
        while ( my $c = $pending_checkouts->next ) {
204
            my $issue = $c->unblessed_all_relateds;
205
206
            # check for reserves
207
            my $restype = GetReserveStatus( $issue->{'itemnumber'} );
208
            if ($restype) {
209
                $issue->{'reserved'} = 1;
210
            }
211
211
212
            # Must be moved in a module if reused
212
        # Must be moved in a module if reused
213
            my $charges = Koha::Account::Lines->search(
213
        my $charges = Koha::Account::Lines->search(
214
                {
214
            {
215
                    borrowernumber    => $patron->borrowernumber,
215
                borrowernumber    => $patron->borrowernumber,
216
                    amountoutstanding => { '>' => 0 },
216
                amountoutstanding => { '>' => 0 },
217
                    debit_type_code   => [ 'OVERDUE', 'LOST' ],
217
                debit_type_code   => [ 'OVERDUE', 'LOST' ],
218
                    itemnumber        => $issue->{itemnumber}
218
                itemnumber        => $issue->{itemnumber}
219
                },
219
            },
220
            );
220
        );
221
            $issue->{charges} = $charges->total_outstanding;
221
        $issue->{charges} = $charges->total_outstanding;
222
222
223
            my $rental_fines = Koha::Account::Lines->search(
223
        my $rental_fines = Koha::Account::Lines->search(
224
                {
224
            {
225
                    borrowernumber    => $patron->borrowernumber,
225
                borrowernumber    => $patron->borrowernumber,
226
                    amountoutstanding => { '>'    => 0 },
226
                amountoutstanding => { '>'    => 0 },
227
                    debit_type_code   => { 'LIKE' => 'RENT_%' },
227
                debit_type_code   => { 'LIKE' => 'RENT_%' },
228
                    itemnumber        => $issue->{itemnumber}
228
                itemnumber        => $issue->{itemnumber}
229
                }
230
            );
231
            $issue->{rentalfines} = $rental_fines->total_outstanding;
232
233
            # check if item is renewable
234
            my ( $status, $renewerror, $info ) = CanBookBeRenewed( $patron, $c );
235
            (
236
                $issue->{'renewcount'},
237
                $issue->{'renewsallowed'},
238
                $issue->{'renewsleft'},
239
                $issue->{'unseencount'},
240
                $issue->{'unseenallowed'},
241
                $issue->{'unseenleft'}
242
            ) = GetRenewCount( $patron, $c->item );
243
            ( $issue->{'renewalfee'}, $issue->{'renewalitemtype'} ) =
244
                GetIssuingCharges( $issue->{'itemnumber'}, $borrowernumber );
245
            $issue->{itemtype_object} = Koha::ItemTypes->find( $c->item->effective_itemtype );
246
            if ( $status && C4::Context->preference("OpacRenewalAllowed") ) {
247
                $are_renewable_items = 1;
248
                $issue->{'status'} = $status;
249
            }
229
            }
230
        );
231
        $issue->{rentalfines} = $rental_fines->total_outstanding;
232
233
        # check if item is renewable
234
        my ( $status, $renewerror, $info ) = CanBookBeRenewed( $patron, $c );
235
        (
236
            $issue->{'renewcount'},
237
            $issue->{'renewsallowed'},
238
            $issue->{'renewsleft'},
239
            $issue->{'unseencount'},
240
            $issue->{'unseenallowed'},
241
            $issue->{'unseenleft'}
242
        ) = GetRenewCount( $patron, $c->item );
243
        ( $issue->{'renewalfee'}, $issue->{'renewalitemtype'} ) =
244
            GetIssuingCharges( $issue->{'itemnumber'}, $borrowernumber );
245
        $issue->{itemtype_object} = Koha::ItemTypes->find( $c->item->effective_itemtype );
246
        if ( $status && C4::Context->preference("OpacRenewalAllowed") ) {
247
            $are_renewable_items = 1;
248
            $issue->{'status'} = $status;
249
        }
250
250
251
            $issue->{'renewed'} = $renewed{ $issue->{'itemnumber'} };
251
        $issue->{'renewed'} = $renewed{ $issue->{'itemnumber'} };
252
252
253
            if ($renewerror) {
253
        if ($renewerror) {
254
                $issue->{'too_many'}                    = 1 if $renewerror eq 'too_many';
254
            $issue->{'too_many'}                    = 1 if $renewerror eq 'too_many';
255
                $issue->{'too_unseen'}                  = 1 if $renewerror eq 'too_unseen';
255
            $issue->{'too_unseen'}                  = 1 if $renewerror eq 'too_unseen';
256
                $issue->{'on_reserve'}                  = 1 if $renewerror eq 'on_reserve';
256
            $issue->{'on_reserve'}                  = 1 if $renewerror eq 'on_reserve';
257
                $issue->{'norenew_overdue'}             = 1 if $renewerror eq 'overdue';
257
            $issue->{'norenew_overdue'}             = 1 if $renewerror eq 'overdue';
258
                $issue->{'auto_renew'}                  = 1 if $renewerror eq 'auto_renew';
258
            $issue->{'auto_renew'}                  = 1 if $renewerror eq 'auto_renew';
259
                $issue->{'auto_too_soon'}               = 1 if $renewerror eq 'auto_too_soon';
259
            $issue->{'auto_too_soon'}               = 1 if $renewerror eq 'auto_too_soon';
260
                $issue->{'auto_too_late'}               = 1 if $renewerror eq 'auto_too_late';
260
            $issue->{'auto_too_late'}               = 1 if $renewerror eq 'auto_too_late';
261
                $issue->{'auto_too_much_oweing'}        = 1 if $renewerror eq 'auto_too_much_oweing';
261
            $issue->{'auto_too_much_oweing'}        = 1 if $renewerror eq 'auto_too_much_oweing';
262
                $issue->{'item_denied_renewal'}         = 1 if $renewerror eq 'item_denied_renewal';
262
            $issue->{'item_denied_renewal'}         = 1 if $renewerror eq 'item_denied_renewal';
263
                $issue->{'item_issued_to_other_patron'} = 1 if $renewerror eq 'item_issued_to_other_patron';
263
            $issue->{'item_issued_to_other_patron'} = 1 if $renewerror eq 'item_issued_to_other_patron';
264
264
265
                if ( $renewerror eq 'too_soon' ) {
265
            if ( $renewerror eq 'too_soon' ) {
266
                    $issue->{'too_soon'}         = 1;
266
                $issue->{'too_soon'}         = 1;
267
                    $issue->{'soonestrenewdate'} = $info->{soonest_renew_date};
267
                $issue->{'soonestrenewdate'} = $info->{soonest_renew_date};
268
                }
269
            }
268
            }
269
        }
270
270
271
            if ( $c->is_overdue ) {
271
        if ( $c->is_overdue ) {
272
                push @overdues, $issue;
272
            push @overdues, $issue;
273
                $overdues_count++;
273
            $overdues_count++;
274
                $issue->{'overdue'} = 1;
274
            $issue->{'overdue'} = 1;
275
            } else {
275
        } else {
276
                $issue->{'issued'} = 1;
276
            $issue->{'issued'} = 1;
277
            }
277
        }
278
278
279
            # imageurl:
279
        # imageurl:
280
            my $itemtype = $issue->{'itemtype'};
280
        my $itemtype = $issue->{'itemtype'};
281
            if ($itemtype) {
281
        if ($itemtype) {
282
                $issue->{'imageurl'}    = getitemtypeimagelocation( 'opac', $itemtypes->{$itemtype}->{'imageurl'} );
282
            $issue->{'imageurl'}    = getitemtypeimagelocation( 'opac', $itemtypes->{$itemtype}->{'imageurl'} );
283
                $issue->{'description'} = $itemtypes->{$itemtype}->{'description'};
283
            $issue->{'description'} = $itemtypes->{$itemtype}->{'description'};
284
            }
284
        }
285
285
286
            if ( C4::Context->preference('OpacStarRatings') eq 'all' ) {
286
        if ( C4::Context->preference('OpacStarRatings') eq 'all' ) {
287
                my $ratings = Koha::Ratings->search( { biblionumber => $issue->{biblionumber} } );
287
            my $ratings = Koha::Ratings->search( { biblionumber => $issue->{biblionumber} } );
288
                $issue->{ratings} = $ratings;
288
            $issue->{ratings} = $ratings;
289
                $issue->{my_rating} =
289
            $issue->{my_rating} =
290
                    $borrowernumber ? $ratings->search( { borrowernumber => $borrowernumber } )->next : undef;
290
                $borrowernumber ? $ratings->search( { borrowernumber => $borrowernumber } )->next : undef;
291
            }
291
        }
292
292
293
            my $biblio_object = Koha::Biblios->find( $issue->{biblionumber} );
293
        my $biblio_object = Koha::Biblios->find( $issue->{biblionumber} );
294
            $issue->{biblio_object} = $biblio_object;
294
        $issue->{biblio_object} = $biblio_object;
295
            push @issuedat, $issue;
295
        push @issuedat, $issue;
296
            $count++;
296
        $count++;
297
297
298
            my $isbn = GetNormalizedISBN( $issue->{'isbn'} );
298
        my $isbn = GetNormalizedISBN( $issue->{'isbn'} );
299
            $issue->{normalized_isbn} = $isbn;
299
        $issue->{normalized_isbn} = $isbn;
300
300
301
            if (   C4::Context->preference('BakerTaylorEnabled')
301
        if (   C4::Context->preference('BakerTaylorEnabled')
302
                || C4::Context->preference('SyndeticsEnabled')
302
            || C4::Context->preference('SyndeticsEnabled')
303
                || C4::Context->preference('SyndeticsCoverImages') )
303
            || C4::Context->preference('SyndeticsCoverImages') )
304
            {
304
        {
305
                my $marcrecord = $biblio_object->metadata_record(
305
            my $marcrecord = $biblio_object->metadata_record(
306
                    {
306
                {
307
                        embed_items => 1,
307
                    embed_items => 1,
308
                        interface   => 'opac',
308
                    interface   => 'opac',
309
                        patron      => $patron
309
                    patron      => $patron
310
                    }
310
                }
311
                );
311
            );
312
                $issue->{normalized_upc}  = GetNormalizedUPC( $marcrecord, C4::Context->preference('marcflavour') );
312
            $issue->{normalized_upc}  = GetNormalizedUPC( $marcrecord, C4::Context->preference('marcflavour') );
313
                $issue->{normalized_oclc} = GetNormalizedOCLCNumber( $marcrecord, C4::Context->preference('marcflavour') );
313
            $issue->{normalized_oclc} = GetNormalizedOCLCNumber( $marcrecord, C4::Context->preference('marcflavour') );
314
            }
314
        }
315
315
316
            if ( C4::Context->preference('UseRecalls') ) {
316
        if ( C4::Context->preference('UseRecalls') ) {
317
                my $maybe_recalls = Koha::Recalls->search(
317
            my $maybe_recalls = Koha::Recalls->search(
318
                    { biblio_id => $issue->{biblionumber}, item_id => [ undef, $issue->{itemnumber} ], completed => 0 } );
318
                { biblio_id => $issue->{biblionumber}, item_id => [ undef, $issue->{itemnumber} ], completed => 0 } );
319
                while ( my $recall = $maybe_recalls->next ) {
319
            while ( my $recall = $maybe_recalls->next ) {
320
                    if ( $recall->checkout and $recall->checkout->issue_id == $issue->{issue_id} ) {
320
                if ( $recall->checkout and $recall->checkout->issue_id == $issue->{issue_id} ) {
321
                        $issue->{recall} = 1;
321
                    $issue->{recall} = 1;
322
                        last;
322
                    last;
323
                    }
324
                }
323
                }
325
            }
324
            }
326
        }
325
        }
327
    }
326
    }
328
    my $overduesblockrenewing = C4::Context->preference('OverduesBlockRenewing');
327
}
329
    $canrenew = 0 if ( $overduesblockrenewing ne 'allow' and $overdues_count == $count ) || !$are_renewable_items;
328
my $overduesblockrenewing = C4::Context->preference('OverduesBlockRenewing');
330
329
$canrenew = 0 if ( $overduesblockrenewing ne 'allow' and $overdues_count == $count ) || !$are_renewable_items;
331
    $template->param( ISSUES         => \@issuedat );
330
332
    $template->param( issues_count   => $count );
331
$template->param( ISSUES         => \@issuedat );
333
    $template->param( canrenew       => $canrenew );
332
$template->param( issues_count   => $count );
334
    $template->param( OVERDUES       => \@overdues );
333
$template->param( canrenew       => $canrenew );
335
    $template->param( overdues_count => $overdues_count );
334
$template->param( OVERDUES       => \@overdues );
336
335
$template->param( overdues_count => $overdues_count );
337
    my $show_barcode = Koha::Patron::Attribute::Types->search(    # FIXME we should not need this search
336
338
        { code => ATTRIBUTE_SHOW_BARCODE }
337
my $show_barcode = Koha::Patron::Attribute::Types->search(    # FIXME we should not need this search
339
    )->count;
338
    { code => ATTRIBUTE_SHOW_BARCODE }
340
    if ($show_barcode) {
339
)->count;
341
        my $patron_show_barcode = $patron->get_extended_attribute(ATTRIBUTE_SHOW_BARCODE);
340
if ($show_barcode) {
342
        undef $show_barcode if $patron_show_barcode and not $patron_show_barcode->attribute;
341
    my $patron_show_barcode = $patron->get_extended_attribute(ATTRIBUTE_SHOW_BARCODE);
343
    }
342
    undef $show_barcode if $patron_show_barcode and not $patron_show_barcode->attribute;
344
    $template->param( show_barcode => 1 ) if $show_barcode;
343
}
344
$template->param( show_barcode => 1 ) if $show_barcode;
345
345
346
# now the reserved items....
346
# now the reserved items....
347
    my $reserves = $patron->holds->filter_out_has_cancellation_requests;
347
my $reserves = $patron->holds->filter_out_has_cancellation_requests;
348
349
    $template->param(
350
        RESERVES       => $reserves,
351
        reserves_count => $reserves->count_holds,
352
        showpriority   => $show_priority,
353
    );
354
355
    if ( C4::Context->preference('UseRecalls') ) {
356
        my $recalls = Koha::Recalls->search( { patron_id => $borrowernumber, completed => 0 } );
357
        $template->param( RECALLS => $recalls );
358
    }
359
348
360
    if ( C4::Context->preference('BakerTaylorEnabled') ) {
349
$template->param(
361
        $template->param(
350
    RESERVES       => $reserves,
362
            BakerTaylorEnabled      => 1,
351
    reserves_count => $reserves->count_holds,
363
            BakerTaylorImageURL     => &image_url(),
352
    showpriority   => $show_priority,
364
            BakerTaylorLinkURL      => &link_url(),
353
);
365
            BakerTaylorBookstoreURL => C4::Context->preference('BakerTaylorBookstoreURL'),
366
        );
367
    }
368
354
369
    if (   C4::Context->preference("OPACAmazonCoverImages")
355
if ( C4::Context->preference('UseRecalls') ) {
370
        or C4::Context->preference("GoogleJackets")
356
    my $recalls = Koha::Recalls->search( { patron_id => $borrowernumber, completed => 0 } );
371
        or C4::Context->preference("BakerTaylorEnabled")
357
    $template->param( RECALLS => $recalls );
372
        or C4::Context->preference("SyndeticsCoverImages")
358
}
373
        or ( C4::Context->preference('OPACCustomCoverImages') and C4::Context->preference('CustomCoverImagesURL') ) )
374
    {
375
        $template->param( JacketImages => 1 );
376
    }
377
359
360
if ( C4::Context->preference('BakerTaylorEnabled') ) {
378
    $template->param(
361
    $template->param(
379
        overdrive_error => scalar $query->param('overdrive_error') || undef,
362
        BakerTaylorEnabled      => 1,
380
        overdrive_tab   => scalar $query->param('overdrive_tab')   || 0,
363
        BakerTaylorImageURL     => &image_url(),
364
        BakerTaylorLinkURL      => &link_url(),
365
        BakerTaylorBookstoreURL => C4::Context->preference('BakerTaylorBookstoreURL'),
381
    );
366
    );
367
}
382
368
383
    my $patron_messages = Koha::Patron::Messages->search(
369
if (   C4::Context->preference("OPACAmazonCoverImages")
384
        {
370
    or C4::Context->preference("GoogleJackets")
385
            borrowernumber => $borrowernumber,
371
    or C4::Context->preference("BakerTaylorEnabled")
386
            message_type   => 'B',
372
    or C4::Context->preference("SyndeticsCoverImages")
387
        }
373
    or ( C4::Context->preference('OPACCustomCoverImages') and C4::Context->preference('CustomCoverImagesURL') ) )
388
    );
374
{
375
    $template->param( JacketImages => 1 );
376
}
389
377
390
    if (   C4::Context->preference('AllowPatronToSetCheckoutsVisibilityForGuarantor')
378
$template->param(
391
        || C4::Context->preference('AllowStaffToSetCheckoutsVisibilityForGuarantor') )
379
    overdrive_error => scalar $query->param('overdrive_error') || undef,
392
    {
380
    overdrive_tab   => scalar $query->param('overdrive_tab')   || 0,
393
        my @relatives;
381
);
394
382
395
        # Filter out guarantees that don't want guarantor to see checkouts
383
my $patron_messages = Koha::Patron::Messages->search(
396
        foreach my $gr ( $patron->guarantee_relationships->as_list ) {
384
    {
397
            my $g = $gr->guarantee;
385
        borrowernumber => $borrowernumber,
398
            push( @relatives, $g ) if $g->privacy_guarantor_checkouts;
386
        message_type   => 'B',
399
        }
400
        $template->param( relatives => \@relatives );
401
    }
387
    }
388
);
402
389
403
    if (   C4::Context->preference('AllowPatronToSetFinesVisibilityForGuarantor')
390
if (   C4::Context->preference('AllowPatronToSetCheckoutsVisibilityForGuarantor')
404
        || C4::Context->preference('AllowStaffToSetFinesVisibilityForGuarantor') )
391
    || C4::Context->preference('AllowStaffToSetCheckoutsVisibilityForGuarantor') )
405
    {
392
{
406
        my @relatives_with_fines;
393
    my @relatives;
407
394
408
        # Filter out guarantees that don't want guarantor to see checkouts
395
    # Filter out guarantees that don't want guarantor to see checkouts
409
        foreach my $gr ( $patron->guarantee_relationships->as_list ) {
396
    foreach my $gr ( $patron->guarantee_relationships->as_list ) {
410
            my $g = $gr->guarantee;
397
        my $g = $gr->guarantee;
411
            push( @relatives_with_fines, $g ) if $g->privacy_guarantor_fines;
398
        push( @relatives, $g ) if $g->privacy_guarantor_checkouts;
412
        }
413
        $template->param( relatives_with_fines => \@relatives_with_fines );
414
    }
399
    }
400
    $template->param( relatives => \@relatives );
401
}
415
402
416
    if ( C4::Context->preference("ArticleRequests") ) {
403
if (   C4::Context->preference('AllowPatronToSetFinesVisibilityForGuarantor')
417
        $template->param(
404
    || C4::Context->preference('AllowStaffToSetFinesVisibilityForGuarantor') )
418
            current_article_requests => [ $patron->article_requests->filter_by_current->as_list ],
405
{
419
        );
406
    my @relatives_with_fines;
420
    }
421
407
422
    my $self_renewal_available = $patron->is_eligible_for_self_renewal();
408
    # Filter out guarantees that don't want guarantor to see checkouts
423
    my $self_renewal_alert;
409
    foreach my $gr ( $patron->guarantee_relationships->as_list ) {
424
    my $self_renewal_success = $query->param('self_renewal_success');
410
        my $g = $gr->guarantee;
425
    $self_renewal_alert = $patron->category->self_renewal_failure_message
411
        push( @relatives_with_fines, $g ) if $g->privacy_guarantor_fines;
426
        if defined $self_renewal_success && !$self_renewal_success;
412
    }
427
    my $self_renewal_confirmation_sent = $query->param('confirmation_sent');
413
    $template->param( relatives_with_fines => \@relatives_with_fines );
414
}
428
415
416
if ( C4::Context->preference("ArticleRequests") ) {
429
    $template->param(
417
    $template->param(
430
    patron_messages          => $patron_messages,
418
        current_article_requests => [ $patron->article_requests->filter_by_current->as_list ],
431
    opacnote                 => $borr->{opacnote},
419
    );
432
    patronupdate             => $patronupdate,
420
}
433
    OpacRenewalAllowed       => C4::Context->preference("OpacRenewalAllowed"),
421
434
    userview                 => 1,
422
my $self_renewal_available = $patron->is_eligible_for_self_renewal();
435
    tab                      => scalar $query->param('tab') || q{},
423
my $self_renewal_alert;
436
    SuspendHoldsOpac         => C4::Context->preference('SuspendHoldsOpac'),
424
my $self_renewal_success = $query->param('self_renewal_success');
437
    AutoResumeSuspendedHolds => C4::Context->preference('AutoResumeSuspendedHolds'),
425
$self_renewal_alert = $patron->category->self_renewal_failure_message
438
    OpacHoldNotes            => C4::Context->preference('OpacHoldNotes'),
426
    if defined $self_renewal_success && !$self_renewal_success;
439
    failed_holds             => scalar $query->param('failed_holds'),
427
my $self_renewal_confirmation_sent = $query->param('confirmation_sent');
428
429
$template->param(
430
    patron_messages                => $patron_messages,
431
    opacnote                       => $borr->{opacnote},
432
    patronupdate                   => $patronupdate,
433
    OpacRenewalAllowed             => C4::Context->preference("OpacRenewalAllowed"),
434
    userview                       => 1,
435
    tab                            => scalar $query->param('tab') || q{},
436
    SuspendHoldsOpac               => C4::Context->preference('SuspendHoldsOpac'),
437
    AutoResumeSuspendedHolds       => C4::Context->preference('AutoResumeSuspendedHolds'),
438
    OpacHoldNotes                  => C4::Context->preference('OpacHoldNotes'),
439
    failed_holds                   => scalar $query->param('failed_holds'),
440
    self_renewal_available         => $self_renewal_available,
440
    self_renewal_available         => $self_renewal_available,
441
    self_renewal_success           => $self_renewal_success,
441
    self_renewal_success           => $self_renewal_success,
442
    self_renewal_alert             => $self_renewal_alert,
442
    self_renewal_alert             => $self_renewal_alert,
(-)a/t/db_dependent/api/v1/patron_categories.t (-7 / +16 lines)
Lines 66-73 subtest 'list() tests' => sub { Link Here
66
66
67
    $t->get_ok("//$userid:$password@/api/v1/patron_categories")->status_is(200);
67
    $t->get_ok("//$userid:$password@/api/v1/patron_categories")->status_is(200);
68
68
69
    $t->get_ok("//$userid:$password@/api/v1/patron_categories?q={\"me.categorycode\":\"TEST\"}")->status_is(200)
69
    $t->get_ok("//$userid:$password@/api/v1/patron_categories?q={\"me.categorycode\":\"TEST\"}")
70
        ->json_has('/0/name')->json_is( '/0/name' => 'Test' )->json_hasnt('/1');
70
        ->status_is(200)
71
        ->json_has('/0/name')
72
        ->json_is( '/0/name' => 'Test' )
73
        ->json_hasnt('/1');
71
74
72
    # Off limits search
75
    # Off limits search
73
76
Lines 92-105 subtest 'list() tests' => sub { Link Here
92
    my $off_limits_userid = $off_limits_librarian->userid;
95
    my $off_limits_userid = $off_limits_librarian->userid;
93
96
94
    $t->get_ok("//$off_limits_userid:$off_limits_password@/api/v1/patron_categories?q={\"me.categorycode\":\"TEST\"}")
97
    $t->get_ok("//$off_limits_userid:$off_limits_password@/api/v1/patron_categories?q={\"me.categorycode\":\"TEST\"}")
95
        ->status_is(200)->json_hasnt('/0');
98
        ->status_is(200)
99
        ->json_hasnt('/0');
96
100
97
    # Off limits librarian category has changed to one within limits
101
    # Off limits librarian category has changed to one within limits
98
102
99
    $off_limits_librarian->branchcode( $library->branchcode )->store;
103
    $off_limits_librarian->branchcode( $library->branchcode )->store;
100
104
101
    $t->get_ok("//$off_limits_userid:$off_limits_password@/api/v1/patron_categories?q={\"me.categorycode\":\"TEST\"}")
105
    $t->get_ok("//$off_limits_userid:$off_limits_password@/api/v1/patron_categories?q={\"me.categorycode\":\"TEST\"}")
102
        ->status_is(200)->json_has('/0/name')->json_is( [ $category->to_api ] )->json_hasnt('/1');
106
        ->status_is(200)
107
        ->json_has('/0/name')
108
        ->json_is( [ $category->to_api ] )
109
        ->json_hasnt('/1');
103
110
104
    $schema->storage->txn_rollback;
111
    $schema->storage->txn_rollback;
105
112
Lines 149-155 subtest 'add() tests' => sub { Link Here
149
    };
156
    };
150
157
151
    $t->post_ok( "//$userid:$password@/api/v1/patron_categories" => json => $category_with_invalid_field )
158
    $t->post_ok( "//$userid:$password@/api/v1/patron_categories" => json => $category_with_invalid_field )
152
        ->status_is(400)->json_is(
159
        ->status_is(400)
160
        ->json_is(
153
        "/errors" => [
161
        "/errors" => [
154
            {
162
            {
155
                message => "Properties not allowed: blah.",
163
                message => "Properties not allowed: blah.",
Lines 161-170 subtest 'add() tests' => sub { Link Here
161
    # Authorized attempt to write
169
    # Authorized attempt to write
162
    my $patron_category_id =
170
    my $patron_category_id =
163
        $t->post_ok( "//$userid:$password@/api/v1/patron_categories" => json => $category )
171
        $t->post_ok( "//$userid:$password@/api/v1/patron_categories" => json => $category )
164
        ->status_is( 201, 'REST3.2.1' )->header_like(
172
        ->status_is( 201, 'REST3.2.1' )
173
        ->header_like(
165
        Location => qr|^\/api\/v1\/patron_categories/\d*|,
174
        Location => qr|^\/api\/v1\/patron_categories/\d*|,
166
        'REST3.4.1'
175
        'REST3.4.1'
167
    )->json_is( '/name' => $category->{name} )->tx->res->json->{patron_category_id};
176
        )->json_is( '/name' => $category->{name} )->tx->res->json->{patron_category_id};
168
177
169
    $schema->storage->txn_rollback;
178
    $schema->storage->txn_rollback;
170
};
179
};
(-)a/t/db_dependent/api/v1/patrons.t (-58 / +116 lines)
Lines 125-137 subtest 'list() tests' => sub { Link Here
125
125
126
        $t->get_ok("//$userid:$password@/api/v1/patrons")->status_is( 200, 'list_borrowers makes /patrons accessible' );
126
        $t->get_ok("//$userid:$password@/api/v1/patrons")->status_is( 200, 'list_borrowers makes /patrons accessible' );
127
127
128
        $t->get_ok( "//$userid:$password@/api/v1/patrons?cardnumber=" . $librarian->cardnumber )->status_is(200)
128
        $t->get_ok( "//$userid:$password@/api/v1/patrons?cardnumber=" . $librarian->cardnumber )
129
            ->status_is(200)
129
            ->json_is( '/0/cardnumber' => $librarian->cardnumber );
130
            ->json_is( '/0/cardnumber' => $librarian->cardnumber );
130
131
131
        $t->get_ok( "//$userid:$password@/api/v1/patrons?q={\"cardnumber\":\"" . $librarian->cardnumber . "\"}" )
132
        $t->get_ok( "//$userid:$password@/api/v1/patrons?q={\"cardnumber\":\"" . $librarian->cardnumber . "\"}" )
132
            ->status_is(200)->json_is( '/0/cardnumber' => $librarian->cardnumber );
133
            ->status_is(200)
134
            ->json_is( '/0/cardnumber' => $librarian->cardnumber );
133
135
134
        $t->get_ok( "//$userid:$password@/api/v1/patrons?address2=" . $librarian->address2 )->status_is(200)
136
        $t->get_ok( "//$userid:$password@/api/v1/patrons?address2=" . $librarian->address2 )
137
            ->status_is(200)
135
            ->json_is( '/0/address2' => $librarian->address2 );
138
            ->json_is( '/0/address2' => $librarian->address2 );
136
139
137
        subtest 'restricted & expired' => sub {
140
        subtest 'restricted & expired' => sub {
Lines 145-157 subtest 'list() tests' => sub { Link Here
145
            $t->get_ok( "//$userid:$password@/api/v1/patrons?restricted="
148
            $t->get_ok( "//$userid:$password@/api/v1/patrons?restricted="
146
                    . Mojo::JSON->true
149
                    . Mojo::JSON->true
147
                    . "&cardnumber="
150
                    . "&cardnumber="
148
                    . $patron->cardnumber )->status_is(200)->json_has('/0/restricted')
151
                    . $patron->cardnumber )
149
                ->json_is( '/0/restricted' => Mojo::JSON->true )->json_has('/0/expired')
152
                ->status_is(200)
150
                ->json_is( '/0/expired'    => Mojo::JSON->false )->json_hasnt('/1');
153
                ->json_has('/0/restricted')
154
                ->json_is( '/0/restricted' => Mojo::JSON->true )
155
                ->json_has('/0/expired')
156
                ->json_is( '/0/expired' => Mojo::JSON->false )
157
                ->json_hasnt('/1');
151
158
152
            $patron->dateexpiry( dt_from_string->subtract( days => 2 ) )->store;
159
            $patron->dateexpiry( dt_from_string->subtract( days => 2 ) )->store;
153
            $t->get_ok( "//$userid:$password@/api/v1/patrons/" . $patron->borrowernumber )->status_is(200)
160
            $t->get_ok( "//$userid:$password@/api/v1/patrons/" . $patron->borrowernumber )
154
                ->json_has('/expired')->json_is( '/expired' => Mojo::JSON->true );
161
                ->status_is(200)
162
                ->json_has('/expired')
163
                ->json_is( '/expired' => Mojo::JSON->true );
155
164
156
            $t->get_ok( "//$userid:$password@/api/v1/patrons?"
165
            $t->get_ok( "//$userid:$password@/api/v1/patrons?"
157
                    . 'q={"extended_attributes.type":"CODE"}' => { 'x-koha-embed' => 'extended_attributes' } )
166
                    . 'q={"extended_attributes.type":"CODE"}' => { 'x-koha-embed' => 'extended_attributes' } )
Lines 180-192 subtest 'list() tests' => sub { Link Here
180
            $t->get_ok( "//$userid:$password@/api/v1/patrons?date_of_birth="
189
            $t->get_ok( "//$userid:$password@/api/v1/patrons?date_of_birth="
181
                    . $date_of_birth
190
                    . $date_of_birth
182
                    . "&cardnumber="
191
                    . "&cardnumber="
183
                    . $patron->cardnumber )->status_is(200)
192
                    . $patron->cardnumber )
193
                ->status_is(200)
184
                ->json_is( '/0/patron_id' => $patron->id, 'Filtering by date works' );
194
                ->json_is( '/0/patron_id' => $patron->id, 'Filtering by date works' );
185
195
186
            $t->get_ok( "//$userid:$password@/api/v1/patrons?last_seen="
196
            $t->get_ok( "//$userid:$password@/api/v1/patrons?last_seen="
187
                    . $last_seen_rfc3339
197
                    . $last_seen_rfc3339
188
                    . "&cardnumber="
198
                    . "&cardnumber="
189
                    . $patron->cardnumber )->status_is(200)
199
                    . $patron->cardnumber )
200
                ->status_is(200)
190
                ->json_is( '/0/patron_id' => $patron->id, 'Filtering by date-time works' );
201
                ->json_is( '/0/patron_id' => $patron->id, 'Filtering by date-time works' );
191
202
192
            my $q = encode_json(
203
            my $q = encode_json(
Lines 196-202 subtest 'list() tests' => sub { Link Here
196
                }
207
                }
197
            );
208
            );
198
209
199
            $t->get_ok("//$userid:$password@/api/v1/patrons?q=$q")->status_is(200)
210
            $t->get_ok("//$userid:$password@/api/v1/patrons?q=$q")
211
                ->status_is(200)
200
                ->json_is( '/0/patron_id' => $patron->id, 'Filtering by date works' );
212
                ->json_is( '/0/patron_id' => $patron->id, 'Filtering by date works' );
201
213
202
            $q = encode_json(
214
            $q = encode_json(
Lines 206-212 subtest 'list() tests' => sub { Link Here
206
                }
218
                }
207
            );
219
            );
208
220
209
            $t->get_ok("//$userid:$password@/api/v1/patrons?q=$q")->status_is(200)
221
            $t->get_ok("//$userid:$password@/api/v1/patrons?q=$q")
222
                ->status_is(200)
210
                ->json_is( '/0/patron_id' => $patron->id, 'Filtering by date-time works' );
223
                ->json_is( '/0/patron_id' => $patron->id, 'Filtering by date-time works' );
211
        };
224
        };
212
225
Lines 250-265 subtest 'list() tests' => sub { Link Here
250
        my $userid = $librarian->userid;
263
        my $userid = $librarian->userid;
251
264
252
        $t->get_ok( "//$userid:$password@/api/v1/patrons?_order_by=patron_id&q="
265
        $t->get_ok( "//$userid:$password@/api/v1/patrons?_order_by=patron_id&q="
253
                . encode_json( { library_id => [ $library_1->id, $library_2->id ] } ) )->status_is(200)
266
                . encode_json( { library_id => [ $library_1->id, $library_2->id ] } ) )
254
            ->json_is( '/0/patron_id' => $patron_1->id )->json_is( '/1/patron_id' => $patron_2->id )
267
            ->status_is(200)
268
            ->json_is( '/0/patron_id' => $patron_1->id )
269
            ->json_is( '/1/patron_id' => $patron_2->id )
255
            ->json_is( '/2/patron_id' => $patron_3->id );
270
            ->json_is( '/2/patron_id' => $patron_3->id );
256
271
257
        @libraries_where_can_see_patrons = ( $library_2->id );
272
        @libraries_where_can_see_patrons = ( $library_2->id );
258
273
259
        my $res =
274
        my $res =
260
            $t->get_ok( "//$userid:$password@/api/v1/patrons?_order_by=patron_id&q="
275
            $t->get_ok( "//$userid:$password@/api/v1/patrons?_order_by=patron_id&q="
261
                . encode_json( { library_id => [ $library_1->id, $library_2->id ] } ) )->status_is(200)
276
                . encode_json( { library_id => [ $library_1->id, $library_2->id ] } ) )
262
            ->json_is( '/0/patron_id' => $patron_3->id, 'Returns the only allowed patron' )->tx->res->json;
277
            ->status_is(200)
278
            ->json_is( '/0/patron_id' => $patron_3->id, 'Returns the only allowed patron' )
279
            ->tx->res->json;
263
280
264
        is( scalar @{$res}, 1, 'Only one patron returned' );
281
        is( scalar @{$res}, 1, 'Only one patron returned' );
265
282
Lines 322-329 subtest 'get() tests' => sub { Link Here
322
339
323
        $t->get_ok( "//$userid:$password@/api/v1/patrons/" . $patron->id )
340
        $t->get_ok( "//$userid:$password@/api/v1/patrons/" . $patron->id )
324
            ->status_is( 200, 'list_borrowers permission makes patron visible' )
341
            ->status_is( 200, 'list_borrowers permission makes patron visible' )
325
            ->json_is( '/patron_id' => $patron->id )->json_is( '/category_id' => $patron->categorycode )
342
            ->json_is( '/patron_id'        => $patron->id )
326
            ->json_is( '/surname'   => $patron->surname )->json_is( '/patron_card_lost' => Mojo::JSON->false );
343
            ->json_is( '/category_id'      => $patron->categorycode )
344
            ->json_is( '/surname'          => $patron->surname )
345
            ->json_is( '/patron_card_lost' => Mojo::JSON->false );
327
346
328
        $schema->storage->txn_rollback;
347
        $schema->storage->txn_rollback;
329
    };
348
    };
Lines 365-382 subtest 'get() tests' => sub { Link Here
365
        $librarian->set_password( { password => $password, skip_validation => 1 } );
384
        $librarian->set_password( { password => $password, skip_validation => 1 } );
366
        my $userid = $librarian->userid;
385
        my $userid = $librarian->userid;
367
386
368
        $t->get_ok( "//$userid:$password@/api/v1/patrons/" . $patron_1->id )->status_is(200)
387
        $t->get_ok( "//$userid:$password@/api/v1/patrons/" . $patron_1->id )
388
            ->status_is(200)
369
            ->json_is( '/patron_id' => $patron_1->id );
389
            ->json_is( '/patron_id' => $patron_1->id );
370
390
371
        $t->get_ok( "//$userid:$password@/api/v1/patrons/" . $patron_2->id )->status_is(200)
391
        $t->get_ok( "//$userid:$password@/api/v1/patrons/" . $patron_2->id )
392
            ->status_is(200)
372
            ->json_is( '/patron_id' => $patron_2->id );
393
            ->json_is( '/patron_id' => $patron_2->id );
373
394
374
        @libraries_where_can_see_patrons = ( $library_1->id );
395
        @libraries_where_can_see_patrons = ( $library_1->id );
375
396
376
        $t->get_ok( "//$userid:$password@/api/v1/patrons/" . $patron_1->id )->status_is(200)
397
        $t->get_ok( "//$userid:$password@/api/v1/patrons/" . $patron_1->id )
398
            ->status_is(200)
377
            ->json_is( '/patron_id' => $patron_1->id );
399
            ->json_is( '/patron_id' => $patron_1->id );
378
400
379
        $t->get_ok( "//$userid:$password@/api/v1/patrons/" . $patron_2->id )->status_is(404)
401
        $t->get_ok( "//$userid:$password@/api/v1/patrons/" . $patron_2->id )
402
            ->status_is(404)
380
            ->json_is( '/error' => 'Patron not found' );
403
            ->json_is( '/error' => 'Patron not found' );
381
404
382
        $schema->storage->txn_rollback;
405
        $schema->storage->txn_rollback;
Lines 465-471 subtest 'add() tests' => sub { Link Here
465
        my $userid = $librarian->userid;
488
        my $userid = $librarian->userid;
466
        t::lib::Mocks::mock_preference( 'PatronDuplicateMatchingAddFields', 'firstname|surname|dateofbirth' );
489
        t::lib::Mocks::mock_preference( 'PatronDuplicateMatchingAddFields', 'firstname|surname|dateofbirth' );
467
490
468
        $t->post_ok( "//$userid:$password@/api/v1/patrons" => json => $newpatron )->status_is(409)
491
        $t->post_ok( "//$userid:$password@/api/v1/patrons" => json => $newpatron )
492
            ->status_is(409)
469
            ->json_is( '/error' => "A patron record matching these details already exists" );
493
            ->json_is( '/error' => "A patron record matching these details already exists" );
470
494
471
        # Create a library just to make sure its ID doesn't exist on the DB
495
        # Create a library just to make sure its ID doesn't exist on the DB
Lines 479-485 subtest 'add() tests' => sub { Link Here
479
503
480
        # Test duplicate userid constraint
504
        # Test duplicate userid constraint
481
        $t->post_ok( "//$userid:$password@/api/v1/patrons" => { 'x-confirm-not-duplicate' => 1 } => json => $newpatron )
505
        $t->post_ok( "//$userid:$password@/api/v1/patrons" => { 'x-confirm-not-duplicate' => 1 } => json => $newpatron )
482
            ->status_is(400)->json_is( '/error' => "Problem with " . $newpatron->{userid} );
506
            ->status_is(400)
507
            ->json_is( '/error' => "Problem with " . $newpatron->{userid} );
483
508
484
        $newpatron->{library_id} = $patron->branchcode;
509
        $newpatron->{library_id} = $patron->branchcode;
485
510
Lines 488-494 subtest 'add() tests' => sub { Link Here
488
        warning_like {
513
        warning_like {
489
            $t->post_ok(
514
            $t->post_ok(
490
                "//$userid:$password@/api/v1/patrons" => { 'x-confirm-not-duplicate' => 1 } => json => $newpatron )
515
                "//$userid:$password@/api/v1/patrons" => { 'x-confirm-not-duplicate' => 1 } => json => $newpatron )
491
                ->status_is(409)->json_has( '/error', 'Fails when trying to POST duplicate cardnumber' )
516
                ->status_is(409)
517
                ->json_has( '/error', 'Fails when trying to POST duplicate cardnumber' )
492
                ->json_like( '/conflict' => qr/(borrowers\.)?cardnumber/ );
518
                ->json_like( '/conflict' => qr/(borrowers\.)?cardnumber/ );
493
        }
519
        }
494
        qr/DBD::mysql::st execute failed: Duplicate entry '(.*?)' for key '(borrowers\.)?cardnumber'/;
520
        qr/DBD::mysql::st execute failed: Duplicate entry '(.*?)' for key '(borrowers\.)?cardnumber'/;
Lines 503-509 subtest 'add() tests' => sub { Link Here
503
        $newpatron->{category_id} = $deleted_category_id;    # Test invalid patron category
529
        $newpatron->{category_id} = $deleted_category_id;    # Test invalid patron category
504
530
505
        $t->post_ok( "//$userid:$password@/api/v1/patrons" => { 'x-confirm-not-duplicate' => 1 } => json => $newpatron )
531
        $t->post_ok( "//$userid:$password@/api/v1/patrons" => { 'x-confirm-not-duplicate' => 1 } => json => $newpatron )
506
            ->status_is(400)->json_is( '/error' => "Given category_id does not exist" );
532
            ->status_is(400)
533
            ->json_is( '/error' => "Given category_id does not exist" );
507
        $newpatron->{category_id} = $patron->categorycode;
534
        $newpatron->{category_id} = $patron->categorycode;
508
535
509
        $newpatron->{falseproperty} = "Non existent property";
536
        $newpatron->{falseproperty} = "Non existent property";
Lines 534-544 subtest 'add() tests' => sub { Link Here
534
        $letter_enqueued = 0;
561
        $letter_enqueued = 0;
535
        $t->post_ok(
562
        $t->post_ok(
536
            "//$userid:$password@/api/v1/patrons" => { 'x-koha-override' => 'welcome_yes' } => json => $newpatron )
563
            "//$userid:$password@/api/v1/patrons" => { 'x-koha-override' => 'welcome_yes' } => json => $newpatron )
537
            ->status_is( 201, 'Patron created successfully' )->header_like(
564
            ->status_is( 201, 'Patron created successfully' )
565
            ->header_like(
538
            Location => qr|^\/api\/v1\/patrons/\d*|,
566
            Location => qr|^\/api\/v1\/patrons/\d*|,
539
            'REST3.4.1'
567
            'REST3.4.1'
540
        )->json_has( '/patron_id', 'got a patron_id' )->json_is( '/cardnumber' => $newpatron->{cardnumber} )
568
            )
541
            ->json_is( '/surname'       => $newpatron->{surname} )->json_is( '/firstname' => $newpatron->{firstname} )
569
            ->json_has( '/patron_id', 'got a patron_id' )
570
            ->json_is( '/cardnumber'    => $newpatron->{cardnumber} )
571
            ->json_is( '/surname'       => $newpatron->{surname} )
572
            ->json_is( '/firstname'     => $newpatron->{firstname} )
542
            ->json_is( '/date_of_birth' => $newpatron->{date_of_birth}, 'Date field set (Bug 28585)' )
573
            ->json_is( '/date_of_birth' => $newpatron->{date_of_birth}, 'Date field set (Bug 28585)' )
543
            ->json_is( '/last_seen'     => $newpatron->{last_seen},     'Date-time field set (Bug 28585)' );
574
            ->json_is( '/last_seen'     => $newpatron->{last_seen},     'Date-time field set (Bug 28585)' );
544
575
Lines 665-671 subtest 'add() tests' => sub { Link Here
665
                    "city"        => "Konstanz",
696
                    "city"        => "Konstanz",
666
                    "library_id"  => "MPL"
697
                    "library_id"  => "MPL"
667
                }
698
                }
668
            )->status_is(400)
699
                )
700
                ->status_is(400)
669
                ->json_is( '/error' => "Tried to add more than one non-repeatable attributes. type=$code value=$attr" );
701
                ->json_is( '/error' => "Tried to add more than one non-repeatable attributes. type=$code value=$attr" );
670
702
671
            is( Koha::Patrons->search->count, $patrons_count, 'No patron added' );
703
            is( Koha::Patrons->search->count, $patrons_count, 'No patron added' );
Lines 680-686 subtest 'add() tests' => sub { Link Here
680
                    "city"        => "Konstanz",
712
                    "city"        => "Konstanz",
681
                    "library_id"  => "MPL"
713
                    "library_id"  => "MPL"
682
                }
714
                }
683
            )->status_is(400)
715
                )
716
                ->status_is(400)
684
                ->json_is(
717
                ->json_is(
685
                '/error' => "Your action breaks a unique constraint on the attribute. type=$code value=$attr" );
718
                '/error' => "Your action breaks a unique constraint on the attribute. type=$code value=$attr" );
686
719
Lines 841-847 subtest 'add() tests' => sub { Link Here
841
                    "city"        => "Bigtown",
874
                    "city"        => "Bigtown",
842
                    "library_id"  => "MPL",
875
                    "library_id"  => "MPL",
843
                }
876
                }
844
            )->status_is( 201, 'Patron added with EnhancedMessagingPreferences disabled' )
877
                )
878
                ->status_is( 201, 'Patron added with EnhancedMessagingPreferences disabled' )
845
                ->tx->res->json->{patron_id};
879
                ->tx->res->json->{patron_id};
846
880
847
            # No messaging preferences should be set
881
            # No messaging preferences should be set
Lines 901-907 subtest 'update() tests' => sub { Link Here
901
        delete $newpatron->{anonymized};
935
        delete $newpatron->{anonymized};
902
        delete $newpatron->{self_renewal_available};
936
        delete $newpatron->{self_renewal_available};
903
937
904
        $t->put_ok( "//$userid:$password@/api/v1/patrons/-1" => json => $newpatron )->status_is(404)
938
        $t->put_ok( "//$userid:$password@/api/v1/patrons/-1" => json => $newpatron )
939
            ->status_is(404)
905
            ->json_has( '/error', 'Fails when trying to PUT nonexistent patron' );
940
            ->json_has( '/error', 'Fails when trying to PUT nonexistent patron' );
906
941
907
        # Create a library just to make sure its ID doesn't exist on the DB
942
        # Create a library just to make sure its ID doesn't exist on the DB
Lines 915-921 subtest 'update() tests' => sub { Link Here
915
        $newpatron->{category_id} = $deleted_category_id;
950
        $newpatron->{category_id} = $deleted_category_id;
916
951
917
        $t->put_ok( "//$userid:$password@/api/v1/patrons/" . $patron_2->borrowernumber => json => $newpatron )
952
        $t->put_ok( "//$userid:$password@/api/v1/patrons/" . $patron_2->borrowernumber => json => $newpatron )
918
            ->status_is(400)->json_is( '/error' => "Given category_id does not exist" );
953
            ->status_is(400)
954
            ->json_is( '/error' => "Given category_id does not exist" );
919
955
920
        # Restore the valid category
956
        # Restore the valid category
921
        $newpatron->{category_id} = $patron_2->categorycode;
957
        $newpatron->{category_id} = $patron_2->categorycode;
Lines 932-938 subtest 'update() tests' => sub { Link Here
932
968
933
        warning_like {
969
        warning_like {
934
            $t->put_ok( "//$userid:$password@/api/v1/patrons/" . $patron_2->borrowernumber => json => $newpatron )
970
            $t->put_ok( "//$userid:$password@/api/v1/patrons/" . $patron_2->borrowernumber => json => $newpatron )
935
                ->status_is(400)->json_is( '/error' => "Given library_id does not exist" );
971
                ->status_is(400)
972
                ->json_is( '/error' => "Given library_id does not exist" );
936
        }
973
        }
937
        qr/DBD::mysql::st execute failed: Cannot add or update a child row: a foreign key constraint fails/;
974
        qr/DBD::mysql::st execute failed: Cannot add or update a child row: a foreign key constraint fails/;
938
975
Lines 943-949 subtest 'update() tests' => sub { Link Here
943
        $newpatron->{falseproperty} = "Non existent property";
980
        $newpatron->{falseproperty} = "Non existent property";
944
981
945
        $t->put_ok( "//$userid:$password@/api/v1/patrons/" . $patron_2->borrowernumber => json => $newpatron )
982
        $t->put_ok( "//$userid:$password@/api/v1/patrons/" . $patron_2->borrowernumber => json => $newpatron )
946
            ->status_is(400)->json_is( '/errors/0/message' => 'Properties not allowed: falseproperty.' );
983
            ->status_is(400)
984
            ->json_is( '/errors/0/message' => 'Properties not allowed: falseproperty.' );
947
985
948
        # Get rid of the invalid attribute
986
        # Get rid of the invalid attribute
949
        delete $newpatron->{falseproperty};
987
        delete $newpatron->{falseproperty};
Lines 953-959 subtest 'update() tests' => sub { Link Here
953
        $newpatron->{userid}     = $patron_1->userid;
991
        $newpatron->{userid}     = $patron_1->userid;
954
992
955
        $t->put_ok( "//$userid:$password@/api/v1/patrons/" . $patron_2->borrowernumber => json => $newpatron )
993
        $t->put_ok( "//$userid:$password@/api/v1/patrons/" . $patron_2->borrowernumber => json => $newpatron )
956
            ->status_is(400)->json_has( '/error', "Problem with userid " . $patron_1->userid );
994
            ->status_is(400)
995
            ->json_has( '/error', "Problem with userid " . $patron_1->userid );
957
996
958
        $newpatron->{cardnumber} = $patron_1->id . $patron_2->id;
997
        $newpatron->{cardnumber} = $patron_1->id . $patron_2->id;
959
        $newpatron->{userid}     = "user" . $patron_1->id . $patron_2->id;
998
        $newpatron->{userid}     = "user" . $patron_1->id . $patron_2->id;
Lines 1040-1046 subtest 'update() tests' => sub { Link Here
1040
            ->status_is(
1079
            ->status_is(
1041
            403,
1080
            403,
1042
            "Non-superlibrarian user change of superlibrarian secondary_email to undefined forbidden"
1081
            "Non-superlibrarian user change of superlibrarian secondary_email to undefined forbidden"
1043
        )->json_is( { error => "Not enough privileges to change a superlibrarian's email" } );
1082
            )->json_is( { error => "Not enough privileges to change a superlibrarian's email" } );
1044
1083
1045
        $newpatron->{secondary_email}  = $superlibrarian->emailpro;
1084
        $newpatron->{secondary_email}  = $superlibrarian->emailpro;
1046
        $newpatron->{altaddress_email} = 'nonsense@no.no';
1085
        $newpatron->{altaddress_email} = 'nonsense@no.no';
Lines 1056-1062 subtest 'update() tests' => sub { Link Here
1056
            ->status_is(
1095
            ->status_is(
1057
            403,
1096
            403,
1058
            "Non-superlibrarian user change of superlibrarian altaddress_email to undefined forbidden"
1097
            "Non-superlibrarian user change of superlibrarian altaddress_email to undefined forbidden"
1059
        )->json_is( { error => "Not enough privileges to change a superlibrarian's email" } );
1098
            )->json_is( { error => "Not enough privileges to change a superlibrarian's email" } );
1060
1099
1061
        # update patron without sending email
1100
        # update patron without sending email
1062
        delete $newpatron->{email};
1101
        delete $newpatron->{email};
Lines 1127-1139 subtest 'update() tests' => sub { Link Here
1127
                    "city"        => "Konstanz",
1166
                    "city"        => "Konstanz",
1128
                    "library_id"  => "MPL"
1167
                    "library_id"  => "MPL"
1129
                }
1168
                }
1130
            )->status_is(400)
1169
                )
1170
                ->status_is(400)
1131
                ->json_is(
1171
                ->json_is(
1132
                '/error' => "Missing mandatory extended attribute (type=" . $attr_type_mandatory->code . ")" );
1172
                '/error' => "Missing mandatory extended attribute (type=" . $attr_type_mandatory->code . ")" );
1133
1173
1134
            $t->put_ok( "//$userid:$password@/api/v1/patrons/"
1174
            $t->put_ok( "//$userid:$password@/api/v1/patrons/"
1135
                    . $superlibrarian->borrowernumber => { 'x-koha-embed' => 'extended_attributes' } => json =>
1175
                    . $superlibrarian->borrowernumber => { 'x-koha-embed' => 'extended_attributes' } => json =>
1136
                    $newpatron )->status_is(400)
1176
                    $newpatron )
1177
                ->status_is(400)
1137
                ->json_is( '/error'      => 'Tried to use an invalid attribute type. type=' . $deleted_attr_code )
1178
                ->json_is( '/error'      => 'Tried to use an invalid attribute type. type=' . $deleted_attr_code )
1138
                ->json_is( '/error_code' => 'invalid_attribute_type' );
1179
                ->json_is( '/error_code' => 'invalid_attribute_type' );
1139
1180
Lines 1150-1160 subtest 'update() tests' => sub { Link Here
1150
1191
1151
            $t->put_ok( "//$userid:$password@/api/v1/patrons/"
1192
            $t->put_ok( "//$userid:$password@/api/v1/patrons/"
1152
                    . $superlibrarian->borrowernumber => { 'x-koha-embed' => 'extended_attributes' } => json =>
1193
                    . $superlibrarian->borrowernumber => { 'x-koha-embed' => 'extended_attributes' } => json =>
1153
                    $newpatron )->status_is(400)
1194
                    $newpatron )
1195
                ->status_is(400)
1154
                ->json_is( '/error' => 'Your action breaks a unique constraint on the attribute. type='
1196
                ->json_is( '/error' => 'Your action breaks a unique constraint on the attribute. type='
1155
                    . $attr_type_unique->code
1197
                    . $attr_type_unique->code
1156
                    . ' value='
1198
                    . ' value='
1157
                    . $unique_attr->attribute )->json_is( '/error_code' => 'attribute_not_unique' );
1199
                    . $unique_attr->attribute )
1200
                ->json_is( '/error_code' => 'attribute_not_unique' );
1158
1201
1159
            $newpatron->{extended_attributes} = [
1202
            $newpatron->{extended_attributes} = [
1160
                { type => $attr_type_repeatable->code, value => 'a' },
1203
                { type => $attr_type_repeatable->code, value => 'a' },
Lines 1165-1175 subtest 'update() tests' => sub { Link Here
1165
1208
1166
            $t->put_ok( "//$userid:$password@/api/v1/patrons/"
1209
            $t->put_ok( "//$userid:$password@/api/v1/patrons/"
1167
                    . $superlibrarian->borrowernumber => { 'x-koha-embed' => 'extended_attributes' } => json =>
1210
                    . $superlibrarian->borrowernumber => { 'x-koha-embed' => 'extended_attributes' } => json =>
1168
                    $newpatron )->status_is(400)
1211
                    $newpatron )
1212
                ->status_is(400)
1169
                ->json_is( '/error' => 'Your action breaks a unique constraint on the attribute. type='
1213
                ->json_is( '/error' => 'Your action breaks a unique constraint on the attribute. type='
1170
                    . $attr_type_unique->code
1214
                    . $attr_type_unique->code
1171
                    . ' value='
1215
                    . ' value='
1172
                    . $unique_attr->attribute )->json_is( '/error_code' => 'attribute_not_unique' );
1216
                    . $unique_attr->attribute )
1217
                ->json_is( '/error_code' => 'attribute_not_unique' );
1173
1218
1174
            $newpatron->{extended_attributes} = [
1219
            $newpatron->{extended_attributes} = [
1175
                { type => $attr_type_repeatable->code, value => 'a' },
1220
                { type => $attr_type_repeatable->code, value => 'a' },
Lines 1179-1188 subtest 'update() tests' => sub { Link Here
1179
1224
1180
            $t->put_ok( "//$userid:$password@/api/v1/patrons/"
1225
            $t->put_ok( "//$userid:$password@/api/v1/patrons/"
1181
                    . $superlibrarian->borrowernumber => { 'x-koha-embed' => 'extended_attributes' } => json =>
1226
                    . $superlibrarian->borrowernumber => { 'x-koha-embed' => 'extended_attributes' } => json =>
1182
                    $newpatron )->status_is(400)
1227
                    $newpatron )
1228
                ->status_is(400)
1183
                ->json_is( '/error' => 'Tried to add more than one non-repeatable attributes. type='
1229
                ->json_is( '/error' => 'Tried to add more than one non-repeatable attributes. type='
1184
                    . $attr_type_mandatory->code
1230
                    . $attr_type_mandatory->code
1185
                    . ' value=pong' )->json_is( '/error_code' => 'non_repeatable_attribute' );
1231
                    . ' value=pong' )
1232
                ->json_is( '/error_code' => 'non_repeatable_attribute' );
1186
1233
1187
            my $unique_value = $unique_attr->attribute;
1234
            my $unique_value = $unique_attr->attribute;
1188
            $unique_attr->delete;
1235
            $unique_attr->delete;
Lines 1241-1247 subtest 'delete() tests' => sub { Link Here
1241
1288
1242
        t::lib::Mocks::mock_preference( 'AnonymousPatron', $patron->borrowernumber );
1289
        t::lib::Mocks::mock_preference( 'AnonymousPatron', $patron->borrowernumber );
1243
        $t->delete_ok( "//$userid:$password@/api/v1/patrons/" . $patron->borrowernumber )
1290
        $t->delete_ok( "//$userid:$password@/api/v1/patrons/" . $patron->borrowernumber )
1244
            ->status_is( 409, 'Anonymous patron cannot be deleted' )->json_is(
1291
            ->status_is( 409, 'Anonymous patron cannot be deleted' )
1292
            ->json_is(
1245
            {
1293
            {
1246
                error      => 'Anonymous patron cannot be deleted',
1294
                error      => 'Anonymous patron cannot be deleted',
1247
                error_code => 'is_anonymous_patron'
1295
                error_code => 'is_anonymous_patron'
Lines 1263-1269 subtest 'delete() tests' => sub { Link Here
1263
        $guarantee->add_guarantor( { guarantor_id => $patron->id, relationship => 'parent' } );
1311
        $guarantee->add_guarantor( { guarantor_id => $patron->id, relationship => 'parent' } );
1264
1312
1265
        $t->delete_ok( "//$userid:$password@/api/v1/patrons/" . $patron->borrowernumber )
1313
        $t->delete_ok( "//$userid:$password@/api/v1/patrons/" . $patron->borrowernumber )
1266
            ->status_is( 409, 'Patron with checkouts cannot be deleted' )->json_is(
1314
            ->status_is( 409, 'Patron with checkouts cannot be deleted' )
1315
            ->json_is(
1267
            {
1316
            {
1268
                error      => 'Pending checkouts prevent deletion',
1317
                error      => 'Pending checkouts prevent deletion',
1269
                error_code => 'has_checkouts'
1318
                error_code => 'has_checkouts'
Lines 1274-1280 subtest 'delete() tests' => sub { Link Here
1274
        $checkout->delete;
1323
        $checkout->delete;
1275
1324
1276
        $t->delete_ok( "//$userid:$password@/api/v1/patrons/" . $patron->borrowernumber )
1325
        $t->delete_ok( "//$userid:$password@/api/v1/patrons/" . $patron->borrowernumber )
1277
            ->status_is( 409, 'Patron with debt cannot be deleted' )->json_is(
1326
            ->status_is( 409, 'Patron with debt cannot be deleted' )
1327
            ->json_is(
1278
            {
1328
            {
1279
                error      => 'Pending debts prevent deletion',
1329
                error      => 'Pending debts prevent deletion',
1280
                error_code => 'has_debt'
1330
                error_code => 'has_debt'
Lines 1285-1291 subtest 'delete() tests' => sub { Link Here
1285
        $patron->account->pay( { amount => 10, debits => [$debit] } );
1335
        $patron->account->pay( { amount => 10, debits => [$debit] } );
1286
1336
1287
        $t->delete_ok( "//$userid:$password@/api/v1/patrons/" . $patron->borrowernumber )
1337
        $t->delete_ok( "//$userid:$password@/api/v1/patrons/" . $patron->borrowernumber )
1288
            ->status_is( 409, 'Patron with guarantees cannot be deleted' )->json_is(
1338
            ->status_is( 409, 'Patron with guarantees cannot be deleted' )
1339
            ->json_is(
1289
            {
1340
            {
1290
                error      => 'Patron is a guarantor and it prevents deletion',
1341
                error      => 'Patron is a guarantor and it prevents deletion',
1291
                error_code => 'has_guarantees'
1342
                error_code => 'has_guarantees'
Lines 1298-1304 subtest 'delete() tests' => sub { Link Here
1298
        $patron->protected(1)->store();
1349
        $patron->protected(1)->store();
1299
1350
1300
        $t->delete_ok( "//$userid:$password@/api/v1/patrons/" . $patron->borrowernumber )
1351
        $t->delete_ok( "//$userid:$password@/api/v1/patrons/" . $patron->borrowernumber )
1301
            ->status_is( 409, 'Protected patron cannot be deleted' )->json_is(
1352
            ->status_is( 409, 'Protected patron cannot be deleted' )
1353
            ->json_is(
1302
            {
1354
            {
1303
                error      => 'Protected patrons cannot be deleted',
1355
                error      => 'Protected patrons cannot be deleted',
1304
                error_code => 'is_protected'
1356
                error_code => 'is_protected'
Lines 1308-1314 subtest 'delete() tests' => sub { Link Here
1308
        $patron->protected(0)->store();
1360
        $patron->protected(0)->store();
1309
1361
1310
        $t->delete_ok( "//$userid:$password@/api/v1/patrons/" . $patron->borrowernumber )
1362
        $t->delete_ok( "//$userid:$password@/api/v1/patrons/" . $patron->borrowernumber )
1311
            ->status_is( 204, 'REST3.2.4' )->content_is( '', 'REST3.3.4' );
1363
            ->status_is( 204, 'REST3.2.4' )
1364
            ->content_is( '', 'REST3.3.4' );
1312
1365
1313
        my $deleted_patrons = Koha::Old::Patrons->search( { borrowernumber => $patron->borrowernumber } );
1366
        my $deleted_patrons = Koha::Old::Patrons->search( { borrowernumber => $patron->borrowernumber } );
1314
        is( $deleted_patrons->count, 1, 'The patron has been moved to the vault' );
1367
        is( $deleted_patrons->count, 1, 'The patron has been moved to the vault' );
Lines 1340-1350 subtest 'guarantors_can_see_charges() tests' => sub { Link Here
1340
    )->status_is(401)->json_is( { error => "Authentication failure." } );
1393
    )->status_is(401)->json_is( { error => "Authentication failure." } );
1341
1394
1342
    $t->put_ok( "//$userid:$password@/api/v1/public/patrons/$other_patron_id/guarantors/can_see_charges" => json =>
1395
    $t->put_ok( "//$userid:$password@/api/v1/public/patrons/$other_patron_id/guarantors/can_see_charges" => json =>
1343
            { allowed => Mojo::JSON->true } )->status_is(403)
1396
            { allowed => Mojo::JSON->true } )
1397
        ->status_is(403)
1344
        ->json_is( { error => "Unprivileged user cannot access another user's resources" } );
1398
        ->json_is( { error => "Unprivileged user cannot access another user's resources" } );
1345
1399
1346
    $t->put_ok( "//$userid:$password@/api/v1/public/patrons/$patron_id/guarantors/can_see_charges" => json =>
1400
    $t->put_ok( "//$userid:$password@/api/v1/public/patrons/$patron_id/guarantors/can_see_charges" => json =>
1347
            { allowed => Mojo::JSON->true } )->status_is(403)
1401
            { allowed => Mojo::JSON->true } )
1402
        ->status_is(403)
1348
        ->json_is( '/error', 'The current configuration doesn\'t allow the requested action.' );
1403
        ->json_is( '/error', 'The current configuration doesn\'t allow the requested action.' );
1349
1404
1350
    t::lib::Mocks::mock_preference( 'AllowPatronToSetFinesVisibilityForGuarantor', 1 );
1405
    t::lib::Mocks::mock_preference( 'AllowPatronToSetFinesVisibilityForGuarantor', 1 );
Lines 1382-1394 subtest 'guarantors_can_see_checkouts() tests' => sub { Link Here
1382
            { allowed => Mojo::JSON->true } )->status_is(401)->json_is( { error => "Authentication failure." } );
1437
            { allowed => Mojo::JSON->true } )->status_is(401)->json_is( { error => "Authentication failure." } );
1383
1438
1384
    $t->put_ok( "//$userid:$password@/api/v1/public/patrons/$other_patron_id/guarantors/can_see_checkouts" => json =>
1439
    $t->put_ok( "//$userid:$password@/api/v1/public/patrons/$other_patron_id/guarantors/can_see_checkouts" => json =>
1385
            { allowed => Mojo::JSON->true } )->status_is(403)
1440
            { allowed => Mojo::JSON->true } )
1441
        ->status_is(403)
1386
        ->json_is( { error => "Unprivileged user cannot access another user's resources" } );
1442
        ->json_is( { error => "Unprivileged user cannot access another user's resources" } );
1387
1443
1388
    t::lib::Mocks::mock_preference( 'AllowPatronToSetCheckoutsVisibilityForGuarantor', 0 );
1444
    t::lib::Mocks::mock_preference( 'AllowPatronToSetCheckoutsVisibilityForGuarantor', 0 );
1389
1445
1390
    $t->put_ok( "//$userid:$password@/api/v1/public/patrons/$patron_id/guarantors/can_see_checkouts" => json =>
1446
    $t->put_ok( "//$userid:$password@/api/v1/public/patrons/$patron_id/guarantors/can_see_checkouts" => json =>
1391
            { allowed => Mojo::JSON->true } )->status_is(403)
1447
            { allowed => Mojo::JSON->true } )
1448
        ->status_is(403)
1392
        ->json_is( '/error', 'The current configuration doesn\'t allow the requested action.' );
1449
        ->json_is( '/error', 'The current configuration doesn\'t allow the requested action.' );
1393
1450
1394
    t::lib::Mocks::mock_preference( 'AllowPatronToSetCheckoutsVisibilityForGuarantor', 1 );
1451
    t::lib::Mocks::mock_preference( 'AllowPatronToSetCheckoutsVisibilityForGuarantor', 1 );
Lines 1431-1437 sub unauthorized_access_tests { Link Here
1431
        $unauthorized_patron->set_password( { password => $password, skip_validation => 1 } );
1488
        $unauthorized_patron->set_password( { password => $password, skip_validation => 1 } );
1432
        my $unauth_userid = $unauthorized_patron->userid;
1489
        my $unauth_userid = $unauthorized_patron->userid;
1433
1490
1434
        $t->$verb_ok( "//$unauth_userid:$password\@$endpoint" => json => $json )->status_is(403)
1491
        $t->$verb_ok( "//$unauth_userid:$password\@$endpoint" => json => $json )
1492
            ->status_is(403)
1435
            ->json_has('/required_permissions');
1493
            ->json_has('/required_permissions');
1436
    };
1494
    };
1437
}
1495
}
(-)a/t/db_dependent/api/v1/patrons_self_renewal.t (-5 / +8 lines)
Lines 64-77 subtest 'start()' => sub { Link Here
64
    my $borrowernumber = $patron->borrowernumber;
64
    my $borrowernumber = $patron->borrowernumber;
65
65
66
    $t->get_ok("//$userid:$password@/api/v1/public/patrons/$borrowernumber/self_renewal")
66
    $t->get_ok("//$userid:$password@/api/v1/public/patrons/$borrowernumber/self_renewal")
67
        ->status_is( 403, 'REST3.2.2' )->json_is( { error => "You are not eligible for self-renewal" } );
67
        ->status_is( 403, 'REST3.2.2' )
68
        ->json_is( { error => "You are not eligible for self-renewal" } );
68
69
69
    $category->self_renewal_enabled(1)->store();
70
    $category->self_renewal_enabled(1)->store();
70
71
71
    t::lib::Mocks::mock_preference( 'OPACPatronDetails', 1 );
72
    t::lib::Mocks::mock_preference( 'OPACPatronDetails', 1 );
72
73
73
    $t->get_ok("//$userid:$password@/api/v1/public/patrons/$borrowernumber/self_renewal")
74
    $t->get_ok("//$userid:$password@/api/v1/public/patrons/$borrowernumber/self_renewal")
74
        ->status_is( 200, 'REST3.2.2' )->json_is(
75
        ->status_is( 200, 'REST3.2.2' )
76
        ->json_is(
75
        {
77
        {
76
            self_renewal_settings => {
78
            self_renewal_settings => {
77
                self_renewal_failure_message      => $category->self_renewal_failure_message,
79
                self_renewal_failure_message      => $category->self_renewal_failure_message,
Lines 92-98 subtest 'start()' => sub { Link Here
92
        }
94
        }
93
    );
95
    );
94
    $t->get_ok("//$userid:$password@/api/v1/public/patrons/$borrowernumber/self_renewal")
96
    $t->get_ok("//$userid:$password@/api/v1/public/patrons/$borrowernumber/self_renewal")
95
        ->status_is( 200, 'REST3.2.2' )->json_is(
97
        ->status_is( 200, 'REST3.2.2' )
98
        ->json_is(
96
        {
99
        {
97
            self_renewal_settings => {
100
            self_renewal_settings => {
98
                self_renewal_failure_message      => $category->self_renewal_failure_message,
101
                self_renewal_failure_message      => $category->self_renewal_failure_message,
Lines 139-145 subtest 'submit()' => sub { Link Here
139
    my $borrowernumber = $patron->borrowernumber;
142
    my $borrowernumber = $patron->borrowernumber;
140
143
141
    $t->post_ok( "//$userid:$password@/api/v1/public/patrons/$borrowernumber/self_renewal" => json => {} )
144
    $t->post_ok( "//$userid:$password@/api/v1/public/patrons/$borrowernumber/self_renewal" => json => {} )
142
        ->status_is( 403, 'REST3.2.2' )->json_is( { error => "You are not eligible for self-renewal" } );
145
        ->status_is( 403, 'REST3.2.2' )
146
        ->json_is( { error => "You are not eligible for self-renewal" } );
143
147
144
    t::lib::Mocks::mock_preference( 'OPACPatronDetails',                1 );
148
    t::lib::Mocks::mock_preference( 'OPACPatronDetails',                1 );
145
    t::lib::Mocks::mock_preference( 'AutoApprovePatronProfileSettings', 0 );
149
    t::lib::Mocks::mock_preference( 'AutoApprovePatronProfileSettings', 0 );
146
- 

Return to bug 26355