Bug 31281: Use correct reply-to email when sending overdue mails
[koha.git] / C4 / ShelfBrowser.pm
1 package C4::ShelfBrowser;
2
3 # Copyright 2010 Catalyst IT
4 #
5 # This file is part of Koha.
6 #
7 # Koha is free software; you can redistribute it and/or modify it
8 # under the terms of the GNU General Public License as published by
9 # the Free Software Foundation; either version 3 of the License, or
10 # (at your option) any later version.
11 #
12 # Koha is distributed in the hope that it will be useful, but
13 # WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 # GNU General Public License for more details.
16 #
17 # You should have received a copy of the GNU General Public License
18 # along with Koha; if not, see <http://www.gnu.org/licenses>.
19
20 use strict;
21 use warnings;
22
23 use C4::Biblio qw( GetAuthorisedValueDesc );
24 use C4::Context;
25 use C4::Koha qw( GetNormalizedUPC GetNormalizedOCLCNumber GetNormalizedISBN GetNormalizedEAN );
26 use Koha::Biblios;
27 use Koha::Libraries;
28
29 our (@ISA, @EXPORT_OK);
30 BEGIN {
31     require Exporter;
32     @ISA       = qw(Exporter);
33     @EXPORT_OK = qw(
34       GetNearbyItems
35     );
36 }
37
38 =head1 NAME
39
40 C4::ShelfBrowser - functions that deal with the shelf browser feature found in
41 the OPAC.
42
43 =head1 SYNOPSIS
44
45   use C4::ShelfBrowser;
46
47 =head1 DESCRIPTION
48
49 This module provides functions to get items nearby to another item, for use
50 in the shelf browser function.
51
52 'Nearby' is controlled by a handful of system preferences that specify what
53 to take into account.
54
55 =head1 FUNCTIONS
56
57 =head2 GetNearbyItems($itemnumber, [$num_each_side])
58
59   $nearby = GetNearbyItems($itemnumber, [$num_each_side]);
60
61   @items = @{ $nearby->{items} };
62
63   foreach (@items) {
64       # These won't format well like this, but here are the fields
65           print $_->{title};
66           print $_->{biblionumber};
67           print $_->{itemnumber};
68           print $_->{browser_normalized_upc};
69           print $_->{browser_normalized_oclc};
70           print $_->{browser_normalized_isbn};
71       print $_->{browser_normalized_ean};
72   }
73
74   # This is the information required to scroll the browser to the next left
75   # or right set. Can be derived from next/prev, but it's here for convenience.
76   print $nearby->{prev_item}{itemnumber};
77   print $nearby->{next_item}{itemnumber};
78   print $nearby->{prev_item}{biblionumber};
79   print $nearby->{next_item}{biblionumber};
80
81   # These will be undef if the values are not used to calculate the 
82   # nearby items.
83   print $nearby->{starting_homebranch}->{code};
84   print $nearby->{starting_homebranch}->{description};
85   print $nearby->{starting_location}->{code};
86   print $nearby->{starting_location}->{description};
87   print $nearby->{starting_ccode}->{code};
88   print $nearby->{starting_ccode}->{description};
89
90 This finds the items that are nearby to the supplied item, and supplies
91 those previous and next, along with the other useful information for displaying
92 the shelf browser.
93
94 It automatically applies the following user preferences to work out how to
95 calculate things: C<ShelfBrowserUsesLocation>, C<ShelfBrowserUsesHomeBranch>, 
96 C<ShelfBrowserUsesCcode>.
97
98 The option C<$num_each_side> value determines how many items will be fetched
99 each side of the supplied item. Note that the item itself is the first entry
100 in the 'next' set, and counts towards this limit (this is to keep the
101 behaviour consistent with the code that this is a refactor of.) Default is
102 3.
103
104 This will throw an exception if something went wrong.
105
106 =cut
107
108 sub GetNearbyItems {
109     my ( $itemnumber, $num_each_side, $gap) = @_;
110     $num_each_side ||= 3;
111     $gap ||= 7; # Should be > $num_each_side
112     die "BAD CALL in C4::ShelfBrowser::GetNearbyItems, gap should be > num_each_side"
113         if $gap <= $num_each_side;
114
115     my $dbh         = C4::Context->dbh;
116
117     my $sth_get_item_details = $dbh->prepare("SELECT cn_sort,homebranch,location,ccode from items where itemnumber=?");
118     $sth_get_item_details->execute($itemnumber);
119     my $item_details_result = $sth_get_item_details->fetchrow_hashref();
120     die "Unable to find item '$itemnumber' for shelf browser" if (!$sth_get_item_details);
121     my $start_cn_sort = $item_details_result->{'cn_sort'};
122
123     my ($start_homebranch, $start_location, $start_ccode);
124     if (C4::Context->preference('ShelfBrowserUsesHomeBranch') && 
125         defined($item_details_result->{'homebranch'})) {
126         $start_homebranch->{code} = $item_details_result->{'homebranch'};
127         $start_homebranch->{description} = Koha::Libraries->find($item_details_result->{'homebranch'})->branchname;
128     }
129     if (C4::Context->preference('ShelfBrowserUsesLocation') && 
130         defined($item_details_result->{'location'})) {
131         $start_location->{code} = $item_details_result->{'location'};
132         $start_location->{description} = GetAuthorisedValueDesc('','',$item_details_result->{'location'},'','','LOC','opac');
133     }
134     if (C4::Context->preference('ShelfBrowserUsesCcode') && 
135         defined($item_details_result->{'ccode'})) {
136         $start_ccode->{code} = $item_details_result->{'ccode'};
137         $start_ccode->{description} = GetAuthorisedValueDesc('', '', $item_details_result->{'ccode'}, '', '', 'CCODE', 'opac');
138     }
139
140     # Build the query for previous and next items
141     my $prev_query ='
142         SELECT itemnumber, biblionumber, cn_sort, itemcallnumber
143         FROM items
144         WHERE
145             ((cn_sort = ? AND itemnumber < ?) OR cn_sort < ?) ';
146     my $next_query ='
147         SELECT itemnumber, biblionumber, cn_sort, itemcallnumber
148         FROM items
149         WHERE
150             ((cn_sort = ? AND itemnumber >= ?) OR cn_sort > ?) ';
151     my @params;
152     my $query_cond;
153     push @params, ($start_cn_sort, $itemnumber, $start_cn_sort);
154     if ($start_homebranch) {
155         $query_cond .= 'AND homebranch = ? ';
156         push @params, $start_homebranch->{code};
157     }
158     if ($start_location) {
159         $query_cond .= 'AND location = ? ';
160         push @params, $start_location->{code};
161     }
162     if ($start_ccode) {
163         $query_cond .= 'AND ccode = ? ';
164         push @params, $start_ccode->{code};
165     }
166
167     my @prev_items = @{
168         $dbh->selectall_arrayref(
169             $prev_query . $query_cond . ' ORDER BY cn_sort DESC, itemnumber DESC LIMIT ?',
170             { Slice => {} },
171             ( @params, $gap )
172         )
173     };
174     my @next_items = @{
175         $dbh->selectall_arrayref(
176             $next_query . $query_cond . ' ORDER BY cn_sort, itemnumber LIMIT ?',
177             { Slice => {} },
178             ( @params, $gap + 1 )
179         )
180     };
181
182     my $prev_item = $prev_items[-1];
183     my $next_item = $next_items[-1];
184     @next_items = splice( @next_items, 0, $num_each_side + 1 );
185     @prev_items = reverse splice( @prev_items, 0, $num_each_side );
186     my @items = ( @prev_items, @next_items );
187
188     $next_item = undef
189         if not $next_item
190             or ( $next_item->{itemnumber} == $items[-1]->{itemnumber}
191                 and ( @prev_items or @next_items <= 1 )
192             );
193     $prev_item = undef
194         if not $prev_item
195             or ( $prev_item->{itemnumber} == $items[0]->{itemnumber}
196                 and ( @next_items or @prev_items <= 1 )
197             );
198
199     # populate the items
200     @items = GetShelfInfo( @items );
201
202     return {
203         items               => \@items,
204         next_item           => $next_item,
205         prev_item           => $prev_item,
206         starting_homebranch => $start_homebranch,
207         starting_location   => $start_location,
208         starting_ccode      => $start_ccode,
209     };
210 }
211
212 # populate an item list with its title and upc, oclc and isbn normalized.
213 # Not really intended to be exported.
214 sub GetShelfInfo {
215     my @items = @_;
216     my $marcflavour = C4::Context->preference("marcflavour");
217     my @valid_items;
218     for my $item ( @items ) {
219         my $biblio = Koha::Biblios->find( $item->{biblionumber} );
220         next unless defined $biblio;
221
222         $item->{biblio_object} = $biblio;
223         $item->{biblionumber}  = $biblio->biblionumber;
224         $item->{title}         = $biblio->title;
225         $item->{subtitle}      = $biblio->subtitle;
226         $item->{medium}        = $biblio->medium;
227         $item->{part_number}   = $biblio->part_number;
228         $item->{part_name}     = $biblio->part_name;
229         my $this_record = $biblio->metadata->record;
230         $item->{'browser_normalized_upc'} = GetNormalizedUPC($this_record,$marcflavour);
231         $item->{'browser_normalized_oclc'} = GetNormalizedOCLCNumber($this_record,$marcflavour);
232         $item->{'browser_normalized_isbn'} = GetNormalizedISBN(undef,$this_record,$marcflavour);
233         $item->{'browser_normalized_ean'} = GetNormalizedEAN($this_record,$marcflavour);
234         push @valid_items, $item;
235     }
236     return @valid_items;
237 }
238
239 1;