]>
Commit | Line | Data |
---|---|---|
41e7c841 TC |
1 | package BSE::UI::Shop; |
2 | use strict; | |
3 | use base 'BSE::UI::Dispatch'; | |
4 | use DevHelp::HTML; | |
5 | use BSE::Util::SQL qw(now_sqldate now_sqldatetime); | |
5d88571c TC |
6 | use BSE::Shop::Util qw(need_logon shop_cart_tags payment_types nice_options |
7 | cart_item_opts basic_tags); | |
41e7c841 TC |
8 | use BSE::CfgInfo qw(custom_class credit_card_class); |
9 | use BSE::TB::Orders; | |
10 | use BSE::TB::OrderItems; | |
11 | use BSE::Mail; | |
718a070d | 12 | use BSE::Util::Tags qw(tag_error_img tag_hash); |
41e7c841 | 13 | use Products; |
718a070d | 14 | use BSE::TB::Seminars; |
41e7c841 | 15 | use DevHelp::Validate qw(dh_validate dh_validate_hash); |
2c9b9618 | 16 | use Digest::MD5 'md5_hex'; |
41e7c841 TC |
17 | |
18 | use constant PAYMENT_CC => 0; | |
19 | use constant PAYMENT_CHEQUE => 1; | |
20 | use constant PAYMENT_CALLME => 2; | |
21 | ||
22 | my %actions = | |
23 | ( | |
24 | add => 1, | |
25 | cart => 1, | |
26 | checkout => 1, | |
27 | checkupdate => 1, | |
28 | recheckout => 1, | |
29 | confirm => 1, | |
30 | recalc=>1, | |
31 | recalculate => 1, | |
32 | #purchase => 1, | |
33 | order => 1, | |
34 | show_payment => 1, | |
35 | payment => 1, | |
36 | orderdone => 1, | |
718a070d | 37 | location => 1, |
41e7c841 TC |
38 | ); |
39 | ||
a392c69e TC |
40 | my %field_map = |
41 | ( | |
42 | name1 => 'delivFirstName', | |
43 | name2 => 'delivLastName', | |
44 | address => 'delivStreet', | |
37dd20ad | 45 | organization => 'delivOrganization', |
a392c69e TC |
46 | city => 'delivSuburb', |
47 | postcode => 'delivPostCode', | |
48 | state => 'delivState', | |
49 | country => 'delivCountry', | |
50 | email => 'emailAddress', | |
51 | cardHolder => 'ccName', | |
52 | cardType => 'ccType', | |
53 | ); | |
54 | ||
55 | my %rev_field_map = reverse %field_map; | |
56 | ||
41e7c841 TC |
57 | sub actions { \%actions } |
58 | ||
59 | sub default_action { 'cart' } | |
60 | ||
61 | sub other_action { | |
62 | my ($class, $cgi) = @_; | |
63 | ||
64 | for my $key ($cgi->param()) { | |
2c9b9618 | 65 | if ($key =~ /^delete_(\d+)(?:\.x)?$/) { |
41e7c841 TC |
66 | return ( remove_item => $1 ); |
67 | } | |
68 | } | |
69 | ||
70 | return; | |
71 | } | |
72 | ||
73 | sub req_cart { | |
74 | my ($class, $req, $msg) = @_; | |
75 | ||
76 | my @cart = @{$req->session->{cart} || []}; | |
a392c69e TC |
77 | my @cart_prods; |
78 | my @items = $class->_build_items($req, \@cart_prods); | |
41e7c841 TC |
79 | my $item_index = -1; |
80 | my @options; | |
81 | my $option_index; | |
82 | ||
83 | $req->session->{custom} ||= {}; | |
84 | my %custom_state = %{$req->session->{custom}}; | |
85 | ||
86 | my $cust_class = custom_class($req->cfg); | |
87 | $cust_class->enter_cart(\@cart, \@cart_prods, \%custom_state, $req->cfg); | |
88 | $msg = '' unless defined $msg; | |
89 | $msg = escape_html($msg); | |
90 | ||
91 | my %acts; | |
92 | %acts = | |
93 | ( | |
94 | $cust_class->cart_actions(\%acts, \@cart, \@cart_prods, \%custom_state, | |
95 | $req->cfg), | |
a392c69e | 96 | shop_cart_tags(\%acts, \@items, \@cart_prods, $req->session, $req->cgi, |
41e7c841 TC |
97 | $req->cfg, 'cart'), |
98 | basic_tags(\%acts), | |
99 | msg => $msg, | |
100 | ); | |
101 | $req->session->{custom} = \%custom_state; | |
102 | $req->session->{order_info_confirmed} = 0; | |
103 | ||
104 | return $req->response('cart', \%acts); | |
105 | } | |
106 | ||
107 | sub req_add { | |
108 | my ($class, $req) = @_; | |
109 | ||
110 | my $cgi = $req->cgi; | |
111 | ||
112 | my $addid = $cgi->param('id'); | |
113 | $addid ||= ''; | |
114 | my $quantity = $cgi->param('quantity'); | |
115 | $quantity ||= 1; | |
116 | my $product; | |
718a070d TC |
117 | if ($addid) { |
118 | $product = BSE::TB::Seminars->getByPkey($addid); | |
119 | $product ||= Products->getByPkey($addid); | |
120 | } | |
41e7c841 TC |
121 | $product or |
122 | return $class->req_cart($req, "Cannot find product $addid"); # oops | |
123 | ||
124 | # collect the product options | |
125 | my @options; | |
126 | my @opt_names = split /,/, $product->{options}; | |
127 | my @not_def; | |
128 | for my $name (@opt_names) { | |
129 | my $value = $cgi->param($name); | |
130 | push @options, $value; | |
131 | unless (defined $value) { | |
132 | push @not_def, $name; | |
133 | } | |
134 | } | |
135 | @not_def | |
136 | and return $class->req_cart($req, "Some product options (@not_def) not supplied"); | |
137 | my $options = join(",", @options); | |
138 | ||
139 | # the product must be non-expired and listed | |
140 | (my $comp_release = $product->{release}) =~ s/ .*//; | |
141 | (my $comp_expire = $product->{expire}) =~ s/ .*//; | |
142 | my $today = now_sqldate(); | |
143 | $comp_release le $today | |
144 | or return $class->req_cart($req, "Product has not been released yet"); | |
145 | $today le $comp_expire | |
146 | or return $class->req_cart($req, "Product has expired"); | |
147 | $product->{listed} or return $class->req_cart($req, "Product not available"); | |
148 | ||
149 | # used to refresh if a logon is needed | |
150 | my $securlbase = $req->cfg->entryVar('site', 'secureurl'); | |
151 | my $r = $securlbase . $ENV{SCRIPT_NAME} . "?add=1&id=$addid"; | |
152 | for my $opt_index (0..$#opt_names) { | |
153 | $r .= "&$opt_names[$opt_index]=".escape_uri($options[$opt_index]); | |
154 | } | |
155 | ||
156 | my $user = $req->siteuser; | |
157 | # need to be logged on if it has any subs | |
158 | if ($product->{subscription_id} != -1) { | |
159 | if ($user) { | |
160 | my $sub = $product->subscription; | |
161 | if ($product->is_renew_sub_only) { | |
162 | unless ($user->subscribed_to_grace($sub)) { | |
37dd20ad | 163 | return $class->req_cart($req, "This product can only be used to renew your subscription to $sub->{title} and you are not subscribed nor within the renewal grace period"); |
41e7c841 TC |
164 | } |
165 | } | |
166 | elsif ($product->is_start_sub_only) { | |
167 | if ($user->subscribed_to_grace($sub)) { | |
37dd20ad | 168 | return $class->req_cart($req, "This product can only be used to start your subscription to $sub->{title} and you are already subscribed or within the grace period"); |
41e7c841 TC |
169 | } |
170 | } | |
171 | } | |
172 | else { | |
173 | return $class->_refresh_logon | |
174 | ($req, "You must be logged on to add this product to your cart", | |
175 | 'prodlogon', $r); | |
176 | } | |
177 | } | |
178 | if ($product->{subscription_required} != -1) { | |
179 | my $sub = $product->subscription_required; | |
180 | if ($user) { | |
181 | unless ($user->subscribed_to($sub)) { | |
182 | return $class->req_cart($req, "You must be subscribed to $sub->{title} to purchase this product"); | |
183 | return; | |
184 | } | |
185 | } | |
186 | else { | |
187 | # we want to refresh back to adding the item to the cart if possible | |
188 | return $class->_refresh_logon | |
189 | ($req, "You must be logged on and subscribed to $sub->{title} to add this product to your cart", | |
190 | 'prodlogonsub', $r); | |
191 | } | |
192 | } | |
193 | ||
194 | # we need a natural integer quantity | |
195 | $quantity =~ /^\d+$/ | |
196 | or return $class->req_cart($req, "Invalid quantity"); | |
197 | ||
718a070d TC |
198 | my %extras; |
199 | if ($product->isa('BSE::TB::Seminar')) { | |
200 | # you must be logged on to add a seminar | |
201 | unless ($user) { | |
202 | return $class->_refresh_logon | |
203 | ($req, "You must be logged on to add seminars to your cart", | |
204 | 'seminarlogon', $r); | |
205 | } | |
206 | ||
207 | # get and validate the session | |
208 | my $session_id = $cgi->param('session_id'); | |
209 | defined $session_id | |
210 | or return $class->req_cart($req, "Please select a session when adding a seminar"); | |
211 | $session_id =~ /^\d+$/ | |
212 | or return $class->req_cart($req, "Invalid session_id supplied"); | |
213 | require BSE::TB::SeminarSessions; | |
214 | my $session = BSE::TB::SeminarSessions->getByPkey($session_id) | |
215 | or return $class->req_cart($req, "Unknown session id supplied"); | |
216 | $session->{seminar_id} == $addid | |
217 | or return $class->req_cart($req, "Session not for this seminar"); | |
218 | ||
219 | # check if the user is already booked for this session | |
220 | grep($_ == $session_id, $user->seminar_sessions_booked($addid)) | |
221 | and return $class->req_cart($req, "You are already booked for this session"); | |
222 | ||
223 | $extras{session_id} = $session_id; | |
224 | } | |
225 | ||
41e7c841 TC |
226 | $req->session->{cart} ||= []; |
227 | my @cart = @{$req->session->{cart}}; | |
228 | ||
229 | # if this is is already present, replace it | |
230 | @cart = grep { $_->{productId} ne $addid || $_->{options} ne $options } | |
231 | @cart; | |
232 | push @cart, | |
233 | { | |
234 | productId => $addid, | |
235 | units => $quantity, | |
236 | price=>$product->{retailPrice}, | |
718a070d TC |
237 | options=>$options, |
238 | %extras, | |
41e7c841 TC |
239 | }; |
240 | ||
241 | $req->session->{cart} = \@cart; | |
242 | $req->session->{order_info_confirmed} = 0; | |
243 | ||
244 | return $class->req_cart($req); | |
245 | } | |
246 | ||
247 | sub req_checkout { | |
248 | my ($class, $req, $message, $olddata) = @_; | |
249 | ||
250 | my $errors = {}; | |
251 | if (defined $message) { | |
252 | if (ref $message) { | |
253 | $errors = $message; | |
254 | $message = $req->message($errors); | |
255 | } | |
256 | } | |
257 | else { | |
258 | $message = ''; | |
259 | } | |
260 | my $cfg = $req->cfg; | |
261 | my $cgi = $req->cgi; | |
262 | ||
263 | $class->update_quantities($req); | |
264 | my @cart = @{$req->session->{cart}}; | |
265 | ||
266 | @cart or return $class->req_cart($req); | |
267 | ||
a392c69e TC |
268 | my @cart_prods; |
269 | my @items = $class->_build_items($req, \@cart_prods); | |
41e7c841 TC |
270 | |
271 | if (my ($msg, $id) = $class->_need_logon($req, \@cart, \@cart_prods)) { | |
272 | return $class->_refresh_logon($req, $msg, $id); | |
273 | return; | |
274 | } | |
275 | ||
276 | my $user = $req->siteuser; | |
277 | ||
278 | $req->session->{custom} ||= {}; | |
279 | my %custom_state = %{$req->session->{custom}}; | |
280 | ||
281 | my $cust_class = custom_class($cfg); | |
282 | $cust_class->enter_cart(\@cart, \@cart_prods, \%custom_state, $cfg); | |
283 | ||
284 | my $affiliate_code = $req->session->{affiliate_code}; | |
285 | defined $affiliate_code or $affiliate_code = ''; | |
286 | ||
a392c69e TC |
287 | my $order_info = $req->session->{order_info}; |
288 | ||
41e7c841 TC |
289 | my $item_index = -1; |
290 | my @options; | |
291 | my $option_index; | |
292 | my %acts; | |
293 | %acts = | |
294 | ( | |
a392c69e | 295 | shop_cart_tags(\%acts, \@items, \@cart_prods, $req->session, $req->cgi, |
41e7c841 TC |
296 | $cfg, 'checkout'), |
297 | basic_tags(\%acts), | |
298 | message => $message, | |
299 | msg => $message, | |
300 | old => | |
301 | sub { | |
302 | my $value; | |
303 | ||
304 | if ($olddata) { | |
305 | $value = $cgi->param($_[0]); | |
306 | unless (defined $value) { | |
307 | $value = $user->{$_[0]} | |
308 | if $user; | |
309 | } | |
310 | } | |
a392c69e TC |
311 | elsif ($order_info && defined $order_info->{$_[0]}) { |
312 | $value = $order_info->{$_[0]}; | |
313 | } | |
41e7c841 | 314 | else { |
a392c69e TC |
315 | my $field = $_[0]; |
316 | $rev_field_map{$field} and $field = $rev_field_map{$field}; | |
317 | $value = $user && defined $user->{$field} ? $user->{$field} : ''; | |
41e7c841 TC |
318 | } |
319 | ||
320 | defined $value or $value = ''; | |
321 | escape_html($value); | |
322 | }, | |
323 | $cust_class->checkout_actions(\%acts, \@cart, \@cart_prods, | |
324 | \%custom_state, $req->cgi, $cfg), | |
325 | ifUser => defined $user, | |
326 | user => $user ? [ \&tag_hash, $user ] : '', | |
327 | affiliate_code => escape_html($affiliate_code), | |
328 | error_img => [ \&tag_error_img, $cfg, $errors ], | |
329 | ); | |
330 | $req->session->{custom} = \%custom_state; | |
331 | ||
332 | return $req->response('checkoutnew', \%acts); | |
333 | } | |
334 | ||
335 | sub req_checkupdate { | |
336 | my ($class, $req) = @_; | |
337 | ||
2c9b9618 | 338 | $req->session->{cart} ||= []; |
41e7c841 TC |
339 | my @cart = @{$req->session->{cart}}; |
340 | my @cart_prods = map { Products->getByPkey($_->{productId}) } @cart; | |
341 | $req->session->{custom} ||= {}; | |
342 | my %custom_state = %{$req->session->{custom}}; | |
343 | custom_class($req->cfg) | |
344 | ->checkout_update($req->cgi, \@cart, \@cart_prods, \%custom_state, $req->cfg); | |
345 | $req->session->{custom} = \%custom_state; | |
346 | $req->session->{order_info_confirmed} = 0; | |
347 | ||
348 | return $class->req_checkout($req, "", 1); | |
349 | } | |
350 | ||
351 | sub req_remove_item { | |
352 | my ($class, $req, $index) = @_; | |
2c9b9618 TC |
353 | |
354 | $req->session->{cart} ||= []; | |
41e7c841 TC |
355 | my @cart = @{$req->session->{cart}}; |
356 | if ($index >= 0 && $index < @cart) { | |
357 | splice(@cart, $index, 1); | |
358 | } | |
359 | $req->session->{cart} = \@cart; | |
360 | $req->session->{order_info_confirmed} = 0; | |
361 | ||
362 | return BSE::Template->get_refresh($ENV{SCRIPT_NAME}, $req->cfg); | |
363 | } | |
364 | ||
41e7c841 TC |
365 | |
366 | # saves order and refresh to payment page | |
367 | sub req_order { | |
368 | my ($class, $req) = @_; | |
369 | ||
370 | my $cfg = $req->cfg; | |
371 | my $cgi = $req->cgi; | |
372 | ||
373 | $req->session->{cart} && @{$req->session->{cart}} | |
374 | or return $class->req_cart($req, "Your cart is empty"); | |
375 | ||
376 | my $msg; | |
377 | $class->_validate_cfg($req, \$msg) | |
378 | or return $class->req_cart($req, $msg); | |
379 | ||
380 | my @products; | |
381 | my @items = $class->_build_items($req, \@products); | |
382 | ||
383 | my $id; | |
384 | if (($msg, $id) = $class->_need_logon($req, \@items, \@products)) { | |
385 | return $class->_refresh_logon($req, $msg, $id); | |
386 | } | |
387 | ||
388 | # some basic validation, in case the user switched off javascript | |
389 | my $cust_class = custom_class($cfg); | |
390 | ||
391 | my %fields = BSE::TB::Order->valid_fields($cfg); | |
392 | my %rules = BSE::TB::Order->valid_rules($cfg); | |
393 | ||
394 | my %errors; | |
395 | my %values; | |
396 | for my $name (keys %fields) { | |
397 | ($values{$name}) = $cgi->param($name); | |
398 | } | |
399 | ||
400 | my @required = | |
401 | $cust_class->required_fields($cgi, $req->session->{custom}, $cfg); | |
402 | ||
403 | for my $name (@required) { | |
404 | $field_map{$name} and $name = $field_map{$name}; | |
405 | ||
406 | $fields{$name}{required} = 1; | |
407 | } | |
408 | ||
409 | dh_validate_hash(\%values, \%errors, { rules=>\%rules, fields=>\%fields }, | |
410 | $cfg, 'Shop Order Validation'); | |
411 | keys %errors | |
412 | and return $class->req_checkout($req, \%errors, 1); | |
413 | ||
414 | $class->_fillout_order($req, \%values, \@items, \$msg, 'payment') | |
415 | or return $class->req_checkout($req, $msg, 1); | |
416 | ||
417 | $req->session->{order_info} = \%values; | |
418 | $req->session->{order_info_confirmed} = 1; | |
419 | ||
420 | return BSE::Template->get_refresh("$ENV{SCRIPT_NAME}?a_show_payment=1", $req->cfg); | |
421 | } | |
422 | ||
423 | sub req_show_payment { | |
424 | my ($class, $req, $errors) = @_; | |
425 | ||
426 | $req->session->{order_info_confirmed} | |
427 | or return $class->req_checkout($req, 'Please proceed via the checkout page'); | |
428 | ||
2c9b9618 TC |
429 | $req->session->{cart} && @{$req->session->{cart}} |
430 | or return $class->req_cart($req, "Your cart is empty"); | |
431 | ||
41e7c841 TC |
432 | my $cfg = $req->cfg; |
433 | my $cgi = $req->cgi; | |
434 | ||
435 | $errors ||= {}; | |
436 | my $msg = $req->message($errors); | |
437 | ||
438 | my $order_values = $req->session->{order_info} | |
439 | or return $class->req_checkout($req, "You need to enter order information first"); | |
440 | ||
441 | my @pay_types = payment_types($cfg); | |
442 | my @payment_types = map $_->{id}, grep $_->{enabled}, @pay_types; | |
443 | my %types_by_name = map { $_->{name} => $_->{id} } @pay_types; | |
444 | @payment_types or @payment_types = ( PAYMENT_CALLME ); | |
445 | @payment_types = sort { $a <=> $b } @payment_types; | |
446 | my %payment_types = map { $_=> 1 } @payment_types; | |
447 | my $payment; | |
448 | $errors and $payment = $cgi->param('paymentType'); | |
449 | defined $payment or $payment = $payment_types[0]; | |
450 | ||
451 | my @products; | |
452 | my @items = $class->_build_items($req, \@products); | |
453 | ||
454 | my %acts; | |
455 | %acts = | |
456 | ( | |
457 | basic_tags(\%acts), | |
458 | message => $msg, | |
459 | msg => $msg, | |
460 | order => [ \&tag_hash, $order_values ], | |
461 | shop_cart_tags(\%acts, \@items, \@products, $req->session, $req->cgi, | |
462 | $req->cfg, 'payment'), | |
463 | ifMultPaymentTypes => @payment_types > 1, | |
464 | checkedPayment => [ \&tag_checkedPayment, $payment, \%types_by_name ], | |
465 | ifPayments => [ \&tag_ifPayments, \@payment_types, \%types_by_name ], | |
466 | error_img => [ \&tag_error_img, $cfg, $errors ], | |
467 | ); | |
468 | for my $type (@pay_types) { | |
469 | my $id = $type->{id}; | |
470 | my $name = $type->{name}; | |
471 | $acts{"if${name}Payments"} = exists $payment_types{$id}; | |
472 | $acts{"if${name}FirstPayment"} = $payment_types[0] == $id; | |
473 | $acts{"checkedIfFirst$name"} = $payment_types[0] == $id ? "checked " : ""; | |
474 | $acts{"checkedPayment$name"} = $payment == $id ? 'checked="checked" ' : ""; | |
475 | } | |
476 | ||
477 | return $req->response('checkoutpay', \%acts); | |
478 | } | |
479 | ||
480 | my %nostore = | |
481 | ( | |
482 | cardNumber => 1, | |
483 | cardExpiry => 1, | |
484 | ); | |
485 | ||
486 | sub req_payment { | |
487 | my ($class, $req, $errors) = @_; | |
488 | ||
489 | $req->session->{order_info_confirmed} | |
490 | or return $class->req_checkout($req, 'Please proceed via the checkout page'); | |
491 | ||
492 | my $order_values = $req->session->{order_info} | |
493 | or return $class->req_checkout($req, "You need to enter order information first"); | |
494 | ||
495 | ||
496 | my $cgi = $req->cgi; | |
497 | my $cfg = $req->cfg; | |
498 | my $session = $req->session; | |
499 | ||
500 | my @pay_types = payment_types($cfg); | |
501 | my @payment_types = map $_->{id}, grep $_->{enabled}, @pay_types; | |
502 | my %pay_types = map { $_->{id} => $_ } @pay_types; | |
503 | my %types_by_name = map { $_->{name} => $_->{id} } @pay_types; | |
504 | @payment_types or @payment_types = ( PAYMENT_CALLME ); | |
505 | @payment_types = sort { $a <=> $b } @payment_types; | |
506 | my %payment_types = map { $_=> 1 } @payment_types; | |
507 | ||
508 | my $paymentType = $cgi->param('paymentType'); | |
509 | defined $paymentType or $paymentType = $payment_types[0]; | |
510 | $payment_types{$paymentType} | |
511 | or return $class->req_show_payment($req, { paymentType => "Invalid payment type" } , 1); | |
512 | ||
513 | my @required; | |
514 | push @required, @{$pay_types{$paymentType}{require}}; | |
515 | ||
516 | my %fields = BSE::TB::Order->valid_payment_fields($cfg); | |
517 | my %rules = BSE::TB::Order->valid_payment_rules($cfg); | |
518 | for my $field (@required) { | |
519 | if (exists $fields{$field}) { | |
520 | $fields{$field}{required} = 1; | |
521 | } | |
522 | else { | |
523 | $fields{$field} = { description => $field, required=> 1 }; | |
524 | } | |
525 | } | |
526 | ||
527 | my %errors; | |
528 | dh_validate($cgi, \%errors, { rules => \%rules, fields=>\%fields }, | |
529 | $cfg, 'Shop Order Validation'); | |
530 | keys %errors | |
531 | and return $class->req_show_payment($req, \%errors); | |
532 | ||
533 | my @products; | |
534 | my @items = $class->_build_items($req, \@products); | |
535 | ||
536 | for my $field (@required) { | |
537 | unless ($nostore{$field}) { | |
538 | ($order_values->{$field}) = $cgi->param($field); | |
539 | } | |
540 | } | |
541 | ||
542 | $order_values->{filled} = 0; | |
543 | $order_values->{paidFor} = 0; | |
544 | ||
545 | my $cust_class = custom_class($req->cfg); | |
546 | eval { | |
547 | my %custom = %{$session->{custom}}; | |
548 | $cust_class->order_save($cgi, $order_values, \@items, \@products, | |
549 | \%custom, $cfg); | |
550 | $session->{custom} = \%custom; | |
551 | }; | |
552 | if ($@) { | |
553 | return $class->req_checkout($req, $@, 1); | |
554 | } | |
555 | ||
556 | my @columns = BSE::TB::Order->columns; | |
557 | my %columns; | |
558 | @columns{@columns} = @columns; | |
559 | ||
560 | for my $col (@columns) { | |
561 | defined $order_values->{$col} or $order_values->{$col} = ''; | |
562 | } | |
563 | ||
564 | $order_values->{paymentType} = $paymentType; | |
565 | ||
566 | my @data = @{$order_values}{@columns}; | |
567 | shift @data; | |
5d88571c TC |
568 | |
569 | my $order; | |
570 | if ($session->{order_work}) { | |
571 | $order = BSE::TB::Orders->getByPkey($session->{order_work}); | |
572 | } | |
573 | if ($order) { | |
574 | print STDERR "Recycling order $order->{id}\n"; | |
575 | ||
576 | my @allbutid = @columns; | |
577 | shift @allbutid; | |
578 | @{$order}{@allbutid} = @data; | |
579 | ||
580 | $order->clear_items; | |
581 | } | |
582 | else { | |
583 | $order = BSE::TB::Orders->add(@data) | |
584 | or die "Cannot add order"; | |
585 | } | |
41e7c841 TC |
586 | |
587 | my @dbitems; | |
588 | my %subscribing_to; | |
589 | my @item_cols = BSE::TB::OrderItem->columns; | |
590 | for my $row_num (0..$#items) { | |
591 | my $item = $items[$row_num]; | |
592 | my $product = $products[$row_num]; | |
593 | $item->{orderId} = $order->{id}; | |
594 | $item->{max_lapsed} = 0; | |
595 | if ($product->{subscription_id} != -1) { | |
596 | my $sub = $product->subscription; | |
597 | $item->{max_lapsed} = $sub->{max_lapsed} if $sub; | |
598 | } | |
599 | my @data = @{$item}{@item_cols}; | |
600 | ||
601 | shift @data; | |
602 | push(@dbitems, BSE::TB::OrderItems->add(@data)); | |
603 | ||
604 | my $sub = $product->subscription; | |
605 | if ($sub) { | |
606 | $subscribing_to{$sub->{text_id}} = $sub; | |
607 | } | |
718a070d TC |
608 | |
609 | if ($item->{session_id}) { | |
610 | my $user = $req->siteuser; | |
611 | require BSE::TB::SeminarSessions; | |
612 | my $session = BSE::TB::SeminarSessions->getByPkey($item->{session_id}); | |
613 | eval { | |
614 | $session->add_attendee($user, 0); | |
615 | }; | |
616 | } | |
41e7c841 | 617 | } |
5d88571c TC |
618 | |
619 | $order->{ccOnline} = 0; | |
41e7c841 TC |
620 | |
621 | my $ccprocessor = $cfg->entry('shop', 'cardprocessor'); | |
622 | if ($paymentType == PAYMENT_CC && $ccprocessor) { | |
623 | my $cc_class = credit_card_class($cfg); | |
624 | ||
625 | $order->{ccOnline} = 1; | |
626 | ||
627 | my $ccNumber = $cgi->param('cardNumber'); | |
628 | my $ccExpiry = $cgi->param('cardExpiry'); | |
629 | $ccExpiry =~ m!^(\d+)\D(\d+)$! or die; | |
630 | my ($month, $year) = ($1, $2); | |
631 | $year > 2000 or $year += 2000; | |
632 | my $expiry = sprintf("%04d%02d", $year, $month); | |
633 | my $verify = $cgi->param('cardVerify'); | |
634 | defined $verify or $verify = ''; | |
635 | my $result = $cc_class->payment(orderno=>$order->{id}, | |
636 | amount => $order->{total}, | |
637 | cardnumber => $ccNumber, | |
638 | expirydate => $expiry, | |
639 | cvv => $verify, | |
640 | ipaddress => $ENV{REMOTE_ADDR}); | |
641 | unless ($result->{success}) { | |
642 | use Data::Dumper; | |
643 | print STDERR Dumper($result); | |
644 | # failed, back to payments | |
645 | $order->{ccSuccess} = 0; | |
646 | $order->{ccStatus} = $result->{statuscode}; | |
647 | $order->{ccStatus2} = 0; | |
648 | $order->{ccStatusText} = $result->{error}; | |
649 | $order->{ccTranId} = ''; | |
650 | $order->save; | |
651 | $errors{cardNumber} = $result->{error}; | |
652 | $session->{order_work} = $order->{id}; | |
653 | return $class->req_show_payment($req, \%errors); | |
654 | } | |
655 | ||
656 | $order->{ccSuccess} = 1; | |
657 | $order->{ccReceipt} = $result->{receipt}; | |
658 | $order->{ccStatus} = 0; | |
659 | $order->{ccStatus2} = 0; | |
660 | $order->{ccStatusText} = ''; | |
661 | $order->{ccTranId} = $result->{transactionid}; | |
662 | $order->{paidFor} = 1; | |
41e7c841 TC |
663 | } |
664 | ||
5d88571c TC |
665 | # order complete |
666 | $order->{complete} = 1; | |
667 | $order->save; | |
668 | ||
41e7c841 TC |
669 | # set the order displayed by orderdone |
670 | $session->{order_completed} = $order->{id}; | |
671 | $session->{order_completed_at} = time; | |
672 | ||
673 | my $noencrypt = $cfg->entryBool('shop', 'noencrypt', 0); | |
674 | $class->_send_order($req, $order, \@dbitems, \@products, $noencrypt, | |
675 | \%subscribing_to); | |
676 | ||
677 | # empty the cart ready for the next order | |
678 | delete @{$session}{qw/order_info order_info_confirmed cart order_work/}; | |
679 | ||
680 | return BSE::Template->get_refresh("$ENV{SCRIPT_NAME}?a_orderdone=1", $req->cfg); | |
681 | } | |
682 | ||
683 | sub req_orderdone { | |
684 | my ($class, $req) = @_; | |
685 | ||
686 | my $session = $req->session; | |
687 | my $cfg = $req->cfg; | |
688 | ||
689 | my $id = $session->{order_completed}; | |
690 | my $when = $session->{order_completed_at}; | |
691 | $id && defined $when && time < $when + 500 | |
692 | or return $class->req_cart($req); | |
693 | ||
694 | my $order = BSE::TB::Orders->getByPkey($id) | |
695 | or return $class->req_cart($req); | |
696 | my @items = $order->items; | |
41e7c841 TC |
697 | my @products = map { Products->getByPkey($_->{productId}) } @items; |
698 | ||
2c9b9618 TC |
699 | my @item_cols = BSE::TB::OrderItem->columns; |
700 | my %copy_cols = map { $_ => 1 } Product->columns; | |
701 | delete @copy_cols{@item_cols}; | |
702 | my @copy_cols = keys %copy_cols; | |
703 | my @showitems; | |
704 | for my $item_index (0..$#items) { | |
705 | my $item = $items[$item_index]; | |
706 | my $product = $products[$item_index]; | |
707 | my %entry; | |
708 | @entry{@item_cols} = @{$item}{@item_cols}; | |
709 | @entry{@copy_cols} = @{$product}{@copy_cols}; | |
710 | ||
711 | push @showitems, \%entry; | |
712 | } | |
713 | ||
41e7c841 TC |
714 | my $cust_class = custom_class($req->cfg); |
715 | ||
716 | my @pay_types = payment_types($cfg); | |
717 | my @payment_types = map $_->{id}, grep $_->{enabled}, @pay_types; | |
718 | my %pay_types = map { $_->{id} => $_ } @pay_types; | |
719 | my %types_by_name = map { $_->{name} => $_->{id} } @pay_types; | |
720 | ||
721 | my $item_index = -1; | |
722 | my @options; | |
723 | my $option_index; | |
718a070d TC |
724 | my $item; |
725 | my $product; | |
726 | my $sem_session; | |
727 | my $location; | |
41e7c841 TC |
728 | my %acts; |
729 | %acts = | |
730 | ( | |
731 | $cust_class->purchase_actions(\%acts, \@items, \@products, | |
732 | $session->{custom}, $cfg), | |
733 | BSE::Util::Tags->static(\%acts, $cfg), | |
734 | iterate_items_reset => sub { $item_index = -1; }, | |
735 | iterate_items => | |
736 | sub { | |
737 | if (++$item_index < @items) { | |
738 | $option_index = -1; | |
739 | @options = cart_item_opts($items[$item_index], | |
740 | $products[$item_index]); | |
718a070d TC |
741 | undef $sem_session; |
742 | undef $location; | |
743 | $item = $items[$item_index]; | |
744 | $product = $products[$item_index]; | |
41e7c841 TC |
745 | return 1; |
746 | } | |
718a070d TC |
747 | undef $item; |
748 | undef $sem_session; | |
749 | undef $product; | |
750 | undef $location; | |
41e7c841 TC |
751 | return 0; |
752 | }, | |
2c9b9618 | 753 | item=> sub { escape_html($showitems[$item_index]{$_[0]}); }, |
41e7c841 TC |
754 | product => |
755 | sub { | |
756 | my $value = $products[$item_index]{$_[0]}; | |
757 | defined $value or $value = ''; | |
758 | ||
759 | escape_html($value); | |
760 | }, | |
761 | extended => | |
762 | sub { | |
763 | my $what = $_[0] || 'retailPrice'; | |
764 | $items[$item_index]{units} * $items[$item_index]{$what}; | |
765 | }, | |
766 | order => sub { escape_html($order->{$_[0]}) }, | |
767 | money => | |
768 | sub { | |
769 | my ($func, $args) = split ' ', $_[0], 2; | |
770 | $acts{$func} || return "<: money $_[0] :>"; | |
771 | return sprintf("%.02f", $acts{$func}->($args)/100); | |
772 | }, | |
773 | _format => | |
774 | sub { | |
775 | my ($value, $fmt) = @_; | |
776 | if ($fmt =~ /^m(\d+)/) { | |
777 | return sprintf("%$1s", sprintf("%.2f", $value/100)); | |
778 | } | |
779 | elsif ($fmt =~ /%/) { | |
780 | return sprintf($fmt, $value); | |
781 | } | |
782 | }, | |
783 | iterate_options_reset => sub { $option_index = -1 }, | |
784 | iterate_options => sub { ++$option_index < @options }, | |
785 | option => sub { escape_html($options[$option_index]{$_[0]}) }, | |
786 | ifOptions => sub { @options }, | |
787 | options => sub { nice_options(@options) }, | |
788 | ifPayment => [ \&tag_ifPayment, $order->{paymentType}, \%types_by_name ], | |
789 | #ifSubscribingTo => [ \&tag_ifSubscribingTo, \%subscribing_to ], | |
718a070d TC |
790 | session => [ \&tag_session, \$item, \$sem_session ], |
791 | location => [ \&tag_location, \$item, \$location ], | |
41e7c841 TC |
792 | ); |
793 | for my $type (@pay_types) { | |
794 | my $id = $type->{id}; | |
795 | my $name = $type->{name}; | |
796 | $acts{"if${name}Payment"} = $order->{paymentType} == $id; | |
797 | } | |
798 | ||
799 | return $req->response('checkoutfinal', \%acts); | |
800 | } | |
801 | ||
718a070d TC |
802 | sub tag_session { |
803 | my ($ritem, $rsession, $arg) = @_; | |
804 | ||
805 | $$ritem or return ''; | |
806 | ||
807 | $$ritem->{session_id} or return ''; | |
808 | ||
809 | unless ($$rsession) { | |
810 | require BSE::TB::SeminarSessions; | |
811 | $$rsession = BSE::TB::SeminarSessions->getByPkey($$ritem->{session_id}) | |
812 | or return ''; | |
813 | } | |
814 | ||
815 | my $value = $$rsession->{$arg}; | |
816 | defined $value or return ''; | |
817 | ||
818 | escape_html($value); | |
819 | } | |
820 | ||
821 | sub tag_location { | |
822 | my ($ritem, $rlocation, $arg) = @_; | |
823 | ||
824 | $$ritem or return ''; | |
825 | ||
826 | $$ritem->{session_id} or return ''; | |
827 | ||
828 | unless ($$rlocation) { | |
829 | require BSE::TB::Locations; | |
830 | ($$rlocation) = BSE::TB::Locations->getSpecial(session_id => $$ritem->{session_id}) | |
831 | or return ''; | |
832 | } | |
833 | ||
834 | my $value = $$rlocation->{$arg}; | |
835 | defined $value or return ''; | |
836 | ||
837 | escape_html($value); | |
838 | } | |
839 | ||
41e7c841 TC |
840 | sub tag_ifPayment { |
841 | my ($payment, $types_by_name, $args) = @_; | |
842 | ||
843 | my $type = $args; | |
844 | if ($type !~ /^\d+$/) { | |
845 | return '' unless exists $types_by_name->{$type}; | |
846 | $type = $types_by_name->{$type}; | |
847 | } | |
848 | ||
849 | return $payment == $type; | |
850 | } | |
851 | ||
852 | ||
853 | sub _validate_cfg { | |
854 | my ($class, $req, $rmsg) = @_; | |
855 | ||
856 | my $cfg = $req->cfg; | |
857 | my $from = $cfg->entry('shop', 'from', $Constants::SHOP_FROM); | |
858 | unless ($from && $from =~ /.\@./) { | |
859 | $$rmsg = "Configuration error: shop from address not set"; | |
860 | return; | |
861 | } | |
862 | my $toEmail = $cfg->entry('shop', 'to_email', $Constants::SHOP_TO_EMAIL); | |
863 | unless ($toEmail && $toEmail =~ /.\@./) { | |
864 | $$rmsg = "Configuration error: shop to_email address not set"; | |
865 | return; | |
866 | } | |
867 | ||
868 | return 1; | |
869 | } | |
870 | ||
41e7c841 TC |
871 | sub req_recalc { |
872 | my ($class, $req) = @_; | |
2c9b9618 | 873 | |
41e7c841 TC |
874 | $class->update_quantities($req); |
875 | $req->session->{order_info_confirmed} = 0; | |
876 | return $class->req_cart($req); | |
877 | } | |
878 | ||
879 | sub req_recalculate { | |
880 | my ($class, $req) = @_; | |
881 | ||
882 | return $class->req_recalc($req); | |
883 | } | |
884 | ||
885 | sub _send_order { | |
886 | my ($class, $req, $order, $items, $products, $noencrypt, | |
887 | $subscribing_to) = @_; | |
888 | ||
889 | my $cfg = $req->cfg; | |
890 | my $cgi = $req->cgi; | |
891 | ||
892 | my $crypto_class = $Constants::SHOP_CRYPTO; | |
893 | my $signing_id = $Constants::SHOP_SIGNING_ID; | |
894 | my $pgp = $Constants::SHOP_PGP; | |
895 | my $pgpe = $Constants::SHOP_PGPE; | |
896 | my $gpg = $Constants::SHOP_GPG; | |
897 | my $passphrase = $Constants::SHOP_PASSPHRASE; | |
898 | my $from = $cfg->entry('shop', 'from', $Constants::SHOP_FROM); | |
899 | my $toName = $cfg->entry('shop', 'to_name', $Constants::SHOP_TO_NAME); | |
900 | my $toEmail = $cfg->entry('shop', 'to_email', $Constants::SHOP_TO_EMAIL); | |
901 | my $subject = $cfg->entry('shop', 'subject', $Constants::SHOP_MAIL_SUBJECT); | |
902 | ||
903 | my $session = $req->session; | |
904 | my %extras = $cfg->entriesCS('extra tags'); | |
905 | for my $key (keys %extras) { | |
906 | # follow any links | |
907 | my $data = $cfg->entryVar('extra tags', $key); | |
908 | $extras{$key} = sub { $data }; | |
909 | } | |
910 | ||
911 | my $item_index = -1; | |
912 | my @options; | |
913 | my $option_index; | |
914 | my %acts; | |
915 | %acts = | |
916 | ( | |
917 | %extras, | |
918 | custom_class($cfg) | |
919 | ->order_mail_actions(\%acts, $order, $items, $products, | |
920 | $session->{custom}, $cfg), | |
921 | BSE::Util::Tags->static(\%acts, $cfg), | |
922 | iterate_items_reset => sub { $item_index = -1; }, | |
923 | iterate_items => | |
924 | sub { | |
925 | if (++$item_index < @$items) { | |
926 | $option_index = -1; | |
927 | @options = cart_item_opts($items->[$item_index], | |
928 | $products->[$item_index]); | |
929 | return 1; | |
930 | } | |
931 | return 0; | |
932 | }, | |
933 | item=> sub { $items->[$item_index]{$_[0]}; }, | |
934 | product => | |
935 | sub { | |
936 | my $value = $products->[$item_index]{$_[0]}; | |
937 | defined($value) or $value = ''; | |
938 | $value; | |
939 | }, | |
940 | order => sub { $order->{$_[0]} }, | |
941 | extended => | |
942 | sub { | |
943 | $items->[$item_index]{units} * $items->[$item_index]{$_[0]}; | |
944 | }, | |
945 | _format => | |
946 | sub { | |
947 | my ($value, $fmt) = @_; | |
948 | if ($fmt =~ /^m(\d+)/) { | |
949 | return sprintf("%$1s", sprintf("%.2f", $value/100)); | |
950 | } | |
951 | elsif ($fmt =~ /%/) { | |
952 | return sprintf($fmt, $value); | |
953 | } | |
954 | elsif ($fmt =~ /^\d+$/) { | |
955 | return substr($value . (" " x $fmt), 0, $fmt); | |
956 | } | |
957 | else { | |
958 | return $value; | |
959 | } | |
960 | }, | |
961 | iterate_options_reset => sub { $option_index = -1 }, | |
962 | iterate_options => sub { ++$option_index < @options }, | |
963 | option => sub { escape_html($options[$option_index]{$_[0]}) }, | |
964 | ifOptions => sub { @options }, | |
965 | options => sub { nice_options(@options) }, | |
966 | with_wrap => \&tag_with_wrap, | |
967 | ifSubscribingTo => [ \&tag_ifSubscribingTo, $subscribing_to ], | |
968 | ); | |
969 | ||
970 | my $mailer = BSE::Mail->new(cfg=>$cfg); | |
971 | # ok, send some email | |
972 | my $confirm = BSE::Template->get_page('mailconfirm', $cfg, \%acts); | |
973 | my $email_order = $cfg->entryBool('shop', 'email_order', $Constants::SHOP_EMAIL_ORDER); | |
974 | if ($email_order) { | |
975 | unless ($noencrypt) { | |
976 | $acts{cardNumber} = $cgi->param('cardNumber'); | |
977 | $acts{cardExpiry} = $cgi->param('cardExpiry'); | |
978 | } | |
979 | my $ordertext = BSE::Template->get_page('mailorder', $cfg, \%acts); | |
980 | ||
981 | my $send_text; | |
982 | if ($noencrypt) { | |
983 | $send_text = $ordertext; | |
984 | } | |
985 | else { | |
986 | eval "use $crypto_class"; | |
987 | !$@ or die $@; | |
988 | my $encrypter = $crypto_class->new; | |
989 | ||
990 | my $debug = $cfg->entryBool('debug', 'mail_encryption', 0); | |
991 | my $sign = $cfg->entryBool('basic', 'sign', 1); | |
992 | ||
993 | # encrypt and sign | |
994 | my %opts = | |
995 | ( | |
996 | sign=> $sign, | |
997 | passphrase=> $passphrase, | |
998 | stripwarn=>1, | |
999 | debug=>$debug, | |
1000 | ); | |
1001 | ||
1002 | $opts{secretkeyid} = $signing_id if $signing_id; | |
1003 | $opts{pgp} = $pgp if $pgp; | |
1004 | $opts{gpg} = $gpg if $gpg; | |
1005 | $opts{pgpe} = $pgpe if $pgpe; | |
1006 | my $recip = "$toName $toEmail"; | |
1007 | ||
1008 | $send_text = $encrypter->encrypt($recip, $ordertext, %opts ) | |
1009 | or die "Cannot encrypt ", $encrypter->error; | |
1010 | } | |
1011 | $mailer->send(to=>$toEmail, from=>$from, subject=>'New Order '.$order->{id}, | |
1012 | body=>$send_text) | |
1013 | or print STDERR "Error sending order to admin: ",$mailer->errstr,"\n"; | |
1014 | } | |
1015 | $mailer->send(to=>$order->{emailAddress}, from=>$from, | |
1016 | subject=>$subject . " " . localtime, | |
1017 | body=>$confirm) | |
1018 | or print STDERR "Error sending order to customer: ",$mailer->errstr,"\n"; | |
1019 | } | |
1020 | ||
1021 | sub tag_with_wrap { | |
1022 | my ($args, $text) = @_; | |
1023 | ||
1024 | my $margin = $args =~ /^\d+$/ && $args > 30 ? $args : 70; | |
1025 | ||
1026 | require Text::Wrap; | |
1027 | # do it twice to prevent a warning | |
1028 | $Text::Wrap::columns = $margin; | |
1029 | $Text::Wrap::columns = $margin; | |
1030 | ||
1031 | return Text::Wrap::fill('', '', split /\n/, $text); | |
1032 | } | |
1033 | ||
1034 | sub _refresh_logon { | |
1035 | my ($class, $req, $msg, $msgid, $r) = @_; | |
1036 | ||
1037 | my $securlbase = $req->cfg->entryVar('site', 'secureurl'); | |
1038 | my $url = $securlbase."/cgi-bin/user.pl"; | |
1039 | ||
1040 | $r ||= $securlbase."/cgi-bin/shop.pl?checkout=1"; | |
1041 | ||
1042 | my %parms; | |
1043 | $parms{r} = $r; | |
1044 | $parms{message} = $msg if $msg; | |
1045 | $parms{mid} = $msgid if $msgid; | |
1046 | $url .= "?" . join("&", map "$_=".escape_uri($parms{$_}), keys %parms); | |
1047 | ||
1048 | return BSE::Template->get_refresh($url, $req->cfg); | |
1049 | } | |
1050 | ||
1051 | sub _need_logon { | |
1052 | my ($class, $req, $cart, $cart_prods) = @_; | |
1053 | ||
1054 | return need_logon($req->cfg, $cart, $cart_prods, $req->session, $req->cgi); | |
1055 | } | |
1056 | ||
1057 | sub tag_checkedPayment { | |
1058 | my ($payment, $types_by_name, $args) = @_; | |
1059 | ||
1060 | my $type = $args; | |
1061 | if ($type !~ /^\d+$/) { | |
1062 | return '' unless exists $types_by_name->{$type}; | |
1063 | $type = $types_by_name->{$type}; | |
1064 | } | |
1065 | ||
1066 | return $payment == $type ? 'checked="checked"' : ''; | |
1067 | } | |
1068 | ||
1069 | sub tag_ifPayments { | |
1070 | my ($enabled, $types_by_name, $args) = @_; | |
1071 | ||
1072 | my $type = $args; | |
1073 | if ($type !~ /^\d+$/) { | |
1074 | return '' unless exists $types_by_name->{$type}; | |
1075 | $type = $types_by_name->{$type}; | |
1076 | } | |
1077 | ||
1078 | my @found = grep $_ == $type, @$enabled; | |
1079 | ||
1080 | return scalar @found; | |
1081 | } | |
1082 | ||
1083 | sub update_quantities { | |
1084 | my ($class, $req) = @_; | |
1085 | ||
1086 | my $session = $req->session; | |
1087 | my $cgi = $req->cgi; | |
1088 | my $cfg = $req->cfg; | |
1089 | my @cart = @{$session->{cart} || []}; | |
1090 | for my $index (0..$#cart) { | |
1091 | my $new_quantity = $cgi->param("quantity_$index"); | |
1092 | if (defined $new_quantity) { | |
1093 | if ($new_quantity =~ /^\s*(\d+)/) { | |
1094 | $cart[$index]{units} = $1; | |
1095 | } | |
1096 | elsif ($new_quantity =~ /^\s*$/) { | |
1097 | $cart[$index]{units} = 0; | |
1098 | } | |
1099 | } | |
1100 | } | |
1101 | @cart = grep { $_->{units} != 0 } @cart; | |
1102 | $session->{cart} = \@cart; | |
1103 | $session->{custom} ||= {}; | |
1104 | my %custom_state = %{$session->{custom}}; | |
1105 | custom_class($cfg)->recalc($cgi, \@cart, [], \%custom_state, $cfg); | |
1106 | $session->{custom} = \%custom_state; | |
1107 | } | |
1108 | ||
1109 | sub _build_items { | |
1110 | my ($class, $req, $products) = @_; | |
1111 | ||
1112 | my $session = $req->session; | |
1113 | $session->{cart} | |
1114 | or return; | |
1115 | my @msgs; | |
1116 | my @cart = @{$req->session->{cart}} | |
1117 | or return; | |
1118 | my @items; | |
1119 | my @prodcols = Product->columns; | |
1120 | my @newcart; | |
1121 | my $today = now_sqldate(); | |
1122 | for my $item (@cart) { | |
1123 | my %work = %$item; | |
1124 | my $product = Products->getByPkey($item->{productId}); | |
1125 | if ($product) { | |
1126 | (my $comp_release = $product->{release}) =~ s/ .*//; | |
1127 | (my $comp_expire = $product->{expire}) =~ s/ .*//; | |
1128 | $comp_release le $today | |
1129 | or do { push @msgs, "'$product->{title}' has not been released yet"; | |
1130 | next; }; | |
1131 | $today le $comp_expire | |
1132 | or do { push @msgs, "'$product->{title}' has expired"; next; }; | |
1133 | $product->{listed} | |
1134 | or do { push @msgs, "'$product->{title}' not available"; next; }; | |
1135 | ||
1136 | for my $col (@prodcols) { | |
1137 | $work{$col} = $product->{$col} unless exists $work{$col}; | |
1138 | } | |
1139 | $work{extended_retailPrice} = $work{units} * $work{retailPrice}; | |
1140 | $work{extended_gst} = $work{units} * $work{gst}; | |
1141 | $work{extended_wholesale} = $work{units} * $work{wholesalePrice}; | |
1142 | ||
1143 | push @newcart, \%work; | |
1144 | push @$products, $product; | |
1145 | } | |
1146 | } | |
1147 | ||
1148 | # we don't use these for anything for now | |
1149 | #if (@msgs) { | |
1150 | # @$rmsg = @msgs; | |
1151 | #} | |
1152 | ||
1153 | return @newcart; | |
1154 | } | |
1155 | ||
1156 | sub _fillout_order { | |
1157 | my ($class, $req, $values, $items, $rmsg, $how) = @_; | |
1158 | ||
1159 | my $session = $req->session; | |
1160 | my $cfg = $req->cfg; | |
1161 | my $cgi = $req->cgi; | |
1162 | ||
1163 | my $total = 0; | |
1164 | my $total_gst = 0; | |
1165 | my $total_wholesale = 0; | |
1166 | for my $item (@$items) { | |
1167 | $total += $item->{extended_retailPrice}; | |
1168 | $total_gst += $item->{extended_gst}; | |
1169 | $total_wholesale += $item->{extended_wholesale}; | |
1170 | } | |
1171 | $values->{total} = $total; | |
1172 | $values->{gst} = $total_gst; | |
1173 | $values->{wholesale} = $total_wholesale; | |
1174 | $values->{shipping_cost} = 0; | |
1175 | ||
1176 | my $cust_class = custom_class($cfg); | |
1177 | ||
1178 | # if it sets shipping cost it must also update the total | |
1179 | eval { | |
1180 | my %custom = %{$session->{custom}}; | |
1181 | $cust_class->order_save($cgi, $values, $items, $items, | |
1182 | \%custom, $cfg); | |
1183 | $session->{custom} = \%custom; | |
1184 | }; | |
1185 | if ($@) { | |
1186 | $$rmsg = $@; | |
1187 | return; | |
1188 | } | |
1189 | ||
1190 | $values->{total} += | |
1191 | $cust_class->total_extras($items, $items, | |
1192 | $session->{custom}, $cfg, $how); | |
1193 | ||
1194 | my $affiliate_code = $session->{affiliate_code}; | |
1195 | defined $affiliate_code && length $affiliate_code | |
1196 | or $affiliate_code = $cgi->param('affiliate_code'); | |
1197 | defined $affiliate_code or $affiliate_code = ''; | |
1198 | $values->{affiliate_code} = $affiliate_code; | |
1199 | ||
1200 | my $user = $req->siteuser; | |
1201 | if ($user) { | |
1202 | $values->{userId} = $user->{userId}; | |
1203 | $values->{siteuser_id} = $user->{id}; | |
1204 | } | |
1205 | else { | |
1206 | $values->{userId} = ''; | |
1207 | $values->{siteuser_id} = -1; | |
1208 | } | |
1209 | ||
1210 | $values->{orderDate} = now_sqldatetime; | |
1211 | ||
1212 | # this should be hard to guess | |
1213 | $values->{randomId} ||= md5_hex(time().rand().{}.$$); | |
1214 | ||
1215 | return 1; | |
1216 | } | |
1217 | ||
1218 | sub action_prefix { '' } | |
1219 | ||
718a070d TC |
1220 | sub req_location { |
1221 | my ($class, $req) = @_; | |
1222 | ||
1223 | require BSE::TB::Locations; | |
1224 | my $cgi = $req->cgi; | |
1225 | my $location_id = $cgi->param('location_id'); | |
1226 | my $location; | |
1227 | if (defined $location_id && $location_id =~ /^\d$/) { | |
1228 | $location = BSE::TB::Locations->getByPkey($location_id); | |
1229 | my %acts; | |
1230 | %acts = | |
1231 | ( | |
1232 | BSE::Util::Tags->static(\%acts, $req->cfg), | |
1233 | location => [ \&tag_hash, $location ], | |
1234 | ); | |
1235 | ||
1236 | return $req->response('location', \%acts); | |
1237 | } | |
1238 | else { | |
1239 | return | |
1240 | { | |
1241 | type=>BSE::Template->get_type($req->cfg, 'error'), | |
1242 | content=>"Missing or invalid location_id", | |
1243 | }; | |
1244 | } | |
1245 | } | |
1246 | ||
41e7c841 | 1247 | 1; |