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