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

(-)a/Koha/Illrequest.pm (+935 lines)
Line 0 Link Here
1
package Koha::Illrequest;
2
3
# Copyright PTFS Europe 2016
4
#
5
# This file is part of Koha.
6
#
7
# Koha is free software; you can redistribute it and/or modify it under the
8
# terms of the GNU General Public License as published by the Free Software
9
# Foundation; either version 3 of the License, or (at your option) any later
10
# version.
11
#
12
# Koha is distributed in the hope that it will be useful, but WITHOUT ANY
13
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14
# FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
15
# details.
16
#
17
# You should have received a copy of the GNU General Public License along with
18
# Koha; if not, write to the Free Software Foundation, Inc., 51 Franklin
19
# Street, Fifth Floor, Boston, MA 02110-1301 USA.
20
21
# use Modern::Perl;
22
23
use Clone 'clone';
24
use File::Basename qw/basename/;
25
use Koha::Database;
26
use Koha::Email;
27
use Koha::Illrequest;
28
use Koha::Illrequestattributes;
29
use Koha::Patron;
30
use Mail::Sendmail;
31
use Try::Tiny;
32
33
use base qw(Koha::Object);
34
35
=head1 NAME
36
37
Koha::Illrequest - Koha Illrequest Object class
38
39
=head1 (Re)Design
40
41
An ILLRequest consists of two parts; the Illrequest Koha::Object, and a series
42
of related Illrequestattributes.
43
44
The former encapsulates the basic necessary information that any ILL requires
45
to be usable in Koha.  The latter is a set of additional properties used by
46
one of the backends.
47
48
The former subsumes the legacy "Status" object.  The latter remains
49
encapsulated in the "Record" object.
50
51
TODO:
52
53
- Anything invoking the ->status method; annotated with:
54
  + # Old use of ->status !
55
56
=head1 API
57
58
=head2 Backend API Response Principles
59
60
All methods should return a hashref in the following format:
61
62
=item * error
63
64
This should be set to 1 if an error was encountered.
65
66
=item * status
67
68
The status should be a string from the list of statuses detailed below.
69
70
=item * message
71
72
The message is a free text field that can be passed on to the end user.
73
74
=item * value
75
76
The value returned by the method.
77
78
=over
79
80
=head2 Interface Status Messages
81
82
=over
83
84
=item * branch_address_incomplete
85
86
An interface request has determined branch address details are incomplete.
87
88
=item * cancel_success
89
90
The interface's cancel_request method was successful in cancelling the
91
Illrequest using the API.
92
93
=item * cancel_fail
94
95
The interface's cancel_request method failed to cancel the Illrequest using
96
the API.
97
98
=item * unavailable
99
100
The interface's request method returned saying that the desired item is not
101
available for request.
102
103
=head2 Class Methods
104
105
=cut
106
107
=head3 type
108
109
=cut
110
111
sub _type {
112
    return 'Illrequest';
113
}
114
115
sub illrequestattributes {
116
    my ( $self ) = @_;
117
    return Koha::Illrequestattributes->_new_from_dbic(
118
        scalar $self->_result->illrequestattributes
119
    );
120
}
121
122
sub patron {
123
    my ( $self ) = @_;
124
    return Koha::Patron->_new_from_dbic(
125
        scalar $self->_result->borrowernumber
126
    );
127
}
128
129
sub load_backend {
130
    my ( $self, $backend_id ) = @_;
131
132
    my @raw = qw/Koha Illbackends/; # Base Path
133
134
    my $backend_name = $backend_id || $self->backend;
135
    $location = join "/", @raw, $backend_name, "Base.pm"; # File to load
136
    $backend_class = join "::", @raw, $backend_name, "Base"; # Package name
137
    require $location;
138
    $self->{_my_backend} = $backend_class->new({ config => $self->_config });
139
    return $self;
140
}
141
142
=head3 _backend
143
144
    my $backend = $abstract->_backend($new_backend);
145
    my $backend = $abstract->_backend;
146
147
Getter/Setter for our API object.
148
149
=cut
150
151
sub _backend {
152
    my ( $self, $backend ) = @_;
153
    $self->{_my_backend} = $backend if ( $backend );
154
    # Dynamically load our backend object, as late as possible.
155
    $self->load_backend unless ( $self->{_my_backend} );
156
    return $self->{_my_backend};
157
}
158
159
=head3 _backend_capability
160
161
    my $backend_capability_result = $self->_backend_capability($name, $args);
162
163
This is a helper method to invoke optional capabilities in the backend.  If
164
the capability named by $name is not supported, return 0, else invoke it,
165
passing $args along with the invocation, and return its return value.
166
167
NOTE: this module suffers from a confusion in termninology:
168
169
in _backend_capability, the notion of capability refers to an optional feature
170
that is implemented in core, but might not be supported by a given backend.
171
172
in capabilities & custom_capability, capability refers to entries in the
173
status_graph (after union between backend and core).
174
175
The easiest way to fix this would be to fix the terminology in
176
capabilities & custom_capability and their callers.
177
178
=cut
179
180
sub _backend_capability {
181
    my ( $self, $name, $args ) = @_;
182
    my $capability = 0;
183
    try {
184
        $capability = $self->_backend->capabilities($name);
185
    } catch {
186
        return 0;
187
    };
188
    if ( $capability ) {
189
        return &{$capability}($args);
190
    } else {
191
        return 0;
192
    }
193
}
194
195
=head3 _config
196
197
    my $config = $abstract->_config($config);
198
    my $config = $abstract->_config;
199
200
Getter/Setter for our config object.
201
202
=cut
203
204
sub _config {
205
    my ( $self, $config ) = @_;
206
    $self->{_my_config} = $config if ( $config );
207
    # Load our config object, as late as possible.
208
    unless ( $self->{_my_config} ) {
209
        $self->{_my_config} = Koha::Illrequest::Config->new;
210
    }
211
    return $self->{_my_config};
212
}
213
214
=head3 metadata
215
216
=cut
217
218
sub metadata {
219
    my ( $self ) = @_;
220
    return $self->_backend->metadata($self);
221
}
222
223
=head3 _core_status_graph
224
225
    my $core_status_graph = $illrequest->_core_status_graph;
226
227
Returns ILL module's default status graph.  A status graph defines the list of
228
available actions at any stage in the ILL workflow.  This is for instance used
229
by the perl script & template to generate the correct buttons to display to
230
the end user at any given point.
231
232
=cut
233
234
sub _core_status_graph {
235
    my ( $self ) = @_;
236
    return {
237
        NEW => {
238
            prev_actions => [ ],                           # Actions containing buttons
239
                                                           # leading to this status
240
            id             => 'NEW',                       # ID of this status
241
            name           => 'New request',               # UI name of this status
242
            ui_method_name => 'New request',               # UI name of method leading
243
                                                           # to this status
244
            method         => 'create',                    # method to this status
245
            next_actions   => [ 'REQ', 'GENREQ', 'KILL' ], # buttons to add to all
246
                                                           # requests with this status
247
            ui_method_icon => 'fa-plus',                   # UI Style class
248
        },
249
        REQ => {
250
            prev_actions   => [ 'NEW', 'REQREV', 'QUEUED', 'CANCREQ' ],
251
            id             => 'REQ',
252
            name           => 'Requested',
253
            ui_method_name => 'Confirm request',
254
            method         => 'confirm',
255
            next_actions   => [ 'REQREV', 'COMP' ],
256
            ui_method_icon => 'fa-check',
257
        },
258
        GENREQ => {
259
            prev_actions   => [ 'NEW', 'REQREV' ],
260
            id             => 'GENREQ',
261
            name           => 'Requested from partners',
262
            ui_method_name => 'Place request with partners',
263
            method         => 'generic_confirm',
264
            next_actions   => [ 'COMP' ],
265
            ui_method_icon => 'fa-send-o',
266
        },
267
        REQREV => {
268
            prev_actions   => [ 'REQ' ],
269
            id             => 'REQREV',
270
            name           => 'Request reverted',
271
            ui_method_name => 'Revert Request',
272
            method         => 'cancel',
273
            next_actions   => [ 'REQ', 'GENREQ', 'KILL' ],
274
            ui_method_icon => 'fa-times',
275
        },
276
        QUEUED => {
277
            prev_actions   => [ ],
278
            id             => 'QUEUED',
279
            name           => 'Queued request',
280
            ui_method_name => 0,
281
            method         => 0,
282
            next_actions   => [ 'REQ', 'KILL' ],
283
            ui_method_icon => 0,
284
        },
285
        CANCREQ => {
286
            prev_actions   => [ 'NEW' ],
287
            id             => 'CANCREQ',
288
            name           => 'Cancellation requested',
289
            ui_method_name => 0,
290
            method         => 0,
291
            next_actions   => [ 'KILL', 'REQ' ],
292
            ui_method_icon => 0,
293
        },
294
        COMP => {
295
            prev_actions   => [ 'REQ' ],
296
            id             => 'COMP',
297
            name           => 'Completed',
298
            ui_method_name => 'Mark completed',
299
            method         => 'mark_completed',
300
            next_actions   => [ ],
301
            ui_method_icon => 'fa-check',
302
        },
303
        KILL => {
304
            prev_actions   => [ 'QUEUED', 'REQREV', 'NEW', 'CANCREQ' ],
305
            id             => 'KILL',
306
            name           => 0,
307
            ui_method_name => 'Delete request',
308
            method         => 'delete',
309
            next_actions   => [ ],
310
            ui_method_icon => 'fa-trash',
311
        },
312
    };
313
}
314
315
=head3 _core_status_graph
316
317
    my $status_graph = $illrequest->_core_status_graph($origin, $new_graph);
318
319
Return a new status_graph, the result of merging $origin & new_graph.  This is
320
operation is a union over the sets defied by the two graphs.
321
322
Each entry in $new_graph is added to $origin.  We do not provide a syntax for
323
'subtraction' of entries from $origin.
324
325
Whilst it is not intended that this works, you can override entries in $origin
326
with entries with the same key in $new_graph.  This can lead to problematic
327
behaviour when $new_graph adds an entry, which modifies a dependent entry in
328
$origin, only for the entry in $origin to be replaced later with a new entry
329
from $new_graph.
330
331
NOTE: this procedure does not "re-link" entries in $origin or $new_graph,
332
i.e. each of the graphs need to be correct at the outset of the operation.
333
334
=cut
335
336
sub _status_graph_union {
337
    my ( $self, $core_status_graph, $backend_status_graph ) = @_;
338
    # Create new status graph with:
339
    # - all core_status_graph
340
    # - for-each each backend_status_graph
341
    #   + add to new status graph
342
    #   + for each core prev_action:
343
    #     * locate core_status
344
    #     * update next_actions with additional next action.
345
    #   + for each core next_action:
346
    #     * locate core_status
347
    #     * update prev_actions with additional prev action
348
349
    my @core_status_ids = keys %{$core_status_graph};
350
    my $status_graph = clone($core_status_graph);
351
352
    foreach my $backend_status_key ( keys %{$backend_status_graph} ) {
353
        $backend_status = $backend_status_graph->{$backend_status_key};
354
        # Add to new status graph
355
        $status_graph->{$backend_status_key} = $backend_status;
356
        # Update all core methods' next_actions.
357
        foreach my $prev_action ( @{$backend_status->{prev_actions}} ) {
358
            if ( grep $prev_action, @core_status_ids ) {
359
                my @next_actions =
360
                     @{$status_graph->{$prev_action}->{next_actions}};
361
                push @next_actions, $backend_status_key;
362
                $status_graph->{$prev_action}->{next_actions}
363
                    = \@next_actions;
364
            }
365
        }
366
        # Update all core methods' prev_actions
367
        foreach my $next_action ( @{$backend_status->{next_actions}} ) {
368
            if ( grep $next_action, @core_status_ids ) {
369
                my @prev_actions =
370
                     @{$status_graph->{$next_action}->{prev_actions}};
371
                push @prev_actions, $backend_status_key;
372
                $status_graph->{$next_action}->{prev_actions}
373
                    = \@prev_actions;
374
            }
375
        }
376
    }
377
378
    return $status_graph;
379
}
380
381
### Core API methods
382
383
=head3 capabilities
384
385
    my $capabilities = $illrequest->capabilities;
386
387
Return a hashref mapping methods to operation names supported by the queried
388
backend.
389
390
Example return value:
391
392
    { create => "Create Request", confirm => "Progress Request" }
393
394
NOTE: this module suffers from a confusion in termninology:
395
396
in _backend_capability, the notion of capability refers to an optional feature
397
that is implemented in core, but might not be supported by a given backend.
398
399
in capabilities & custom_capability, capability refers to entries in the
400
status_graph (after union between backend and core).
401
402
The easiest way to fix this would be to fix the terminology in
403
capabilities & custom_capability and their callers.
404
405
=cut
406
407
sub capabilities {
408
    my ( $self, $status ) = @_;
409
    # Generate up to date status_graph
410
    my $status_graph = $self->_status_graph_union(
411
        $self->_core_status_graph,
412
        $self->_backend->status_graph({
413
            request => $self,
414
            other   => {}
415
        })
416
    );
417
    # Extract available actions from graph.
418
    return $status_graph->{$status} if $status;
419
    # Or return entire graph.
420
    return $status_graph;
421
}
422
423
=head3 custom_capability
424
425
Return the result of invoking $CANDIDATE on this request's backend with
426
$PARAMS, or 0 if $CANDIDATE is an unknown method on backend.
427
428
NOTE: this module suffers from a confusion in termninology:
429
430
in _backend_capability, the notion of capability refers to an optional feature
431
that is implemented in core, but might not be supported by a given backend.
432
433
in capabilities & custom_capability, capability refers to entries in the
434
status_graph (after union between backend and core).
435
436
The easiest way to fix this would be to fix the terminology in
437
capabilities & custom_capability and their callers.
438
439
=cut
440
441
sub custom_capability {
442
    my ( $self, $candidate, $params ) = @_;
443
    foreach my $capability ( values %{$self->capabilities} ) {
444
        if ( $candidate eq $capability->{method} ) {
445
            my $response =
446
                $self->_backend->$candidate({
447
                    request    => $self,
448
                    other      => $params,
449
                });
450
            return $self->expandTemplate($response);
451
        }
452
    }
453
    return 0;
454
}
455
456
sub available_backends {
457
    my ( $self ) = @_;
458
    my $backend_dir = $self->_config->backend_dir;
459
    my @backends = ();
460
    @backends = <$backend_dir/*> if ( $backend_dir );
461
    @backends = map { basename($_) } @backends;
462
    return \@backends;
463
}
464
465
sub available_actions {
466
    my ( $self ) = @_;
467
    my $current_action = $self->capabilities($self->status);
468
    my @available_actions = map { $self->capabilities($_) }
469
        @{$current_action->{next_actions}};
470
    return \@available_actions;
471
}
472
473
sub mark_completed {
474
    my ( $self ) = @_;
475
    $self->status('COMP')->store;
476
    return {
477
        error   => 0,
478
        status  => '',
479
        message => '',
480
        method  => 'mark_completed',
481
        stage   => 'commit',
482
        next    => 'illview',
483
    };
484
}
485
486
sub backend_confirm {
487
    my ( $self, $params ) = @_;
488
489
    # The backend handles setting of mandatory fields in the commit stage:
490
    # - orderid
491
    # - accessurl, cost (if available).
492
    my $response = $self->_backend->confirm({
493
            request    => $self,
494
            other      => $params,
495
        });
496
    return $self->expandTemplate($response);
497
}
498
499
sub backend_update_status {
500
    my ( $self, $params ) = @_;
501
    return $self->expandTemplate($self->_backend->update_status($params));
502
}
503
504
=head3 backend_cancel
505
506
    my $ILLResponse = $illRequest->backend_cancel;
507
508
The standard interface method allowing for request cancellation.
509
510
=cut
511
512
sub backend_cancel {
513
    my ( $self, $params ) = @_;
514
515
    my $result = $self->_backend->cancel({
516
        request => $self,
517
        other => $params
518
    });
519
520
    return $self->expandTemplate($result);
521
}
522
523
=head3 backend_renew
524
525
    my $renew_response = $illRequest->backend_renew;
526
527
The standard interface method allowing for request renewal queries.
528
529
=cut
530
531
sub backend_renew {
532
    my ( $self ) = @_;
533
    return $self->expandTemplate(
534
        $self->_backend->renew({
535
            request    => $self,
536
        })
537
    );
538
}
539
540
=head3 backend_create
541
542
    my $create_response = $abstractILL->backend_create($params);
543
544
Return an array of Record objects created by querying our backend with
545
a Search query.
546
547
In the context of the other ILL methods, this is a special method: we only
548
pass it $params, as it does not yet have any other data associated with it.
549
550
=cut
551
552
sub backend_create {
553
    my ( $self, $params ) = @_;
554
555
    # Establish whether we need to do a generic copyright clearance.
556
    if ( ( !$params->{stage} || $params->{stage} eq 'init' )
557
             && C4::Context->preference("ILLModuleCopyrightClearance") ) {
558
        return {
559
            error   => 0,
560
            status  => '',
561
            message => '',
562
            method  => 'create',
563
            stage   => 'copyrightclearance',
564
            value   => {
565
                backend => $self->_backend->name
566
            }
567
        };
568
    } elsif ( $params->{stage} eq 'copyrightclearance' ) {
569
        $params->{stage} = 'init';
570
    }
571
572
    # First perform API action, then...
573
    my $args = {
574
        request => $self,
575
        other   => $params,
576
    };
577
    my $result = $self->_backend->create($args);
578
579
    # ... simple case: we're not at 'commit' stage.
580
    my $stage = $result->{stage};
581
    return $self->expandTemplate($result)
582
        unless ( 'commit' eq $stage );
583
584
    # ... complex case: commit!
585
586
    # Do we still have space for an ILL or should we queue?
587
    my $permitted = $self->check_limits(
588
        { patron => $self->patron }, { librarycode => $self->branchcode }
589
    );
590
591
    # Now augment our committed request.
592
593
    $result->{permitted} = $permitted;             # Queue request?
594
595
    # This involves...
596
597
    # ...Updating status!
598
    $self->status('QUEUED')->store unless ( $permitted );
599
600
    return $self->expandTemplate($result);
601
}
602
603
=head3 expandTemplate
604
605
    my $params = $abstract->expandTemplate($params);
606
607
Return a version of $PARAMS augmented with our required template path.
608
609
=cut
610
611
sub expandTemplate {
612
    my ( $self, $params ) = @_;
613
    my $backend = $self->_backend->name;
614
    # Generate path to file to load
615
    my $backend_dir = $self->_config->backend_dir;
616
    my $backend_tmpl = join "/", $backend_dir, $backend;
617
    my $intra_tmpl =  join "/", $backend_tmpl, "intra-includes",
618
        $params->{method} . ".inc";
619
    my $opac_tmpl =  join "/", $backend_tmpl, "opac-includes",
620
        $params->{method} . ".inc";
621
    # Set files to load
622
    $params->{template} = $intra_tmpl;
623
    $params->{opac_template} = $opac_tmpl;
624
    return $params;
625
}
626
627
#### Abstract Imports
628
629
=head3 getLimits
630
631
    my $limit_rules = $abstract->getLimits( {
632
        type  => 'brw_cat' | 'branch',
633
        value => $value
634
    } );
635
636
Return the ILL limit rules for the supplied combination of type / value.
637
638
As the config may have no rules for this particular type / value combination,
639
or for the default, we must define fall-back values here.
640
641
=cut
642
643
sub getLimits {
644
    my ( $self, $params ) = @_;
645
    my $limits = $self->_config->getLimitRules($params->{type});
646
647
    return $limits->{$params->{value}}
648
        || $limits->{default}
649
        || { count => -1, method => 'active' };
650
}
651
652
=head3 getPrefix
653
654
    my $prefix = $abstract->getPrefix( {
655
        brw_cat => $brw_cat,
656
        branch  => $branch_code,
657
    } );
658
659
Return the ILL prefix as defined by our $params: either per borrower category,
660
per branch or the default.
661
662
=cut
663
664
sub getPrefix {
665
    my ( $self, $params ) = @_;
666
    my $brn_prefixes = $self->_config->getPrefixes('branch');
667
    my $brw_prefixes = $self->_config->getPrefixes('brw_cat');
668
669
    return $brw_prefixes->{$params->{brw_cat}}
670
        || $brn_prefixes->{$params->{branch}}
671
        || $brw_prefixes->{default}
672
        || "";                  # "the empty prefix"
673
}
674
675
#### Illrequests Imports
676
677
=head3 check_limits
678
679
    my $ok = $illRequests->check_limits( {
680
        borrower   => $borrower,
681
        branchcode => 'branchcode' | undef,
682
    } );
683
684
Given $PARAMS, a hashref containing a $borrower object and a $branchcode,
685
see whether we are still able to place ILLs.
686
687
LimitRules are derived from koha-conf.xml:
688
 + default limit counts, and counting method
689
 + branch specific limit counts & counting method
690
 + borrower category specific limit counts & counting method
691
 + err on the side of caution: a counting fail will cause fail, even if
692
   the other counts passes.
693
694
=cut
695
696
sub check_limits {
697
    my ( $self, $params ) = @_;
698
    my $patron     = $params->{patron};
699
    my $branchcode = $params->{librarycode} || $patron->branchcode;
700
701
    # Establish maximum number of allowed requests
702
    my ( $branch_rules, $brw_rules ) = (
703
        $self->getLimits( {
704
            type => 'branch',
705
            value => $branchcode
706
        } ),
707
        $self->getLimits( {
708
            type => 'brw_cat',
709
            value => $patron->categorycode,
710
        } ),
711
    );
712
    my ( $branch_limit, $brw_limit )
713
        = ( $branch_rules->{count}, $brw_rules->{count} );
714
    # Establish currently existing requests
715
    my ( $branch_count, $brw_count ) = (
716
        $self->_limit_counter(
717
            $branch_rules->{method}, { branchcode => $branchcode }
718
        ),
719
        $self->_limit_counter(
720
            $brw_rules->{method}, { borrowernumber => $patron->borrowernumber }
721
        ),
722
    );
723
724
    # Compare and return
725
    # A limit of -1 means no limit exists.
726
    # We return blocked if either branch limit or brw limit is reached.
727
    if ( ( $branch_limit != -1 && $branch_limit <= $branch_count )
728
             || ( $brw_limit != -1 && $brw_limit <= $brw_count ) ) {
729
        return 0;
730
    } else {
731
        return 1;
732
    }
733
}
734
735
sub _limit_counter {
736
    my ( $self, $method, $target ) = @_;
737
738
    # Establish parameters of counts
739
    my $resultset;
740
    if ($method && $method eq 'annual') {
741
        $resultset = Koha::Illrequests->search({
742
            -and => [
743
                %{$target},
744
                \"YEAR(placed) = YEAR(NOW())"
745
            ]
746
        });
747
    } else {                    # assume 'active'
748
        # XXX: This status list is ugly. There should be a method in config
749
        # to return these.
750
        $where = { status => { -not_in => [ 'QUEUED', 'COMP' ] } };
751
        $resultset = Koha::Illrequests->search({ %{$target}, %{$where} });
752
    }
753
754
    # Fetch counts
755
    return $resultset->count;
756
}
757
758
=head3 requires_moderation
759
760
    my $status = $illRequest->requires_moderation;
761
762
Return the name of the status if moderation by staff is required; or 0
763
otherwise.
764
765
=cut
766
767
sub requires_moderation {
768
    my ( $self ) = @_;
769
    my $require_moderation = {
770
        'CANCREQ' => 'CANCREQ',
771
    };
772
    return $require_moderation->{$self->status};
773
}
774
775
=head3 generic_confirm
776
777
    my $stage_summary = $illRequest->generic_confirm;
778
779
Handle the generic_confirm extended method.  The first stage involves creating
780
a template email for the end user to edit in the browser.  The second stage
781
attempts to submit the email.
782
783
=cut
784
785
sub generic_confirm {
786
    my ( $self, $params ) = @_;
787
    my $branch = Koha::Libraries->find($params->{current_branchcode})
788
        || die "Invalid current branchcode. Are you logged in as the database user?";
789
    if ( !$params->{stage}|| $params->{stage} eq 'init' ) {
790
        my $draft->{subject} = "ILL Request";
791
        $draft->{body} = <<EOF;
792
Dear Sir/Madam,
793
794
    We would like to request an interlibrary loan for a title matching the
795
following description:
796
797
EOF
798
799
        my $details = $self->metadata;
800
        while (my ($title, $value) = each %{$details}) {
801
            $draft->{body} .= "  - " . $title . ": " . $value . "\n"
802
                if $value;
803
        }
804
        $draft->{body} .= <<EOF;
805
806
Please let us know if you are able to supply this to us.
807
808
Kind Regards
809
810
EOF
811
812
        my @address = map { $branch->$_ }
813
            qw/ branchname branchaddress1 branchaddress2 branchaddress3
814
                branchzip branchcity branchstate branchcountry branchphone
815
                branchemail /;
816
        my $address = "";
817
        foreach my $line ( @address ) {
818
            $address .= $line . "\n" if $line;
819
        }
820
821
        $draft->{body} .= $address;
822
823
        my $partners = Koha::Patrons->search({
824
            categorycode => $self->_config->partner_code
825
        });
826
        return {
827
            error   => 0,
828
            status  => '',
829
            message => '',
830
            method  => 'generic_confirm',
831
            stage   => 'draft',
832
            value   => {
833
                draft    => $draft,
834
                partners => $partners,
835
            }
836
        };
837
838
    } elsif ( 'draft' eq $params->{stage} ) {
839
        # Create the to header
840
        my $to = $params->{partners};
841
        $to =~ s/^\x00//;       # Strip leading NULLs
842
        $to =~ s/\x00/; /;      # Replace others with '; '
843
        die "No target email addresses found. Either select at least one partner or check your ILL partner library records." if ( !$to );
844
        # Create the from, replyto and sender headers
845
        my $from = $branch->branchemail;
846
        my $replyto = $branch->branchreplyto || $from;
847
        die "Your branch has no email address. Please set it."
848
            if ( !$from );
849
        # Create the email
850
        my $message = Koha::Email->new;
851
        my %mail = $message->create_message_headers(
852
            {
853
                to          => $to,
854
                from        => $from,
855
                replyto     => $replyto,
856
                subject     => Encode::encode( "utf8", $params->{subject} ),
857
                message     => Encode::encode( "utf8", $params->{body} ),
858
                contenttype => 'text/plain',
859
            }
860
        );
861
        # Send it
862
        my $result = sendmail(%mail);
863
        if ( $result ) {
864
            $self->status("GENREQ")->store;
865
            return {
866
                error   => 0,
867
                status  => '',
868
                message => '',
869
                method  => 'generic_confirm',
870
                stage   => 'commit',
871
                next    => 'illview',
872
            };
873
        } else {
874
            return {
875
                error   => 1,
876
                status  => 'email_failed',
877
                message => $Mail::Sendmail::error,
878
                method  => 'generic_confirm',
879
                stage   => 'draft',
880
            };
881
        }
882
    } else {
883
        die "Unknown stage, should not have happened."
884
    }
885
}
886
887
=head3 id_prefix
888
889
    my $prefix = $record->id_prefix;
890
891
Return the prefix appropriate for the current Illrequest as derived from the
892
borrower and branch associated with this request's Status, and the config
893
file.
894
895
=cut
896
897
sub id_prefix {
898
    my ( $self ) = @_;
899
    my $brw = $self->patron;
900
    my $brw_cat = "dummy";
901
    $brw_cat = $brw->categorycode
902
        unless ( 'HASH' eq ref($brw) && $brw->{deleted} );
903
    my $prefix = $self->getPrefix( {
904
        brw_cat => $brw_cat,
905
        branch  => $self->branchcode,
906
    } );
907
    $prefix .= "-" if ( $prefix );
908
    return $prefix;
909
}
910
911
=head3 _censor
912
913
    my $params = $illRequest->_censor($params);
914
915
Return $params, modified to reflect our censorship requirements.
916
917
=cut
918
919
sub _censor {
920
    my ( $self, $params ) = @_;
921
    my $censorship = $self->_config->censorship;
922
    $params->{censor_notes_staff} = $censorship->{censor_notes_staff}
923
        if ( $params->{opac} );
924
    $params->{display_reply_date} = ( $censorship->{censor_reply_date} ) ? 0 : 1;
925
926
    return $params;
927
}
928
929
=head1 AUTHOR
930
931
Alex Sassmannshausen <alex.sassmannshausen@ptfs-europe.com>
932
933
=cut
934
935
1;
(-)a/Koha/Illrequest/Config.pm (+384 lines)
Line 0 Link Here
1
package Koha::Illrequest::Config;
2
3
# Copyright 2013,2014 PTFS Europe Ltd
4
#
5
# This file is part of Koha.
6
#
7
# Koha is free software; you can redistribute it and/or modify it under the
8
# terms of the GNU General Public License as published by the Free Software
9
# Foundation; either version 3 of the License, or (at your option) any later
10
# version.
11
#
12
# Koha is distributed in the hope that it will be useful, but WITHOUT ANY
13
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
14
# A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
15
#
16
# You should have received a copy of the GNU General Public License along
17
# with Koha; if not, write to the Free Software Foundation, Inc.,
18
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19
20
use Modern::Perl;
21
use C4::Context;
22
23
=head1 NAME
24
25
Koha::Illrequest::Config - Koha ILL Configuration Object
26
27
=head1 SYNOPSIS
28
29
Object-oriented class that giving access to the illconfig data derived
30
from ill/config.yaml.
31
32
=head1 DESCRIPTION
33
34
Config object providing abstract representation of the expected XML
35
returned by ILL API.
36
37
In particular the config object uses a YAML file, whose path is
38
defined by <illconfig> in koha-conf.xml. That YAML file provides the
39
data structure exposed in this object.
40
41
By default the configured data structure complies with fields used by
42
the British Library Interlibrary Loan DSS API.
43
44
The config file also provides mappings for Record Object accessors.
45
46
=head1 API
47
48
=head2 Class Methods
49
50
=head3 new
51
52
    my $config = Koha::Illrequest::Config->new();
53
54
Create a new Koha::Illrequest::Config object, with mapping data loaded from the
55
ILL configuration file.
56
57
=cut
58
59
sub new {
60
    my ( $class ) = @_;
61
    my $self  = {};
62
63
    $self->{configuration} = _load_configuration(
64
        C4::Context->config("interlibrary_loans"),
65
        C4::Context->preference("UnmediatedILL")
66
      );
67
68
    bless $self, $class;
69
70
    return $self;
71
}
72
73
=head3 backend
74
75
    $backend = $config->backend($name);
76
    $backend = $config->backend;
77
78
Standard setter/accessor for our backend.
79
80
=cut
81
82
sub backend {
83
    my ( $self, $new ) = @_;
84
    $self->{configuration}->{backend} = $new if $new;
85
    return $self->{configuration}->{backend};
86
}
87
88
=head3 backend_dir
89
90
    $backend_dir = $config->backend_dir($new_path);
91
    $backend_dir = $config->backend_dir;
92
93
Standard setter/accessor for our backend_directory.
94
95
=cut
96
97
sub backend_dir {
98
    my ( $self, $new ) = @_;
99
    $self->{configuration}->{backend_directory} = $new if $new;
100
    return $self->{configuration}->{backend_directory};
101
}
102
103
=head3 partner_code
104
105
    $partner_code = $config->partner_code($new_code);
106
    $partner_code = $config->partner_code;
107
108
Standard setter/accessor for our partner_code.
109
110
=cut
111
112
sub partner_code {
113
    my ( $self, $new ) = @_;
114
    $self->{configuration}->{partner_code} = $new if $new;
115
    return $self->{configuration}->{partner_code};
116
}
117
118
=head3 limits
119
120
    $limits = $config->limits($limitshash);
121
    $limits = $config->limits;
122
123
Standard setter/accessor for our limits.  No parsing is performed on
124
$LIMITSHASH, so caution should be exercised when using this setter.
125
126
=cut
127
128
sub limits {
129
    my ( $self, $new ) = @_;
130
    $self->{configuration}->{limits} = $new if $new;
131
    return $self->{configuration}->{limits};
132
}
133
134
=head3 getPrefixes
135
136
    my $prefixes = $config->getPrefixes('brw_cat' | 'branch');
137
138
Return the prefix for ILLs defined by our config.
139
140
=cut
141
142
sub getPrefixes {
143
    my ( $self, $type ) = @_;
144
    die "Unexpected type." unless ( $type eq 'brw_cat' || $type eq 'branch' );
145
    my $values = $self->{configuration}->{prefixes}->{$type};
146
    $values->{default} = $self->{configuration}->{prefixes}->{default};
147
    return $values;
148
}
149
150
=head3 getLimitRules
151
152
    my $rules = $config->getLimitRules('brw_cat' | 'branch')
153
154
Return the hash of ILL limit rules defined by our config.
155
156
=cut
157
158
sub getLimitRules {
159
    my ( $self, $type ) = @_;
160
    die "Unexpected type." unless ( $type eq 'brw_cat' || $type eq 'branch' );
161
    my $values = $self->{configuration}->{limits}->{$type};
162
    $values->{default} = $self->{configuration}->{limits}->{default};
163
    return $values;
164
}
165
166
=head3 getDigitalRecipients
167
168
    my $recipient_rules= $config->getDigitalRecipients('brw_cat' | 'branch');
169
170
Return the hash of digital_recipient settings defined by our config.
171
172
=cut
173
174
sub getDigitalRecipients {
175
    my ( $self, $type ) = @_;
176
    die "Unexpected type." unless ( $type eq 'brw_cat' || $type eq 'branch' );
177
    my $values = $self->{configuration}->{digital_recipients}->{$type};
178
    $values->{default} =
179
        $self->{configuration}->{digital_recipients}->{default};
180
    return $values;
181
}
182
183
=head3 censorship
184
185
    my $censoredValues = $config->censorship($hash);
186
    my $censoredValues = $config->censorship;
187
188
Standard setter/accessor for our limits.  No parsing is performed on $HASH, so
189
caution should be exercised when using this setter.
190
191
Return our censorship values for the OPAC as loaded from the koha-conf.xml, or
192
the fallback value (no censorship).
193
194
=cut
195
196
sub censorship {
197
    my ( $self, $new ) = @_;
198
    $self->{configuration}->{censorship} = $new if $new;
199
    return $self->{configuration}->{censorship};
200
}
201
202
=head3 _load_configuration
203
204
    my $configuration = $config->_load_configuration($config_from_xml);
205
206
Read the configuration values passed as the parameter, and populate a hashref
207
suitable for use with these.
208
209
A key task performed here is the parsing of the input in the configuration
210
file to ensure we have only valid input there.
211
212
=cut
213
214
sub _load_configuration {
215
    my ( $xml_config, $unmediated ) = @_;
216
    my $xml_backend_dir = $xml_config->{backend_directory};
217
218
    # Default data structure to be returned
219
    my $configuration = {
220
        backend_directory  => $xml_backend_dir,
221
        censorship         => {
222
            censor_notes_staff => 0,
223
            censor_reply_date => 0,
224
        },
225
        limits             => {},
226
        digital_recipients => {},
227
        prefixes           => {},
228
        partner_code       => 'ILLLIBS',
229
        raw_config         => $xml_config,
230
    };
231
232
    # Per Branch Configuration
233
    my $branches = $xml_config->{branch};
234
    if ( ref($branches) eq "ARRAY" ) {
235
        # Multiple branch overrides defined
236
        map {
237
            _load_unit_config({
238
                unit   => $_,
239
                id     => $_->{code},
240
                config => $configuration,
241
                type   => 'branch'
242
            })
243
        } @{$branches};
244
    } elsif ( ref($branches) eq "HASH" ) {
245
        # Single branch override defined
246
        _load_unit_config({
247
            unit   => $branches,
248
            id     => $branches->{code},
249
            config => $configuration,
250
            type   => 'branch'
251
        });
252
    }
253
254
    # Per Borrower Category Configuration
255
    my $brw_cats = $xml_config->{borrower_category};
256
    if ( ref($brw_cats) eq "ARRAY" ) {
257
        # Multiple borrower category overrides defined
258
        map {
259
            _load_unit_config({
260
                unit   => $_,
261
                id     => $_->{code},
262
                config => $configuration,
263
                type   => 'brw_cat'
264
            })
265
        } @{$brw_cats};
266
    } elsif ( ref($brw_cats) eq "HASH" ) {
267
        # Single branch override defined
268
        _load_unit_config({
269
            unit   => $brw_cats,
270
            id     => $brw_cats->{code},
271
            config => $configuration,
272
            type   => 'brw_cat'
273
        });
274
    }
275
276
    # Default Configuration
277
    _load_unit_config({
278
        unit   => $xml_config,
279
        id     => 'default',
280
        config => $configuration
281
    });
282
283
    # Censorship
284
    my $staff_comments = $xml_config->{staff_request_comments} || 0;
285
    $configuration->{censorship}->{censor_notes_staff} = 1
286
        if ( $staff_comments && 'hide' eq $staff_comments );
287
    my $reply_date = $xml_config->{reply_date} || 0;
288
    $configuration->{censorship}->{censor_reply_date} = 1
289
        if ( $reply_date && 'hide' eq $reply_date );
290
291
    # ILL Partners
292
    $configuration->{partner_code} = $xml_config->{partner_code} || 'ILLLIBS';
293
294
    die "No DEFAULT_FORMATS has been defined in koha-conf.xml, but UNMEDIATEDILL is active."
295
        if ( $unmediated && !$configuration->{default_formats}->{default} );
296
297
    return $configuration;
298
}
299
300
=head3 _load_unit_config
301
302
    my $configuration->{part} = _load_unit_config($params);
303
304
$PARAMS is a hashref with the following elements:
305
- unit: the part of the configuration we are parsing.
306
- id: the name within which we will store the parsed unit in config.
307
- config: the configuration we are augmenting.
308
- type: the type of config unit we are parsing.  Assumed to be 'default'.
309
310
Read `unit', and augment `config' with these under `id'.
311
312
This is a helper for _load_configuration.
313
314
A key task performed here is the parsing of the input in the configuration
315
file to ensure we have only valid input there.
316
317
=cut
318
319
sub _load_unit_config {
320
    my ( $params ) = @_;
321
    my $unit = $params->{unit};
322
    my $id = $params->{id};
323
    my $config = $params->{config};
324
    my $type = $params->{type};
325
    die "TYPE should be either 'branch' or 'brw_cat' if ID is not 'default'."
326
        if ( $id ne 'default' && ( $type ne 'branch' && $type ne 'brw_cat') );
327
    return $config unless $id;
328
329
    if ( $unit->{api_key} && $unit->{api_auth} ) {
330
        $config->{credentials}->{api_keys}->{$id} = {
331
            api_key  => $unit->{api_key},
332
            api_auth => $unit->{api_auth},
333
        };
334
    }
335
    # Add request_limit rules.
336
    # METHOD := 'annual' || 'active'
337
    # COUNT  := x >= -1
338
    if ( ref $unit->{request_limit} eq 'HASH' ) {
339
        my $method  = $unit->{request_limit}->{method};
340
        my $count = $unit->{request_limit}->{count};
341
        if ( 'default' eq $id ) {
342
            $config->{limits}->{$id}->{method}  = $method
343
                if ( $method && ( 'annual' eq $method || 'active' eq $method ) );
344
            $config->{limits}->{$id}->{count} = $count
345
                if ( $count && ( -1 <= $count ) );
346
        } else {
347
            $config->{limits}->{$type}->{$id}->{method}  = $method
348
                if ( $method && ( 'annual' eq $method || 'active' eq $method ) );
349
            $config->{limits}->{$type}->{$id}->{count} = $count
350
                if ( $count && ( -1 <= $count ) );
351
        }
352
    }
353
354
    # Add prefix rules.
355
    # PREFIX := string
356
    if ( $unit->{prefix} ) {
357
        if ( 'default' eq $id ) {
358
            $config->{prefixes}->{$id} = $unit->{prefix};
359
        } else {
360
            $config->{prefixes}->{$type}->{$id} = $unit->{prefix};
361
        }
362
    }
363
364
    # Add digital_recipient rules.
365
    # DIGITAL_RECIPIENT := borrower || branch (defaults to borrower)
366
    if ( $unit->{digital_recipient} ) {
367
        if ( 'default' eq $id ) {
368
            $config->{digital_recipients}->{$id} = $unit->{digital_recipient};
369
        } else {
370
            $config->{digital_recipients}->{$type}->{$id} =
371
                $unit->{digital_recipient};
372
        }
373
    }
374
375
    return $config;
376
}
377
378
=head1 AUTHOR
379
380
Alex Sassmannshausen <alex.sassmannshausen@ptfs-europe.com>
381
382
=cut
383
384
1;
(-)a/Koha/Illrequestattribute.pm (+51 lines)
Line 0 Link Here
1
package Koha::Illrequestattribute;
2
3
# Copyright PTFS Europe 2016
4
#
5
# This file is part of Koha.
6
#
7
# Koha is free software; you can redistribute it and/or modify it under the
8
# terms of the GNU General Public License as published by the Free Software
9
# Foundation; either version 3 of the License, or (at your option) any later
10
# version.
11
#
12
# Koha is distributed in the hope that it will be useful, but WITHOUT ANY
13
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14
# FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
15
# details.
16
#
17
# You should have received a copy of the GNU General Public License along with
18
# Koha; if not, write to the Free Software Foundation, Inc., 51 Franklin
19
# Street, Fifth Floor, Boston, MA 02110-1301 USA.
20
21
use Modern::Perl;
22
23
use Koha::Database;
24
25
use base qw(Koha::Object);
26
27
=head1 NAME
28
29
Koha::Illrequestattribute - Koha Illrequestattribute Object class
30
31
=head1 API
32
33
=head2 Class Methods
34
35
=cut
36
37
=head3 type
38
39
=cut
40
41
sub _type {
42
    return 'Illrequestattribute';
43
}
44
45
=head1 AUTHOR
46
47
Alex Sassmannshausen <alex.sassmannshausen@ptfs-europe.com>
48
49
=cut
50
51
1;
(-)a/Koha/Illrequestattributes.pm (+55 lines)
Line 0 Link Here
1
package Koha::Illrequestattributes;
2
3
# Copyright PTFS Europe 2016
4
#
5
# This file is part of Koha.
6
#
7
# Koha is free software; you can redistribute it and/or modify it under the
8
# terms of the GNU General Public License as published by the Free Software
9
# Foundation; either version 3 of the License, or (at your option) any later
10
# version.
11
#
12
# Koha is distributed in the hope that it will be useful, but WITHOUT ANY
13
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
14
# A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
15
#
16
# You should have received a copy of the GNU General Public License along
17
# with Koha; if not, write to the Free Software Foundation, Inc.,
18
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19
20
use Modern::Perl;
21
22
use Koha::Database;
23
use Koha::Illrequestattribute;
24
25
use base qw(Koha::Objects);
26
27
=head1 NAME
28
29
Koha::Illrequestattributes - Koha Illrequestattributes Object class
30
31
=head1 API
32
33
=head2 Class Methods
34
35
=cut
36
37
=head3 type
38
39
=cut
40
41
sub _type {
42
    return 'Illrequestattribute';
43
}
44
45
sub object_class {
46
    return 'Koha::Illrequestattribute';
47
}
48
49
=head1 AUTHOR
50
51
Alex Sassmannshausen <alex.sassmannshausen@ptfs-europe.com>
52
53
=cut
54
55
1;
(-)a/Koha/Illrequests.pm (+97 lines)
Line 0 Link Here
1
package Koha::Illrequests;
2
3
# Copyright PTFS Europe 2016
4
#
5
# This file is part of Koha.
6
#
7
# Koha is free software; you can redistribute it and/or modify it under the
8
# terms of the GNU General Public License as published by the Free Software
9
# Foundation; either version 3 of the License, or (at your option) any later
10
# version.
11
#
12
# Koha is distributed in the hope that it will be useful, but WITHOUT ANY
13
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
14
# A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
15
#
16
# You should have received a copy of the GNU General Public License along
17
# with Koha; if not, write to the Free Software Foundation, Inc.,
18
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19
20
use Modern::Perl;
21
22
use Koha::Database;
23
use Koha::Illrequest;
24
use Koha::Illrequest::Config;
25
26
use base qw(Koha::Objects);
27
28
=head1 NAME
29
30
Koha::Illrequests - Koha Illrequests Object class
31
32
=head1 API
33
34
=head2 Class Methods
35
36
=cut
37
38
=head3 type
39
40
=cut
41
42
sub _type {
43
    return 'Illrequest';
44
}
45
46
sub object_class {
47
    return 'Koha::Illrequest';
48
}
49
50
##### To be implemented Facade
51
52
=head3 new
53
54
    my $illRequests = Koha::Illrequests->new();
55
56
Create an ILLREQUESTS object, a singleton through which we can interact with
57
ILLREQUEST objects stored in the database or search for ILL candidates at API
58
backends.
59
60
=cut
61
62
sub new {
63
    my ( $class, $attributes ) = @_;
64
65
    my $self = $class->SUPER::new($class, $attributes);
66
67
    my $config = Koha::Illrequest::Config->new; # <- Necessary
68
    $self->{_config} = $config;                 # <- Necessary
69
70
    return $self;
71
}
72
73
=head3 search_incomplete
74
75
    my $requests = $illRequests->search_incomplete;
76
77
A specialised version of `search`, returning all requests currently
78
not considered completed.
79
80
=cut
81
82
sub search_incomplete {
83
    my ( $self ) = @_;
84
    $self->search( {
85
        status => [
86
            -and => { '!=', 'COMP' }, { '!=', 'GENCOMP' }
87
        ]
88
    } );
89
}
90
91
=head1 AUTHOR
92
93
Alex Sassmannshausen <alex.sassmannshausen@ptfs-europe.com>
94
95
=cut
96
97
1;
(-)a/Koha/REST/V1/Illrequests.pm (+85 lines)
Line 0 Link Here
1
package Koha::REST::V1::Illrequests;
2
3
# This file is part of Koha.
4
#
5
# Koha is free software; you can redistribute it and/or modify it under the
6
# terms of the GNU General Public License as published by the Free Software
7
# Foundation; either version 3 of the License, or (at your option) any later
8
# version.
9
#
10
# Koha is distributed in the hope that it will be useful, but WITHOUT ANY
11
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
12
# A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
13
#
14
# You should have received a copy of the GNU General Public License along
15
# with Koha; if not, write to the Free Software Foundation, Inc.,
16
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17
18
use Modern::Perl;
19
20
use Mojo::Base 'Mojolicious::Controller';
21
22
use Koha::Illrequests;
23
use Koha::Library;
24
25
sub list {
26
    my ($c, $args, $cb) = @_;
27
28
    my $filter;
29
    $args //= {};
30
    my $output = [];
31
32
    # Create a hash where all keys are embedded values
33
    # Enables easy checking
34
    my %embed;
35
    if (defined $args->{embed}) {
36
        %embed = map { $_ => 1 }  @{$args->{embed}};
37
        delete $args->{embed};
38
    }
39
40
    for my $filter_param ( keys %$args ) {
41
        my @values = split(/,/, $args->{$filter_param});
42
        $filter->{$filter_param} = \@values;
43
    }
44
45
    my $requests = Koha::Illrequests->search($filter);
46
47
    while (my $request = $requests->next) {
48
        my $unblessed = $request->unblessed;
49
        # Add the request's id_prefix
50
        $unblessed->{id_prefix} = $request->id_prefix;
51
        # Augment the request response with patron details
52
        # if appropriate
53
        if (defined $embed{patron}) {
54
            my $patron = $request->patron;
55
            $unblessed->{patron} = {
56
                firstname  => $patron->firstname,
57
                surname    => $patron->surname,
58
                cardnumber => $patron->cardnumber
59
            };
60
        }
61
        # Augment the request response with metadata details
62
        # if appropriate
63
        if (defined $embed{metadata}) {
64
            $unblessed->{metadata} = $request->metadata;
65
        }
66
        # Augment the request response with status details
67
        # if appropriate
68
        if (defined $embed{capabilities}) {
69
            $unblessed->{capabilities} = $request->capabilities;
70
        }
71
        # Augment the request response with branch details
72
        # if appropriate
73
        if (defined $embed{branch}) {
74
            $unblessed->{branch} = Koha::Libraries->find(
75
                $request->branchcode
76
            )->unblessed;
77
        }
78
        push @{$output}, $unblessed
79
    }
80
81
    return $c->$cb( $output, 200 );
82
83
}
84
85
1;
(-)a/Makefile.PL (+1 lines)
Lines 312-317 my $target_map = { Link Here
312
  './etc/zebradb'               => { target => 'ZEBRA_CONF_DIR', trimdir => -1 },
312
  './etc/zebradb'               => { target => 'ZEBRA_CONF_DIR', trimdir => -1 },
313
  './etc/pazpar2'               => { target => 'PAZPAR2_CONF_DIR', trimdir => -1 },
313
  './etc/pazpar2'               => { target => 'PAZPAR2_CONF_DIR', trimdir => -1 },
314
  './help.pl'                   => 'INTRANET_CGI_DIR',
314
  './help.pl'                   => 'INTRANET_CGI_DIR',
315
  './ill'                       => 'INTRANET_CGI_DIR',
315
  './installer-CPAN.pl'         => 'NONE',
316
  './installer-CPAN.pl'         => 'NONE',
316
  './installer'                 => 'INTRANET_CGI_DIR',
317
  './installer'                 => 'INTRANET_CGI_DIR',
317
  './errors'                    => {target => 'INTRANET_CGI_DIR'},
318
  './errors'                    => {target => 'INTRANET_CGI_DIR'},
(-)a/api/v1/swagger/paths.json (+3 lines)
Lines 22-26 Link Here
22
  },
22
  },
23
  "/patrons/{borrowernumber}": {
23
  "/patrons/{borrowernumber}": {
24
    "$ref": "paths/patrons.json#/~1patrons~1{borrowernumber}"
24
    "$ref": "paths/patrons.json#/~1patrons~1{borrowernumber}"
25
  },
26
  "/illrequests": {
27
    "$ref": "paths/illrequests.json#/~1illrequests"
25
  }
28
  }
26
}
29
}
(-)a/api/v1/swagger/paths/illrequests.json (+98 lines)
Line 0 Link Here
1
{
2
    "/illrequests": {
3
        "get": {
4
            "x-mojo-controller": "Koha::REST::V1::Illrequests",
5
            "operationId": "list",
6
            "tags": ["illrequests"],
7
            "parameters": [{
8
                "name": "embed",
9
                "in": "query",
10
                "description": "Additional objects that should be embedded in the response",
11
                "required": false,
12
                "type": "array",
13
                "collectionFormat": "csv",
14
                "items": {
15
                    "type": "string",
16
                    "enum": [
17
                        "patron",
18
                        "branch",
19
                        "capabilities"
20
                    ]
21
                }
22
            }, {
23
                "name": "backend",
24
                "in": "query",
25
                "description": "The name of a ILL backend",
26
                "required": false,
27
                "type": "string"
28
            }, {
29
                "name": "orderid",
30
                "in": "query",
31
                "description": "The order ID of a request",
32
                "required": false,
33
                "type": "string"
34
            }, {
35
                "name": "biblio_id",
36
                "in": "query",
37
                "description": "The biblio ID associated with a request",
38
                "required": false,
39
                "type": "integer"
40
            }, {
41
                "name": "borrower_id",
42
                "in": "query",
43
                "description": "The borrower ID associated with a request",
44
                "required": false,
45
                "type": "integer"
46
            }, {
47
                "name": "completed",
48
                "in": "query",
49
                "description": "The date the request was considered completed",
50
                "required": false,
51
                "type": "string"
52
            }, {
53
                "name": "status",
54
                "in": "query",
55
                "description": "A full status string e.g. REQREV",
56
                "required": false,
57
                "type": "string"
58
            }, {
59
                "name": "medium",
60
                "in": "query",
61
                "description": "The medium of the requested item",
62
                "required": false,
63
                "type": "string"
64
            }, {
65
                "name": "updated",
66
                "in": "query",
67
                "description": "The last updated date of the request",
68
                "required": false,
69
                "type": "string"
70
            }, {
71
                "name": "placed",
72
                "in": "query",
73
                "description": "The date the request was placed",
74
                "required": false,
75
                "type": "string"
76
            }, {
77
                "name": "branch_id",
78
                "in": "query",
79
                "description": "The ID of the pickup branch",
80
                "required": false,
81
                "type": "string"
82
            }],
83
            "produces": [
84
                "application/json"
85
            ],
86
            "responses": {
87
                "200": {
88
                    "description": "OK"
89
                }
90
            },
91
            "x-koha-authorization": {
92
                "permissions": {
93
                    "borrowers": "1"
94
                }
95
            }
96
        }
97
    }
98
}
(-)a/etc/koha-conf.xml (+21 lines)
Lines 153-157 __PAZPAR2_TOGGLE_XML_POST__ Link Here
153
 <plack_max_requests>50</plack_max_requests>
153
 <plack_max_requests>50</plack_max_requests>
154
 <plack_workers>2</plack_workers>
154
 <plack_workers>2</plack_workers>
155
155
156
 <interlibrary_loans>
157
     <!-- Path to where Illbackends are located on the system
158
          - This setting should normally not be touched -->
159
     <backend_directory>__PERL_MODULE_DIR__/Koha/Illbackends</backend_directory>
160
     <!-- How should we treat staff comments?
161
          - hide: don't show in OPAC
162
          - show: show in OPAC -->
163
     <staff_request_comments>hide</staff_request_comments>
164
     <!-- How should we treat the reply_date field?
165
          - hide: don't show this field in the UI
166
          - any other string: show, with this label -->
167
     <reply_date>hide</reply_date>
168
     <!-- Where should digital ILLs be sent?
169
          - borrower: send it straight to the borrower email
170
          - branch: send the ILL to the branch email -->
171
     <digital_recipient>branch</digital_recipient>
172
     <!-- What patron category should we use for p2p ILL requests?
173
          - By default this is set to 'ILLLIBS' -->
174
     <partner_code>ILLLIBS</partner_code>
175
 </interlibrary_loans>
176
156
</config>
177
</config>
157
</yazgfs>
178
</yazgfs>
(-)a/ill/ill-requests.pl (+252 lines)
Line 0 Link Here
1
#!/usr/bin/perl
2
3
# Copyright 2013 PTFS-Europe Ltd and Mark Gavillet
4
# Copyright 2014 PTFS-Europe Ltd
5
#
6
# This file is part of Koha.
7
# Koha is free software; you can redistribute it and/or modify it under the
8
# terms of the GNU General Public License as published by the Free Software
9
# Foundation; either version 3 of the License, or (at your option) any later
10
# version.
11
#
12
# Koha is distributed in the hope that it will be useful, but WITHOUT ANY
13
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
14
# A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
15
#
16
# You should have received a copy of the GNU General Public License along
17
# with Koha; if not, write to the Free Software Foundation, Inc.,
18
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19
20
use Modern::Perl;
21
22
use CGI;
23
24
use C4::Auth;
25
use C4::Output;
26
use Koha::AuthorisedValues;
27
use Koha::Illrequests;
28
use Koha::Libraries;
29
30
my $cgi = CGI->new;
31
my $illRequests = Koha::Illrequests->new;
32
33
# Grab all passed data
34
# 'our' since Plack changes the scoping
35
# of 'my'
36
our $params = $cgi->Vars();
37
38
my $op = $params->{method} || 'illlist';
39
40
my ( $template, $patronnumber, $cookie ) = get_template_and_user( {
41
    template_name => 'ill/ill-requests.tt',
42
    query         => $cgi,
43
    type          => 'intranet',
44
    flagsrequired => { ill => '*' },
45
} );
46
47
if ( $op eq 'illview' ) {
48
    # View the details of an ILL
49
    my $request = Koha::Illrequests->find($params->{illrequest_id});
50
51
    $template->param(
52
        request => $request
53
    );
54
55
} elsif ( $op eq 'create' ) {
56
    # We're in the process of creating a request
57
    my $request = Koha::Illrequest->new
58
        ->load_backend($params->{backend});
59
    my $backend_result = $request->backend_create($params);
60
    $template->param(
61
        whole   => $backend_result,
62
        request => $request
63
    );
64
    handle_commit_maybe($backend_result, $request);
65
66
} elsif ( $op eq 'confirm' ) {
67
    # Backend 'confirm' method
68
    # confirm requires a specific request, so first, find it.
69
    my $request = Koha::Illrequests->find($params->{illrequest_id});
70
    my $backend_result = $request->backend_confirm($params);
71
    $template->param(
72
        whole   => $backend_result,
73
        request => $request,
74
    );
75
76
    # handle special commit rules & update type
77
    handle_commit_maybe($backend_result, $request);
78
79
} elsif ( $op eq 'cancel' ) {
80
    # Backend 'cancel' method
81
    # cancel requires a specific request, so first, find it.
82
    my $request = Koha::Illrequests->find($params->{illrequest_id});
83
    my $backend_result = $request->backend_cancel($params);
84
    $template->param(
85
        whole   => $backend_result,
86
        request => $request,
87
    );
88
89
    # handle special commit rules & update type
90
    handle_commit_maybe($backend_result, $request);
91
92
} elsif ( $op eq 'edit_action' ) {
93
    # Handle edits to the Illrequest object.
94
    # (not the Illrequestattributes)
95
    # We simulate the API for backend requests for uniformity.
96
    # So, init:
97
    my $request = Koha::Illrequests->find($params->{illrequest_id});
98
    if ( !$params->{stage} ) {
99
        my $backend_result = {
100
            error   => 0,
101
            status  => '',
102
            message => '',
103
            method  => 'edit_action',
104
            stage   => 'init',
105
            next    => '',
106
            value   => {}
107
        };
108
        $template->param(
109
            whole   => $backend_result,
110
            request => $request
111
        );
112
    } else {
113
        # Commit:
114
        # Save the changes
115
        $request->borrowernumber($params->{borrowernumber});
116
        $request->biblio_id($params->{biblio_id});
117
        $request->branchcode($params->{branchcode});
118
        $request->notesopac($params->{notesopac});
119
        $request->notesstaff($params->{notesstaff});
120
        $request->store;
121
        my $backend_result = {
122
            error   => 0,
123
            status  => '',
124
            message => '',
125
            method  => 'edit_action',
126
            stage   => 'commit',
127
            next    => 'illlist',
128
            value   => {}
129
        };
130
        handle_commit_maybe($backend_result, $request);
131
    }
132
133
} elsif ( $op eq 'moderate_action' ) {
134
    # Moderate action is required for an ILL submodule / syspref.
135
    # Currently still needs to be implemented.
136
    redirect_to_list();
137
138
} elsif ( $op eq 'delete_confirm') {
139
    my $request = Koha::Illrequests->find($params->{illrequest_id});
140
141
    $template->param(
142
        request => $request
143
    );
144
145
} elsif ( $op eq 'delete' ) {
146
147
    # Check if the request is confirmed, if not, redirect
148
    # to the confirmation view
149
    if ($params->{confirmed} == 1) {
150
        # We simply delete the request...
151
        my $request = Koha::Illrequests->find(
152
            $params->{illrequest_id}
153
        )->delete;
154
        # ... then return to list view.
155
        redirect_to_list();
156
    } else {
157
        print $cgi->redirect(
158
            "/cgi-bin/koha/ill/ill-requests.pl?" .
159
            "method=delete_confirm&illrequest_id=" .
160
            $params->{illrequest_id});
161
    }
162
163
} elsif ( $op eq 'mark_completed' ) {
164
    my $request = Koha::Illrequests->find($params->{illrequest_id});
165
    my $backend_result = $request->mark_completed($params);
166
    $template->param(
167
        whole => $backend_result,
168
        request => $request,
169
    );
170
171
    # handle special commit rules & update type
172
    handle_commit_maybe($backend_result, $request);
173
174
} elsif ( $op eq 'generic_confirm' ) {
175
    my $request = Koha::Illrequests->find($params->{illrequest_id});
176
    $params->{current_branchcode} = C4::Context->mybranch;
177
    my $backend_result = $request->generic_confirm($params);
178
    $template->param(
179
        whole => $backend_result,
180
        request => $request,
181
    );
182
183
    # handle special commit rules & update type
184
    handle_commit_maybe($backend_result, $request);
185
186
} elsif ( $op eq 'illlist') {
187
    # Display all current ILLs
188
    my $requests = $illRequests->search();
189
190
    $template->param(
191
        requests => $requests
192
    );
193
194
    # If we receive a pre-filter, make it available to the template
195
    my $possible_filters = ['borrowernumber'];
196
    my $active_filters = [];
197
    foreach my $filter(@{$possible_filters}) {
198
        if ($params->{$filter}) {
199
            push @{$active_filters},
200
                { name => $filter, value => $params->{$filter}};
201
        }
202
    }
203
    if (scalar @{$active_filters} > 0) {
204
        $template->param(
205
            prefilters => $active_filters
206
        );
207
    }
208
} else {
209
    my $request = Koha::Illrequests->find($params->{illrequest_id});
210
    my $backend_result = $request->custom_capability($op, $params);
211
    $template->param(
212
        whole => $backend_result,
213
        request => $request,
214
    );
215
216
    # handle special commit rules & update type
217
    handle_commit_maybe($backend_result, $request);
218
}
219
220
# Get a list of backends
221
my $ir = Koha::Illrequest->new;
222
223
$template->param(
224
    backends    => $ir->available_backends,
225
    media       => [ "Book", "Article", "Journal" ],
226
    query_type  => $op,
227
    branches    => Koha::Libraries->search->unblessed,
228
    here_link   => "/cgi-bin/koha/ill/ill-requests.pl"
229
);
230
231
output_html_with_http_headers( $cgi, $cookie, $template->output );
232
233
sub handle_commit_maybe {
234
    my ( $backend_result, $request ) = @_;
235
    # We need to special case 'commit'
236
    if ( $backend_result->{stage} eq 'commit' ) {
237
        if ( $backend_result->{next} eq 'illview' ) {
238
            # Redirect to a view of the newly created request
239
            print $cgi->redirect(
240
                '/cgi-bin/koha/ill/ill-requests.pl?method=illview&illrequest_id='.
241
                $request->id
242
            );
243
        } else {
244
            # Redirect to a requests list view
245
            redirect_to_list();
246
        }
247
    }
248
}
249
250
sub redirect_to_list {
251
    print $cgi->redirect('/cgi-bin/koha/ill/ill-requests.pl');
252
}
(-)a/koha-tmpl/intranet-tmpl/prog/css/staff-global.css (+92 lines)
Lines 3031-3033 fieldset.rows + fieldset.action { Link Here
3031
#patron_search #filters {
3031
#patron_search #filters {
3032
    display: none;
3032
    display: none;
3033
}
3033
}
3034
3035
#interlibraryloans h1 {
3036
    margin: 1em 0;
3037
}
3038
3039
#interlibraryloans h2 {
3040
    margin-bottom: 20px;
3041
}
3042
3043
#interlibraryloans h3 {
3044
    margin-top: 20px;
3045
}
3046
3047
#interlibraryloans .bg-info {
3048
    overflow: auto;
3049
    position: relative;
3050
}
3051
3052
#interlibraryloans #search-summary {
3053
    -webkit-transform: translateY(-50%);
3054
    -ms-transform: translateY(-50%);
3055
    -o-transform: translateY(-50%);
3056
    transform: translateY(-50%);
3057
    position: absolute;
3058
    top: 50%;
3059
}
3060
3061
#interlibraryloans .format h5 {
3062
    margin-top: 20px;
3063
}
3064
3065
#interlibraryloans .format li {
3066
    list-style: none;
3067
}
3068
3069
#interlibraryloans .format h4 {
3070
    margin-bottom: 20px;
3071
}
3072
3073
#interlibraryloans .format input {
3074
    margin: 10px 0;
3075
}
3076
3077
#interlibraryloans #freeform-fields .custom-name {
3078
    width: 9em;
3079
    margin-right: 1em;
3080
    text-align: right;
3081
}
3082
3083
#interlibraryloans #freeform-fields .delete-new-field {
3084
    margin-left: 1em;
3085
}
3086
3087
#interlibraryloans #add-new-fields {
3088
    margin: 1em;
3089
}
3090
3091
#interlibraryloans #column-toggle,
3092
#interlibraryloans #reset-toggle {
3093
    margin: 15px 0;
3094
    line-height: 1.5em;
3095
    font-weight: 700;
3096
}
3097
3098
#ill-view-panel {
3099
    margin-top: 15px;
3100
}
3101
3102
#ill-view-panel h3 {
3103
    margin-bottom: 10px;
3104
}
3105
3106
#ill-view-panel h4 {
3107
    margin-bottom: 20px;
3108
}
3109
3110
#ill-view-panel .rows div {
3111
    height: 1em;
3112
    margin-bottom: 1em;
3113
}
3114
3115
#ill-view-panel #requestattributes .label {
3116
    width: auto;
3117
}
3118
3119
table#ill-requests {
3120
    width: 100% !important;
3121
}
3122
3123
table#ill-requests th {
3124
    text-transform: capitalize;
3125
}
(-)a/koha-tmpl/intranet-tmpl/prog/en/includes/circ-menu.inc (+3 lines)
Lines 112-116 Link Here
112
    [% IF Koha.Preference('HouseboundModule') %]
112
    [% IF Koha.Preference('HouseboundModule') %]
113
        [% IF houseboundview %]<li class="active">[% ELSE %]<li>[% END %]<a href="/cgi-bin/koha/members/housebound.pl?borrowernumber=[% borrowernumber %]">Housebound</a></li>
113
        [% IF houseboundview %]<li class="active">[% ELSE %]<li>[% END %]<a href="/cgi-bin/koha/members/housebound.pl?borrowernumber=[% borrowernumber %]">Housebound</a></li>
114
    [% END %]
114
    [% END %]
115
    [% IF Koha.Preference('ILLModule') %]
116
        <li><a href="/cgi-bin/koha/ill/ill-requests.pl?borrowernumber=[% borrowernumber %]">Interlibrary loans</a></li>
117
    [% END %]
115
</ul></div>
118
</ul></div>
116
[% END %]
119
[% END %]
(-)a/koha-tmpl/intranet-tmpl/prog/en/includes/ill-toolbar.inc (+24 lines)
Line 0 Link Here
1
[% USE Koha %]
2
[% IF Koha.Preference('ILLModule ') %]
3
    <div id="toolbar" class="btn-toolbar">
4
        [% IF backends.size > 1 %]
5
            <div class="dropdown btn-group">
6
                <button class="btn btn-sm btn-default dropdown-toggle" type="button" id="ill-backend-dropdown" data-toggle="dropdown" aria-haspopup="true" aria-expanded="true">
7
                    <i class="fa fa-plus"></i> New ILL request <span class="caret"></span>
8
                </button>
9
                <ul class="dropdown-menu" aria-labelledby="ill-backend-dropdown">
10
                    [% FOREACH backend IN backends %]
11
                        <li><a href="/cgi-bin/koha/ill/ill-requests.pl?method=create&amp;backend=[% backend %]">[% backend %]</a></li>
12
                    [% END %]
13
                </ul>
14
            </div>
15
        [% ELSE %]
16
            <a id="ill-new" class="btn btn-sm btn-default" href="/cgi-bin/koha/ill/ill-requests.pl?method=create&amp;backend=[% backends.0 %]">
17
                <i class="fa fa-plus"></i> New ILL request
18
            </a>
19
        [% END %]
20
        <a id="ill-list" class="btn btn-sm btn-default btn-group" href="/cgi-bin/koha/ill/ill-requests.pl">
21
            <i class="fa fa-list"></i> List requests
22
        </a>
23
    </div>
24
[% END %]
(-)a/koha-tmpl/intranet-tmpl/prog/en/includes/permissions.inc (+1 lines)
Lines 20-25 Link Here
20
    [%- CASE 'plugins' -%]<span>Koha plugins</span>
20
    [%- CASE 'plugins' -%]<span>Koha plugins</span>
21
    [%- CASE 'lists' -%]<span>Lists</span>
21
    [%- CASE 'lists' -%]<span>Lists</span>
22
    [%- CASE 'clubs' -%]<span>Patron clubs</span>
22
    [%- CASE 'clubs' -%]<span>Patron clubs</span>
23
    [%- CASE 'ill' -%]<span>Create and modify Interlibrary loan requests</span>
23
    [%- END -%]
24
    [%- END -%]
24
[%- END -%]
25
[%- END -%]
25
26
(-)a/koha-tmpl/intranet-tmpl/prog/en/modules/ill/ill-requests.tt (+698 lines)
Line 0 Link Here
1
[% USE Branches %]
2
[% USE Koha %]
3
4
[% INCLUDE 'doc-head-open.inc' %]
5
<title>Koha &rsaquo; ILL requests  &rsaquo;</title>
6
[% INCLUDE 'doc-head-close.inc' %]
7
<script type="text/javascript" src="[% themelang %]/lib/jquery/plugins/jquery.tablesorter.min.js"></script>
8
<script type="text/javascript" src="[% themelang %]/lib/jquery/plugins/jquery.checkboxes.min.js"></script>
9
<link rel="stylesheet" type="text/css" href="[% interface %]/[% theme %]/css/datatables.css">
10
[% INCLUDE 'datatables.inc' %]
11
<script type="text/javascript">
12
    //<![CDATA[
13
    $(document).ready(function() {
14
15
        // Illview Datatable setup
16
17
        // Fields we don't want to display
18
        var ignore = [
19
            'accessurl',
20
            'backend',
21
            'completed',
22
            'branch',
23
            'capabilities',
24
            'cost',
25
            'medium',
26
            'notesopac',
27
            'notesstaff',
28
            'placed',
29
            'replied'
30
        ];
31
32
        // Fields we need to expand (flatten)
33
        var expand = [
34
            'metadata',
35
            'patron'
36
        ];
37
38
        // Expanded fields
39
        // This is auto populated
40
        var expanded = {};
41
42
        // The core fields that should be displayed first
43
        var core = [
44
            'metadata_Author',
45
            'metadata_Title',
46
            'borrowername',
47
            'biblio_id',
48
            'branchcode',
49
            'status',
50
            'updated',
51
            'illrequest_id',
52
            'action'
53
        ];
54
55
        // Extra fields that we need to tack on to the end
56
        var extra = [ 'action' ];
57
58
        // Remove any fields we're ignoring
59
        var removeIgnore = function(dataObj) {
60
            dataObj.forEach(function(thisRow) {
61
                ignore.forEach(function(thisIgnore) {
62
                    if (thisRow.hasOwnProperty(thisIgnore)) {
63
                        delete thisRow[thisIgnore];
64
                    }
65
                });
66
            });
67
        };
68
69
        // Expand any fields we're expanding
70
        var expandExpand = function(row) {
71
            expand.forEach(function(thisExpand) {
72
                if (row.hasOwnProperty(thisExpand)) {
73
                    if (!expanded.hasOwnProperty(thisExpand)) {
74
                        expanded[thisExpand] = [];
75
                    }
76
                    var expandObj = row[thisExpand];
77
                    Object.keys(expandObj).forEach(
78
                        function(thisExpandCol) {
79
                            var expColName = thisExpand + '_' + thisExpandCol;
80
                            // Keep a list of fields that have been expanded
81
                            // so we can create toggle links for them
82
                            if (expanded[thisExpand].indexOf(expColName) == -1) {
83
                                expanded[thisExpand].push(expColName);
84
                            }
85
                            expandObj[expColName] =
86
                                expandObj[thisExpandCol];
87
                            delete expandObj[thisExpandCol];
88
                        }
89
                    );
90
                    $.extend(true, row, expandObj);
91
                    delete row[thisExpand];
92
                }
93
            });
94
        };
95
96
        // Build a de-duped list of all column names
97
        var allCols = {};
98
        core.map(function(thisCore) {
99
            allCols[thisCore] = 1;
100
        });
101
        var unionColumns = function(row) {
102
            Object.keys(row).forEach(function(col) {
103
                if (ignore.indexOf(col) == -1) {
104
                    allCols[col] = 1;
105
                }
106
            });
107
        };
108
109
        // Some rows may not have fields that other rows have,
110
        // so make sure all rows have the same fields
111
        var fillMissing = function(row) {
112
            Object.keys(allCols).forEach(function(thisCol) {
113
                row[thisCol] = (!row.hasOwnProperty(thisCol)) ?
114
                    null :
115
                    row[thisCol];
116
            });
117
        }
118
119
        // Strip the expand prefix if it exists, we do this for display
120
        var stripPrefix = function(value) {
121
            expand.forEach(function(thisExpand) {
122
                var regex = new RegExp(thisExpand + '_', 'g');
123
                value = value.replace(regex, '');
124
            });
125
            return value;
126
        };
127
128
        // Our 'render' function for borrowerlink
129
        var createBorrowerLink = function(data, type, row) {
130
            return '<a title="View borrower details" ' +
131
                'href="/cgi-bin/koha/members/moremember.pl?' +
132
                'borrowernumber='+row.borrowernumber+'">' +
133
                row.patron_firstname + ' ' + row.patron_surname +
134
                '</a>';
135
        };
136
137
        // Render function for request ID
138
        var createRequestId = function(data, type, row) {
139
            return row.id_prefix + row.illrequest_id;
140
        };
141
142
        // Render function for request status
143
        var createStatus = function(data, type, row, meta) {
144
            var origData = meta.settings.oInit.originalData;
145
            if (origData.length > 0) {
146
                return meta.settings.oInit.originalData[0].capabilities[
147
                    row.status
148
                ].name;
149
            } else {
150
                return '';
151
            }
152
        };
153
154
        // Render function for creating a row's action link
155
        var createActionLink = function(data, type, row) {
156
            return '<a class="btn btn-default btn-sm" ' +
157
                'href="/cgi-bin/koha/ill/ill-requests.pl?' +
158
                'method=illview&amp;illrequest_id=' +
159
                row.illrequest_id +
160
                '">Manage request</a>' +
161
                '</div>'
162
        };
163
164
        // Columns that require special treatment
165
        var specialCols = {
166
            action: {
167
                name: '',
168
                func: createActionLink
169
            },
170
            borrowername: {
171
                name: 'Borrower',
172
                func: createBorrowerLink
173
            },
174
            illrequest_id: {
175
                name: 'Request number',
176
                func: createRequestId
177
            },
178
            status: {
179
                name: 'Status',
180
                func: createStatus
181
            },
182
            biblio_id: {
183
                name: 'Biblio number'
184
            },
185
            branchcode: {
186
                name: 'Branch code'
187
            }
188
        };
189
190
        // Helper for handling prefilter column names
191
        function toColumnName(myVal) {
192
            return myVal
193
                .replace(/^filter/, '')
194
                .replace(/([A-Z])/g, "_$1")
195
                .replace(/^_/,'').toLowerCase();
196
        };
197
198
        // Toggle request attributes in Illview
199
        $('#toggle_requestattributes').click(function() {
200
            $('#requestattributes').toggleClass('content_hidden');
201
        });
202
203
        // Filter partner list
204
        $('#partner_filter').keyup(function() {
205
            var needle = $('#partner_filter').val();
206
            $('#partners > option').each(function() {
207
                var regex = new RegExp(needle, 'i');
208
                if (
209
                    needle.length == 0 ||
210
                    $(this).is(':selected') ||
211
                    $(this).text().match(regex)
212
                ) {
213
                    $(this).show();
214
                } else {
215
                    $(this).hide();
216
                }
217
            });
218
        });
219
220
        // Get our data from the API and process it prior to passing
221
        // it to datatables
222
        var ajax = $.ajax(
223
            '/api/v1/illrequests?embed=metadata,patron,capabilities,branch'
224
            ).done(function() {
225
                var data = JSON.parse(ajax.responseText);
226
                // Make a copy, we'll be removing columns next and need
227
                // to be able to refer to data that has been removed
228
                var dataCopy = $.extend(true, [], data);
229
                // Remove all columns we're not interested in
230
                removeIgnore(dataCopy);
231
                // Expand columns that need it and create an array
232
                // of all column names
233
                $.each(dataCopy, function(k, row) {
234
                    expandExpand(row);
235
                    unionColumns(row);
236
                });
237
                // Append any extra columns we need to tag on
238
                if (extra.length > 0) {
239
                    extra.forEach(function(thisExtra) {
240
                        allCols[thisExtra] = 1;
241
                    });
242
                };
243
                // Different requests will have different columns,
244
                // make sure they all have the same
245
                $.each(dataCopy, function(k, row) {
246
                    fillMissing(row);
247
                });
248
249
                // Assemble an array of column definitions for passing
250
                // to datatables
251
                var colData = [];
252
                Object.keys(allCols).forEach(function(thisCol) {
253
                    // We may have defined a pretty name for this column
254
                    var colName = (
255
                        specialCols.hasOwnProperty(thisCol) &&
256
                        specialCols[thisCol].hasOwnProperty('name')
257
                    ) ?
258
                        specialCols[thisCol].name :
259
                        thisCol;
260
                    // Create the table header for this column
261
                    var str = '<th>' + stripPrefix(colName) + '</th>';
262
                    $(str).appendTo('#illview-header');
263
                    // Create the base column object
264
                    var colObj = {
265
                        name: thisCol,
266
                        className: thisCol
267
                    };
268
                    // We may need to process the data going in this
269
                    // column, so do it if necessary
270
                    if (
271
                        specialCols.hasOwnProperty(thisCol) &&
272
                        specialCols[thisCol].hasOwnProperty('func')
273
                    ) {
274
                        colObj.render = specialCols[thisCol].func;
275
                    } else {
276
                        colObj.data = thisCol
277
                    }
278
                    colData.push(colObj);
279
                });
280
281
                // Create the toggle links for all metadata fields
282
                var links = [];
283
                expanded.metadata.forEach(function(thisExpanded) {
284
                    if (core.indexOf(thisExpanded) == -1) {
285
                        links.push(
286
                            '<a href="#" class="toggle-vis" data-column="' +
287
                            thisExpanded + '">' + stripPrefix(thisExpanded) +
288
                            '</a>'
289
                        );
290
                    }
291
                });
292
                $('#column-toggle').append(links.join(' | '));
293
294
                // Initialise the datatable
295
                var myTable = $('#ill-requests').DataTable($.extend(true, {}, dataTablesDefaults, {
296
                    aoColumnDefs: [  // Last column shouldn't be sortable or searchable
297
                        {
298
                            aTargets: [ 'action' ],
299
                            bSortable: false,
300
                            bSearchable: false
301
                        },
302
                    ],
303
                    aaSorting: [[ 6, 'desc' ]], // Default sort, updated descending
304
                    processing: true, // Display a message when manipulating
305
                    language: {
306
                        loadingRecords: "Please wait - loading requests...",
307
                        zeroRecords: "No requests were found"
308
                    },
309
                    iDisplayLength: 10, // 10 results per page
310
                    sPaginationType: "full_numbers", // Pagination display
311
                    deferRender: true, // Improve performance on big datasets
312
                    data: dataCopy,
313
                    columns: colData,
314
                    originalData: data // Enable render functions to access
315
                                       // our original data
316
                }));
317
318
                // Reset columns to default
319
                var resetColumns = function() {
320
                    Object.keys(allCols).forEach(function(thisCol) {
321
                        myTable.column(thisCol + ':name').visible(core.indexOf(thisCol) != -1);
322
                    });
323
                    myTable.columns.adjust().draw(false);
324
                };
325
326
                // Handle the click event on a toggle link
327
                $('a.toggle-vis').on('click', function(e) {
328
                    e.preventDefault();
329
                    var column = myTable.column(
330
                        $(this).data('column') + ':name'
331
                    );
332
                    column.visible(!column.visible());
333
                });
334
335
                // Reset column toggling
336
                $('#reset-toggle').click(function() {
337
                    resetColumns();
338
                });
339
340
                // Handle a prefilter request and do the prefiltering
341
                var filters = $('#ill-requests').data();
342
                if (typeof filters !== 'undefined') {
343
                    var filterNames = Object.keys(filters).filter(
344
                        function(thisData) {
345
                            return thisData.match(/^filter/);
346
                        }
347
                    );
348
                    filterNames.forEach(function(thisFilter) {
349
                        var filterName = toColumnName(thisFilter) + ':name';
350
                        var regex = '^'+filters[thisFilter]+'$';
351
                        console.log(regex);
352
                        myTable.columns(filterName).search(regex, true, false);
353
                    });
354
                    myTable.draw();
355
                }
356
357
                // Initialise column hiding
358
                resetColumns();
359
360
            }
361
        );
362
363
    });
364
    //]]>
365
</script>
366
</head>
367
368
<body id="acq_suggestion" class="acq">
369
[% INCLUDE 'header.inc' %]
370
[% INCLUDE 'cat-search.inc' %]
371
372
<div id="breadcrumbs">
373
    <a href="/cgi-bin/koha/mainpage.pl">Home</a> &rsaquo;
374
    [% IF query_type == 'create' %]
375
        <a href=[% parent %]>ILL requests</a> &rsaquo; New request
376
    [% ELSIF query_type == 'status' %]
377
        <a href=[% parent %]>ILL requests</a> &rsaquo; Status
378
    [% ELSE %]
379
        ILL requests
380
    [% END %]
381
</div>
382
383
<div id="doc3" class="yui-t2">
384
    <div id="bd">
385
        <div id="yui-main">
386
            <div id="interlibraryloans" class="yui-b">
387
                [% INCLUDE 'ill-toolbar.inc' %]
388
389
                [% IF whole.error %]
390
                    <h1>Error performing operation</h1>
391
                    <!-- Dispatch on Status -->
392
                    <p>We encountered an error:</p>
393
                    <p>
394
                      <pre>[% whole.message %] ([% whole.status %])</pre>
395
                    </p>
396
                [% END %]
397
398
                [% IF query_type == 'create' %]
399
                    <h1>New ILL request</h1>
400
                    [% IF whole.stage == 'copyrightclearance' %]
401
                        <div>
402
                            <p>
403
                                [% Koha.Preference('ILLModuleCopyrightClearance') %]
404
                            </p>
405
                            <a href="?method=create&stage=copyrightclearance&backend=[% whole.value.backend %]"
406
                               class="btn btn-sm btn-default btn-group"><i class="fa fa-check">Yes</i></a>
407
                            <a href="/cgi-bin/koha/ill/ill-requests.pl"
408
                               class="btn btn-sm btn-default btn-group"><i class="fa fa-times">No</i></a>
409
                        </div>
410
                    [% ELSE %]
411
                        [% PROCESS $whole.template %]
412
                    [% END %]
413
414
                [% ELSIF query_type == 'confirm' %]
415
                    <h1>Confirm ILL request</h1>
416
                    [% PROCESS $whole.template %]
417
418
                [% ELSIF query_type == 'cancel' and !whole.error %]
419
                    <h1>Cancel a confirmed request</h1>
420
                    [% PROCESS $whole.template %]
421
422
                [% ELSIF query_type == 'generic_confirm' %]
423
                    <h1>Place request with partner libraries</h1>
424
                    <!-- Start of GENERIC_EMAIL case -->
425
                    [% IF whole.value.partners %]
426
                       [% ill_url = here_link _ "?method=illview&illrequest_id=" _ request.illrequest_id %]
427
                        <form method="POST" action=[% here_link %]>
428
                            <fieldset class="rows">
429
                                <legend>Interlibrary loan request details</legend>
430
                                <ol>
431
                                    <li>
432
                                        <label for="partner_filter">Filter partner libraries:</label>
433
                                        <input type="text" id="partner_filter">
434
                                    </li>
435
                                    <li>
436
                                        <label for="partners">Select partner libraries:</label>
437
                                        <select size="5" multiple="true" id="partners"
438
                                                name="partners">
439
                                            [% FOREACH partner IN whole.value.partners %]
440
                                                <option value=[% partner.email %]>
441
                                                    [% partner.branchcode _ " - " _ partner.surname %]
442
                                                </option>
443
                                            [% END %]
444
                                        </select>
445
446
                                    </li>
447
                                    <li>
448
                                        <label for="subject">Subject Line</label>
449
                                        <input type="text" name="subject"
450
                                               id="subject" type="text"
451
                                               value="[% whole.value.draft.subject %]"/>
452
                                    </li>
453
                                    <li>
454
                                        <label for="body">Email text:</label>
455
                                        <textarea name="body" id="body" rows="20" cols="80">[% whole.value.draft.body %]</textarea>
456
                                    </li>
457
                                </ol>
458
                                <input type="hidden" value="generic_confirm" name="method">
459
                                <input type="hidden" value="draft" name="stage">
460
                                <input type="hidden" value="[% request.illrequest_id %]" name="illrequest_id">
461
                            </fieldset>
462
                            <fieldset class="action">
463
                                <input type="submit" class="btn btn-default" value="Send email"/>
464
                                <span><a href="[% ill_url %]" title="Return to request details">Cancel</a></span>
465
                            </fieldset>
466
                        </form>
467
                    [% ELSE %]
468
                        <fieldset class="rows">
469
                            <legend>Interlibrary loan request details</legend>
470
                            <p>No partners have been defined yet. Please create appropriate patron records (by default ILLLIBS category).</p>
471
                            <p>Be sure to provide email addresses for these patrons.</p>
472
                            <p><span><a href="[% ill_url %]" title="Return to request details">Cancel</a></span></p>
473
                        </fieldset>
474
                    [% END %]
475
                <!-- generic_confirm ends here -->
476
477
                [% ELSIF query_type == 'edit_action' %]
478
                    <form method="POST" action=[% here_link %]>
479
                        <fieldset class="rows">
480
                            <legend>Request details</legend>
481
                            <ol>
482
                                <li class="borrowernumber">
483
                                    <label for="borrowernumber">Borrower number:</label>
484
                                    <input name="borrowernumber" id="borrowernumber" type="text" value="[% request.borrowernumber %]">
485
                                </li>
486
                                <li class="biblio_id">
487
                                    <label for="biblio_id" class="biblio_id">Biblio number:</label>
488
                                    <input name="biblio_id" id="biblio_id" type="text" value="[% request.biblio_id %]">
489
                                </li>
490
                                <li class="branchcode">
491
                                    <label for="branchcode" class="branchcode">Branch:</label>
492
                                    <select name="branchcode" id="branch">
493
                                        [% FOREACH branch IN branches %]
494
                                            [% IF ( branch.branchcode == request.branchcode ) %]
495
                                                <option value="[% branch.branchcode %]" selected="selected">
496
                                                    [% branch.branchname %]
497
                                                </option>
498
                                            [% ELSE %]
499
                                                <option value="[% branch.branchcode %]">
500
                                                    [% branch.branchname %]
501
                                                </option>
502
                                            [% END %]
503
                                        [% END %]
504
                                    </select>
505
                                </li>
506
                                <li class="status">
507
                                    <label class="status">Status:</label>
508
                                    [% stat = request.status %]
509
                                    [% request.capabilities.$stat.name %]
510
                                </li>
511
                                <li class="updated">
512
                                    <label class="updated">Last updated:</label>
513
                                    [% request.updated %]
514
                                </li>
515
                                <li class="medium">
516
                                    <label class="medium">Request type:</label>
517
                                    [% request.medium %]
518
                                </li>
519
                                <li class="cost">
520
                                    <label class="cost">Cost:</label>
521
                                    [% request.cost %]
522
                                </li>
523
                                <li class="req_id">
524
                                    <label class="req_id">Request number:</label>
525
                                    [% request.id_prefix _ request.illrequest_id %]
526
                                </li>
527
                                <li class="notesstaff">
528
                                    <label for="notesstaff" class="notesstaff">Staff notes:</label>
529
                                    <textarea name="notesstaff" id="notesstaff" rows="5">[% request.notesstaff %]</textarea>
530
                                </li>
531
                                <li class="notesopac">
532
                                    <label for="notesopac" class="notesopac">Opac notes:</label>
533
                                    <textarea name="notesopac" id="notesopac" rows="5">[% request.notesopac %]</textarea>
534
                                </li>
535
                            </ol>
536
                        </fieldset>
537
                        <fieldset class="action">
538
                            <input type="hidden" value="edit_action" name="method">
539
                            <input type="hidden" value="form" name="stage">
540
                            <input type="hidden" value="[% request.illrequest_id %]" name="illrequest_id">
541
                            <input type="submit" value="Submit">
542
                            <a class="cancel" href="/cgi-bin/koha/ill/ill-requests.pl?method=illview&amp;illrequest_id=[% request.id %]">Cancel</a>
543
                        </fieldset>
544
                    </form>
545
546
                [% ELSIF query_type == 'delete_confirm' %]
547
548
                    <div class="dialog alert">
549
                        <h3>Are you sure you wish to delete this request?</h3>
550
                        <p>
551
                            <a class="btn btn-default btn-sm approve" href="?method=delete&amp;illrequest_id=[% request.id %]&amp;confirmed=1"><i class="fa fa-fw fa-check"></i>Yes</a>
552
                            <a class="btn btn-default btn-sm deny" href="?method=illview&amp;illrequest_id=[% request.id %]"><i class="fa fa-fw fa-remove"></i>No</a>
553
                        </p>
554
                    </div>
555
556
557
                [% ELSIF query_type == 'illview' %]
558
                    [% actions = request.available_actions %]
559
                    [% capabilities = request.capabilities %]
560
                    [% req_status = request.status %]
561
                    <h1>Manage ILL request</h1>
562
                    <div id="toolbar" class="btn-toolbar">
563
                        <a title="Edit request" id="ill-toolbar-btn-edit-action" class="btn btn-sm btn-default" href="/cgi-bin/koha/ill/ill-requests.pl?method=edit_action&amp;illrequest_id=[% request.illrequest_id %]">
564
                        <span class="fa fa-pencil"></span>
565
                        Edit request
566
                        </a>
567
                        [% FOREACH action IN actions %]
568
                            [% IF action.method != 0 %]
569
                                <a title="[% action.ui_method_name %]" id="ill-toolbar-btn-[% action.id | lower %]" class="btn btn-sm btn-default" href="/cgi-bin/koha/ill/ill-requests.pl?method=[% action.method %]&amp;illrequest_id=[% request.illrequest_id %]">
570
                                <span class="fa [% action.ui_method_icon %]"></span>
571
                                [% action.ui_method_name %]
572
                                </a>
573
                            [% END %]
574
                        [% END %]
575
                    </div>
576
                    <div id="ill-view-panel" class="panel panel-default">
577
                        <div class="panel-heading">
578
                            <h3>Request details</h3>
579
                        </div>
580
                        <div class="panel-body">
581
                            <h4>Details from library</h4>
582
                            <div class="rows">
583
                                <div class="orderid">
584
                                    <span class="label orderid">Order ID:</span>
585
                                    [% request.orderid || "N/A" %]
586
                                </div>
587
                                <div class="borrowernumber">
588
                                    <span class="label borrowernumber">Borrower:</span>
589
                                    [% borrowerlink = "/cgi-bin/koha/members/moremember.pl"
590
                                    _ "?borrowernumber=" _ request.patron.borrowernumber %]
591
                                    <a href="[% borrowerlink %]" title="View borrower details">
592
                                    [% request.patron.firstname _ " "
593
                                    _ request.patron.surname _ " ["
594
                                    _ request.patron.cardnumber
595
                                    _ "]" %]
596
                                    </a>
597
                                </div>
598
599
                                <div class="biblio_id">
600
                                    <span class="label biblio_id">Biblio number:</span>
601
                                    [% request.biblio_id || "N/A" %]
602
                                </div>
603
                                <div class="branchcode">
604
                                    <span class="label branchcode">Branch:</span>
605
                                    [% Branches.GetName(request.branchcode) %]
606
                                </div>
607
                                <div class="status">
608
                                    <span class="label status">Status:</span>
609
                                    [% capabilities.$req_status.name %]
610
                                </div>
611
                                <div class="updated">
612
                                    <span class="label updated">Last updated:</span>
613
                                    [% request.updated %]
614
                                </div>
615
                                <div class="medium">
616
                                    <span class="label medium">Request type:</span>
617
                                    [% request.medium %]
618
                                </div>
619
                                <div class="cost">
620
                                    <span class="label cost">Cost:</span>
621
                                    [% request.cost || "N/A" %]
622
                                </div>
623
                                <div class="req_id">
624
                                    <span class="label req_id">Request number:</span>
625
                                    [% request.id_prefix _ request.illrequest_id %]
626
                                </div>
627
                                <div class="notesstaff">
628
                                    <span class="label notes_staff">Staff notes:</span>
629
                                    <pre>[% request.notesstaff %]</pre>
630
                                </div>
631
                                <div class="notesopac">
632
                                    <span class="label notes_opac">Notes:</span>
633
                                    <pre>[% request.notesopac %]</pre>
634
                                </div>
635
                            </div>
636
                            <div class="rows">
637
                                <h4>Details from supplier ([% request.backend %])</h4>
638
                                [% FOREACH meta IN request.metadata %]
639
                                    <div class="requestmeta-[% meta.key %]">
640
                                        <span class="label">[% meta.key %]:</span>
641
                                        [% meta.value %]
642
                                    </div>
643
                                [% END %]
644
                            </div>
645
                            <div class="rows">
646
                                <h3><a id="toggle_requestattributes" href="#">Toggle full supplier metadata</a></h3>
647
                                <div id="requestattributes" class="content_hidden">
648
                                    [% FOREACH attr IN request.illrequestattributes %]
649
                                        <div class="requestattr-[% attr.type %]">
650
                                            <span class="label">[% attr.type %]:</span>
651
                                            [% attr.value %]
652
                                        </div>
653
                                    [% END %]
654
                                </div>
655
656
                            </div>
657
                        </div>
658
                    </div>
659
660
                [% ELSIF query_type == 'illlist' %]
661
                    <!-- illlist -->
662
                    <h1>View ILL requests</h1>
663
                    <div id="results">
664
                        <h3>Details for all requests</h3>
665
666
                        <div id="column-toggle">
667
                            Toggle additional columns:
668
                        </div>
669
                        <div id="reset-toggle"><a href="#">Reset toggled columns</a></div>
670
671
                        <table
672
                            [% FOREACH filter IN prefilters %]
673
                            data-filter-[% filter.name %]="[% filter.value %]"
674
                            [% END %]
675
                            id="ill-requests">
676
                            <thead>
677
                                <tr id="illview-header"></tr>
678
                            </thead>
679
                            <tbody id="illview-body">
680
                            </tbody>
681
                        </table>
682
                    </div>
683
                [% ELSE %]
684
                <!-- Custom Backend Action -->
685
                [% INCLUDE $whole.template %]
686
687
                [% END %]
688
            </div>
689
        </div>
690
    </div>
691
</div>
692
693
[% TRY %]
694
[% PROCESS backend_jsinclude %]
695
[% CATCH %]
696
[% END %]
697
698
[% INCLUDE 'intranet-bottom.inc' %]
(-)a/koha-tmpl/intranet-tmpl/prog/en/modules/intranet-main.tt (+5 lines)
Lines 63-68 Link Here
63
                    <li>
63
                    <li>
64
                        <a class="icon_general icon_authorities" href="/cgi-bin/koha/authorities/authorities-home.pl">Authorities</a>
64
                        <a class="icon_general icon_authorities" href="/cgi-bin/koha/authorities/authorities-home.pl">Authorities</a>
65
                    </li>
65
                    </li>
66
                    [% IF Koha.Preference('ILLModule') %]
67
                    <li>
68
                        <a class="icon_general icon_ill" href="/cgi-bin/koha/ill/ill-requests.pl">ILL requests</a>
69
                    </li>
70
                    [% END %]
66
                </ul>
71
                </ul>
67
            </div><!-- /area-list-left -->
72
            </div><!-- /area-list-left -->
68
        </div><!-- /yui-u first -->
73
        </div><!-- /yui-u first -->
(-)a/koha-tmpl/opac-tmpl/bootstrap/en/includes/usermenu.inc (+9 lines)
Lines 104-109 Link Here
104
                [% END %]
104
                [% END %]
105
                <a href="/cgi-bin/koha/opac-discharge.pl">ask for a discharge</a></li>
105
                <a href="/cgi-bin/koha/opac-discharge.pl">ask for a discharge</a></li>
106
            [% END %]
106
            [% END %]
107
108
            [% IF Koha.Preference( 'ILLModule' ) == 1 %]
109
                [% IF ( illrequestsview ) %]
110
                    <li class="active">
111
                [% ELSE %]
112
                    <li>
113
                [% END %]
114
                <a href="/cgi-bin/koha/opac-illrequests.pl">your interlibrary loan requests</a></li>
115
            [% END %]
107
        </ul>
116
        </ul>
108
    </div>
117
    </div>
109
[% END %]
118
[% END %]
(-)a/koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-illrequests.tt (+221 lines)
Line 0 Link Here
1
[% USE Koha %]
2
[% USE Branches %]
3
[% INCLUDE 'doc-head-open.inc' %]
4
<title>[% IF ( LibraryNameTitle ) %][% LibraryNameTitle %][% ELSE %]Koha online[% END %] catalog &rsaquo;   Your Interlibrary loan requests</title>[% INCLUDE 'doc-head-close.inc' %]
5
[% BLOCK cssinclude %][% END %]
6
</head>
7
[% INCLUDE 'bodytag.inc' bodyid='opac-illrequests' bodyclass='scrollto' %]
8
[% BLOCK messages %]
9
    [% IF message == "1" %]
10
        <div class="alert alert-success" role="alert">Request updated</div>
11
    [% ELSIF message == "2" %]
12
        <div class="alert alert-success" role="alert">Request placed</div>
13
    [% END %]
14
[% END %]
15
[% INCLUDE 'masthead.inc' %]
16
<div class="main">
17
    <ul class="breadcrumb noprint">
18
        <li><a href="/cgi-bin/koha/opac-main.pl">Home</a> <span class="divider">&rsaquo;</span></li>
19
        [% IF ( loggedinusername ) %]
20
            <li><a href="/cgi-bin/koha/opac-user.pl">[% USER_INFO.title %] [% USER_INFO.firstname %] [% USER_INFO.surname %]</a> <span class="divider">&rsaquo;</span></li>
21
        [% END %]
22
23
        [% IF method != 'list' %]
24
            <li><a href="/cgi-bin/koha/opac-illrequests.pl">Interlibrary loan requests</a> <span class="divider">&rsaquo;</span></li>
25
            [% IF method == 'create' %]
26
                <li>New Interlibrary loan request</li>
27
            [% ELSIF method == 'view' %]
28
                <li>View Interlibrary loan request</li>
29
            [% END %]
30
        [% ELSE %]
31
            <li>Interlibrary loan requests</li>
32
        [% END %]
33
34
    </ul> <!-- / .breadcrumb -->
35
36
<div class="container-fluid">
37
    <div class="row-fluid">
38
        [% IF ( OpacNav||loggedinusername ) && !print %]
39
            <div class="span2">
40
                <div id="navigation">
41
                    [% INCLUDE 'navigation.inc' IsPatronPage=1 %]
42
                </div>
43
            </div>
44
        [% END %]
45
46
        [% IF ( OpacNav||loggedinusername ) %]
47
            <div class="span10">
48
        [% ELSE %]
49
            <div class="span12">
50
        [% END %]
51
            <div id="illrequests" class="maincontent">
52
                [% IF method == 'create' %]
53
                    <h2>New Interlibrary loan request</h2>
54
                    [% INCLUDE messages %]
55
                    [% IF backends %]
56
                        <form method="post" id="illrequestcreate-form" novalidate="novalidate">
57
                            <fieldset class="rows">
58
                                <label for="backend">Provider:</label>
59
                                <select name="backend">
60
                                    [% FOREACH backend IN backends %]
61
                                        <option value="[% backend %]">[% backend %]</option>
62
                                    [% END %]
63
                                </select>
64
                            </fieldset>
65
                            <fieldset class="action">
66
                                <input type="hidden" name="method" value="create">
67
                                <input type="submit" name="create_select_backend" value="Next &raquo;">
68
                            </fieldset>
69
                        </form>
70
                    [% ELSE %]
71
                        [% PROCESS $whole.opac_template %]
72
                    [% END %]
73
                [% ELSIF method == 'list' %]
74
                    <h2>Interlibrary loan requests</h2>
75
                    [% INCLUDE messages %]
76
77
                    <div id="illrequests-create-button" class="dropdown btn-group">
78
                        [% IF backends.size > 1 %]
79
                                <button class="btn btn-default dropdown-toggle" type="button" id="ill-backend-dropdown" data-toggle="dropdown" aria-haspopup="true" aria-expanded="true">
80
                                    <i class="fa fa-plus"></i> Create a new request <span class="caret"></span>
81
                                </button>
82
                                <ul id="backend-dropdown-options" class="dropdown-menu nojs" aria-labelledby="ill-backend-dropdown">
83
                                    [% FOREACH backend IN backends %]
84
                                        <li><a href="/cgi-bin/koha/opac-illrequests.pl?method=create&amp;backend=[% backend %]">[% backend %]</a></li>
85
                                    [% END %]
86
                                </ul>
87
                        [% ELSE %]
88
                            <a id="ill-new" class="btn btn-default" href="/cgi-bin/koha/opac-illrequests.pl?method=create&amp;backend=[% backends.0 %]">
89
                                <i class="fa fa-plus"></i> Create a new request
90
                            </a>
91
                        [% END %]
92
                    </div>
93
94
                    <table id="illrequestlist" class="table table-bordered table-striped">
95
                        <thead>
96
                            <tr>
97
                                <th>Author</th>
98
                                <th>Title</th>
99
                                <th>Requested from</th>
100
                                <th>Request type</th>
101
                                <th>Status</th>
102
                                <th>Request placed</th>
103
                                <th>Last updated</th>
104
                                <th></th>
105
                            </tr>
106
                        </thead>
107
                        <tbody>
108
                            [% FOREACH request IN requests %]
109
                                [% status = request.status %]
110
                                <tr>
111
                                    <td>[% request.metadata.Author || 'N/A' %]</td>
112
                                    <td>[% request.metadata.Title || 'N/A' %]</td>
113
                                    <td>[% request.backend %]</td>
114
                                    <td>[% request.medium %]</td>
115
                                    <td>[% request.capabilities.$status.name %]</td>
116
                                    <td>[% request.placed %]</td>
117
                                    <td>[% request.updated %]</td>
118
                                    <td>
119
                                        <a href="/cgi-bin/koha/opac-illrequests.pl?method=view&amp;illrequest_id=[% request.id %]" class="btn btn-default btn-small pull-right">View</a>
120
                                    </td>
121
                                </tr>
122
                            [% END %]
123
                        </tbody>
124
                    </table>
125
                [% ELSIF method == 'view' %]
126
                    <h2>View Interlibrary loan request</h2>
127
                    [% INCLUDE messages %]
128
                    [% status = request.status %]
129
                    <form method="post" action="?method=update" id="illrequestupdate-form" novalidate="novalidate">
130
                            <fieldset class="rows">
131
                                <legend id="library_legend">Details from library</legend>
132
                                <ol>
133
                                    <li>
134
                                        <label for="backend">Requested from:</label>
135
                                        [% request.backend %]
136
                                    </li>
137
                                    [% IF request.biblio_id %]
138
                                        <li>
139
                                            <label for="biblio">Requested item:</label>
140
                                            <a href="/cgi-bin/koha/opac-detail.pl?biblionumber=[% request.biblio_id %]">Click here to view</a>
141
                                        </li>
142
                                    [% END %]
143
                                    <li>
144
                                        <label for="branchcode">Collection library:</label>
145
                                        [% Branches.GetName(request.branchcode) %]
146
                                    </li>
147
                                    <li>
148
                                        <label for="status">Status:</label>
149
                                        [% request.capabilities.$status.name %]
150
                                    </li>
151
                                    <li>
152
                                        <label for="medium">Request type:</label>
153
                                        [% request.medium %]
154
                                    </li>
155
                                    <li>
156
                                        <label for="placed">Request placed:</label>
157
                                        [% request.placed %]
158
                                    </li>
159
                                    <li>
160
                                        <label for="updated">Last updated:</label>
161
                                        [% request.updated %]
162
                                    </li>
163
                                    <li>
164
                                        <label for="notesopac">Notes:</label>
165
                                        [% IF !request.completed %]
166
                                            <textarea name="notesopac" rows="5" cols="50">[% request.notesopac %]</textarea>
167
                                        [% ELSE %]
168
                                            [% request.notesopac %]
169
                                        [% END %]
170
                                    </li>
171
                                </ol>
172
                            </fieldset>
173
                            <div class="rows">
174
                                <legend id="backend_legend">Details from [% request.backend %]</legend>
175
                                [% FOREACH meta IN request.metadata %]
176
                                    <div class="requestattr-[% meta.key %]">
177
                                        <span class="label">[% meta.key %]:</span>
178
                                        [% meta.value || 'N/A' %]
179
                                    </div>
180
                                [% END %]
181
                            </div>
182
                            <fieldset class="action illrequest-actions">
183
                                <input type="hidden" name="illrequest_id" value="[% request.illrequest_id %]">
184
                                <input type="hidden" name="method" value="update">
185
                                [% IF !request.completed %]
186
                                    [% IF request.status == "NEW" %]
187
                                        <a class="cancel-illrequest btn btn-danger" href="/cgi-bin/koha/opac-illrequests.pl?method=cancreq&amp;illrequest_id=[% request.illrequest_id %]">Request cancellation</a>
188
                                    [% END %]
189
                                    <input type="submit" class="update-illrequest btn btn-default" value="Submit modifications">
190
                                [% END %]
191
                                <span class="cancel"><a href="/cgi-bin/koha/opac-illrequests.pl">Cancel</a></span>
192
                            </fieldset>
193
                        </form>
194
                    [% END %]
195
                </div> <!-- / .maincontent -->
196
            </div> <!-- / .span10/12 -->
197
        </div> <!-- / .row-fluid -->
198
    </div> <!-- / .container-fluid -->
199
</div> <!-- / .main -->
200
201
[% INCLUDE 'opac-bottom.inc' %]
202
203
[% BLOCK jsinclude %]
204
[% INCLUDE 'datatables.inc' %]
205
<script type="text/javascript">
206
    //<![CDATA[
207
        $("#illrequestlist").dataTable($.extend(true, {}, dataTablesDefaults, {
208
            "aoColumnDefs": [
209
                { "aTargets": [ -1 ], "bSortable": false, "bSearchable": false }
210
            ],
211
            "aaSorting": [[ 3, "desc" ]],
212
            "deferRender": true
213
        }));
214
        $("#backend-dropdown-options").removeClass("nojs");
215
    //]]>
216
</script>
217
[% TRY %]
218
[% PROCESS backend_jsinclude %]
219
[% CATCH %]
220
[% END %]
221
[% END %]
(-)a/koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-results-grouped.tt (-7 / +23 lines)
Lines 253-266 href="/cgi-bin/koha/opac-rss.pl?[% query_cgi %][% limit_cgi |html %]" /> Link Here
253
                            [% INCLUDE 'page-numbers.inc' %]
253
                            [% INCLUDE 'page-numbers.inc' %]
254
                        [% END # / IF total %]
254
                        [% END # / IF total %]
255
255
256
                        [% IF Koha.Preference( 'suggestion' ) == 1 %]
256
                        [% IF
257
                            [% IF Koha.Preference( 'AnonSuggestions' ) == 1 %]
257
                            Koha.Preference( 'suggestion' ) == 1 &&
258
                                <div class="suggestion">Not finding what you're looking for?<br />  Make a <a href="/cgi-bin/koha/opac-suggestions.pl?op=add">purchase suggestion</a></div>
258
                            (
259
                            [% ELSE %]
259
                                Koha.Preference( 'AnonSuggestions' ) == 1 ||
260
                                [% IF ( loggedinusername ) %]<div class="suggestion">Not finding what you're looking for?<br />  Make a <a href="/cgi-bin/koha/opac-suggestions.pl?op=add">purchase suggestion</a></div>[% END %]
260
                                loggedinusername ||
261
                            [% END %]
261
                                Koha.Preference( 'ILLModule' ) == 1
262
                            )
263
                        %]
264
                            <div class="suggestion">
265
                                Not finding what you're looking for?
266
                                <ul>
267
                                    [% IF Koha.Preference( 'AnonSuggestions' ) == 1 %]
268
                                        <li>Make a <a href="/cgi-bin/koha/opac-suggestions.pl?op=add">purchase suggestion</a></li>
269
                                    [% ELSE %]
270
                                        [% IF ( loggedinusername ) %]
271
                                            <li>Make a <a href="/cgi-bin/koha/opac-suggestions.pl?op=add">purchase suggestion</a></li>
272
                                        [% END %]
273
                                    [% END %]
274
                                    [% IF Koha.Preference( 'ILLModule' ) == 1 && loggedinusername %]
275
                                        <li>Make an <a href="/cgi-bin/koha/opac-illrequests.pl?op=create">Interlibrary loan request</a></li>
276
                                    [% END %]
277
                                </ul>
278
                            </div>
262
                        [% END %]
279
                        [% END %]
263
264
                    </div> <!-- / #grouped-results -->
280
                    </div> <!-- / #grouped-results -->
265
                </div> <!-- /.span10/12 -->
281
                </div> <!-- /.span10/12 -->
266
            </div> <!-- / .row-fluid -->
282
            </div> <!-- / .row-fluid -->
(-)a/koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-results.tt (-10 / +23 lines)
Lines 562-577 Link Here
562
562
563
                    [% END # / IF total %]
563
                    [% END # / IF total %]
564
564
565
                    [% IF Koha.Preference( 'suggestion' ) == 1 %]
565
                    [% IF
566
                        [% IF Koha.Preference( 'AnonSuggestions' ) == 1 %]
566
                        Koha.Preference( 'suggestion' ) == 1 &&
567
                            <div class="suggestion">Not finding what you're looking for?<br />  Make a <a href="/cgi-bin/koha/opac-suggestions.pl?op=add">purchase suggestion</a></div>
567
                        (
568
                        [% ELSE %]
568
                            Koha.Preference( 'AnonSuggestions' ) == 1 ||
569
                            [% IF ( loggedinusername ) %]
569
                            loggedinusername ||
570
                                <div class="suggestion">
570
                            Koha.Preference( 'ILLModule' ) == 1
571
                                    Not finding what you're looking for?<br />  Make a <a href="/cgi-bin/koha/opac-suggestions.pl?op=add">purchase suggestion</a>
571
                        )
572
                                </div>
572
                    %]
573
                            [% END %]
573
                        <div class="suggestion">
574
                        [% END %]
574
                            Not finding what you're looking for?
575
                            <ul>
576
                                [% IF Koha.Preference( 'AnonSuggestions' ) == 1 %]
577
                                    <li>Make a <a href="/cgi-bin/koha/opac-suggestions.pl?op=add">purchase suggestion</a></li>
578
                                [% ELSE %]
579
                                    [% IF ( loggedinusername ) %]
580
                                        <li>Make a <a href="/cgi-bin/koha/opac-suggestions.pl?op=add">purchase suggestion</a></li>
581
                                    [% END %]
582
                                [% END %]
583
                                [% IF Koha.Preference( 'ILLModule' ) == 1 && loggedinusername %]
584
                                    <li>Make an <a href="/cgi-bin/koha/opac-illrequests.pl?op=create">Interlibrary loan request</a></li>
585
                                [% END %]
586
                            </ul>
587
                        </div>
575
                    [% END %]
588
                    [% END %]
576
                    </div> <!-- / #userresults -->
589
                    </div> <!-- / #userresults -->
577
                </div> <!-- /.span10/12 -->
590
                </div> <!-- /.span10/12 -->
(-)a/koha-tmpl/opac-tmpl/bootstrap/less/opac.less (+38 lines)
Lines 2508-2513 a.reviewlink:visited { Link Here
2508
    font-size: 90%;
2508
    font-size: 90%;
2509
}
2509
}
2510
2510
2511
#illrequests {
2512
    .illrequest-actions {
2513
        .btn,
2514
        .cancel {
2515
            margin-right: 5px;
2516
        }
2517
        padding-top: 20px;
2518
        margin-bottom: 20px;
2519
    }
2520
    #illrequests-create-button {
2521
        margin-bottom: 20px;
2522
    }
2523
    .bg-info {
2524
        overflow: auto;
2525
        position: relative;
2526
    }
2527
    .bg-info {
2528
        #search-summary {
2529
            -webkit-transform: translateY(-50%);
2530
            -ms-transform: translateY(-50%);
2531
            -o-transform: translateY(-50%);
2532
            transform: translateY(-50%);
2533
            position: absolute;
2534
            top: 50%;
2535
        }
2536
2537
    }
2538
    #freeform-fields .custom-name {
2539
        float: left;
2540
        width: 8em;
2541
        margin-right: 1em;
2542
        text-align: right;
2543
    }
2544
    .dropdown:hover .dropdown-menu.nojs {
2545
        display: block;
2546
    }
2547
}
2548
2511
#dc_fieldset {
2549
#dc_fieldset {
2512
    border: 1px solid #dddddd;
2550
    border: 1px solid #dddddd;
2513
    border-width: 1px;
2551
    border-width: 1px;
(-)a/opac/opac-illrequests.pl (+129 lines)
Line 0 Link Here
1
#!/usr/bin/perl
2
3
# Copyright 2017 PTFS-Europe Ltd
4
#
5
# This file is part of Koha.
6
#
7
# Koha is free software; you can redistribute it and/or modify it
8
# under the terms of the GNU General Public License as published by
9
# the Free Software Foundation; either version 3 of the License, or
10
# (at your option) any later version.
11
#
12
# Koha is distributed in the hope that it will be useful, but
13
# WITHOUT ANY WARRANTY; without even the implied warranty of
14
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
# GNU General Public License for more details.
16
#
17
# You should have received a copy of the GNU General Public License
18
# along with Koha; if not, see <http://www.gnu.org/licenses>.
19
20
use Modern::Perl;
21
22
use CGI qw ( -utf8 );
23
use C4::Auth;
24
use C4::Koha;
25
use C4::Output;
26
27
use Koha::Illrequests;
28
use Koha::Libraries;
29
use Koha::Patrons;
30
31
my $query = new CGI;
32
33
# Grab all passed data
34
# 'our' since Plack changes the scoping
35
# of 'my'
36
our $params = $query->Vars();
37
38
# if illrequests is disabled, leave immediately
39
if ( ! C4::Context->preference('ILLModule') ) {
40
    print $query->redirect("/cgi-bin/koha/errors/404.pl");
41
    exit;
42
}
43
44
my ( $template, $loggedinuser, $cookie ) = get_template_and_user({
45
    template_name   => "opac-illrequests.tt",
46
    query           => $query,
47
    type            => "opac",
48
    authnotrequired => ( C4::Context->preference("OpacPublic") ? 1 : 0 ),
49
});
50
51
my $op = $params->{'method'} || 'list';
52
53
if ( $op eq 'list' ) {
54
55
    my $requests = Koha::Illrequests->search(
56
        { borrowernumber => $loggedinuser }
57
    );
58
    my $req = Koha::Illrequest->new;
59
    $template->param(
60
        requests => $requests,
61
        backends    => $req->available_backends
62
    );
63
64
} elsif ( $op eq 'view') {
65
    my $request = Koha::Illrequests->find({
66
        borrowernumber => $loggedinuser,
67
        illrequest_id  => $params->{illrequest_id}
68
    });
69
    $template->param(
70
        request => $request
71
    );
72
73
} elsif ( $op eq 'update') {
74
    my $request = Koha::Illrequests->find({
75
        borrowernumber => $loggedinuser,
76
        illrequest_id  => $params->{illrequest_id}
77
    });
78
    $request->notesopac($params->{notesopac})->store;
79
    print $query->redirect(
80
        '/cgi-bin/koha/opac-illrequests.pl?method=view&illrequest_id=' .
81
        $params->{illrequest_id} .
82
        '&message=1'
83
    );
84
} elsif ( $op eq 'cancreq') {
85
    my $request = Koha::Illrequests->find({
86
        borrowernumber => $loggedinuser,
87
        illrequest_id  => $params->{illrequest_id}
88
    });
89
    $request->status('CANCREQ')->store;
90
    print $query->redirect(
91
        '/cgi-bin/koha/opac-illrequests.pl?method=view&illrequest_id=' .
92
        $params->{illrequest_id} .
93
        '&message=1'
94
    );
95
96
} elsif ( $op eq 'create' ) {
97
    if (!$params->{backend}) {
98
        my $req = Koha::Illrequest->new;
99
        $template->param(
100
            backends    => $req->available_backends
101
        );
102
    } else {
103
        my $request = Koha::Illrequest->new
104
            ->load_backend($params->{backend});
105
        $params->{cardnumber} = Koha::Patrons->find({
106
            borrowernumber => $loggedinuser
107
        })->cardnumber;
108
        my $backend_result = $request->backend_create($params);
109
        $template->param(
110
            media       => [ "Book", "Article", "Journal" ],
111
            branches    => Koha::Libraries->search->unblessed,
112
            whole       => $backend_result,
113
            request     => $request
114
        );
115
        if ($backend_result->{stage} eq 'commit') {
116
            print $query->redirect('/cgi-bin/koha/opac-illrequests.pl?message=2');
117
        }
118
    }
119
120
121
}
122
123
$template->param(
124
    message         => $params->{message},
125
    illrequestsview => 1,
126
    method              => $op
127
);
128
129
output_html_with_http_headers $query, $cookie, $template->output;
(-)a/t/db_dependent/Illrequest/Config.t (+473 lines)
Line 0 Link Here
1
#!/usr/bin/perl
2
3
# This file is part of Koha.
4
#
5
# Koha is free software; you can redistribute it and/or modify it
6
# under the terms of the GNU General Public License as published by
7
# the Free Software Foundation; either version 3 of the License, or
8
# (at your option) any later version.
9
#
10
# Koha is distributed in the hope that it will be useful, but
11
# WITHOUT ANY WARRANTY; without even the implied warranty of
12
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
# GNU General Public License for more details.
14
#
15
# You should have received a copy of the GNU General Public License
16
# along with Koha; if not, see <http://www.gnu.org/licenses>.
17
18
use Modern::Perl;
19
20
use Koha::Database;
21
use t::lib::Mocks;
22
use t::lib::TestBuilder;
23
use Test::MockObject;
24
use Test::Exception;
25
26
use Test::More tests => 5;
27
28
my $schema = Koha::Database->new->schema;
29
my $builder = t::lib::TestBuilder->new;
30
use_ok('Koha::Illrequest::Config');
31
32
my $base_limits = {
33
    branch => { CPL => { count => 1, method => 'annual' } },
34
    brw_cat => { A => { count => -1, method => 'active' } },
35
    default => { count => 10, method => 'annual' },
36
};
37
38
my $base_censorship = { censor_notes_staff => 1, censor_reply_date => 1 };
39
40
subtest 'Basics' => sub {
41
42
    plan tests => 19;
43
44
    $schema->storage->txn_begin;
45
46
    t::lib::Mocks::mock_preference("UnmediatedILL", 0);
47
    t::lib::Mocks::mock_config("interlibrary_loans", {});
48
49
    my $config = Koha::Illrequest::Config->new;
50
    isa_ok($config, "Koha::Illrequest::Config",
51
           "Correctly create and load a config object.");
52
53
    # backend:
54
    is($config->backend, undef, "backend: Undefined backend is undefined.");
55
    is($config->backend("Mock"), "Mock", "backend: setter works.");
56
    is($config->backend, "Mock", "backend: setter is persistent.");
57
58
    # backend_dir:
59
    is($config->backend_dir, undef, "backend_dir: Undefined backend_dir is undefined.");
60
    is($config->backend_dir("/tmp/"), "/tmp/", "backend_dir: setter works.");
61
    is($config->backend_dir, "/tmp/", "backend_dir: setter is persistent.");
62
63
    # partner_code:
64
    is($config->partner_code, "ILLLIBS", "partner_code: Undefined partner_code is undefined.");
65
    is($config->partner_code("ILLLIBSTST"), "ILLLIBSTST", "partner_code: setter works.");
66
    is($config->partner_code, "ILLLIBSTST", "partner_code: setter is persistent.");
67
68
    # limits:
69
    is_deeply($config->limits, {}, "limits: Undefined limits is empty hash.");
70
    is_deeply($config->limits($base_limits), $base_limits, "limits: setter works.");
71
    is_deeply($config->limits, $base_limits, "limits: setter is persistent.");
72
73
    # censorship:
74
    is_deeply($config->censorship, { censor_notes_staff => 0, censor_reply_date => 0 },
75
              "censorship: Undefined censorship is default values.");
76
    is_deeply($config->censorship($base_censorship), $base_censorship, "censorship: setter works.");
77
    is_deeply($config->censorship, $base_censorship, "censorship: setter is persistent.");
78
79
    # getLimitRules
80
    dies_ok( sub { $config->getLimitRules("FOO") }, "getLimitRules: die if not correct type.");
81
    is_deeply($config->getLimitRules("brw_cat"), {
82
        A => { count => -1, method => 'active' },
83
        default => { count => 10, method => 'annual' },
84
    }, "getLimitRules: fetch brw_cat limits.");
85
    is_deeply($config->getLimitRules("branch"), {
86
        CPL => { count => 1, method => 'annual' },
87
        default => { count => 10, method => 'annual' },
88
    }, "getLimitRules: fetch brw_cat limits.");
89
90
    $schema->storage->txn_rollback;
91
};
92
93
# _load_unit_config:
94
95
subtest '_load_unit_config' => sub {
96
97
    plan tests => 10;
98
99
    $schema->storage->txn_begin;
100
101
    my $config = Koha::Illrequest::Config->new;
102
103
    dies_ok(
104
        sub { Koha::Illrequest::Config::_load_unit_config({
105
            id => 'durineadu', type => 'baz'
106
        }) },
107
        "_load_unit_config: die if ID is not default, and type is not branch or brw_cat."
108
    );
109
    is_deeply(
110
        Koha::Illrequest::Config::_load_unit_config({
111
            unit => {}, id => 'default', config => {}, test => 1
112
        }), {}, "_load_unit_config: invocation without id returns unmodified config."
113
    );
114
115
    is_deeply(
116
        Koha::Illrequest::Config::_load_unit_config({
117
            unit => { api_key => 'foo', api_auth => 'bar' },
118
            id => "CPL", type => 'branch', config => {}
119
        }),
120
        { credentials => { api_keys => { CPL => { api_key => 'foo', api_auth => 'bar' } } } },
121
        "_load_unit_config: add auth values."
122
    );
123
124
    # Populate request_limits
125
    is_deeply(
126
        Koha::Illrequest::Config::_load_unit_config({
127
            unit => { request_limit => [ 'heelo', 1234 ] },
128
            id => "CPL", type => 'branch', config => {}
129
        }), {}, "_load_unit_config: invalid request_limit structure."
130
    );
131
    is_deeply(
132
        Koha::Illrequest::Config::_load_unit_config({
133
            unit => { request_limit => { method => 'eudiren', count => '-5465' } },
134
            id => "CPL", type => 'branch', config => {}
135
        }), {}, "_load_unit_config: invalid method & count."
136
    );
137
    is_deeply(
138
        Koha::Illrequest::Config::_load_unit_config({
139
            unit => { request_limit => { method => 'annual', count => 6 } },
140
            id => "default", config => {}
141
        }),
142
        { limits => { default => { method => 'annual', count => 6 } } },
143
        "_load_unit_config: correct default request_limits."
144
    );
145
146
    # Populate prefix
147
    is_deeply(
148
        Koha::Illrequest::Config::_load_unit_config({
149
            unit => { prefix => 'Foo-ill' },
150
            id => "default", config => {}
151
        }),
152
        { prefixes => { default => 'Foo-ill' } },
153
        "_load_unit_config: correct default prefix."
154
    );
155
    is_deeply(
156
        Koha::Illrequest::Config::_load_unit_config({
157
            unit => { prefix => 'Foo-ill' },
158
            id => "A", config => {}, type => 'brw_cat'
159
        }),
160
        { prefixes => { brw_cat => { A => 'Foo-ill' } } },
161
        "_load_unit_config: correct brw_cat prefix."
162
    );
163
164
    # Populate digital_recipient
165
    is_deeply(
166
        Koha::Illrequest::Config::_load_unit_config({
167
            unit => { digital_recipient => 'borrower' },
168
            id => "default", config => {}
169
        }),
170
        { digital_recipients => { default => 'borrower' } },
171
        "_load_unit_config: correct default digital_recipient."
172
    );
173
    is_deeply(
174
        Koha::Illrequest::Config::_load_unit_config({
175
            unit => { digital_recipient => 'branch' },
176
            id => "A", config => {}, type => 'brw_cat'
177
        }),
178
        { digital_recipients => { brw_cat => { A => 'branch' } } },
179
        "_load_unit_config: correct brw_cat digital_recipient."
180
    );
181
182
    $schema->storage->txn_rollback;
183
};
184
185
# _load_configuration:
186
187
# We have already tested _load_unit_config, so we are reasonably confident
188
# that the per-branch, per-borrower_category & default sections parsing is
189
# good.
190
#
191
# Now we need to ensure that Arrays & Hashes are handled correctly, that
192
# censorship & ill partners are loaded correctly and that the backend
193
# directory is set correctly.
194
195
subtest '_load_configuration' => sub {
196
197
    plan tests => 9;
198
199
    $schema->storage->txn_begin;
200
201
    my $config = Koha::Illrequest::Config->new;
202
203
    # Return basic configuration
204
    is_deeply(
205
        Koha::Illrequest::Config::_load_configuration({}, 0),
206
        {
207
            backend_directory  => undef,
208
            censorship         => {
209
                censor_notes_staff => 0,
210
                censor_reply_date => 0,
211
            },
212
            limits             => {},
213
            digital_recipients => {},
214
            prefixes           => {},
215
            partner_code       => 'ILLLIBS',
216
            raw_config         => {},
217
        },
218
        "load_configuration: return the base configuration."
219
    );
220
221
    # Return correct backend_dir
222
    is_deeply(
223
        Koha::Illrequest::Config::_load_configuration({ backend_directory => '/tmp/' }, 0),
224
        {
225
            backend_directory  => '/tmp/',
226
            censorship         => {
227
                censor_notes_staff => 0,
228
                censor_reply_date => 0,
229
            },
230
            limits             => {},
231
            digital_recipients => {},
232
            prefixes           => {},
233
            partner_code       => 'ILLLIBS',
234
            raw_config         => { backend_directory => '/tmp/' },
235
        },
236
        "load_configuration: return the correct backend_dir."
237
    );
238
239
    # Map over branch configs
240
    my $xml_config = {
241
        backend_directory => '/tmp/',
242
        branch => [
243
            { code => '1', request_limit => { method => 'annual', count => 1 } },
244
            { code => '2', prefix => '2-prefix' },
245
            { code => '3', digital_recipient => 'branch' }
246
        ]
247
    };
248
    is_deeply(
249
        Koha::Illrequest::Config::_load_configuration($xml_config, 0),
250
        {
251
            backend_directory  => '/tmp/',
252
            censorship         => {
253
                censor_notes_staff => 0,
254
                censor_reply_date => 0,
255
            },
256
            limits             => { branch => { 1 => { method => 'annual', count => 1 } } },
257
            digital_recipients => { branch => { 3 => 'branch' } },
258
            prefixes           => { branch => { 2 => '2-prefix' } },
259
            partner_code       => 'ILLLIBS',
260
            raw_config         => $xml_config,
261
        },
262
        "load_configuration: multi branch config parsed correctly."
263
    );
264
    # Single branch config
265
    $xml_config = {
266
        backend_directory => '/tmp/',
267
        branch => {
268
            code => '1',
269
            request_limit => { method => 'annual', count => 1 },
270
            prefix => '2-prefix',
271
            digital_recipient => 'branch',
272
        }
273
    };
274
    is_deeply(
275
        Koha::Illrequest::Config::_load_configuration($xml_config, 0),
276
        {
277
            backend_directory  => '/tmp/',
278
            censorship         => {
279
                censor_notes_staff => 0,
280
                censor_reply_date => 0,
281
            },
282
            limits             => { branch => { 1 => { method => 'annual', count => 1 } } },
283
            digital_recipients => { branch => { 1 => 'branch' } },
284
            prefixes           => { branch => { 1 => '2-prefix' } },
285
            partner_code       => 'ILLLIBS',
286
            raw_config         => $xml_config,
287
        },
288
        "load_configuration: single branch config parsed correctly."
289
    );
290
291
    # Map over borrower_category settings
292
    $xml_config = {
293
        backend_directory => '/tmp/',
294
        borrower_category => [
295
            { code => 'A', request_limit => { method => 'annual', count => 1 } },
296
            { code => 'B', prefix => '2-prefix' },
297
            { code => 'C', digital_recipient => 'branch' }
298
        ]
299
    };
300
    is_deeply(
301
        Koha::Illrequest::Config::_load_configuration($xml_config, 0),
302
        {
303
            backend_directory  => '/tmp/',
304
            censorship         => {
305
                censor_notes_staff => 0,
306
                censor_reply_date => 0,
307
            },
308
            limits             => { brw_cat => { A => { method => 'annual', count => 1 } } },
309
            digital_recipients => { brw_cat => { C => 'branch' } },
310
            prefixes           => { brw_cat => { B => '2-prefix' } },
311
            partner_code       => 'ILLLIBS',
312
            raw_config         => $xml_config,
313
        },
314
        "load_configuration: multi borrower_category config parsed correctly."
315
    );
316
    # Single borrower_category config
317
    $xml_config = {
318
        backend_directory => '/tmp/',
319
        borrower_category => {
320
            code => '1',
321
            request_limit => { method => 'annual', count => 1 },
322
            prefix => '2-prefix',
323
            digital_recipient => 'branch',
324
        }
325
    };
326
    is_deeply(
327
        Koha::Illrequest::Config::_load_configuration($xml_config, 0),
328
        {
329
            backend_directory  => '/tmp/',
330
            censorship         => {
331
                censor_notes_staff => 0,
332
                censor_reply_date => 0,
333
            },
334
            limits             => { brw_cat => { 1 => { method => 'annual', count => 1 } } },
335
            digital_recipients => { brw_cat => { 1 => 'branch' } },
336
            prefixes           => { brw_cat => { 1 => '2-prefix' } },
337
            partner_code       => 'ILLLIBS',
338
            raw_config         => $xml_config,
339
        },
340
        "load_configuration: single borrower_category config parsed correctly."
341
    );
342
343
    # Default Configuration
344
    $xml_config = {
345
        backend_directory => '/tmp/',
346
        request_limit => { method => 'annual', count => 1 },
347
        prefix => '2-prefix',
348
        digital_recipient => 'branch',
349
    };
350
    is_deeply(
351
        Koha::Illrequest::Config::_load_configuration($xml_config, 0),
352
        {
353
            backend_directory  => '/tmp/',
354
            censorship         => {
355
                censor_notes_staff => 0,
356
                censor_reply_date => 0,
357
            },
358
            limits             => { default => { method => 'annual', count => 1 } },
359
            digital_recipients => { default => 'branch' },
360
            prefixes           => { default => '2-prefix' },
361
            partner_code       => 'ILLLIBS',
362
            raw_config         => $xml_config,
363
        },
364
        "load_configuration: parse the default configuration."
365
    );
366
367
    # Censorship
368
    $xml_config = {
369
        backend_directory => '/tmp/',
370
        staff_request_comments => 'hide',
371
        reply_date => 'hide'
372
    };
373
    is_deeply(
374
        Koha::Illrequest::Config::_load_configuration($xml_config, 0),
375
        {
376
            backend_directory  => '/tmp/',
377
            censorship         => {
378
                censor_notes_staff => 1,
379
                censor_reply_date => 1,
380
            },
381
            limits             => {},
382
            digital_recipients => {},
383
            prefixes           => {},
384
            partner_code       => 'ILLLIBS',
385
            raw_config         => $xml_config,
386
        },
387
        "load_configuration: parse censorship settings configuration."
388
    );
389
390
    # Partner library category
391
    is_deeply(
392
        Koha::Illrequest::Config::_load_configuration({ partner_code => 'FOOBAR' }),
393
        {
394
            backend_directory  => undef,
395
            censorship         => {
396
                censor_notes_staff => 0,
397
                censor_reply_date => 0,
398
            },
399
            limits             => {},
400
            digital_recipients => {},
401
            prefixes           => {},
402
            partner_code       => 'FOOBAR',
403
            raw_config         => { partner_code => 'FOOBAR' },
404
        },
405
        "load_configuration: Set partner code."
406
    );
407
408
    $schema->storage->txn_rollback;
409
};
410
411
412
subtest 'Final tests' => sub {
413
414
    plan tests => 10;
415
416
    $schema->storage->txn_begin;
417
418
    t::lib::Mocks::mock_preference("UnmediatedILL", 0);
419
    t::lib::Mocks::mock_config("interlibrary_loans", {});
420
421
    my $config = Koha::Illrequest::Config->new;
422
423
    # getPrefixes (error & undef):
424
    dies_ok( sub { $config->getPrefixes("FOO") }, "getPrefixes: die if not correct type.");
425
    is_deeply($config->getPrefixes("brw_cat"), { default => undef},
426
              "getPrefixes: Undefined brw_cat prefix is undefined.");
427
    is_deeply($config->getPrefixes("branch"), { default => undef},
428
              "getPrefixes: Undefined branch prefix is undefined.");
429
430
    # getDigitalRecipients (error & undef):
431
    dies_ok( sub { $config->getDigitalRecipients("FOO") },
432
             "getDigitalRecipients: die if not correct type.");
433
    is_deeply($config->getDigitalRecipients("brw_cat"), { default => undef},
434
              "getDigitalRecipients: Undefined brw_cat dig rec is undefined.");
435
    is_deeply($config->getDigitalRecipients("branch"), { default => undef},
436
              "getDigitalRecipients: Undefined branch dig rec is undefined.");
437
438
    $config->{configuration} = Koha::Illrequest::Config::_load_configuration({
439
            backend_directory => '/tmp/',
440
            prefix => 'DEFAULT-prefix',
441
            digital_recipient => 'branch',
442
            borrower_category => [
443
                { code => 'B', prefix => '2-prefix' },
444
                { code => 'C', digital_recipient => 'branch' }
445
            ],
446
            branch => [
447
                { code => '1', prefix => 'T-prefix' },
448
                { code => '2', digital_recipient => 'borrower' }
449
            ]
450
        }, 0
451
    );
452
453
    # getPrefixes (values):
454
    is_deeply($config->getPrefixes("brw_cat"),
455
              { B => '2-prefix', default => 'DEFAULT-prefix' },
456
              "getPrefixes: return configuration brw_cat prefixes.");
457
    is_deeply($config->getPrefixes("branch"),
458
              { 1 => 'T-prefix', default => 'DEFAULT-prefix' },
459
              "getPrefixes: return configuration branch prefixes.");
460
461
    # getDigitalRecipients (values):
462
    is_deeply($config->getDigitalRecipients("brw_cat"),
463
              { C => 'branch', default => 'branch' },
464
              "getDigitalRecipients: return brw_cat digital_recipients.");
465
    is_deeply($config->getDigitalRecipients("branch"),
466
              { 2 => 'borrower', default => 'branch' },
467
              "getDigitalRecipients: return branch digital_recipients.");
468
469
    $schema->storage->txn_rollback;
470
};
471
472
473
1;
(-)a/t/db_dependent/Illrequestattributes.t (+63 lines)
Line 0 Link Here
1
#!/usr/bin/perl
2
#
3
# This file is part of Koha.
4
#
5
# Koha is free software; you can redistribute it and/or modify it under the
6
# terms of the GNU General Public License as published by the Free Software
7
# Foundation; either version 2 of the License, or (at your option) any later
8
# version.
9
#
10
# Koha is distributed in the hope that it will be useful, but WITHOUT ANY
11
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
12
# A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
13
#
14
# You should have received a copy of the GNU General Public License along
15
# with Koha; if not, write to the Free Software Foundation, Inc.,
16
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17
#
18
19
use Modern::Perl;
20
21
use File::Basename qw/basename/;
22
use Koha::Database;
23
use Koha::Patrons;
24
use t::lib::TestBuilder;
25
26
use Test::More tests => 3;
27
28
my $schema = Koha::Database->new->schema;
29
use_ok('Koha::Illrequestattribute');
30
use_ok('Koha::Illrequestattributes');
31
32
subtest 'Basic object tests' => sub {
33
34
    plan tests => 5;
35
36
    $schema->storage->txn_begin;
37
38
    my $builder = t::lib::TestBuilder->new;
39
40
    my $illrqattr = $builder->build({ source => 'Illrequestattribute' });
41
42
    my $illrqattr_obj = Koha::Illrequestattributes->find(
43
        $illrqattr->{illrequest_id},
44
        $illrqattr->{type}
45
    );
46
    isa_ok($illrqattr_obj, 'Koha::Illrequestattribute',
47
        "Correctly create and load an illrequestattribute object.");
48
    is($illrqattr_obj->illrequest_id, $illrqattr->{illrequest_id},
49
       "Illrequest_id getter works.");
50
    is($illrqattr_obj->type, $illrqattr->{type},
51
       "Type getter works.");
52
    is($illrqattr_obj->value, $illrqattr->{value},
53
       "Value getter works.");
54
55
    $illrqattr_obj->delete;
56
57
    is(Koha::Illrequestattributes->search->count, 0,
58
        "No attributes found after delete.");
59
60
    $schema->storage->txn_rollback;
61
};
62
63
1;
(-)a/t/db_dependent/Illrequests.t (+792 lines)
Line 0 Link Here
1
#!/usr/bin/perl
2
#
3
# This file is part of Koha.
4
#
5
# Koha is free software; you can redistribute it and/or modify it under the
6
# terms of the GNU General Public License as published by the Free Software
7
# Foundation; either version 2 of the License, or (at your option) any later
8
# version.
9
#
10
# Koha is distributed in the hope that it will be useful, but WITHOUT ANY
11
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
12
# A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
13
#
14
# You should have received a copy of the GNU General Public License along
15
# with Koha; if not, write to the Free Software Foundation, Inc.,
16
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17
#
18
19
use Modern::Perl;
20
21
use File::Basename qw/basename/;
22
use Koha::Database;
23
use Koha::Illrequestattributes;
24
use Koha::Illrequest::Config;
25
use Koha::Patrons;
26
use t::lib::Mocks;
27
use t::lib::TestBuilder;
28
use Test::MockObject;
29
use Test::Exception;
30
31
use Test::More tests => 10;
32
33
my $schema = Koha::Database->new->schema;
34
my $builder = t::lib::TestBuilder->new;
35
use_ok('Koha::Illrequest');
36
use_ok('Koha::Illrequests');
37
38
subtest 'Basic object tests' => sub {
39
40
    plan tests => 21;
41
42
    $schema->storage->txn_begin;
43
44
    my $illrq = $builder->build({ source => 'Illrequest' });
45
    my $illrq_obj = Koha::Illrequests->find($illrq->{illrequest_id});
46
47
    isa_ok($illrq_obj, 'Koha::Illrequest',
48
           "Correctly create and load an illrequest object.");
49
    isa_ok($illrq_obj->_config, 'Koha::Illrequest::Config',
50
           "Created a config object as part of Illrequest creation.");
51
52
    is($illrq_obj->illrequest_id, $illrq->{illrequest_id},
53
       "Illrequest_id getter works.");
54
    is($illrq_obj->borrowernumber, $illrq->{borrowernumber},
55
       "Borrowernumber getter works.");
56
    is($illrq_obj->biblio_id, $illrq->{biblio_id},
57
       "Biblio_Id getter works.");
58
    is($illrq_obj->branchcode, $illrq->{branchcode},
59
       "Branchcode getter works.");
60
    is($illrq_obj->status, $illrq->{status},
61
       "Status getter works.");
62
    is($illrq_obj->placed, $illrq->{placed},
63
       "Placed getter works.");
64
    is($illrq_obj->replied, $illrq->{replied},
65
       "Replied getter works.");
66
    is($illrq_obj->updated, $illrq->{updated},
67
       "Updated getter works.");
68
    is($illrq_obj->completed, $illrq->{completed},
69
       "Completed getter works.");
70
    is($illrq_obj->medium, $illrq->{medium},
71
       "Medium getter works.");
72
    is($illrq_obj->accessurl, $illrq->{accessurl},
73
       "Accessurl getter works.");
74
    is($illrq_obj->cost, $illrq->{cost},
75
       "Cost getter works.");
76
    is($illrq_obj->notesopac, $illrq->{notesopac},
77
       "Notesopac getter works.");
78
    is($illrq_obj->notesstaff, $illrq->{notesstaff},
79
       "Notesstaff getter works.");
80
    is($illrq_obj->orderid, $illrq->{orderid},
81
       "Orderid getter works.");
82
    is($illrq_obj->backend, $illrq->{backend},
83
       "Backend getter works.");
84
85
    isnt($illrq_obj->status, 'COMP',
86
         "ILL is not currently marked complete.");
87
    $illrq_obj->mark_completed;
88
    is($illrq_obj->status, 'COMP',
89
       "ILL is now marked complete.");
90
91
    $illrq_obj->delete;
92
93
    is(Koha::Illrequests->search->count, 0,
94
       "No illrequest found after delete.");
95
96
    $schema->storage->txn_rollback;
97
};
98
99
subtest 'Working with related objects' => sub {
100
101
    plan tests => 5;
102
103
    $schema->storage->txn_begin;
104
105
    my $patron = $builder->build({ source => 'Borrower' });
106
    my $illrq = $builder->build({
107
        source => 'Illrequest',
108
        value => { borrowernumber => $patron->{borrowernumber} }
109
    });
110
    my $illrq_obj = Koha::Illrequests->find($illrq->{illrequest_id});
111
112
    isa_ok($illrq_obj->patron, 'Koha::Patron',
113
           "OK accessing related patron.");
114
115
    $builder->build({
116
        source => 'Illrequestattribute',
117
        value  => { illrequest_id => $illrq_obj->illrequest_id, type => 'X' }
118
    });
119
    $builder->build({
120
        source => 'Illrequestattribute',
121
        value  => { illrequest_id => $illrq_obj->illrequest_id, type => 'Y' }
122
    });
123
    $builder->build({
124
        source => 'Illrequestattribute',
125
        value  => { illrequest_id => $illrq_obj->illrequest_id, type => 'Z' }
126
    });
127
128
    is($illrq_obj->illrequestattributes->count, Koha::Illrequestattributes->search->count,
129
       "Fetching expected number of Illrequestattributes for our request.");
130
131
    my $illrq1 = $builder->build({ source => 'Illrequest' });
132
    $builder->build({
133
        source => 'Illrequestattribute',
134
        value  => { illrequest_id => $illrq1->{illrequest_id}, type => 'X' }
135
    });
136
137
    is($illrq_obj->illrequestattributes->count + 1, Koha::Illrequestattributes->search->count,
138
       "Fetching expected number of Illrequestattributes for our request.");
139
140
    $illrq_obj->delete;
141
    is(Koha::Illrequestattributes->search->count, 1,
142
       "Correct number of illrequestattributes after delete.");
143
144
    isa_ok(Koha::Patrons->find($patron->{borrowernumber}), 'Koha::Patron',
145
           "Borrower was not deleted after illrq delete.");
146
147
    $schema->storage->txn_rollback;
148
};
149
150
subtest 'Status Graph tests' => sub {
151
152
    plan tests => 4;
153
154
    $schema->storage->txn_begin;
155
156
    my $illrq = $builder->build({source => 'Illrequest'});
157
    my $illrq_obj = Koha::Illrequests->find($illrq->{illrequest_id});
158
159
    # _core_status_graph tests: it's just a constant, so here we just make
160
    # sure it returns a hashref.
161
    is(ref $illrq_obj->_core_status_graph, "HASH",
162
       "_core_status_graph returns a hash.");
163
164
    # _status_graph_union: let's try different merge operations.
165
    # Identity operation
166
    is_deeply(
167
        $illrq_obj->_status_graph_union($illrq_obj->_core_status_graph, {}),
168
        $illrq_obj->_core_status_graph,
169
        "core_status_graph + null = core_status_graph"
170
    );
171
172
    # Simple addition
173
    is_deeply(
174
        $illrq_obj->_status_graph_union({}, $illrq_obj->_core_status_graph),
175
        $illrq_obj->_core_status_graph,
176
        "null + core_status_graph = core_status_graph"
177
    );
178
179
    # Correct merge behaviour
180
    is_deeply(
181
        $illrq_obj->_status_graph_union({
182
            REQ => {
183
                prev_actions   => [ ],
184
                id             => 'REQ',
185
                next_actions   => [ ],
186
            },
187
        }, {
188
            QER => {
189
                prev_actions   => [ 'REQ' ],
190
                id             => 'QER',
191
                next_actions   => [ 'REQ' ],
192
            },
193
        }),
194
        {
195
            REQ => {
196
                prev_actions   => [ 'QER' ],
197
                id             => 'REQ',
198
                next_actions   => [ 'QER' ],
199
            },
200
            QER => {
201
                prev_actions   => [ 'REQ' ],
202
                id             => 'QER',
203
                next_actions   => [ 'REQ' ],
204
            },
205
        },
206
        "REQ atom + linking QER = cyclical status graph"
207
    );
208
209
    $schema->storage->txn_rollback;
210
};
211
212
subtest 'Backend testing (mocks)' => sub {
213
214
    plan tests => 13;
215
216
    $schema->storage->txn_begin;
217
218
    # testing load_backend & available_backends requires that we have at least
219
    # the Dummy plugin installed.  load_backend & available_backends don't
220
    # currently have tests as a result.
221
222
    my $backend = Test::MockObject->new;
223
    $backend->set_isa('Koha::Illbackends::Mock');
224
    $backend->set_always('name', 'Mock');
225
226
    my $patron = $builder->build({ source => 'Borrower' });
227
    my $illrq = $builder->build({
228
        source => 'Illrequest',
229
        value => { borrowernumber => $patron->{borrowernumber} }
230
    });
231
    my $illrq_obj = Koha::Illrequests->find($illrq->{illrequest_id});
232
233
    $illrq_obj->_backend($backend);
234
235
    isa_ok($illrq_obj->_backend, 'Koha::Illbackends::Mock',
236
           "OK accessing mocked backend.");
237
238
    # _backend_capability tests:
239
    # We need to test whether this optional feature of a mocked backend
240
    # behaves as expected.
241
    # 3 scenarios: feature not implemented, feature implemented, but requested
242
    # capability is not provided by backend, & feature is implemented &
243
    # capability exists.  This method can be used to implement custom backend
244
    # functionality, such as unmediated in the BLDSS backend (also see
245
    # bugzilla 18837).
246
    $backend->set_always('capabilities', undef);
247
    is($illrq_obj->_backend_capability('Test'), 0,
248
       "0 returned on Mock not implementing capabilities.");
249
250
    $backend->set_always('capabilities', 0);
251
    is($illrq_obj->_backend_capability('Test'), 0,
252
       "0 returned on Mock not implementing Test capability.");
253
254
    $backend->set_always('capabilities', sub { return 'bar'; } );
255
    is($illrq_obj->_backend_capability('Test'), 'bar',
256
       "'bar' returned on Mock implementing Test capability.");
257
258
    # metadata test: we need to be sure that we return the arbitrary values
259
    # from the backend.
260
    $backend->mock(
261
        'metadata',
262
        sub {
263
            my ( $self, $rq ) = @_;
264
            return {
265
                ID => $rq->illrequest_id,
266
                Title => $rq->patron->borrowernumber
267
            }
268
        }
269
    );
270
271
    is_deeply(
272
        $illrq_obj->metadata,
273
        {
274
            ID => $illrq_obj->illrequest_id,
275
            Title => $illrq_obj->patron->borrowernumber
276
        },
277
        "Test metadata."
278
    );
279
280
    # capabilities:
281
282
    # No backend graph extension
283
    $backend->set_always('status_graph', {});
284
    is_deeply($illrq_obj->capabilities('COMP'),
285
              {
286
                  prev_actions   => [ 'REQ' ],
287
                  id             => 'COMP',
288
                  name           => 'Completed',
289
                  ui_method_name => 'Mark completed',
290
                  method         => 'mark_completed',
291
                  next_actions   => [ ],
292
                  ui_method_icon => 'fa-check',
293
              },
294
              "Dummy status graph for COMP.");
295
    is($illrq_obj->capabilities('UNKNOWN'), undef,
296
       "Dummy status graph for UNKNOWN.");
297
    is_deeply($illrq_obj->capabilities(),
298
              $illrq_obj->_core_status_graph,
299
              "Dummy full status graph.");
300
    # Simple backend graph extension
301
    $backend->set_always('status_graph',
302
                         {
303
                             QER => {
304
                                 prev_actions   => [ 'REQ' ],
305
                                 id             => 'QER',
306
                                 next_actions   => [ 'REQ' ],
307
                             },
308
                         });
309
    is_deeply($illrq_obj->capabilities('QER'),
310
              {
311
                  prev_actions   => [ 'REQ' ],
312
                  id             => 'QER',
313
                  next_actions   => [ 'REQ' ],
314
              },
315
              "Simple status graph for QER.");
316
    is($illrq_obj->capabilities('UNKNOWN'), undef,
317
       "Simple status graph for UNKNOWN.");
318
    is_deeply($illrq_obj->capabilities(),
319
              $illrq_obj->_status_graph_union(
320
                  $illrq_obj->_core_status_graph,
321
                  {
322
                      QER => {
323
                          prev_actions   => [ 'REQ' ],
324
                          id             => 'QER',
325
                          next_actions   => [ 'REQ' ],
326
                      },
327
                  }
328
              ),
329
              "Simple full status graph.");
330
331
    # custom_capability:
332
333
    # No backend graph extension
334
    $backend->set_always('status_graph', {});
335
    is($illrq_obj->custom_capability('unknown', {}), 0,
336
       "Unknown candidate.");
337
338
    # Simple backend graph extension
339
    $backend->set_always('status_graph',
340
                         {
341
                             ID => {
342
                                 prev_actions   => [ 'REQ' ],
343
                                 id             => 'ID',
344
                                 method         => 'identity',
345
                                 next_actions   => [ 'REQ' ],
346
                             },
347
                         });
348
    $backend->mock('identity',
349
                   sub { my ( $self, $params ) = @_; return $params->{other}; });
350
    is($illrq_obj->custom_capability('identity', { test => 1 })->{test}, 1,
351
       "Resolve identity custom_capability");
352
353
    $schema->storage->txn_rollback;
354
};
355
356
357
subtest 'Backend core methods' => sub {
358
359
    plan tests => 16;
360
361
    $schema->storage->txn_begin;
362
363
    # Build infrastructure
364
    my $backend = Test::MockObject->new;
365
    $backend->set_isa('Koha::Illbackends::Mock');
366
    $backend->set_always('name', 'Mock');
367
368
    my $config = Test::MockObject->new;
369
    $config->set_always('backend_dir', "/tmp");
370
    $config->set_always('getLimitRules',
371
                        { default => { count => 0, method => 'active' } });
372
373
    my $illrq = $builder->build({source => 'Illrequest'});
374
    my $illrq_obj = Koha::Illrequests->find($illrq->{illrequest_id});
375
    $illrq_obj->_config($config);
376
    $illrq_obj->_backend($backend);
377
378
    # expandTemplate:
379
    is_deeply($illrq_obj->expandTemplate({ test => 1, method => "bar" }),
380
              {
381
                  test => 1,
382
                  method => "bar",
383
                  template => "/tmp/Mock/intra-includes/bar.inc",
384
                  opac_template => "/tmp/Mock/opac-includes/bar.inc",
385
              },
386
              "ExpandTemplate");
387
388
    # backend_create
389
    # we are testing simple cases.
390
    $backend->set_series('create',
391
                         { stage => 'bar', method => 'create' },
392
                         { stage => 'commit', method => 'create' },
393
                         { stage => 'commit', method => 'create' });
394
    # Test Copyright Clearance
395
    t::lib::Mocks::mock_preference("ILLModuleCopyrightClearance", "Test Copyright Clearance.");
396
    is_deeply($illrq_obj->backend_create({test => 1}),
397
              {
398
                  error   => 0,
399
                  status  => '',
400
                  message => '',
401
                  method  => 'create',
402
                  stage   => 'copyrightclearance',
403
                  value   => {
404
                      backend => "Mock"
405
                  }
406
              },
407
              "Backend create: copyright clearance.");
408
    t::lib::Mocks::mock_preference("ILLModuleCopyrightClearance", "");
409
    # Test non-commit
410
    is_deeply($illrq_obj->backend_create({test => 1}),
411
              {
412
                  stage => 'bar', method => 'create',
413
                  template => "/tmp/Mock/intra-includes/create.inc",
414
                  opac_template => "/tmp/Mock/opac-includes/create.inc",
415
              },
416
              "Backend create: arbitrary stage.");
417
    # Test commit
418
    is_deeply($illrq_obj->backend_create({test => 1}),
419
              {
420
                  stage => 'commit', method => 'create', permitted => 0,
421
                  template => "/tmp/Mock/intra-includes/create.inc",
422
                  opac_template => "/tmp/Mock/opac-includes/create.inc",
423
              },
424
              "Backend create: arbitrary stage, not permitted.");
425
    is($illrq_obj->status, "QUEUED", "Backend create: queued if restricted.");
426
    $config->set_always('getLimitRules', {});
427
    $illrq_obj->status('NEW');
428
    is_deeply($illrq_obj->backend_create({test => 1}),
429
              {
430
                  stage => 'commit', method => 'create', permitted => 1,
431
                  template => "/tmp/Mock/intra-includes/create.inc",
432
                  opac_template => "/tmp/Mock/opac-includes/create.inc",
433
              },
434
              "Backend create: arbitrary stage, permitted.");
435
    is($illrq_obj->status, "NEW", "Backend create: not-queued.");
436
437
    # backend_renew
438
    $backend->set_series('renew', { stage => 'bar', method => 'renew' });
439
    is_deeply($illrq_obj->backend_renew({test => 1}),
440
              {
441
                  stage => 'bar', method => 'renew',
442
                  template => "/tmp/Mock/intra-includes/renew.inc",
443
                  opac_template => "/tmp/Mock/opac-includes/renew.inc",
444
              },
445
              "Backend renew: arbitrary stage.");
446
447
    # backend_cancel
448
    $backend->set_series('cancel', { stage => 'bar', method => 'cancel' });
449
    is_deeply($illrq_obj->backend_cancel({test => 1}),
450
              {
451
                  stage => 'bar', method => 'cancel',
452
                  template => "/tmp/Mock/intra-includes/cancel.inc",
453
                  opac_template => "/tmp/Mock/opac-includes/cancel.inc",
454
              },
455
              "Backend cancel: arbitrary stage.");
456
457
    # backend_update_status
458
    $backend->set_series('update_status', { stage => 'bar', method => 'update_status' });
459
    is_deeply($illrq_obj->backend_update_status({test => 1}),
460
              {
461
                  stage => 'bar', method => 'update_status',
462
                  template => "/tmp/Mock/intra-includes/update_status.inc",
463
                  opac_template => "/tmp/Mock/opac-includes/update_status.inc",
464
              },
465
              "Backend update_status: arbitrary stage.");
466
467
    # backend_confirm
468
    $backend->set_series('confirm', { stage => 'bar', method => 'confirm' });
469
    is_deeply($illrq_obj->backend_confirm({test => 1}),
470
              {
471
                  stage => 'bar', method => 'confirm',
472
                  template => "/tmp/Mock/intra-includes/confirm.inc",
473
                  opac_template => "/tmp/Mock/opac-includes/confirm.inc",
474
              },
475
              "Backend confirm: arbitrary stage.");
476
477
    $config->set_always('partner_code', "ILLTSTLIB");
478
    $backend->set_always('metadata', { Test => "Foobar" });
479
    my $illbrn = $builder->build({
480
        source => 'Branch',
481
        value => { branchemail => "", branchreplyto => "" }
482
    });
483
    my $partner1 = $builder->build({
484
        source => 'Borrower',
485
        value => { categorycode => "ILLTSTLIB" },
486
    });
487
    my $partner2 = $builder->build({
488
        source => 'Borrower',
489
        value => { categorycode => "ILLTSTLIB" },
490
    });
491
    my $gen_conf = $illrq_obj->generic_confirm({
492
        current_branchcode => $illbrn->{branchcode}
493
    });
494
    isnt(index($gen_conf->{value}->{draft}->{body}, $backend->metadata->{Test}), -1,
495
         "Generic confirm: draft contains metadata."
496
    );
497
    is($gen_conf->{value}->{partners}->next->borrowernumber, $partner1->{borrowernumber},
498
       "Generic cofnirm: partner 1 is correct."
499
    );
500
    is($gen_conf->{value}->{partners}->next->borrowernumber, $partner2->{borrowernumber},
501
       "Generic confirm: partner 2 is correct."
502
    );
503
504
    dies_ok { $illrq_obj->generic_confirm({
505
        current_branchcode => $illbrn->{branchcode},
506
        stage => 'draft'
507
    }) }
508
        "Generic confirm: missing to dies OK.";
509
510
    dies_ok { $illrq_obj->generic_confirm({
511
        current_branchcode => $illbrn->{branchcode},
512
        partners => $partner1->{email},
513
        stage => 'draft'
514
    }) }
515
        "Generic confirm: missing from dies OK.";
516
517
    $schema->storage->txn_rollback;
518
};
519
520
521
subtest 'Helpers' => sub {
522
523
    plan tests => 9;
524
525
    $schema->storage->txn_begin;
526
527
    # Build infrastructure
528
    my $backend = Test::MockObject->new;
529
    $backend->set_isa('Koha::Illbackends::Mock');
530
    $backend->set_always('name', 'Mock');
531
532
    my $config = Test::MockObject->new;
533
    $config->set_always('backend_dir', "/tmp");
534
535
    my $patron = $builder->build({
536
        source => 'Borrower',
537
        value => { categorycode => "A" }
538
    });
539
    my $illrq = $builder->build({
540
        source => 'Illrequest',
541
        value => { branchcode => "CPL", borrowernumber => $patron->{borrowernumber} }
542
    });
543
    my $illrq_obj = Koha::Illrequests->find($illrq->{illrequest_id});
544
    $illrq_obj->_config($config);
545
    $illrq_obj->_backend($backend);
546
547
    # getPrefix
548
    $config->set_series('getPrefixes',
549
                        { CPL => "TEST", TSL => "BAR", default => "DEFAULT" },
550
                        { A => "ATEST", C => "CBAR", default => "DEFAULT" });
551
    is($illrq_obj->getPrefix({ brw_cat => "C", branch => "CPL" }), "CBAR",
552
       "getPrefix: brw_cat");
553
    $config->set_series('getPrefixes',
554
                        { CPL => "TEST", TSL => "BAR", default => "DEFAULT" },
555
                        { A => "ATEST", C => "CBAR", default => "DEFAULT" });
556
    is($illrq_obj->getPrefix({ brw_cat => "UNKNOWN", branch => "CPL" }), "TEST",
557
       "getPrefix: branch");
558
    $config->set_series('getPrefixes',
559
                        { CPL => "TEST", TSL => "BAR", default => "DEFAULT" },
560
                        { A => "ATEST", C => "CBAR", default => "DEFAULT" });
561
    is($illrq_obj->getPrefix({ brw_cat => "UNKNOWN", branch => "UNKNOWN" }), "DEFAULT",
562
       "getPrefix: default");
563
    $config->set_always('getPrefixes', {});
564
    is($illrq_obj->getPrefix({ brw_cat => "UNKNOWN", branch => "UNKNOWN" }), "",
565
       "getPrefix: the empty prefix");
566
567
    # id_prefix
568
    $config->set_series('getPrefixes',
569
                        { CPL => "TEST", TSL => "BAR", default => "DEFAULT" },
570
                        { A => "ATEST", C => "CBAR", default => "DEFAULT" });
571
    is($illrq_obj->id_prefix, "ATEST-", "id_prefix: brw_cat");
572
    $config->set_series('getPrefixes',
573
                        { CPL => "TEST", TSL => "BAR", default => "DEFAULT" },
574
                        { AB => "ATEST", CD => "CBAR", default => "DEFAULT" });
575
    is($illrq_obj->id_prefix, "TEST-", "id_prefix: branch");
576
    $config->set_series('getPrefixes',
577
                        { CPLT => "TEST", TSLT => "BAR", default => "DEFAULT" },
578
                        { AB => "ATEST", CD => "CBAR", default => "DEFAULT" });
579
    is($illrq_obj->id_prefix, "DEFAULT-", "id_prefix: default");
580
581
    # requires_moderation
582
    $illrq_obj->status('NEW')->store;
583
    is($illrq_obj->requires_moderation, undef, "requires_moderation: No.");
584
    $illrq_obj->status('CANCREQ')->store;
585
    is($illrq_obj->requires_moderation, 'CANCREQ', "requires_moderation: Yes.");
586
587
    $schema->storage->txn_rollback;
588
};
589
590
591
subtest 'Censorship' => sub {
592
593
    plan tests => 2;
594
595
    $schema->storage->txn_begin;
596
597
    # Build infrastructure
598
    my $backend = Test::MockObject->new;
599
    $backend->set_isa('Koha::Illbackends::Mock');
600
    $backend->set_always('name', 'Mock');
601
602
    my $config = Test::MockObject->new;
603
    $config->set_always('backend_dir', "/tmp");
604
605
    my $illrq = $builder->build({source => 'Illrequest'});
606
    my $illrq_obj = Koha::Illrequests->find($illrq->{illrequest_id});
607
    $illrq_obj->_config($config);
608
    $illrq_obj->_backend($backend);
609
610
    $config->set_always('censorship', { censor_notes_staff => 1, censor_reply_date => 0 });
611
612
    my $censor_out = $illrq_obj->_censor({ foo => 'bar', baz => 564 });
613
    is_deeply($censor_out, { foo => 'bar', baz => 564, display_reply_date => 1 },
614
              "_censor: not OPAC, reply_date = 1");
615
616
    $censor_out = $illrq_obj->_censor({ foo => 'bar', baz => 564, opac => 1 });
617
    is_deeply($censor_out, {
618
        foo => 'bar', baz => 564, censor_notes_staff => 1,
619
        display_reply_date => 1, opac => 1
620
    }, "_censor: notes_staff = 0, reply_date = 0");
621
622
    $schema->storage->txn_rollback;
623
};
624
625
subtest 'Checking Limits' => sub {
626
627
    plan tests => 30;
628
629
    $schema->storage->txn_begin;
630
631
    # Build infrastructure
632
    my $backend = Test::MockObject->new;
633
    $backend->set_isa('Koha::Illbackends::Mock');
634
    $backend->set_always('name', 'Mock');
635
636
    my $config = Test::MockObject->new;
637
    $config->set_always('backend_dir', "/tmp");
638
639
    my $illrq = $builder->build({source => 'Illrequest'});
640
    my $illrq_obj = Koha::Illrequests->find($illrq->{illrequest_id});
641
    $illrq_obj->_config($config);
642
    $illrq_obj->_backend($backend);
643
644
    # getLimits
645
    $config->set_series('getLimitRules',
646
                        { CPL => { count => 1, method => 'test' } },
647
                        { default => { count => 0, method => 'active' } });
648
    is_deeply($illrq_obj->getLimits({ type => 'branch', value => "CPL" }),
649
              { count => 1, method => 'test' },
650
              "getLimits: by value.");
651
    is_deeply($illrq_obj->getLimits({ type => 'branch' }),
652
              { count => 0, method => 'active' },
653
              "getLimits: by default.");
654
    is_deeply($illrq_obj->getLimits({ type => 'branch', value => "CPL" }),
655
              { count => -1, method => 'active' },
656
              "getLimits: by hard-coded.");
657
658
    #_limit_counter
659
    is($illrq_obj->_limit_counter('annual', { branchcode => $illrq_obj->branchcode }),
660
       1, "_limit_counter: Initial branch annual count.");
661
    is($illrq_obj->_limit_counter('active', { branchcode => $illrq_obj->branchcode }),
662
       1, "_limit_counter: Initial branch active count.");
663
    is($illrq_obj->_limit_counter('annual', { borrowernumber => $illrq_obj->borrowernumber }),
664
       1, "_limit_counter: Initial patron annual count.");
665
    is($illrq_obj->_limit_counter('active', { borrowernumber => $illrq_obj->borrowernumber }),
666
       1, "_limit_counter: Initial patron active count.");
667
    $builder->build({
668
        source => 'Illrequest',
669
        value => {
670
            branchcode => $illrq_obj->branchcode,
671
            borrowernumber => $illrq_obj->borrowernumber,
672
        }
673
    });
674
    is($illrq_obj->_limit_counter('annual', { branchcode => $illrq_obj->branchcode }),
675
       2, "_limit_counter: Add a qualifying request for branch annual count.");
676
    is($illrq_obj->_limit_counter('active', { branchcode => $illrq_obj->branchcode }),
677
       2, "_limit_counter: Add a qualifying request for branch active count.");
678
    is($illrq_obj->_limit_counter('annual', { borrowernumber => $illrq_obj->borrowernumber }),
679
       2, "_limit_counter: Add a qualifying request for patron annual count.");
680
    is($illrq_obj->_limit_counter('active', { borrowernumber => $illrq_obj->borrowernumber }),
681
       2, "_limit_counter: Add a qualifying request for patron active count.");
682
    $builder->build({
683
        source => 'Illrequest',
684
        value => {
685
            branchcode => $illrq_obj->branchcode,
686
            borrowernumber => $illrq_obj->borrowernumber,
687
            placed => "2005-05-31",
688
        }
689
    });
690
    is($illrq_obj->_limit_counter('annual', { branchcode => $illrq_obj->branchcode }),
691
       2, "_limit_counter: Add an out-of-date branch request.");
692
    is($illrq_obj->_limit_counter('active', { branchcode => $illrq_obj->branchcode }),
693
       3, "_limit_counter: Add a qualifying request for branch active count.");
694
    is($illrq_obj->_limit_counter('annual', { borrowernumber => $illrq_obj->borrowernumber }),
695
       2, "_limit_counter: Add an out-of-date patron request.");
696
    is($illrq_obj->_limit_counter('active', { borrowernumber => $illrq_obj->borrowernumber }),
697
       3, "_limit_counter: Add a qualifying request for patron active count.");
698
    $builder->build({
699
        source => 'Illrequest',
700
        value => {
701
            branchcode => $illrq_obj->branchcode,
702
            borrowernumber => $illrq_obj->borrowernumber,
703
            status => "COMP",
704
        }
705
    });
706
    is($illrq_obj->_limit_counter('annual', { branchcode => $illrq_obj->branchcode }),
707
       3, "_limit_counter: Add a qualifying request for branch annual count.");
708
    is($illrq_obj->_limit_counter('active', { branchcode => $illrq_obj->branchcode }),
709
       3, "_limit_counter: Add a completed request for branch active count.");
710
    is($illrq_obj->_limit_counter('annual', { borrowernumber => $illrq_obj->borrowernumber }),
711
       3, "_limit_counter: Add a qualifying request for patron annual count.");
712
    is($illrq_obj->_limit_counter('active', { borrowernumber => $illrq_obj->borrowernumber }),
713
       3, "_limit_counter: Add a completed request for patron active count.");
714
715
    # check_limits:
716
717
    # We've tested _limit_counter, so all we need to test here is whether the
718
    # current counts of 3 for each work as they should against different
719
    # configuration declarations.
720
721
    # No limits
722
    $config->set_always('getLimitRules', undef);
723
    is($illrq_obj->check_limits({patron => $illrq_obj->patron,
724
                                 librarycode => $illrq_obj->branchcode}),
725
       1, "check_limits: no configuration => no limits.");
726
727
    # Branch tests
728
    $config->set_always('getLimitRules',
729
                        { $illrq_obj->branchcode => { count => 1, method => 'active' } });
730
    is($illrq_obj->check_limits({patron => $illrq_obj->patron,
731
                                 librarycode => $illrq_obj->branchcode}),
732
       0, "check_limits: branch active limit exceeded.");
733
    $config->set_always('getLimitRules',
734
                        { $illrq_obj->branchcode => { count => 1, method => 'annual' } });
735
    is($illrq_obj->check_limits({patron => $illrq_obj->patron,
736
                                 librarycode => $illrq_obj->branchcode}),
737
       0, "check_limits: branch annual limit exceeded.");
738
    $config->set_always('getLimitRules',
739
                        { $illrq_obj->branchcode => { count => 4, method => 'active' } });
740
    is($illrq_obj->check_limits({patron => $illrq_obj->patron,
741
                                 librarycode => $illrq_obj->branchcode}),
742
       1, "check_limits: branch active limit OK.");
743
    $config->set_always('getLimitRules',
744
                        { $illrq_obj->branchcode => { count => 4, method => 'annual' } });
745
    is($illrq_obj->check_limits({patron => $illrq_obj->patron,
746
                                 librarycode => $illrq_obj->branchcode}),
747
       1, "check_limits: branch annual limit OK.");
748
749
    # Patron tests
750
    $config->set_always('getLimitRules',
751
                        { $illrq_obj->patron->categorycode => { count => 1, method => 'active' } });
752
    is($illrq_obj->check_limits({patron => $illrq_obj->patron,
753
                                 librarycode => $illrq_obj->branchcode}),
754
       0, "check_limits: patron category active limit exceeded.");
755
    $config->set_always('getLimitRules',
756
                        { $illrq_obj->patron->categorycode => { count => 1, method => 'annual' } });
757
    is($illrq_obj->check_limits({patron => $illrq_obj->patron,
758
                                 librarycode => $illrq_obj->branchcode}),
759
       0, "check_limits: patron category annual limit exceeded.");
760
    $config->set_always('getLimitRules',
761
                        { $illrq_obj->patron->categorycode => { count => 4, method => 'active' } });
762
    is($illrq_obj->check_limits({patron => $illrq_obj->patron,
763
                                 librarycode => $illrq_obj->branchcode}),
764
       1, "check_limits: patron category active limit OK.");
765
    $config->set_always('getLimitRules',
766
                        { $illrq_obj->patron->categorycode => { count => 4, method => 'annual' } });
767
    is($illrq_obj->check_limits({patron => $illrq_obj->patron,
768
                                 librarycode => $illrq_obj->branchcode}),
769
       1, "check_limits: patron category annual limit OK.");
770
771
    # One rule cancels the other
772
    $config->set_series('getLimitRules',
773
                        # Branch rules allow request
774
                        { $illrq_obj->branchcode => { count => 4, method => 'active' } },
775
                        # Patron rule forbids it
776
                        { $illrq_obj->patron->categorycode => { count => 1, method => 'annual' } });
777
    is($illrq_obj->check_limits({patron => $illrq_obj->patron,
778
                                 librarycode => $illrq_obj->branchcode}),
779
       0, "check_limits: patron category veto overrides branch OK.");
780
    $config->set_series('getLimitRules',
781
                        # Branch rules allow request
782
                        { $illrq_obj->branchcode => { count => 1, method => 'active' } },
783
                        # Patron rule forbids it
784
                        { $illrq_obj->patron->categorycode => { count => 4, method => 'annual' } });
785
    is($illrq_obj->check_limits({patron => $illrq_obj->patron,
786
                                 librarycode => $illrq_obj->branchcode}),
787
       0, "check_limits: branch veto overrides patron category OK.");
788
789
    $schema->storage->txn_rollback;
790
};
791
792
1;
(-)a/t/db_dependent/api/v1/illrequests.t (-1 / +136 lines)
Line 0 Link Here
0
- 
1
#!/usr/bin/env perl
2
3
# This file is part of Koha.
4
#
5
# Koha is free software; you can redistribute it and/or modify it under the
6
# terms of the GNU General Public License as published by the Free Software
7
# Foundation; either version 3 of the License, or (at your option) any later
8
# version.
9
#
10
# Koha is distributed in the hope that it will be useful, but WITHOUT ANY
11
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
12
# A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
13
#
14
# You should have received a copy of the GNU General Public License along
15
# with Koha; if not, write to the Free Software Foundation, Inc.,
16
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17
18
use Modern::Perl;
19
20
use Test::More tests => 1;
21
use Test::Mojo;
22
use Test::Warn;
23
24
use t::lib::TestBuilder;
25
use t::lib::Mocks;
26
27
use C4::Auth;
28
use Koha::Illrequests;
29
30
my $schema  = Koha::Database->new->schema;
31
my $builder = t::lib::TestBuilder->new;
32
33
# FIXME: sessionStorage defaults to mysql, but it seems to break transaction handling
34
# this affects the other REST api tests
35
t::lib::Mocks::mock_preference( 'SessionStorage', 'tmp' );
36
37
my $remote_address = '127.0.0.1';
38
my $t              = Test::Mojo->new('Koha::REST::V1');
39
40
subtest 'list() tests' => sub {
41
42
    plan tests => 6;
43
44
    $schema->storage->txn_begin;
45
46
    Koha::Illrequests->search->delete;
47
    my ( $borrowernumber, $session_id ) =
48
      create_user_and_session( { authorized => 1 } );
49
50
    ## Authorized user tests
51
    # No requests, so empty array should be returned
52
    my $tx = $t->ua->build_tx( GET => '/api/v1/illrequests' );
53
    $tx->req->cookies( { name => 'CGISESSID', value => $session_id } );
54
    $tx->req->env( { REMOTE_ADDR => $remote_address } );
55
    $t->request_ok($tx)->status_is(200)->json_is( [] );
56
57
#    my $city_country = 'France';
58
#    my $city         = $builder->build(
59
#        { source => 'City', value => { city_country => $city_country } } );
60
#
61
#    # One city created, should get returned
62
#    $tx = $t->ua->build_tx( GET => '/api/v1/cities' );
63
#    $tx->req->cookies( { name => 'CGISESSID', value => $session_id } );
64
#    $tx->req->env( { REMOTE_ADDR => $remote_address } );
65
#    $t->request_ok($tx)->status_is(200)->json_is( [$city] );
66
#
67
#    my $another_city = $builder->build(
68
#        { source => 'City', value => { city_country => $city_country } } );
69
#    my $city_with_another_country = $builder->build( { source => 'City' } );
70
#
71
#    # Two cities created, they should both be returned
72
#    $tx = $t->ua->build_tx( GET => '/api/v1/cities' );
73
#    $tx->req->cookies( { name => 'CGISESSID', value => $session_id } );
74
#    $tx->req->env( { REMOTE_ADDR => $remote_address } );
75
#    $t->request_ok($tx)->status_is(200)
76
#      ->json_is( [ $city, $another_city, $city_with_another_country ] );
77
#
78
#    # Filtering works, two cities sharing city_country
79
#    $tx =
80
#      $t->ua->build_tx( GET => "/api/v1/cities?city_country=" . $city_country );
81
#    $tx->req->cookies( { name => 'CGISESSID', value => $session_id } );
82
#    $tx->req->env( { REMOTE_ADDR => $remote_address } );
83
#    my $result =
84
#      $t->request_ok($tx)->status_is(200)->json_is( [ $city, $another_city ] );
85
#
86
#    $tx = $t->ua->build_tx(
87
#        GET => "/api/v1/cities?city_name=" . $city->{city_name} );
88
#    $tx->req->cookies( { name => 'CGISESSID', value => $session_id } );
89
#    $tx->req->env( { REMOTE_ADDR => $remote_address } );
90
#    $t->request_ok($tx)->status_is(200)->json_is( [$city] );
91
92
    # Warn on unsupported query parameter
93
    $tx = $t->ua->build_tx( GET => '/api/v1/illrequests?request_blah=blah' );
94
    $tx->req->cookies( { name => 'CGISESSID', value => $session_id } );
95
    $tx->req->env( { REMOTE_ADDR => $remote_address } );
96
    $t->request_ok($tx)->status_is(400)->json_is(
97
        [{ path => '/query/request_blah', message => 'Malformed query string'}]
98
    );
99
100
    $schema->storage->txn_rollback;
101
};
102
103
sub create_user_and_session {
104
105
    my $args  = shift;
106
    my $flags = ( $args->{authorized} ) ? $args->{authorized} : 0;
107
    my $dbh   = C4::Context->dbh;
108
109
    my $user = $builder->build(
110
        {
111
            source => 'Borrower',
112
            value  => {
113
                flags => $flags
114
            }
115
        }
116
    );
117
118
    # Create a session for the authorized user
119
    my $session = C4::Auth::get_session('');
120
    $session->param( 'number',   $user->{borrowernumber} );
121
    $session->param( 'id',       $user->{userid} );
122
    $session->param( 'ip',       '127.0.0.1' );
123
    $session->param( 'lasttime', time() );
124
    $session->flush;
125
126
    if ( $args->{authorized} ) {
127
        $dbh->do( "
128
            INSERT INTO user_permissions (borrowernumber,module_bit,code)
129
            VALUES (?,3,'parameters_remaining_permissions')", undef,
130
            $user->{borrowernumber} );
131
    }
132
133
    return ( $user->{borrowernumber}, $session->id );
134
}
135
136
1;

Return to bug 7317