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