Make new warning suppression version-safe
[interchange.git] / lib / Vend / Options.pm
1 # Vend::Options - Interchange item options base module
2 #
3 # $Id: Options.pm,v 2.8 2007-08-09 13:40:53 pajamian Exp $
4 #
5 # Copyright (C) 2002-2007 Interchange Development Group
6 # Copyright (C) 1996-2002 Red Hat, Inc.
7 #
8 # This program is free software; you can redistribute it and/or modify
9 # it under the terms of the GNU General Public License as published by
10 # the Free Software Foundation; either version 2 of the License, or
11 # (at your option) any later version.
12 #
13 # This program is distributed in the hope that it will be useful,
14 # but 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.
17 #
18 # You should have received a copy of the GNU General Public
19 # License along with this program; if not, write to the Free
20 # Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
21 # MA  02110-1301  USA.
22
23 package Vend::Options;
24 require Exporter;
25
26 $VERSION = substr(q$Revision: 2.8 $, 10);
27
28 @ISA = qw(Exporter);
29
30 @EXPORT = qw(
31                                 find_joiner
32                                 find_options_type
33                                 find_sort
34                                 inventory_function
35                                 option_cost
36                                 remap_option_record
37                 );
38
39 use Vend::Util;
40 use Vend::Data;
41 use Vend::Interpolate;
42 use strict;
43
44 sub remap_option_record {
45         my ($record, $map) = @_;
46
47         my %rec;
48         my @del;
49         my ($k, $v);
50         while (($k, $v) = each %$map) {
51                 next unless defined $record->{$v};
52                 $rec{$k} = $record->{$v};
53                 push @del, $v;
54         }
55         delete @{$record}{@del};
56         @{$record}{keys %rec} = (values %rec);
57         
58         return;
59 }
60
61 sub find_options_type {
62         my ($item, $opt) = @_;
63
64         my $attrib;
65         return $item->{$attrib}
66                 if      $attrib = $Vend::Cfg->{OptionsAttribute}
67                 and defined $item->{$attrib};
68
69         my $sku = $item->{mv_sku} || $item->{code};
70
71         return unless defined $sku && $sku ne '';
72
73         $opt = get_option_hash($opt);
74
75         my $module;
76
77         if($Vend::Cfg->{OptionsEnable}) {
78                 my ($tab, $field) = split /:+/, $Vend::Cfg->{OptionsEnable};
79                 if(! $field) {
80                         $field = $tab;
81                         undef $tab;
82                 }
83                 elsif($tab =~ /=/) {
84                         my $att;
85                         ($att, $tab) = split /\s*=\s*/, $tab;
86                         $attrib ||= $att;
87                 }
88                 $attrib ||= $field;
89                 $Vend::Cfg->{OptionsAttribute} ||= $attrib;
90
91                 if(! defined $item->{$attrib}) {
92                         $tab = $item->{mv_ib} || product_code_exists_tag($sku)
93                                         or do {
94                                                 logOnce('error', "options: Unknown product %s.", $sku);
95                                                 return;
96                                         };
97                         $item->{$attrib} = tag_data($tab, $field, $sku);
98                 }
99                 $module = $item->{$attrib} || '';
100         }
101         else {
102                 ## Old style options
103                 my $loc = $Vend::Cfg->{Options_repository}{Old48} || {};
104                 my $table = $opt->{table}
105                                   ||= (
106                                                 $loc->{table} || $::Variable->{MV_OPTION_TABLE} || 'options'
107                                         );
108                 my $db = $Vend::Interpolate::Db{$table} || database_exists_ref($table)
109                                 or return;
110                 $db->record_exists($sku)
111                                 or return;
112                 my $record = $opt->{options_record} = $db->row_hash($sku)
113                                 or return;
114                 $opt->{options_db} = $db;
115                 remap_option_record($record, $loc->{map})
116                         if  $loc->{remap};
117
118                 return '' unless $record->{o_enable};
119
120                 $module = 'Old48';
121
122                 if($record->{o_matrix}) {
123                         $opt->{display_routine}
124                                 = 'Vend::Options::Old48::display_options_matrix';
125                 }
126                 elsif($record->{o_modular}) {
127                         $module = 'Modular';
128                 }
129                 else {
130                         $opt->{display_routine}
131                                 = 'Vend::Options::Old48::display_options_simple';
132                 }
133         }
134
135         return $module;
136 }
137
138 sub inventory_function {
139         my $opt = shift;
140         return unless $opt->{inventory};
141         my $inv_func;
142         my ($t, $c) = split /[.:]+/, $opt->{inventory};
143         my $idb;
144         if($idb = database_exists_ref($t)) {
145                 $inv_func = $idb->field_accessor($c);
146         }
147         return $inv_func;
148 }
149
150 sub find_joiner {
151         my $opt = shift;
152         if($opt->{report}) {
153                 $opt->{joiner}          ||= ', ';
154                 $opt->{separator}       ||= ': ';
155                 $opt->{type}            ||= 'display';
156         }
157         else {
158                 $opt->{joiner} ||= "<br$Vend::Xtrailer>";
159         }
160         return;
161 }
162
163 sub find_sort {
164         my $opt = shift;
165         my $db = shift;
166         my $loc = shift || $Vend::Cfg->{Options_repository}{$opt->{options_type}} || {};
167 #::logDebug("called find_sort from " . scalar(caller()) . ", opt=" . ::uneval($opt));
168         $opt->{sort} = defined $opt->{sort} ? $opt->{sort} : $loc->{sort};
169         return '' unless $opt->{sort};
170         return " ORDER BY $opt->{sort}" if $opt->{rawsort} || $loc->{rawsort};
171         my @fields = split /\s*,\s*/, $opt->{sort};
172         my $map = $loc->{map} ||= {};
173         for(@fields) {
174                 my $extra; 
175                 $extra = ' DESC' if s/\s+(r(?:ev(?:erse)?)?|desc(?:ending)?)//i;
176                 $_ = $map->{$_} || $_;
177                 unless (defined $db->test_column($_)) {
178                         logOnce(
179                                 "%s options sort field %s does not exist, returning unsorted",
180                                 'Matrix',
181                                 $_,
182                                 );
183                         return undef;
184                 }
185                 $_ .= $extra if $extra;
186         }
187
188         return " ORDER BY " . join(",", @fields);
189 }
190
191 sub tag_options {
192         my ($sku, $opt) = @_;
193         my $item;
194         if(ref $sku) {
195                 $item = $sku;
196                 $sku = $item->{mv_sku} || $item->{code};
197         }
198         $item ||= { code => $sku };
199         $opt = get_option_hash($opt);
200         find_joiner($opt);
201
202         my $module = find_options_type($item, $opt)
203                 or return '';
204         $opt->{options_type} = $module;
205 #::logDebug("tag_options module=$module");
206
207         my $loc = $Vend::Cfg->{Options_repository}{$module} || {};
208         no strict 'refs';
209         my $routine;
210         if($opt->{admin_page}) {
211                 $opt->{routine_description} ||= "admin page";
212                 $routine = $opt->{admin_page_routine}
213                         ||= "Vend::Options::${module}::admin_page";
214         }
215         else {
216                 $opt->{routine_description} ||= "display";
217                 $routine = $opt->{display_routine};
218                 $routine ||= $loc->{display_routine}
219                                 ||= "Vend::Options::${module}::display_options";
220 #::logDebug("tag_options display routine=$routine");
221         }
222         my $sub = \&{"$routine"};
223         if(! defined $sub) {
224                 ::logOnce(
225                         "Options type %s %s routine %s not found, aborting options for %s.",
226                         $module,
227                         $opt->{routine_description},
228                         $routine,
229                         $sku,
230                         );
231                 return undef;
232         }
233 #::logDebug("main tag_options item=" . ::uneval($item) . ", opt=" . ::uneval($opt));
234         return $sub->($item, $opt, $loc);
235 }
236
237 sub option_cost {
238         my ($item, $table, $final) = @_;
239
240         my $module = find_options_type($item)
241                 or return undef;
242 #::logDebug("price_options module=$module");
243         my $loc = $Vend::Cfg->{Options_repository}{$module} || {};
244         return undef if $loc->{no_pricing};
245         no strict 'refs';
246         my $routine = $loc->{price_routine};
247         $routine ||= "Vend::Options::${module}::price_options";
248         my $sub = \&{"$routine"};
249 #::logDebug("price_options sub=$sub");
250
251         if(! defined $sub) {
252                 ::logOnce(
253                         "Options type %s not found, aborting option_cost for %s.",
254                         $module,
255                         $item->{code},
256                         );
257                 return undef;
258         }
259         return $sub->($item, $table, $final, $loc);
260 }
261
262 1;
263 __END__