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
23package Vend::Options;
24require 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
39use Vend::Util;
40use Vend::Data;
41use Vend::Interpolate;
42use strict;
43
44sub 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
61sub 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	$opt = get_option_hash($opt);
72
73	my $module;
74
75	if($Vend::Cfg->{OptionsEnable}) {
76		my ($tab, $field) = split /:+/, $Vend::Cfg->{OptionsEnable};
77		if(! $field) {
78			$field = $tab;
79			undef $tab;
80		}
81		elsif($tab =~ /=/) {
82			my $att;
83			($att, $tab) = split /\s*=\s*/, $tab;
84			$attrib ||= $att;
85		}
86		$attrib ||= $field;
87		$Vend::Cfg->{OptionsAttribute} ||= $attrib;
88
89		if(! defined $item->{$attrib}) {
90			$tab = $item->{mv_ib} || product_code_exists_tag($sku)
91					or do {
92						logOnce('error', "options: Unknown product %s.", $sku);
93						return;
94					};
95			$item->{$attrib} = tag_data($tab, $field, $sku);
96		}
97		$module = $item->{$attrib} || '';
98	}
99	else {
100		## Old style options
101		my $loc = $Vend::Cfg->{Options_repository}{Old48} || {};
102		my $table = $opt->{table}
103				  ||= (
104				  		$loc->{table} || $::Variable->{MV_OPTION_TABLE} || 'options'
105					);
106		my $db = $Vend::Interpolate::Db{$table} || database_exists_ref($table)
107				or return;
108		$db->record_exists($sku)
109				or return;
110		my $record = $opt->{options_record} = $db->row_hash($sku)
111				or return;
112		$opt->{options_db} = $db;
113		remap_option_record($record, $loc->{map})
114			if  $loc->{remap};
115
116		return '' unless $record->{o_enable};
117
118		$module = 'Old48';
119
120		if($record->{o_matrix}) {
121			$opt->{display_routine}
122				= 'Vend::Options::Old48::display_options_matrix';
123		}
124		elsif($record->{o_modular}) {
125			$module = 'Modular';
126		}
127		else {
128			$opt->{display_routine}
129				= 'Vend::Options::Old48::display_options_simple';
130		}
131	}
132
133	return $module;
134}
135
136sub inventory_function {
137	my $opt = shift;
138	return unless $opt->{inventory};
139	my $inv_func;
140	my ($t, $c) = split /[.:]+/, $opt->{inventory};
141	my $idb;
142	if($idb = database_exists_ref($t)) {
143		$inv_func = $idb->field_accessor($c);
144	}
145	return $inv_func;
146}
147
148sub find_joiner {
149	my $opt = shift;
150	if($opt->{report}) {
151		$opt->{joiner}		||= ', ';
152		$opt->{separator} 	||= ': ';
153		$opt->{type}		||= 'display';
154	}
155	else {
156		$opt->{joiner} ||= "<br$Vend::Xtrailer>";
157	}
158	return;
159}
160
161sub find_sort {
162	my $opt = shift;
163	my $db = shift;
164	my $loc = shift || $Vend::Cfg->{Options_repository}{$opt->{options_type}} || {};
165#::logDebug("called find_sort from " . scalar(caller()) . ", opt=" . ::uneval($opt));
166	$opt->{sort} = defined $opt->{sort} ? $opt->{sort} : $loc->{sort};
167	return '' unless $opt->{sort};
168	my @fields = split /\s*,\s*/, $opt->{sort};
169	my $map = $loc->{map} ||= {};
170	for(@fields) {
171		my $extra;
172		$extra = ' DESC' if s/\s+(r(?:ev(?:erse)?)?|desc(?:ending)?)//i;
173		$_ = $map->{$_} || $_;
174		unless (defined $db->test_column($_)) {
175			logOnce(
176				"%s options sort field %s does not exist, returning unsorted",
177				'Matrix',
178				$_,
179				);
180			return undef;
181		}
182		$_ .= $extra if $extra;
183	}
184
185	return "ORDER BY " . join(",", @fields);
186}
187
188sub tag_options {
189	my ($sku, $opt) = @_;
190	my $item;
191	if(ref $sku) {
192		$item = $sku;
193		$sku = $item->{mv_sku} || $item->{code};
194	}
195	$item ||= { code => $sku };
196	$opt = get_option_hash($opt);
197	find_joiner($opt);
198
199	my $module = find_options_type($item, $opt)
200		or return '';
201	$opt->{options_type} = $module;
202#::logDebug("tag_options module=$module");
203
204	my $loc = $Vend::Cfg->{Options_repository}{$module} || {};
205	no strict 'refs';
206	my $routine;
207	if($opt->{admin_page}) {
208		$opt->{routine_description} ||= "admin page";
209		$routine = $opt->{admin_page_routine}
210			||= "Vend::Options::${module}::admin_page";
211	}
212	else {
213		$opt->{routine_description} ||= "display";
214		$routine = $opt->{display_routine};
215		$routine ||= $loc->{display_routine}
216				||= "Vend::Options::${module}::display_options";
217#::logDebug("tag_options display routine=$routine");
218	}
219	my $sub = \&{"$routine"};
220	if(! defined $sub) {
221		::logOnce(
222			"Options type %s %s routine %s not found, aborting options for %s.",
223			$module,
224			$opt->{routine_description},
225			$routine,
226			$sku,
227			);
228		return undef;
229	}
230#::logDebug("main tag_options item=" . ::uneval($item) . ", opt=" . ::uneval($opt));
231	return $sub->($item, $opt, $loc);
232}
233
234sub option_cost {
235	my ($item, $table, $final) = @_;
236
237	my $module = find_options_type($item)
238		or return undef;
239#::logDebug("price_options module=$module");
240	my $loc = $Vend::Cfg->{Options_repository}{$module} || {};
241	return undef if $loc->{no_pricing};
242	no strict 'refs';
243	my $routine = $loc->{price_routine};
244	$routine ||= "Vend::Options::${module}::price_options";
245	my $sub = \&{"$routine"};
246#::logDebug("price_options sub=$sub");
247
248	if(! defined $sub) {
249		::logOnce(
250			"Options type %s not found, aborting option_cost for %s.",
251			$module,
252			$item->{code},
253			);
254		return undef;
255	}
256	return $sub->($item, $table, $final, $loc);
257}
258
2591;
260__END__
261