Bug 22411: Date and time in log viewer should respect
[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->{opac}) {
604         if ( ( !$params->{stage} || $params->{stage} eq 'init' )
605                 && C4::Context->preference("ILLModuleCopyrightClearance") ) {
606             return {
607                 error   => 0,
608                 status  => '',
609                 message => '',
610                 method  => 'create',
611                 stage   => 'copyrightclearance',
612                 value   => {
613                     backend => $self->_backend->name
614                 }
615             };
616         } elsif (     defined $params->{stage}
617                 && $params->{stage} eq 'copyrightclearance' ) {
618             $params->{stage} = 'init';
619         }
620     }
621     # First perform API action, then...
622     my $args = {
623         request => $self,
624         other   => $params,
625     };
626     my $result = $self->_backend->create($args);
627
628     # ... simple case: we're not at 'commit' stage.
629     my $stage = $result->{stage};
630     return $self->expandTemplate($result)
631         unless ( 'commit' eq $stage );
632
633     # ... complex case: commit!
634
635     # Do we still have space for an ILL or should we queue?
636     my $permitted = $self->check_limits(
637         { patron => $self->patron }, { librarycode => $self->branchcode }
638     );
639
640     # Now augment our committed request.
641
642     $result->{permitted} = $permitted;             # Queue request?
643
644     # This involves...
645
646     # ...Updating status!
647     $self->status('QUEUED')->store unless ( $permitted );
648
649     return $self->expandTemplate($result);
650 }
651
652 =head3 expandTemplate
653
654     my $params = $abstract->expandTemplate($params);
655
656 Return a version of $PARAMS augmented with our required template path.
657
658 =cut
659
660 sub expandTemplate {
661     my ( $self, $params ) = @_;
662     my $backend = $self->_backend->name;
663     # Generate path to file to load
664     my $backend_dir = $self->_config->backend_dir;
665     my $backend_tmpl = join "/", $backend_dir, $backend;
666     my $intra_tmpl =  join "/", $backend_tmpl, "intra-includes",
667         $params->{method} . ".inc";
668     my $opac_tmpl =  join "/", $backend_tmpl, "opac-includes",
669         $params->{method} . ".inc";
670     # Set files to load
671     $params->{template} = $intra_tmpl;
672     $params->{opac_template} = $opac_tmpl;
673     return $params;
674 }
675
676 #### Abstract Imports
677
678 =head3 getLimits
679
680     my $limit_rules = $abstract->getLimits( {
681         type  => 'brw_cat' | 'branch',
682         value => $value
683     } );
684
685 Return the ILL limit rules for the supplied combination of type / value.
686
687 As the config may have no rules for this particular type / value combination,
688 or for the default, we must define fall-back values here.
689
690 =cut
691
692 sub getLimits {
693     my ( $self, $params ) = @_;
694     my $limits = $self->_config->getLimitRules($params->{type});
695
696     if (     defined $params->{value}
697           && defined $limits->{$params->{value}} ) {
698             return $limits->{$params->{value}};
699     }
700     else {
701         return $limits->{default} || { count => -1, method => 'active' };
702     }
703 }
704
705 =head3 getPrefix
706
707     my $prefix = $abstract->getPrefix( {
708         brw_cat => $brw_cat,
709         branch  => $branch_code,
710     } );
711
712 Return the ILL prefix as defined by our $params: either per borrower category,
713 per branch or the default.
714
715 =cut
716
717 sub getPrefix {
718     my ( $self, $params ) = @_;
719     my $brn_prefixes = $self->_config->getPrefixes('branch');
720     my $brw_prefixes = $self->_config->getPrefixes('brw_cat');
721
722     return $brw_prefixes->{$params->{brw_cat}}
723         || $brn_prefixes->{$params->{branch}}
724         || $brw_prefixes->{default}
725         || "";                  # "the empty prefix"
726 }
727
728 #### Illrequests Imports
729
730 =head3 check_limits
731
732     my $ok = $illRequests->check_limits( {
733         borrower   => $borrower,
734         branchcode => 'branchcode' | undef,
735     } );
736
737 Given $PARAMS, a hashref containing a $borrower object and a $branchcode,
738 see whether we are still able to place ILLs.
739
740 LimitRules are derived from koha-conf.xml:
741  + default limit counts, and counting method
742  + branch specific limit counts & counting method
743  + borrower category specific limit counts & counting method
744  + err on the side of caution: a counting fail will cause fail, even if
745    the other counts passes.
746
747 =cut
748
749 sub check_limits {
750     my ( $self, $params ) = @_;
751     my $patron     = $params->{patron};
752     my $branchcode = $params->{librarycode} || $patron->branchcode;
753
754     # Establish maximum number of allowed requests
755     my ( $branch_rules, $brw_rules ) = (
756         $self->getLimits( {
757             type => 'branch',
758             value => $branchcode
759         } ),
760         $self->getLimits( {
761             type => 'brw_cat',
762             value => $patron->categorycode,
763         } ),
764     );
765     my ( $branch_limit, $brw_limit )
766         = ( $branch_rules->{count}, $brw_rules->{count} );
767     # Establish currently existing requests
768     my ( $branch_count, $brw_count ) = (
769         $self->_limit_counter(
770             $branch_rules->{method}, { branchcode => $branchcode }
771         ),
772         $self->_limit_counter(
773             $brw_rules->{method}, { borrowernumber => $patron->borrowernumber }
774         ),
775     );
776
777     # Compare and return
778     # A limit of -1 means no limit exists.
779     # We return blocked if either branch limit or brw limit is reached.
780     if ( ( $branch_limit != -1 && $branch_limit <= $branch_count )
781              || ( $brw_limit != -1 && $brw_limit <= $brw_count ) ) {
782         return 0;
783     } else {
784         return 1;
785     }
786 }
787
788 sub _limit_counter {
789     my ( $self, $method, $target ) = @_;
790
791     # Establish parameters of counts
792     my $resultset;
793     if ($method && $method eq 'annual') {
794         $resultset = Koha::Illrequests->search({
795             -and => [
796                 %{$target},
797                 \"YEAR(placed) = YEAR(NOW())"
798             ]
799         });
800     } else {                    # assume 'active'
801         # XXX: This status list is ugly. There should be a method in config
802         # to return these.
803         my $where = { status => { -not_in => [ 'QUEUED', 'COMP' ] } };
804         $resultset = Koha::Illrequests->search({ %{$target}, %{$where} });
805     }
806
807     # Fetch counts
808     return $resultset->count;
809 }
810
811 =head3 requires_moderation
812
813     my $status = $illRequest->requires_moderation;
814
815 Return the name of the status if moderation by staff is required; or 0
816 otherwise.
817
818 =cut
819
820 sub requires_moderation {
821     my ( $self ) = @_;
822     my $require_moderation = {
823         'CANCREQ' => 'CANCREQ',
824     };
825     return $require_moderation->{$self->status};
826 }
827
828 =head3 generic_confirm
829
830     my $stage_summary = $illRequest->generic_confirm;
831
832 Handle the generic_confirm extended method.  The first stage involves creating
833 a template email for the end user to edit in the browser.  The second stage
834 attempts to submit the email.
835
836 =cut
837
838 sub generic_confirm {
839     my ( $self, $params ) = @_;
840     my $branch = Koha::Libraries->find($params->{current_branchcode})
841         || die "Invalid current branchcode. Are you logged in as the database user?";
842     if ( !$params->{stage}|| $params->{stage} eq 'init' ) {
843         my $draft->{subject} = "ILL Request";
844         $draft->{body} = <<EOF;
845 Dear Sir/Madam,
846
847     We would like to request an interlibrary loan for a title matching the
848 following description:
849
850 EOF
851
852         my $details = $self->metadata;
853         while (my ($title, $value) = each %{$details}) {
854             $draft->{body} .= "  - " . $title . ": " . $value . "\n"
855                 if $value;
856         }
857         $draft->{body} .= <<EOF;
858
859 Please let us know if you are able to supply this to us.
860
861 Kind Regards
862
863 EOF
864
865         my @address = map { $branch->$_ }
866             qw/ branchname branchaddress1 branchaddress2 branchaddress3
867                 branchzip branchcity branchstate branchcountry branchphone
868                 branchemail /;
869         my $address = "";
870         foreach my $line ( @address ) {
871             $address .= $line . "\n" if $line;
872         }
873
874         $draft->{body} .= $address;
875
876         my $partners = Koha::Patrons->search({
877             categorycode => $self->_config->partner_code
878         });
879         return {
880             error   => 0,
881             status  => '',
882             message => '',
883             method  => 'generic_confirm',
884             stage   => 'draft',
885             value   => {
886                 draft    => $draft,
887                 partners => $partners,
888             }
889         };
890
891     } elsif ( 'draft' eq $params->{stage} ) {
892         # Create the to header
893         my $to = $params->{partners};
894         if ( defined $to ) {
895             $to =~ s/^\x00//;       # Strip leading NULLs
896             $to =~ s/\x00/; /;      # Replace others with '; '
897         }
898         Koha::Exceptions::Ill::NoTargetEmail->throw(
899             "No target email addresses found. Either select at least one partner or check your ILL partner library records.")
900           if ( !$to );
901         # Create the from, replyto and sender headers
902         my $from = $branch->branchemail;
903         my $replyto = $branch->branchreplyto || $from;
904         Koha::Exceptions::Ill::NoLibraryEmail->throw(
905             "Your library has no usable email address. Please set it.")
906           if ( !$from );
907
908         # Create the email
909         my $message = Koha::Email->new;
910         my %mail = $message->create_message_headers(
911             {
912                 to          => $to,
913                 from        => $from,
914                 replyto     => $replyto,
915                 subject     => Encode::encode( "utf8", $params->{subject} ),
916                 message     => Encode::encode( "utf8", $params->{body} ),
917                 contenttype => 'text/plain',
918             }
919         );
920         # Send it
921         my $result = sendmail(%mail);
922         if ( $result ) {
923             $self->status("GENREQ")->store;
924             return {
925                 error   => 0,
926                 status  => '',
927                 message => '',
928                 method  => 'generic_confirm',
929                 stage   => 'commit',
930                 next    => 'illview',
931             };
932         } else {
933             return {
934                 error   => 1,
935                 status  => 'email_failed',
936                 message => $Mail::Sendmail::error,
937                 method  => 'generic_confirm',
938                 stage   => 'draft',
939             };
940         }
941     } else {
942         die "Unknown stage, should not have happened."
943     }
944 }
945
946 =head3 id_prefix
947
948     my $prefix = $record->id_prefix;
949
950 Return the prefix appropriate for the current Illrequest as derived from the
951 borrower and branch associated with this request's Status, and the config
952 file.
953
954 =cut
955
956 sub id_prefix {
957     my ( $self ) = @_;
958     my $brw = $self->patron;
959     my $brw_cat = "dummy";
960     $brw_cat = $brw->categorycode
961         unless ( 'HASH' eq ref($brw) && $brw->{deleted} );
962     my $prefix = $self->getPrefix( {
963         brw_cat => $brw_cat,
964         branch  => $self->branchcode,
965     } );
966     $prefix .= "-" if ( $prefix );
967     return $prefix;
968 }
969
970 =head3 _censor
971
972     my $params = $illRequest->_censor($params);
973
974 Return $params, modified to reflect our censorship requirements.
975
976 =cut
977
978 sub _censor {
979     my ( $self, $params ) = @_;
980     my $censorship = $self->_config->censorship;
981     $params->{censor_notes_staff} = $censorship->{censor_notes_staff}
982         if ( $params->{opac} );
983     $params->{display_reply_date} = ( $censorship->{censor_reply_date} ) ? 0 : 1;
984
985     return $params;
986 }
987
988 =head3 TO_JSON
989
990     $json = $illrequest->TO_JSON
991
992 Overloaded I<TO_JSON> method that takes care of inserting calculated values
993 into the unblessed representation of the object.
994
995 =cut
996
997 sub TO_JSON {
998     my ( $self, $embed ) = @_;
999
1000     my $object = $self->SUPER::TO_JSON();
1001     $object->{id_prefix} = $self->id_prefix;
1002
1003     if ( scalar (keys %$embed) ) {
1004         # Augment the request response with patron details if appropriate
1005         if ( $embed->{patron} ) {
1006             my $patron = $self->patron;
1007             $object->{patron} = {
1008                 firstname  => $patron->firstname,
1009                 surname    => $patron->surname,
1010                 cardnumber => $patron->cardnumber
1011             };
1012         }
1013         # Augment the request response with metadata details if appropriate
1014         if ( $embed->{metadata} ) {
1015             $object->{metadata} = $self->metadata;
1016         }
1017         # Augment the request response with status details if appropriate
1018         if ( $embed->{capabilities} ) {
1019             $object->{capabilities} = $self->capabilities;
1020         }
1021         # Augment the request response with library details if appropriate
1022         if ( $embed->{library} ) {
1023             $object->{library} = Koha::Libraries->find(
1024                 $self->branchcode
1025             )->TO_JSON;
1026         }
1027     }
1028
1029     return $object;
1030 }
1031
1032 =head2 Internal methods
1033
1034 =head3 _type
1035
1036 =cut
1037
1038 sub _type {
1039     return 'Illrequest';
1040 }
1041
1042 =head1 AUTHOR
1043
1044 Alex Sassmannshausen <alex.sassmannshausen@ptfs-europe.com>
1045
1046 =cut
1047
1048 1;