1# Vend::Options::Old48 - Interchange 4.8 compatible product options 2# 3# $Id: Old48.pm,v 1.14 2007-08-09 13:40:55 pajamian Exp $ 4# 5# Copyright (C) 2002-2007 Interchange Development Group <interchange@icdevgroup.org> 6# Copyright (C) 2002-2003 Mike Heins <mikeh@perusion.net> 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 24package Vend::Options::Old48; 25 26$VERSION = substr(q$Revision: 1.14 $, 10); 27 28=head1 NAME 29 30Vend::Options::Old48 - Interchange Compatibility Options Support 31 32=head1 SYNOPSIS 33 34 [item-options] 35 36 or 37 38 [price code=SKU] 39 40=head1 PREREQUISITES 41 42Vend::Options 43 44=head1 DESCRIPTION 45 46The Vend::Options::Old48 module implements simple and matrix product 47options for Interchange. It is compatible with Interchange 4.8.x 48matrix options. Newer versions use Simple and Matrix options instead. 49 50If the Interchange Variable MV_OPTION_TABLE is not set, it defaults 51to "options", which combines options for Simple, Matrix, and 52Modular into that one table. This goes along with foundation and 53construct demos up until Interchange 4.9.8. 54 55The "options" table remains the default for matrix options. 56 57=head1 AUTHORS 58 59Mike Heins <mikeh@perusion.net> 60 61=head1 CREDITS 62 63 Jon Jensen <jon@swelter.net> 64 65=cut 66 67use Vend::Util; 68use Vend::Data; 69use Vend::Interpolate; 70use Vend::Options; 71 72sub option_cost { 73 my ($item, $opt) = @_; 74 75} 76 77sub display_options_matrix { 78 my ($item, $opt, $loc) = @_; 79 80 $loc ||= $Vend::Cfg->{Options_repository}{Old48} || \%Default; 81#::logDebug("Matrix options by module, old"); 82 my $sku = $item->{mv_sku} || $item->{code}; 83 my $db; 84 my $tab; 85 86 if(not $db = $opt->{options_db}) { 87 $tab = $opt->{table} || $::Variable->{MV_OPTION_TABLE} || 'options'; 88 $db = database_exists_ref($tab) 89 or do { 90 logOnce( 91 "Matrix options: unable to find table %s for item %s", 92 $tab, 93 $sku, 94 ); 95 return undef; 96 }; 97 } 98 99 my $record; 100 if(not $record = $opt->{options_record}) { 101 $db->record_exists($sku) 102 or do { 103 logOnce( 104 "Matrix options: unable to find record in table %s for item %s", 105 $tab, 106 $sku, 107 ); 108 return; 109 }; 110 $record = $db->row_hash($sku) || {}; 111 } 112 113 my $tname = $db->name(); 114 115 if(not $opt->{display_type} ||= $record->{display_type}) { 116 $opt->{display_type} = $record->{o_matrix} == 2 ? 'separate' : 'single'; 117 } 118 119 $opt->{display_type} = lc $opt->{display_type}; 120 121 my $map; 122 if(not $map = $opt->{options_map}) { 123 $map = $opt->{options_map} = {}; 124 if(my $remap = $opt->{remap} || $::Variable->{MV_OPTION_TABLE_MAP}) { 125 remap_option_record($record, $map, $remap); 126 } 127 } 128 129 my @rf; 130 my @out; 131 my $out; 132 133 my $inv_func; 134 if($opt->{inventory}) { 135 my ($tab, $col) = split /:+/, $opt->{inventory}; 136 MAKEFUNC: { 137 my $idb = dbref($tab) 138 or do { 139 logError("Bad table %s for inventory function.", $tab); 140 last MAKEFUNC; 141 }; 142 $idb->test_column($col) 143 or do { 144 logError( 145 "Bad column %s in table %s for inventory function.", 146 $col, 147 $tab, 148 ); 149 last MAKEFUNC; 150 }; 151 $inv_func = sub { 152 my $key = shift; 153 return $idb->field($key, $col); 154 }; 155 } 156 } 157 158 my $rsort = find_sort($opt, $db, $loc); 159 160 if($opt->{display_type} eq 'separate') { 161 for(qw/code o_enable o_group o_value o_label o_widget price/) { 162 push @rf, ($map->{$_} || $_); 163 } 164 my @def; 165 if($item and $item->{code}) { 166 @def = split /-/, $item->{code}; 167 } 168 my $fsel = $map->{sku} || 'sku'; 169 my $rsel = $db->quote($sku, $fsel); 170 171 my $q = "SELECT " . 172 join (",", @rf) . 173 " FROM $tname where $fsel = $rsel $rsort"; 174#::logDebug("tag_options matrix query: $q"); 175 my $ary = $db->query($q); 176#::logDebug("tag_options matrix ary: " . ::uneval($ary)); 177 my $ref; 178 my $i = 0; 179 my $phony = { %{$item || { }} }; 180 foreach $ref (@$ary) { 181 182 next unless $ref->[3]; 183 184 # skip based on inventory if enabled 185 if($inv_func) { 186 my $oh = $inv_func->($ref->[0]); 187 next if $oh <= 0; 188 } 189 190 $i++; 191 192 # skip unless o_value 193 $phony->{mv_sku} = $def[$i]; 194 195 if ($opt->{label}) { 196 $ref->[4] = "<b>$ref->[4]</b>" if $opt->{bold}; 197 push @out, $ref->[4]; 198 } 199 push @out, Vend::Interpolate::tag_accessories( 200 $sku, 201 '', 202 { 203 passed => $ref->[3], 204 type => $opt->{type} || $ref->[5] || 'select', 205 attribute => 'mv_sku', 206 price_data => $ref->[6], 207 price => $opt->{price}, 208 extra => $opt->{extra}, 209 js => $opt->{js}, 210 item => $phony, 211 }, 212 $phony || undef, 213 ); 214 } 215 216 $phony->{mv_sku} = $sku; 217 my $begin = Vend::Interpolate::tag_accessories( 218 $sku, 219 '', 220 { 221 type => 'hidden', 222 attribute => 'mv_sku', 223 item => $phony, 224 default => $sku, 225 }, 226 $phony, 227 ); 228 if($opt->{td}) { 229 for(@out) { 230 $out .= "<td>$begin$_</td>"; 231 $begin = ''; 232 } 233 } 234 else { 235 $opt->{joiner} = "<br$Vend::Xtrailer>" if ! $opt->{joiner}; 236 $out .= $begin; 237 $out .= join $opt->{joiner}, @out; 238 } 239 } 240 else { 241 for(qw/code o_enable o_group description price weight volume differential o_widget/) { 242 push @rf, ($map->{$_} || $_); 243 } 244 my $ccol = $map->{code} || 'code'; 245 my $lcol = $map->{sku} || 'sku'; 246 my $lval = $db->quote($sku, $lcol); 247 248 my $q = "SELECT " . join(",", @rf); 249 $q .= " FROM $tname where $lcol = $lval AND $ccol <> $lval $rsort"; 250#::logDebug("tag_options matrix query: $q"); 251 my $ary = $db->query($q); 252#::logDebug("tag_options matrix ary: " . ::uneval($ary)); 253 my $ref; 254 my $price = {}; 255 foreach $ref (@$ary) { 256 # skip unless description 257 next unless $ref->[3]; 258 259 # skip based on inventory if enabled 260 if($inv_func) { 261 my $oh = $inv_func->($ref->[0]); 262 next if $oh <= 0; 263 } 264 265 $ref->[3] =~ s/,/,/g; 266 $ref->[3] =~ s/=/=/g; 267 $price->{$ref->[0]} = $ref->[4]; 268 push @out, "$ref->[0]=$ref->[3]"; 269 } 270 $out .= "<td>" if $opt->{td}; 271 $out .= Vend::Interpolate::tag_accessories( 272 $sku, 273 '', 274 { 275 attribute => 'code', 276 default => undef, 277 extra => $opt->{extra}, 278 item => $item, 279 js => $opt->{js}, 280 name => 'mv_sku', 281 passed => join(",", @out), 282 price => $opt->{price}, 283 price_data => $price, 284 type => $opt->{type} || $ref->[8] || 'select', 285 }, 286 $item || undef, 287 ); 288 $out .= "</td>" if $opt->{td}; 289#::logDebug("matrix option returning $out"); 290 } 291 292 return $out; 293} 294 295sub price_options { 296 my ($item, $table, $final, $loc) = @_; 297 298#::logDebug("option_cost table=$table"); 299 $loc ||= $Vend::Cfg->{Options_repository}{Old48} || {}; 300 301 my $sku = $item->{mv_sku} || $item->{code}; 302 my $db = database_exists_ref($table) 303 or return undef; 304#::logDebug("option_cost db=$db"); 305 306 my $map = $loc->{map} || {}; 307 my $fsel = $map->{sku} || 'sku'; 308 my $rsel = $db->quote($sku, $fsel); 309 my @rf; 310 for(qw/o_group price/) { 311 push @rf, ($map->{$_} || $_); 312 } 313 314 my $q = "SELECT " . join (",", @rf) . " FROM $table WHERE $fsel = $rsel"; 315#::logDebug("option_cost query=$q"); 316 my $ary = $db->query($q); 317 return if ! $ary->[0]; 318 my $ref; 319 my $price = 0; 320 my $f; 321 322 foreach $ref (@$ary) { 323#::logDebug("checking option " . uneval_it($ref)); 324 next unless defined $item->{$ref->[0]}; 325 $ref->[1] =~ s/^\s+//; 326 $ref->[1] =~ s/\s+$//; 327 $ref->[1] =~ s/==/=:/g; 328 my %info = split /\s*[=,]\s*/, $ref->[1]; 329 if(defined $info{ $item->{$ref->[0]} } ) { 330 my $atom = $info{ $item->{$ref->[0]} }; 331 if($atom =~ s/^://) { 332 $f = $atom; 333 next; 334 } 335 elsif ($atom =~ s/\%$//) { 336 $f = $final if ! defined $f; 337 $f += ($atom * $final / 100); 338 } 339 else { 340 $price += $atom; 341 } 342 } 343 } 344#::logDebug("option_cost returning price=$price f=$f"); 345 return ($price, $f); 346} 347 348sub display_options_simple { 349 my ($item, $opt) = @_; 350#::logDebug("Simple options, item=" . ::uneval($item) . "\nopt=" . ::uneval($opt)); 351 my $map = $opt->{options_map} ||= {}; 352#::logDebug("Simple options by module, old"); 353 354 my $sku = $item->{code}; 355 my $db; 356 my $tab; 357 if(not $db = $opt->{options_db}) { 358 $tab = $opt->{table} ||= $::Variable->{MV_OPTION_TABLE_SIMPLE} 359 ||= $::Variable->{MV_OPTION_TABLE} 360 ||= 'options'; 361 $db = database_exists_ref($tab) 362 or do { 363 logOnce( 364 "Simple options: unable to find table %s for item %s", 365 $tab, 366 $sku, 367 ); 368 return undef; 369 }; 370 } 371 372 my $tname = $db->name(); 373 374 my @rf; 375 my @out; 376 my $out; 377 378 my $ishash = defined $item->{mv_ip} ? 1 : 0; 379 380 for(qw/code o_enable o_group o_value o_label o_widget price o_height o_width/) { 381 push @rf, ($map->{$_} || $_); 382 } 383 384 my $fsel = $map->{sku} || 'sku'; 385 my $rsel = $db->quote($sku, $fsel); 386 387 my $q = "SELECT " . join (",", @rf) . " FROM $tname where $fsel = $rsel"; 388 389 if(my $rsort = find_sort($opt, $db, $loc)) { 390 $q .= " $rsort"; 391 } 392#::logDebug("tag_options simple query: $q"); 393 394 my $ary = $db->query($q) 395 or return; 396 397 my $ref; 398 foreach $ref (@$ary) { 399 # skip unless o_value 400 next unless $ref->[3]; 401 if ($opt->{label}) { 402 $ref->[4] = "<b>$ref->[4]</b>" if $opt->{bold}; 403 push @out, $ref->[4]; 404 } 405 my $precursor = $opt->{report} 406 ? "$ref->[2]$opt->{separator}" 407 : qq{<input type="hidden" name="mv_item_option" value="$ref->[2]">}; 408 push @out, $precursor . Vend::Interpolate::tag_accessories( 409 $sku, 410 '', 411 { 412 attribute => $ref->[2], 413 default => undef, 414 extra => $opt->{extra}, 415 item => $item, 416 name => $ishash ? undef : "mv_order_$ref->[2]", 417 js => $opt->{js}, 418 passed => $ref->[3], 419 price => $opt->{price}, 420 price_data => $ref->[6], 421 height => $opt->{height} || $ref->[7], 422 width => $opt->{width} || $ref->[8], 423 type => $opt->{type} || $ref->[5] || 'select', 424 }, 425 $item || undef, 426 ); 427 } 428 if($opt->{td}) { 429 for(@out) { 430 $out .= "<td>$_</td>"; 431 } 432 } 433 else { 434 $opt->{joiner} = "<br$Vend::Xtrailer>" if ! $opt->{joiner}; 435 $out .= join $opt->{joiner}, @out; 436 } 437 return $out; 438} 439 440*display_options = \&display_options_simple; 441 4421; 443