db5b9a9fe7475b3a1736588797b3d5088cfdfb4c
[koha.git] / Koha / Illrequest.pm
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::Exceptions::Ill;
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 =over
63
64 =item * error
65
66 This should be set to 1 if an error was encountered.
67
68 =item * status
69
70 The status should be a string from the list of statuses detailed below.
71
72 =item * message
73
74 The message is a free text field that can be passed on to the end user.
75
76 =item * value
77
78 The value returned by the method.
79
80 =back
81
82 =head2 Interface Status Messages
83
84 =over
85
86 =item * branch_address_incomplete
87
88 An interface request has determined branch address details are incomplete.
89
90 =item * cancel_success
91
92 The interface's cancel_request method was successful in cancelling the
93 Illrequest using the API.
94
95 =item * cancel_fail
96
97 The interface's cancel_request method failed to cancel the Illrequest using
98 the API.
99
100 =item * unavailable
101
102 The interface's request method returned saying that the desired item is not
103 available for request.
104
105 =back
106
107 =head2 Class methods
108
109 =head3 illrequestattributes
110
111 =cut
112
113 sub illrequestattributes {
114     my ( $self ) = @_;
115     return Koha::Illrequestattributes->_new_from_dbic(
116         scalar $self->_result->illrequestattributes
117     );
118 }
119
120 =head3 patron
121
122 =cut
123
124 sub patron {
125     my ( $self ) = @_;
126     return Koha::Patron->_new_from_dbic(
127         scalar $self->_result->borrowernumber
128     );
129 }
130
131 =head3 load_backend
132
133 Require "Base.pm" from the relevant ILL backend.
134
135 =cut
136
137 sub load_backend {
138     my ( $self, $backend_id ) = @_;
139
140     my @raw = qw/Koha Illbackends/; # Base Path
141
142     my $backend_name = $backend_id || $self->backend;
143
144     unless ( defined $backend_name && $backend_name ne '' ) {
145         Koha::Exceptions::Ill::InvalidBackendId->throw(
146             "An invalid backend ID was requested ('')");
147     }
148
149     my $location = join "/", @raw, $backend_name, "Base.pm";    # File to load
150     my $backend_class = join "::", @raw, $backend_name, "Base"; # Package name
151     require $location;
152     $self->{_my_backend} = $backend_class->new({ config => $self->_config });
153     return $self;
154 }
155
156
157 =head3 _backend
158
159     my $backend = $abstract->_backend($new_backend);
160     my $backend = $abstract->_backend;
161
162 Getter/Setter for our API object.
163
164 =cut
165
166 sub _backend {
167     my ( $self, $backend ) = @_;
168     $self->{_my_backend} = $backend if ( $backend );
169     # Dynamically load our backend object, as late as possible.
170     $self->load_backend unless ( $self->{_my_backend} );
171     return $self->{_my_backend};
172 }
173
174 =head3 _backend_capability
175
176     my $backend_capability_result = $self->_backend_capability($name, $args);
177
178 This is a helper method to invoke optional capabilities in the backend.  If
179 the capability named by $name is not supported, return 0, else invoke it,
180 passing $args along with the invocation, and return its return value.
181
182 NOTE: this module suffers from a confusion in termninology:
183
184 in _backend_capability, the notion of capability refers to an optional feature
185 that is implemented in core, but might not be supported by a given backend.
186
187 in capabilities & custom_capability, capability refers to entries in the
188 status_graph (after union between backend and core).
189
190 The easiest way to fix this would be to fix the terminology in
191 capabilities & custom_capability and their callers.
192
193 =cut
194
195 sub _backend_capability {
196     my ( $self, $name, $args ) = @_;
197     my $capability = 0;
198     try {
199         $capability = $self->_backend->capabilities($name);
200     } catch {
201         return 0;
202     };
203     if ( $capability ) {
204         return &{$capability}($args);
205     } else {
206         return 0;
207     }
208 }
209
210 =head3 _config
211
212     my $config = $abstract->_config($config);
213     my $config = $abstract->_config;
214
215 Getter/Setter for our config object.
216
217 =cut
218
219 sub _config {
220     my ( $self, $config ) = @_;
221     $self->{_my_config} = $config if ( $config );
222     # Load our config object, as late as possible.
223     unless ( $self->{_my_config} ) {
224         $self->{_my_config} = Koha::Illrequest::Config->new;
225     }
226     return $self->{_my_config};
227 }
228
229 =head3 metadata
230
231 =cut
232
233 sub metadata {
234     my ( $self ) = @_;
235     return $self->_backend->metadata($self);
236 }
237
238 =head3 _core_status_graph
239
240     my $core_status_graph = $illrequest->_core_status_graph;
241
242 Returns ILL module's default status graph.  A status graph defines the list of
243 available actions at any stage in the ILL workflow.  This is for instance used
244 by the perl script & template to generate the correct buttons to display to
245 the end user at any given point.
246
247 =cut
248
249 sub _core_status_graph {
250     my ( $self ) = @_;
251     return {
252         NEW => {
253             prev_actions => [ ],                           # Actions containing buttons
254                                                            # leading to this status
255             id             => 'NEW',                       # ID of this status
256             name           => 'New request',               # UI name of this status
257             ui_method_name => 'New request',               # UI name of method leading
258                                                            # to this status
259             method         => 'create',                    # method to this status
260             next_actions   => [ 'REQ', 'GENREQ', 'KILL' ], # buttons to add to all
261                                                            # requests with this status
262             ui_method_icon => 'fa-plus',                   # UI Style class
263         },
264         REQ => {
265             prev_actions   => [ 'NEW', 'REQREV', 'QUEUED', 'CANCREQ' ],
266             id             => 'REQ',
267             name           => 'Requested',
268             ui_method_name => 'Confirm request',
269             method         => 'confirm',
270             next_actions   => [ 'REQREV', 'COMP' ],
271             ui_method_icon => 'fa-check',
272         },
273         GENREQ => {
274             prev_actions   => [ 'NEW', 'REQREV' ],
275             id             => 'GENREQ',
276             name           => 'Requested from partners',
277             ui_method_name => 'Place request with partners',
278             method         => 'generic_confirm',
279             next_actions   => [ 'COMP' ],
280             ui_method_icon => 'fa-send-o',
281         },
282         REQREV => {
283             prev_actions   => [ 'REQ' ],
284             id             => 'REQREV',
285             name           => 'Request reverted',
286             ui_method_name => 'Revert Request',
287             method         => 'cancel',
288             next_actions   => [ 'REQ', 'GENREQ', 'KILL' ],
289             ui_method_icon => 'fa-times',
290         },
291         QUEUED => {
292             prev_actions   => [ ],
293             id             => 'QUEUED',
294             name           => 'Queued request',
295             ui_method_name => 0,
296             method         => 0,
297             next_actions   => [ 'REQ', 'KILL' ],
298             ui_method_icon => 0,
299         },
300         CANCREQ => {
301             prev_actions   => [ 'NEW' ],
302             id             => 'CANCREQ',
303             name           => 'Cancellation requested',
304             ui_method_name => 0,
305             method         => 0,
306             next_actions   => [ 'KILL', 'REQ' ],
307             ui_method_icon => 0,
308         },
309         COMP => {
310             prev_actions   => [ 'REQ' ],
311             id             => 'COMP',
312             name           => 'Completed',
313             ui_method_name => 'Mark completed',
314             method         => 'mark_completed',
315             next_actions   => [ ],
316             ui_method_icon => 'fa-check',
317         },
318         KILL => {
319             prev_actions   => [ 'QUEUED', 'REQREV', 'NEW', 'CANCREQ' ],
320             id             => 'KILL',
321             name           => 0,
322             ui_method_name => 'Delete request',
323             method         => 'delete',
324             next_actions   => [ ],
325             ui_method_icon => 'fa-trash',
326         },
327     };
328 }
329
330 =head3 _core_status_graph
331
332     my $status_graph = $illrequest->_core_status_graph($origin, $new_graph);
333
334 Return a new status_graph, the result of merging $origin & new_graph.  This is
335 operation is a union over the sets defied by the two graphs.
336
337 Each entry in $new_graph is added to $origin.  We do not provide a syntax for
338 'subtraction' of entries from $origin.
339
340 Whilst it is not intended that this works, you can override entries in $origin
341 with entries with the same key in $new_graph.  This can lead to problematic
342 behaviour when $new_graph adds an entry, which modifies a dependent entry in
343 $origin, only for the entry in $origin to be replaced later with a new entry
344 from $new_graph.
345
346 NOTE: this procedure does not "re-link" entries in $origin or $new_graph,
347 i.e. each of the graphs need to be correct at the outset of the operation.
348
349 =cut
350
351 sub _status_graph_union {
352     my ( $self, $core_status_graph, $backend_status_graph ) = @_;
353     # Create new status graph with:
354     # - all core_status_graph
355     # - for-each each backend_status_graph
356     #   + add to new status graph
357     #   + for each core prev_action:
358     #     * locate core_status
359     #     * update next_actions with additional next action.
360     #   + for each core next_action:
361     #     * locate core_status
362     #     * update prev_actions with additional prev action
363
364     my @core_status_ids = keys %{$core_status_graph};
365     my $status_graph = clone($core_status_graph);
366
367     foreach my $backend_status_key ( keys %{$backend_status_graph} ) {
368         my $backend_status = $backend_status_graph->{$backend_status_key};
369         # Add to new status graph
370         $status_graph->{$backend_status_key} = $backend_status;
371         # Update all core methods' next_actions.
372         foreach my $prev_action ( @{$backend_status->{prev_actions}} ) {
373             if ( grep $prev_action, @core_status_ids ) {
374                 my @next_actions =
375                      @{$status_graph->{$prev_action}->{next_actions}};
376                 push @next_actions, $backend_status_key;
377                 $status_graph->{$prev_action}->{next_actions}
378                     = \@next_actions;
379             }
380         }
381         # Update all core methods' prev_actions
382         foreach my $next_action ( @{$backend_status->{next_actions}} ) {
383             if ( grep $next_action, @core_status_ids ) {
384                 my @prev_actions =
385                      @{$status_graph->{$next_action}->{prev_actions}};
386                 push @prev_actions, $backend_status_key;
387                 $status_graph->{$next_action}->{prev_actions}
388                     = \@prev_actions;
389             }
390         }
391     }
392
393     return $status_graph;
394 }
395
396 ### Core API methods
397
398 =head3 capabilities
399
400     my $capabilities = $illrequest->capabilities;
401
402 Return a hashref mapping methods to operation names supported by the queried
403 backend.
404
405 Example return value:
406
407     { create => "Create Request", confirm => "Progress Request" }
408
409 NOTE: this module suffers from a confusion in termninology:
410
411 in _backend_capability, the notion of capability refers to an optional feature
412 that is implemented in core, but might not be supported by a given backend.
413
414 in capabilities & custom_capability, capability refers to entries in the
415 status_graph (after union between backend and core).
416
417 The easiest way to fix this would be to fix the terminology in
418 capabilities & custom_capability and their callers.
419
420 =cut
421
422 sub capabilities {
423     my ( $self, $status ) = @_;
424     # Generate up to date status_graph
425     my $status_graph = $self->_status_graph_union(
426         $self->_core_status_graph,
427         $self->_backend->status_graph({
428             request => $self,
429             other   => {}
430         })
431     );
432     # Extract available actions from graph.
433     return $status_graph->{$status} if $status;
434     # Or return entire graph.
435     return $status_graph;
436 }
437
438 =head3 custom_capability
439
440 Return the result of invoking $CANDIDATE on this request's backend with
441 $PARAMS, or 0 if $CANDIDATE is an unknown method on backend.
442
443 NOTE: this module suffers from a confusion in termninology:
444
445 in _backend_capability, the notion of capability refers to an optional feature
446 that is implemented in core, but might not be supported by a given backend.
447
448 in capabilities & custom_capability, capability refers to entries in the
449 status_graph (after union between backend and core).
450
451 The easiest way to fix this would be to fix the terminology in
452 capabilities & custom_capability and their callers.
453
454 =cut
455
456 sub custom_capability {
457     my ( $self, $candidate, $params ) = @_;
458     foreach my $capability ( values %{$self->capabilities} ) {
459         if ( $candidate eq $capability->{method} ) {
460             my $response =
461                 $self->_backend->$candidate({
462                     request    => $self,
463                     other      => $params,
464                 });
465             return $self->expandTemplate($response);
466         }
467     }
468     return 0;
469 }
470
471 =head3 available_backends
472
473 Return a list of available backends.
474
475 =cut
476
477 sub available_backends {
478     my ( $self ) = @_;
479     my @backends = $self->_config->available_backends;
480     return \@backends;
481 }
482
483 =head3 available_actions
484
485 Return a list of available actions.
486
487 =cut
488
489 sub available_actions {
490     my ( $self ) = @_;
491     my $current_action = $self->capabilities($self->status);
492     my @available_actions = map { $self->capabilities($_) }
493         @{$current_action->{next_actions}};
494     return \@available_actions;
495 }
496
497 =head3 mark_completed
498
499 Mark a request as completed (status = COMP).
500
501 =cut
502
503 sub mark_completed {
504     my ( $self ) = @_;
505     $self->status('COMP')->store;
506     return {
507         error   => 0,
508         status  => '',
509         message => '',
510         method  => 'mark_completed',
511         stage   => 'commit',
512         next    => 'illview',
513     };
514 }
515
516 =head2 backend_confirm
517
518 Confirm a request. The backend handles setting of mandatory fields in the commit stage:
519
520 =over
521
522 =item * orderid
523
524 =item * accessurl, cost (if available).
525
526 =back
527
528 =cut
529
530 sub backend_confirm {
531     my ( $self, $params ) = @_;
532
533     my $response = $self->_backend->confirm({
534             request    => $self,
535             other      => $params,
536         });
537     return $self->expandTemplate($response);
538 }
539
540 =head3 backend_update_status
541
542 =cut
543
544 sub backend_update_status {
545     my ( $self, $params ) = @_;
546     return $self->expandTemplate($self->_backend->update_status($params));
547 }
548
549 =head3 backend_cancel
550
551     my $ILLResponse = $illRequest->backend_cancel;
552
553 The standard interface method allowing for request cancellation.
554
555 =cut
556
557 sub backend_cancel {
558     my ( $self, $params ) = @_;
559
560     my $result = $self->_backend->cancel({
561         request => $self,
562         other => $params
563     });
564
565     return $self->expandTemplate($result);
566 }
567
568 =head3 backend_renew
569
570     my $renew_response = $illRequest->backend_renew;
571
572 The standard interface method allowing for request renewal queries.
573
574 =cut
575
576 sub backend_renew {
577     my ( $self ) = @_;
578     return $self->expandTemplate(
579         $self->_backend->renew({
580             request    => $self,
581         })
582     );
583 }
584
585 =head3 backend_create
586
587     my $create_response = $abstractILL->backend_create($params);
588
589 Return an array of Record objects created by querying our backend with
590 a Search query.
591
592 In the context of the other ILL methods, this is a special method: we only
593 pass it $params, as it does not yet have any other data associated with it.
594
595 =cut
596
597 sub backend_create {
598     my ( $self, $params ) = @_;
599
600     # Establish whether we need to do a generic copyright clearance.
601     if ( ( !$params->{stage} || $params->{stage} eq 'init' )
602              && C4::Context->preference("ILLModuleCopyrightClearance") ) {
603         return {
604             error   => 0,
605             status  => '',
606             message => '',
607             method  => 'create',
608             stage   => 'copyrightclearance',
609             value   => {
610                 backend => $self->_backend->name
611             }
612         };
613     } elsif (     defined $params->{stage}
614                && $params->{stage} eq 'copyrightclearance' ) {
615         $params->{stage} = 'init';
616     }
617
618     # First perform API action, then...
619     my $args = {
620         request => $self,
621         other   => $params,
622     };
623     my $result = $self->_backend->create($args);
624
625     # ... simple case: we're not at 'commit' stage.
626     my $stage = $result->{stage};
627     return $self->expandTemplate($result)
628         unless ( 'commit' eq $stage );
629
630     # ... complex case: commit!
631
632     # Do we still have space for an ILL or should we queue?
633     my $permitted = $self->check_limits(
634         { patron => $self->patron }, { librarycode => $self->branchcode }
635     );
636
637     # Now augment our committed request.
638
639     $result->{permitted} = $permitted;             # Queue request?
640
641     # This involves...
642
643     # ...Updating status!
644     $self->status('QUEUED')->store unless ( $permitted );
645
646     return $self->expandTemplate($result);
647 }
648
649 =head3 expandTemplate
650
651     my $params = $abstract->expandTemplate($params);
652
653 Return a version of $PARAMS augmented with our required template path.
654
655 =cut
656
657 sub expandTemplate {
658     my ( $self, $params ) = @_;
659     my $backend = $self->_backend->name;
660     # Generate path to file to load
661     my $backend_dir = $self->_config->backend_dir;
662     my $backend_tmpl = join "/", $backend_dir, $backend;
663     my $intra_tmpl =  join "/", $backend_tmpl, "intra-includes",
664         $params->{method} . ".inc";
665     my $opac_tmpl =  join "/", $backend_tmpl, "opac-includes",
666         $params->{method} . ".inc";
667     # Set files to load
668     $params->{template} = $intra_tmpl;
669     $params->{opac_template} = $opac_tmpl;
670     return $params;
671 }
672
673 #### Abstract Imports
674
675 =head3 getLimits
676
677     my $limit_rules = $abstract->getLimits( {
678         type  => 'brw_cat' | 'branch',
679         value => $value
680     } );
681
682 Return the ILL limit rules for the supplied combination of type / value.
683
684 As the config may have no rules for this particular type / value combination,
685 or for the default, we must define fall-back values here.
686
687 =cut
688
689 sub getLimits {
690     my ( $self, $params ) = @_;
691     my $limits = $self->_config->getLimitRules($params->{type});
692
693     if (     defined $params->{value}
694           && defined $limits->{$params->{value}} ) {
695             return $limits->{$params->{value}};
696     }
697     else {
698         return $limits->{default} || { count => -1, method => 'active' };
699     }
700 }
701
702 =head3 getPrefix
703
704     my $prefix = $abstract->getPrefix( {
705         brw_cat => $brw_cat,
706         branch  => $branch_code,
707     } );
708
709 Return the ILL prefix as defined by our $params: either per borrower category,
710 per branch or the default.
711
712 =cut
713
714 sub getPrefix {
715     my ( $self, $params ) = @_;
716     my $brn_prefixes = $self->_config->getPrefixes('branch');
717     my $brw_prefixes = $self->_config->getPrefixes('brw_cat');
718
719     return $brw_prefixes->{$params->{brw_cat}}
720         || $brn_prefixes->{$params->{branch}}
721         || $brw_prefixes->{default}
722         || "";                  # "the empty prefix"
723 }
724
725 #### Illrequests Imports
726
727 =head3 check_limits
728
729     my $ok = $illRequests->check_limits( {
730         borrower   => $borrower,
731         branchcode => 'branchcode' | undef,
732     } );
733
734 Given $PARAMS, a hashref containing a $borrower object and a $branchcode,
735 see whether we are still able to place ILLs.
736
737 LimitRules are derived from koha-conf.xml:
738  + default limit counts, and counting method
739  + branch specific limit counts & counting method
740  + borrower category specific limit counts & counting method
741  + err on the side of caution: a counting fail will cause fail, even if
742    the other counts passes.
743
744 =cut
745
746 sub check_limits {
747     my ( $self, $params ) = @_;
748     my $patron     = $params->{patron};
749     my $branchcode = $params->{librarycode} || $patron->branchcode;
750
751     # Establish maximum number of allowed requests
752     my ( $branch_rules, $brw_rules ) = (
753         $self->getLimits( {
754             type => 'branch',
755             value => $branchcode
756         } ),
757         $self->getLimits( {
758             type => 'brw_cat',
759             value => $patron->categorycode,
760         } ),
761     );
762     my ( $branch_limit, $brw_limit )
763         = ( $branch_rules->{count}, $brw_rules->{count} );
764     # Establish currently existing requests
765     my ( $branch_count, $brw_count ) = (
766         $self->_limit_counter(
767             $branch_rules->{method}, { branchcode => $branchcode }
768         ),
769         $self->_limit_counter(
770             $brw_rules->{method}, { borrowernumber => $patron->borrowernumber }
771         ),
772     );
773
774     # Compare and return
775     # A limit of -1 means no limit exists.
776     # We return blocked if either branch limit or brw limit is reached.
777     if ( ( $branch_limit != -1 && $branch_limit <= $branch_count )
778              || ( $brw_limit != -1 && $brw_limit <= $brw_count ) ) {
779         return 0;
780     } else {
781         return 1;
782     }
783 }
784
785 sub _limit_counter {
786     my ( $self, $method, $target ) = @_;
787
788     # Establish parameters of counts
789     my $resultset;
790     if ($method && $method eq 'annual') {
791         $resultset = Koha::Illrequests->search({
792             -and => [
793                 %{$target},
794                 \"YEAR(placed) = YEAR(NOW())"
795             ]
796         });
797     } else {                    # assume 'active'
798         # XXX: This status list is ugly. There should be a method in config
799         # to return these.
800         my $where = { status => { -not_in => [ 'QUEUED', 'COMP' ] } };
801         $resultset = Koha::Illrequests->search({ %{$target}, %{$where} });
802     }
803
804     # Fetch counts
805     return $resultset->count;
806 }
807
808 =head3 requires_moderation
809
810     my $status = $illRequest->requires_moderation;
811
812 Return the name of the status if moderation by staff is required; or 0
813 otherwise.
814
815 =cut
816
817 sub requires_moderation {
818     my ( $self ) = @_;
819     my $require_moderation = {
820         'CANCREQ' => 'CANCREQ',
821     };
822     return $require_moderation->{$self->status};
823 }
824
825 =head3 generic_confirm
826
827     my $stage_summary = $illRequest->generic_confirm;
828
829 Handle the generic_confirm extended method.  The first stage involves creating
830 a template email for the end user to edit in the browser.  The second stage
831 attempts to submit the email.
832
833 =cut
834
835 sub generic_confirm {
836     my ( $self, $params ) = @_;
837     my $branch = Koha::Libraries->find($params->{current_branchcode})
838         || die "Invalid current branchcode. Are you logged in as the database user?";
839     if ( !$params->{stage}|| $params->{stage} eq 'init' ) {
840         my $draft->{subject} = "ILL Request";
841         $draft->{body} = <<EOF;
842 Dear Sir/Madam,
843
844     We would like to request an interlibrary loan for a title matching the
845 following description:
846
847 EOF
848
849         my $details = $self->metadata;
850         while (my ($title, $value) = each %{$details}) {
851             $draft->{body} .= "  - " . $title . ": " . $value . "\n"
852                 if $value;
853         }
854         $draft->{body} .= <<EOF;
855
856 Please let us know if you are able to supply this to us.
857
858 Kind Regards
859
860 EOF
861
862         my @address = map { $branch->$_ }
863             qw/ branchname branchaddress1 branchaddress2 branchaddress3
864                 branchzip branchcity branchstate branchcountry branchphone
865                 branchemail /;
866         my $address = "";
867         foreach my $line ( @address ) {
868             $address .= $line . "\n" if $line;
869         }
870
871         $draft->{body} .= $address;
872
873         my $partners = Koha::Patrons->search({
874             categorycode => $self->_config->partner_code
875         });
876         return {
877             error   => 0,
878             status  => '',
879             message => '',
880             method  => 'generic_confirm',
881             stage   => 'draft',
882             value   => {
883                 draft    => $draft,
884                 partners => $partners,
885             }
886         };
887
888     } elsif ( 'draft' eq $params->{stage} ) {
889         # Create the to header
890         my $to = $params->{partners};
891         if ( defined $to ) {
892             $to =~ s/^\x00//;       # Strip leading NULLs
893             $to =~ s/\x00/; /;      # Replace others with '; '
894         }
895         die "No target email addresses found. Either select at least one partner or check your ILL partner library records." if ( !$to );
896         # Create the from, replyto and sender headers
897         my $from = $branch->branchemail;
898         my $replyto = $branch->branchreplyto || $from;
899         die "Your branch has no email address. Please set it."
900             if ( !$from );
901         # Create the email
902         my $message = Koha::Email->new;
903         my %mail = $message->create_message_headers(
904             {
905                 to          => $to,
906                 from        => $from,
907                 replyto     => $replyto,
908                 subject     => Encode::encode( "utf8", $params->{subject} ),
909                 message     => Encode::encode( "utf8", $params->{body} ),
910                 contenttype => 'text/plain',
911             }
912         );
913         # Send it
914         my $result = sendmail(%mail);
915         if ( $result ) {
916             $self->status("GENREQ")->store;
917             return {
918                 error   => 0,
919                 status  => '',
920                 message => '',
921                 method  => 'generic_confirm',
922                 stage   => 'commit',
923                 next    => 'illview',
924             };
925         } else {
926             return {
927                 error   => 1,
928                 status  => 'email_failed',
929                 message => $Mail::Sendmail::error,
930                 method  => 'generic_confirm',
931                 stage   => 'draft',
932             };
933         }
934     } else {
935         die "Unknown stage, should not have happened."
936     }
937 }
938
939 =head3 id_prefix
940
941     my $prefix = $record->id_prefix;
942
943 Return the prefix appropriate for the current Illrequest as derived from the
944 borrower and branch associated with this request's Status, and the config
945 file.
946
947 =cut
948
949 sub id_prefix {
950     my ( $self ) = @_;
951     my $brw = $self->patron;
952     my $brw_cat = "dummy";
953     $brw_cat = $brw->categorycode
954         unless ( 'HASH' eq ref($brw) && $brw->{deleted} );
955     my $prefix = $self->getPrefix( {
956         brw_cat => $brw_cat,
957         branch  => $self->branchcode,
958     } );
959     $prefix .= "-" if ( $prefix );
960     return $prefix;
961 }
962
963 =head3 _censor
964
965     my $params = $illRequest->_censor($params);
966
967 Return $params, modified to reflect our censorship requirements.
968
969 =cut
970
971 sub _censor {
972     my ( $self, $params ) = @_;
973     my $censorship = $self->_config->censorship;
974     $params->{censor_notes_staff} = $censorship->{censor_notes_staff}
975         if ( $params->{opac} );
976     $params->{display_reply_date} = ( $censorship->{censor_reply_date} ) ? 0 : 1;
977
978     return $params;
979 }
980
981 =head3 TO_JSON
982
983     $json = $illrequest->TO_JSON
984
985 Overloaded I<TO_JSON> method that takes care of inserting calculated values
986 into the unblessed representation of the object.
987
988 =cut
989
990 sub TO_JSON {
991     my ( $self, $embed ) = @_;
992
993     my $object = $self->SUPER::TO_JSON();
994     $object->{id_prefix} = $self->id_prefix;
995
996     if ( scalar (keys %$embed) ) {
997         # Augment the request response with patron details if appropriate
998         if ( $embed->{patron} ) {
999             my $patron = $self->patron;
1000             $object->{patron} = {
1001                 firstname  => $patron->firstname,
1002                 surname    => $patron->surname,
1003                 cardnumber => $patron->cardnumber
1004             };
1005         }
1006         # Augment the request response with metadata details if appropriate
1007         if ( $embed->{metadata} ) {
1008             $object->{metadata} = $self->metadata;
1009         }
1010         # Augment the request response with status details if appropriate
1011         if ( $embed->{capabilities} ) {
1012             $object->{capabilities} = $self->capabilities;
1013         }
1014         # Augment the request response with library details if appropriate
1015         if ( $embed->{branch} ) {
1016             $object->{branch} = Koha::Libraries->find(
1017                 $self->branchcode
1018             )->TO_JSON;
1019         }
1020     }
1021
1022     return $object;
1023 }
1024
1025 =head2 Internal methods
1026
1027 =head3 _type
1028
1029 =cut
1030
1031 sub _type {
1032     return 'Illrequest';
1033 }
1034
1035 =head1 AUTHOR
1036
1037 Alex Sassmannshausen <alex.sassmannshausen@ptfs-europe.com>
1038
1039 =cut
1040
1041 1;