* Add enclair_db option to UserDB.pm. Allows logging of enclair password
[interchange.git] / code / UserTag / weight.tag
1 # Copyright 2002-2007 Interchange Development Group and others
2
3 # This program is free software; you can redistribute it and/or modify
4 # it under the terms of the GNU General Public License as published by
5 # the Free Software Foundation; either version 2 of the License, or
6 # (at your option) any later version.  See the LICENSE file for details.
7
8 # $Id: weight.tag,v 1.9 2007-07-18 00:16:26 jon Exp $
9
10 UserTag weight Order   attribute
11 UserTag weight addAttr
12 UserTag weight Version $Revision: 1.9 $
13 UserTag weight Routine <<EOR
14 sub {
15         my ($attr, $opt) = @_;
16         $opt ||= {};
17         
18         my $cart;
19         if($opt->{cart}) {
20                 $cart = $Vend::Session->{carts}{$opt->{cart}} || [];
21         }
22         else {
23                 $cart = $Vend::Items;
24         }
25
26         my $wsub;
27
28         my $field = $opt->{field} || 'weight';
29         my $table = $opt->{table};
30         my $osub;
31
32         if($opt->{options}) {
33            BUILDO: {
34                  my $oattr = $Vend::Cfg->{OptionsAttribute}
35                         or last BUILDO;
36                  my $odb = dbref($opt->{options_table} || 'options')
37                         or last BUILDO;
38                  my $otab = $odb->name();
39                  my $q = qq{
40                                         SELECT o_group, weight FROM $otab
41                                         WHERE  sku = ?
42                                         AND    weight is not null
43                                         AND    weight <> ''
44                                         };
45                  my $sth = $odb->dbh()->prepare($q)
46                         or last BUILDO;
47                  if($oattr and $odb) {
48                          $osub = sub {
49                                 my $it = shift;
50                                 my $oweight = 0;
51                                 if($it->{$oattr} eq 'Simple') {
52                                         $sth->execute($it->{code});
53                                         while(my $ref = $sth->fetchrow_arrayref) {
54                                                 my ($opt, $wtext) = @$ref;
55                                                 next unless length($it->{$opt});
56                                                 my $whash = get_option_hash($wtext);
57                                                 next unless $whash;
58                                                 $oweight += $whash->{$it->{$opt}};
59                                         }
60                                 }
61                                 return $oweight;
62                         };
63                 };
64           }
65         }
66
67         my $exclude;
68         my %exclude;
69         if(my $thing = $opt->{exclude_attribute}) {
70           eval {
71                 if(ref($thing) eq 'HASH') {
72                         for(keys %$thing) {
73                                 $exclude{$_} = qr{$thing->{$_}};
74                         }
75                 }
76                 else {
77                         my ($k, $v) = split /=/, $thing;
78                         $exclude{$k} = qr{$v};
79                 }
80           };
81           if($@) {
82                 ::logError("Bad weight exclude option: %s", ::uneval($thing));
83           }
84           else {
85                 $exclude = 1;
86           }
87         }
88
89         my $zero_unless;
90         my %zero_unless;
91         if(my $thing = $opt->{zero_unless_attribute}) {
92           eval {
93                 if(ref($thing) eq 'HASH') {
94                         for(keys %$thing) {
95                                 $zero_unless{$_} = qr{$thing->{$_}};
96                         }
97                 }
98                 else {
99                         my ($k, $v) = split /=/, $thing;
100                         $zero_unless{$k} = qr{$v};
101                 }
102           };
103           if($@) {
104                 ::logError("Bad weight zero_unless option: %s", ::uneval($thing));
105           }
106           else {
107                 $zero_unless = 1;
108           }
109         }
110
111         if($attr) {
112                 $attr = $opt->{field} || 'weight';
113                 $wsub = sub {
114                         return shift(@_)->{$attr};
115                 };
116         }
117         elsif($opt->{fill_attribute}) {
118                 $attr = $opt->{fill_attribute};
119                 $wsub = sub {
120                         my $it = shift;
121                         return $it->{$attr} if defined $it->{$attr};
122                         my $tab = $table || $it->{mv_ib} || $Vend::Cfg->{ProductFiles}[0];
123                         $it->{$attr} = tag_data($tab,$field,$it->{code}) || 0;
124                         if($opt->{matrix} and ! $it->{$attr} and $it->{mv_sku}) {
125                                 $it->{$attr} = Vend::Data::product_field($field,$it->{mv_sku});
126                         }
127                         return $it->{$attr};
128                 };
129         }
130         else {
131                 $wsub = sub {
132                         my $it = shift;
133                         my $tab = $table || $it->{mv_ib} || $Vend::Cfg->{ProductFiles}[0];
134                         my $w = tag_data($tab,$field,$it->{code}) || 0;
135                         if(! $w and $opt->{matrix} and $it->{mv_sku}) {
136                                 $w = Vend::Data::product_field($field,$it->{mv_sku});
137                         }
138                         return $w;
139                 };
140         }
141
142         my $total = 0;
143         CARTCHECK:
144         for(@$cart) {
145                 if($exclude) {
146                         my $found;
147                         for my $k (keys %exclude) {
148                                 $found = 1, last if $_->{$k} =~ $exclude{$k};
149                         }
150                         next if $found;
151                 }
152                 if($zero_unless) {
153                         for my $k (keys %zero_unless) {
154                                 return 0 unless $_->{$k} =~ $zero_unless{$k};
155                         }
156                 }
157                 next if $_->{mv_free_shipping} && ! $opt->{no_free_shipping};
158                 $total += $_->{quantity} * $wsub->($_);
159                 next unless $osub;
160                 $total += $_->{quantity} * $osub->($_);
161         }
162         
163         unless($opt->{no_set}) {
164                 $::Scratch->{$opt->{weight_scratch} ||= 'total_weight'} = $total;
165         }
166
167         return $total unless $opt->{hide};
168         return;
169 }
170 EOR
171
172 UserTag weight Documentation <<EOD
173 =head1 NAME
174
175 ITL tag [weight] -- calculate shipping weight from cart
176
177 =head1 SYNOPSIS
178
179  [weight]
180  [weight
181     attribute=1*
182     cart=cartname*
183     field=sh_weight*
184     fill-attribute=weight*
185     zero-unless-attribute="attribute=regex"
186     exclude-attribute="attribute=regex"
187     hide=1|0*
188         matrix=1
189     no-set=1|0*
190     table=weights*
191     weight-scratch=sh_weight*
192  ]
193
194 =head1 DESCRIPTION
195
196 Calculates total weight of items in shopping cart, by default setting
197 a scratch variable (default "total_weight").
198
199 =head2 Options
200
201 =over 4
202
203 =item attribute
204
205 If set, weight tag will calculate from the field in the item itself instead
206 of going to the database. This is the most efficient, and can be enabled
207 by using this in catalog.cfg:
208
209         AutoModifier  weight
210
211 The default is not set, using the database every time.
212
213 =item cart
214
215 The cart to calculate for. Defaults to current cart.
216
217 =item field
218
219 The fieldname to use -- default "weight". This applies both to attribute
220 and database.
221
222 =item exclude-attribute
223
224 If an attribute I<already in the cart hash> matches the regex, it
225 will not show up as weight. Can be a scalar or hash.
226
227         [weight exclude-attribute="prod_group=Gift Certificates"]
228
229 and 
230
231         [weight exclude-attribute.prod_group="Gift Certificates"]
232
233 are identical, but with the second form you can do:
234
235         [weight
236                 exclude-attribute.prod_group="Gift Certificates"
237                 exclude-attribute.category="Downloads"
238         ]
239
240 The value is a regular expression, so you can group with C<|>,
241 or make case insensitive with:
242
243         [weight exclude-attribute.prod_group="(?i)certificate"]
244
245 If the regular expression does not compile, an error is logged
246 and no exclusion is done.
247
248 It is IMPORTANT to note that you must have the attribute pre-filled
249 for this to work -- no database accesses will be done. If you want
250 to do this, use L<AutoModifier>, i.e. put in catalog.cfg:
251
252         AutoModifier prod_group
253
254 =item fill-attribute
255
256 Sets the attribute from the database the first time, and uses it thereafter.
257 Sets to weight of a single unit, of course.
258
259 =item hide
260
261 Don't display the weight, only set in Scratch. It makes no sense to
262 use hide=1 and no-set=1.
263
264 =item matrix
265
266 If set, will get the weight from the ProductFiles for the mv_sku
267 attribute of the item. In other words, if the weight for a variant
268 is not set, it will use the weight for the base SKU.
269
270 =item no-set
271
272 Don't set the weight in scratch.
273
274 =item options
275
276 Scan the options table for applicable options and adjust weight
277 accordingly. Only works for "Simple" type options set in the
278 OptionsEnable attribute, and the o_group and weight fields must
279 represent the option attribute and the weight text. The weight text is a
280 normal Interchange option hash string type, i.e. 
281
282         titanium=-1.2, iron=1.5
283
284 where "titanium" and "iron" are the values of an option
285 setting like "blade".
286
287 Will only work if your options table is SQL/DBI.
288
289 =item table
290
291 Specify a table to use to look up weights. Defaults to the table the
292 product was ordered from (or the first ProductFiles).
293
294 =item weight-scratch
295
296 The scratch variable name to set -- default is "total_weight".
297
298 =item zero-unless-attribute
299
300 Same as C<exclude-attribute> except that a zero weight is returned
301 unless B<all> items match the expression. This allows you to do
302 something like only offer Book Rate shipping when all items have
303 a prod_group of "Books".
304
305 =back
306
307 =head1 AUTHOR
308
309 Mike Heins
310
311 =cut
312 EOD