Bug 23261: Add notice for login/registering to RBdigital results
[koha.git] / C4 / Suggestions.pm
blobcab30805ac48a8063f1f886e4afb2d6dcca65559
1 package C4::Suggestions;
3 # Copyright 2000-2002 Katipo Communications
4 # Parts Copyright Biblibre 2011
6 # This file is part of Koha.
8 # Koha is free software; you can redistribute it and/or modify it
9 # under the terms of the GNU General Public License as published by
10 # the Free Software Foundation; either version 3 of the License, or
11 # (at your option) any later version.
13 # Koha is distributed in the hope that it will be useful, but
14 # WITHOUT ANY WARRANTY; without even the implied warranty of
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 # GNU General Public License for more details.
18 # You should have received a copy of the GNU General Public License
19 # along with Koha; if not, see <http://www.gnu.org/licenses>.
21 use strict;
23 #use warnings; FIXME - Bug 2505
24 use CGI qw ( -utf8 );
26 use C4::Context;
27 use C4::Output;
28 use C4::Debug;
29 use C4::Letters;
30 use C4::Biblio qw( GetMarcFromKohaField );
31 use Koha::DateUtils;
32 use Koha::Suggestions;
34 use List::MoreUtils qw(any);
35 use base qw(Exporter);
37 our @EXPORT = qw(
38 ConnectSuggestionAndBiblio
39 CountSuggestion
40 DelSuggestion
41 GetSuggestion
42 GetSuggestionByStatus
43 GetSuggestionFromBiblionumber
44 GetSuggestionInfoFromBiblionumber
45 GetSuggestionInfo
46 ModStatus
47 ModSuggestion
48 NewSuggestion
49 SearchSuggestion
50 DelSuggestionsOlderThan
51 GetUnprocessedSuggestions
52 MarcRecordFromNewSuggestion
55 =head1 NAME
57 C4::Suggestions - Some useful functions for dealings with aqorders.
59 =head1 SYNOPSIS
61 use C4::Suggestions;
63 =head1 DESCRIPTION
65 The functions in this module deal with the aqorders in OPAC and in librarian interface
67 A suggestion is done in the OPAC. It has the status "ASKED"
69 When a librarian manages the suggestion, they can set the status to "REJECTED" or "ACCEPTED".
71 When the book is ordered, the suggestion status becomes "ORDERED"
73 When a book is ordered and arrived in the library, the status becomes "AVAILABLE"
75 All aqorders of a borrower can be seen by the borrower itself.
76 Suggestions done by other borrowers can be seen when not "AVAILABLE"
78 =head1 FUNCTIONS
80 =head2 SearchSuggestion
82 (\@array) = &SearchSuggestion($suggestionhashref_to_search)
84 searches for a suggestion
86 return :
87 C<\@array> : the aqorders found. Array of hash.
88 Note the status is stored twice :
89 * in the status field
90 * as parameter ( for example ASKED => 1, or REJECTED => 1) . This is for template & translation purposes.
92 =cut
94 sub SearchSuggestion {
95 my ($suggestion) = @_;
96 my $dbh = C4::Context->dbh;
97 my @sql_params;
98 my @query = (
100 SELECT suggestions.*,
101 U1.branchcode AS branchcodesuggestedby,
102 B1.branchname AS branchnamesuggestedby,
103 U1.surname AS surnamesuggestedby,
104 U1.firstname AS firstnamesuggestedby,
105 U1.cardnumber AS cardnumbersuggestedby,
106 U1.email AS emailsuggestedby,
107 U1.borrowernumber AS borrnumsuggestedby,
108 U1.categorycode AS categorycodesuggestedby,
109 C1.description AS categorydescriptionsuggestedby,
110 U2.surname AS surnamemanagedby,
111 U2.firstname AS firstnamemanagedby,
112 B2.branchname AS branchnamesuggestedby,
113 U2.email AS emailmanagedby,
114 U2.branchcode AS branchcodemanagedby,
115 U2.borrowernumber AS borrnummanagedby,
116 BU.budget_name AS budget_name
117 FROM suggestions
118 LEFT JOIN borrowers AS U1 ON suggestedby=U1.borrowernumber
119 LEFT JOIN branches AS B1 ON B1.branchcode=U1.branchcode
120 LEFT JOIN categories AS C1 ON C1.categorycode=U1.categorycode
121 LEFT JOIN borrowers AS U2 ON managedby=U2.borrowernumber
122 LEFT JOIN branches AS B2 ON B2.branchcode=U2.branchcode
123 LEFT JOIN categories AS C2 ON C2.categorycode=U2.categorycode
124 LEFT JOIN aqbudgets AS BU ON budgetid=BU.budget_id
125 WHERE 1=1
129 # filter on biblio informations
130 foreach my $field (
131 qw( title author isbn publishercode copyrightdate collectiontitle ))
133 if ( $suggestion->{$field} ) {
134 push @sql_params, '%' . $suggestion->{$field} . '%';
135 push @query, qq{ AND suggestions.$field LIKE ? };
139 # filter on user branch
140 if ( C4::Context->preference('IndependentBranches')
141 && !C4::Context->IsSuperLibrarian() )
143 # If IndependentBranches is set and the logged in user is not superlibrarian
144 # Then we want to filter by the user's library (i.e. cannot see suggestions from other libraries)
145 my $userenv = C4::Context->userenv;
146 if ($userenv) {
148 push @sql_params, $$userenv{branch};
149 push @query, q{
150 AND (suggestions.branchcode=? OR suggestions.branchcode='')
155 elsif (defined $suggestion->{branchcode}
156 && $suggestion->{branchcode}
157 && $suggestion->{branchcode} ne '__ANY__' )
159 # If IndependentBranches is not set OR the logged in user is not superlibrarian
160 # AND the branchcode filter is passed and not '__ANY__'
161 # Then we want to filter using this parameter
162 push @sql_params, $suggestion->{branchcode};
163 push @query, qq{ AND suggestions.branchcode=? };
166 # filter on nillable fields
167 foreach my $field (
168 qw( STATUS itemtype suggestedby managedby acceptedby budgetid biblionumber )
171 if ( exists $suggestion->{$field}
172 and defined $suggestion->{$field}
173 and $suggestion->{$field} ne '__ANY__'
174 and (
175 $suggestion->{$field} ne q||
176 or $field eq 'STATUS'
179 if ( $suggestion->{$field} eq '__NONE__' ) {
180 push @query, qq{ AND (suggestions.$field = '' OR suggestions.$field IS NULL) };
182 else {
183 push @sql_params, $suggestion->{$field};
184 push @query, qq{ AND suggestions.$field = ? };
189 # filter on date fields
190 foreach my $field (qw( suggesteddate manageddate accepteddate )) {
191 my $from = $field . "_from";
192 my $to = $field . "_to";
193 my $from_dt;
194 $from_dt = eval { dt_from_string( $suggestion->{$from} ) } if ( $suggestion->{$from} );
195 my $from_sql = '0000-00-00';
196 $from_sql = output_pref({ dt => $from_dt, dateformat => 'iso', dateonly => 1 })
197 if ($from_dt);
198 $debug && warn "SQL for start date ($field): $from_sql";
199 if ( $suggestion->{$from} || $suggestion->{$to} ) {
200 push @query, qq{ AND suggestions.$field BETWEEN ? AND ? };
201 push @sql_params, $from_sql;
202 push @sql_params,
203 output_pref({ dt => dt_from_string( $suggestion->{$to} ), dateformat => 'iso', dateonly => 1 }) || output_pref({ dt => dt_from_string, dateformat => 'iso', dateonly => 1 });
207 $debug && warn "@query";
208 my $sth = $dbh->prepare("@query");
209 $sth->execute(@sql_params);
210 my @results;
212 # add status as field
213 while ( my $data = $sth->fetchrow_hashref ) {
214 $data->{ $data->{STATUS} } = 1;
215 push( @results, $data );
218 return ( \@results );
221 =head2 GetSuggestion
223 \%sth = &GetSuggestion($suggestionid)
225 this function get the detail of the suggestion $suggestionid (input arg)
227 return :
228 the result of the SQL query as a hash : $sth->fetchrow_hashref.
230 =cut
232 sub GetSuggestion {
233 my ($suggestionid) = @_;
234 my $dbh = C4::Context->dbh;
235 my $query = q{
236 SELECT *
237 FROM suggestions
238 WHERE suggestionid=?
240 my $sth = $dbh->prepare($query);
241 $sth->execute($suggestionid);
242 return ( $sth->fetchrow_hashref );
245 =head2 GetSuggestionFromBiblionumber
247 $ordernumber = &GetSuggestionFromBiblionumber($biblionumber)
249 Get a suggestion from it's biblionumber.
251 return :
252 the id of the suggestion which is related to the biblionumber given on input args.
254 =cut
256 sub GetSuggestionFromBiblionumber {
257 my ($biblionumber) = @_;
258 my $query = q{
259 SELECT suggestionid
260 FROM suggestions
261 WHERE biblionumber=? LIMIT 1
263 my $dbh = C4::Context->dbh;
264 my $sth = $dbh->prepare($query);
265 $sth->execute($biblionumber);
266 my ($suggestionid) = $sth->fetchrow;
267 return $suggestionid;
270 =head2 GetSuggestionInfoFromBiblionumber
272 Get a suggestion and borrower's informations from it's biblionumber.
274 return :
275 all informations (suggestion and borrower) of the suggestion which is related to the biblionumber given.
277 =cut
279 sub GetSuggestionInfoFromBiblionumber {
280 my ($biblionumber) = @_;
281 my $query = q{
282 SELECT suggestions.*,
283 U1.surname AS surnamesuggestedby,
284 U1.firstname AS firstnamesuggestedby,
285 U1.borrowernumber AS borrnumsuggestedby
286 FROM suggestions
287 LEFT JOIN borrowers AS U1 ON suggestedby=U1.borrowernumber
288 WHERE biblionumber=?
289 LIMIT 1
291 my $dbh = C4::Context->dbh;
292 my $sth = $dbh->prepare($query);
293 $sth->execute($biblionumber);
294 return $sth->fetchrow_hashref;
297 =head2 GetSuggestionInfo
299 Get a suggestion and borrower's informations from it's suggestionid
301 return :
302 all informations (suggestion and borrower) of the suggestion which is related to the suggestionid given.
304 =cut
306 sub GetSuggestionInfo {
307 my ($suggestionid) = @_;
308 my $query = q{
309 SELECT suggestions.*,
310 U1.surname AS surnamesuggestedby,
311 U1.firstname AS firstnamesuggestedby,
312 U1.borrowernumber AS borrnumsuggestedby
313 FROM suggestions
314 LEFT JOIN borrowers AS U1 ON suggestedby=U1.borrowernumber
315 WHERE suggestionid=?
316 LIMIT 1
318 my $dbh = C4::Context->dbh;
319 my $sth = $dbh->prepare($query);
320 $sth->execute($suggestionid);
321 return $sth->fetchrow_hashref;
324 =head2 GetSuggestionByStatus
326 $aqorders = &GetSuggestionByStatus($status,[$branchcode])
328 Get a suggestion from it's status
330 return :
331 all the suggestion with C<$status>
333 =cut
335 sub GetSuggestionByStatus {
336 my $status = shift;
337 my $branchcode = shift;
338 my $dbh = C4::Context->dbh;
339 my @sql_params = ($status);
340 my $query = q{
341 SELECT suggestions.*,
342 U1.surname AS surnamesuggestedby,
343 U1.firstname AS firstnamesuggestedby,
344 U1.branchcode AS branchcodesuggestedby,
345 B1.branchname AS branchnamesuggestedby,
346 U1.borrowernumber AS borrnumsuggestedby,
347 U1.categorycode AS categorycodesuggestedby,
348 C1.description AS categorydescriptionsuggestedby,
349 U2.surname AS surnamemanagedby,
350 U2.firstname AS firstnamemanagedby,
351 U2.borrowernumber AS borrnummanagedby
352 FROM suggestions
353 LEFT JOIN borrowers AS U1 ON suggestedby=U1.borrowernumber
354 LEFT JOIN borrowers AS U2 ON managedby=U2.borrowernumber
355 LEFT JOIN categories AS C1 ON C1.categorycode=U1.categorycode
356 LEFT JOIN branches AS B1 on B1.branchcode=U1.branchcode
357 WHERE status = ?
360 # filter on branch
361 if ( C4::Context->preference("IndependentBranches") || $branchcode ) {
362 my $userenv = C4::Context->userenv;
363 if ($userenv) {
364 unless ( C4::Context->IsSuperLibrarian() ) {
365 push @sql_params, $userenv->{branch};
366 $query .= q{ AND (U1.branchcode = ? OR U1.branchcode ='') };
369 if ($branchcode) {
370 push @sql_params, $branchcode;
371 $query .= q{ AND (U1.branchcode = ? OR U1.branchcode ='') };
375 my $sth = $dbh->prepare($query);
376 $sth->execute(@sql_params);
377 my $results;
378 $results = $sth->fetchall_arrayref( {} );
379 return $results;
382 =head2 CountSuggestion
384 &CountSuggestion($status)
386 Count the number of aqorders with the status given on input argument.
387 the arg status can be :
389 =over 2
391 =item * ASKED : asked by the user, not dealed by the librarian
393 =item * ACCEPTED : accepted by the librarian, but not yet ordered
395 =item * REJECTED : rejected by the librarian (definitive status)
397 =item * ORDERED : ordered by the librarian (acquisition module)
399 =back
401 return :
402 the number of suggestion with this status.
404 =cut
406 sub CountSuggestion {
407 my ($status) = @_;
408 my $dbh = C4::Context->dbh;
409 my $sth;
410 my $userenv = C4::Context->userenv;
411 if ( C4::Context->preference("IndependentBranches")
412 && !C4::Context->IsSuperLibrarian() )
414 my $query = q{
415 SELECT count(*)
416 FROM suggestions
417 LEFT JOIN borrowers ON borrowers.borrowernumber=suggestions.suggestedby
418 WHERE STATUS=?
419 AND (suggestions.branchcode='' OR suggestions.branchcode=?)
421 $sth = $dbh->prepare($query);
422 $sth->execute( $status, $userenv->{branch} );
424 else {
425 my $query = q{
426 SELECT count(*)
427 FROM suggestions
428 WHERE STATUS=?
430 $sth = $dbh->prepare($query);
431 $sth->execute($status);
433 my ($result) = $sth->fetchrow;
434 return $result;
437 =head2 NewSuggestion
440 &NewSuggestion($suggestion);
442 Insert a new suggestion on database with value given on input arg.
444 =cut
446 sub NewSuggestion {
447 my ($suggestion) = @_;
449 $suggestion->{STATUS} = "ASKED" unless $suggestion->{STATUS};
451 $suggestion->{suggesteddate} = dt_from_string unless $suggestion->{suggesteddate};
453 delete $suggestion->{branchcode} if $suggestion->{branchcode} eq '';
455 my $suggestion_object = Koha::Suggestion->new( $suggestion )->store;
456 my $suggestion_id = $suggestion_object->suggestionid;
458 my $emailpurchasesuggestions = C4::Context->preference("EmailPurchaseSuggestions");
459 if ($emailpurchasesuggestions) {
460 my $full_suggestion = GetSuggestion( $suggestion_id); # We should not need to refetch it!
461 if (
462 my $letter = C4::Letters::GetPreparedLetter(
463 module => 'suggestions',
464 letter_code => 'NEW_SUGGESTION',
465 tables => {
466 'branches' => $full_suggestion->{branchcode},
467 'borrowers' => $full_suggestion->{suggestedby},
468 'suggestions' => $full_suggestion,
473 my $toaddress;
474 if ( $emailpurchasesuggestions eq "BranchEmailAddress" ) {
475 my $library =
476 Koha::Libraries->find( $full_suggestion->{branchcode} );
477 $toaddress =
478 $library->branchreplyto
479 || $library->branchemail
480 || C4::Context->preference('ReplytoDefault')
481 || C4::Context->preference('KohaAdminEmailAddress');
483 elsif ( $emailpurchasesuggestions eq "KohaAdminEmailAddress" ) {
484 $toaddress = C4::Context->preference('ReplytoDefault')
485 || C4::Context->preference('KohaAdminEmailAddress');
487 else {
488 $toaddress =
489 C4::Context->preference($emailpurchasesuggestions)
490 || C4::Context->preference('ReplytoDefault')
491 || C4::Context->preference('KohaAdminEmailAddress');
494 C4::Letters::EnqueueLetter(
496 letter => $letter,
497 borrowernumber => $full_suggestion->{suggestedby},
498 suggestionid => $full_suggestion->{suggestionid},
499 to_address => $toaddress,
500 message_transport_type => 'email',
502 ) or warn "can't enqueue letter $letter";
506 return $suggestion_id;
509 =head2 ModSuggestion
511 &ModSuggestion($suggestion)
513 Modify the suggestion according to the hash passed by ref.
514 The hash HAS to contain suggestionid
515 Data not defined is not updated unless it is a note or sort1
516 Send a mail to notify the user that did the suggestion.
518 Note that there is no function to modify a suggestion.
520 =cut
522 sub ModSuggestion {
523 my ($suggestion) = @_;
524 return unless( $suggestion and defined($suggestion->{suggestionid}) );
526 my $suggestion_object = Koha::Suggestions->find( $suggestion->{suggestionid} );
527 eval { # FIXME Must raise an exception instead
528 $suggestion_object->set($suggestion)->store;
530 return 0 if $@;
532 if ( $suggestion->{STATUS} ) {
534 # fetch the entire updated suggestion so that we can populate the letter
535 my $full_suggestion = GetSuggestion( $suggestion->{suggestionid} );
536 my $patron = Koha::Patrons->find( $full_suggestion->{suggestedby} );
538 my $transport = (C4::Context->preference("FallbackToSMSIfNoEmail")) && ($patron->smsalertnumber) && (!$patron->email) ? 'sms' : 'email';
540 if (
541 my $letter = C4::Letters::GetPreparedLetter(
542 module => 'suggestions',
543 letter_code => $full_suggestion->{STATUS},
544 branchcode => $full_suggestion->{branchcode},
545 lang => $patron->lang,
546 tables => {
547 'branches' => $full_suggestion->{branchcode},
548 'borrowers' => $full_suggestion->{suggestedby},
549 'suggestions' => $full_suggestion,
550 'biblio' => $full_suggestion->{biblionumber},
555 C4::Letters::EnqueueLetter(
557 letter => $letter,
558 borrowernumber => $full_suggestion->{suggestedby},
559 suggestionid => $full_suggestion->{suggestionid},
560 LibraryName => C4::Context->preference("LibraryName"),
561 message_transport_type => $transport,
563 ) or warn "can't enqueue letter $letter";
566 return 1; # No useful if the exception is raised earlier
569 =head2 ConnectSuggestionAndBiblio
571 &ConnectSuggestionAndBiblio($ordernumber,$biblionumber)
573 connect a suggestion to an existing biblio
575 =cut
577 sub ConnectSuggestionAndBiblio {
578 my ( $suggestionid, $biblionumber ) = @_;
579 my $dbh = C4::Context->dbh;
580 my $query = q{
581 UPDATE suggestions
582 SET biblionumber=?
583 WHERE suggestionid=?
585 my $sth = $dbh->prepare($query);
586 $sth->execute( $biblionumber, $suggestionid );
589 =head2 DelSuggestion
591 &DelSuggestion($borrowernumber,$ordernumber)
593 Delete a suggestion. A borrower can delete a suggestion only if they are its owner.
595 =cut
597 sub DelSuggestion {
598 my ( $borrowernumber, $suggestionid, $type ) = @_;
599 my $dbh = C4::Context->dbh;
601 # check that the suggestion comes from the suggestor
602 my $query = q{
603 SELECT suggestedby
604 FROM suggestions
605 WHERE suggestionid=?
607 my $sth = $dbh->prepare($query);
608 $sth->execute($suggestionid);
609 my ($suggestedby) = $sth->fetchrow;
610 if ( $type eq 'intranet' || $suggestedby eq $borrowernumber ) {
611 my $queryDelete = q{
612 DELETE FROM suggestions
613 WHERE suggestionid=?
615 $sth = $dbh->prepare($queryDelete);
616 my $suggestiondeleted = $sth->execute($suggestionid);
617 return $suggestiondeleted;
621 =head2 DelSuggestionsOlderThan
622 &DelSuggestionsOlderThan($days)
624 Delete all suggestions older than TODAY-$days , that have be accepted or rejected.
625 We do now allow a negative number. If you want to delete all suggestions, just use Koha::Suggestions->delete or so.
627 =cut
629 sub DelSuggestionsOlderThan {
630 my ($days) = @_;
631 return unless $days && $days > 0;
632 my $dbh = C4::Context->dbh;
633 my $sth = $dbh->prepare(
635 DELETE FROM suggestions
636 WHERE STATUS<>'ASKED'
637 AND date < ADDDATE(NOW(), ?)
640 $sth->execute("-$days");
643 sub GetUnprocessedSuggestions {
644 my ( $number_of_days_since_the_last_modification ) = @_;
646 $number_of_days_since_the_last_modification ||= 0;
648 my $dbh = C4::Context->dbh;
650 my $s = $dbh->selectall_arrayref(q|
651 SELECT *
652 FROM suggestions
653 WHERE STATUS = 'ASKED'
654 AND budgetid IS NOT NULL
655 AND CAST(NOW() AS DATE) - INTERVAL ? DAY = CAST(suggesteddate AS DATE)
656 |, { Slice => {} }, $number_of_days_since_the_last_modification );
657 return $s;
660 =head2 MarcRecordFromNewSuggestion
662 $record = MarcRecordFromNewSuggestion ( $suggestion )
664 This function build a marc record object from a suggestion
666 =cut
668 sub MarcRecordFromNewSuggestion {
669 my ($suggestion) = @_;
670 my $record = MARC::Record->new();
672 my ($title_tag, $title_subfield) = GetMarcFromKohaField('biblio.title', '');
673 $record->append_fields(
674 MARC::Field->new($title_tag, ' ', ' ', $title_subfield => $suggestion->{title})
677 my ($author_tag, $author_subfield) = GetMarcFromKohaField('biblio.author', '');
678 if ($record->field( $author_tag )) {
679 $record->field( $author_tag )->add_subfields( $author_subfield => $suggestion->{author} );
681 else {
682 $record->append_fields(
683 MARC::Field->new($author_tag, ' ', ' ', $author_subfield => $suggestion->{author})
687 my ($it_tag, $it_subfield) = GetMarcFromKohaField('biblioitems.itemtype', '');
688 if ($record->field( $it_tag )) {
689 $record->field( $it_tag )->add_subfields( $it_subfield => $suggestion->{itemtype} );
691 else {
692 $record->append_fields(
693 MARC::Field->new($it_tag, ' ', ' ', $it_subfield => $suggestion->{itemtype})
697 return $record;
701 __END__
704 =head1 AUTHOR
706 Koha Development Team <http://koha-community.org/>
708 =cut