pass the dbh when validating custom fields, not just on config
[bse.git] / site / cgi-bin / modules / BSE / Cart.pm
CommitLineData
11af7272
TC
1package BSE::Cart;
2use strict;
3use Scalar::Util;
4
88a03daa 5our $VERSION = "1.002";
11af7272
TC
6
7=head1 NAME
8
9BSE::Cart - abstraction for the BSE cart.
10
11=head1 SYNOPSIS
12
13 use BSE::Cart;
8757d2fb 14 my $cart = BSE::Cart->new($req, $stage);
11af7272
TC
15
16 my $items = $cart->items;
17 my $products = $cart->products;
18
19=head1 DESCRIPTION
20
21This class provides a simple abstraction for access to the BSE
22shopping cart.
23
24This is intended for use in templates, but may be expanded further.
25
26=head1 METHODS
27
28=over
29
30=item new()
31
32Create a new cart object based on the session.
33
34=cut
35
36sub new {
8757d2fb 37 my ($class, $req, $stage) = @_;
11af7272
TC
38
39 my $self = bless
40 {
41 products => {},
42 req => $req,
8757d2fb 43 stage => $stage,
11af7272
TC
44 }, $class;
45 Scalar::Util::weaken($self->{req});
46 my $items = $req->session->{cart} || [];
47 my $myself = $self;
48 Scalar::Util::weaken($myself);
49 my $index = 0;
8757d2fb 50 $self->{items} = [ map BSE::Cart::Item->new($_, $index++, $self), @$items ];
11af7272
TC
51
52 return $self;
53}
54
55=item items()
56
57Return an array reference of cart items.
58
59=cut
60
61sub items {
62 return $_[0]{items};
63}
64
65=item products().
66
67Return an array reference of products in the cart, corresponding to
68the array reference returned by items().
69
70=cut
71
72sub products {
73 my $self = shift;
74 return [ map $self->_product($_->{productId}), @{$self->items} ];
75}
76
77=item total_cost
78
79Return the total cost of the items in the cart.
80
81=cut
82
83sub total_cost {
84 my ($self) = @_;
85
86 my $total_cost = 0;
87 for my $item (@{$self->items}) {
88 $total_cost += $item->{extended};
89 }
90
91 return $total_cost;
92}
93
94=item total_units
95
96Return the total number of units in the cart.
97
98=cut
99
100sub total_units {
101 my ($self) = @_;
102
103 my $total_units = 0;
104 for my $item (@{$self->items}) {
105 $total_units += $item->{units};
106 }
107
108 return $total_units;
109}
110
111=item total
112
113Total of items in the cart and any custom extras.
114
115=cut
116
8757d2fb
TC
117sub total {
118 my ($self) = @_;
11af7272 119
8757d2fb
TC
120 "FIXME";
121}
11af7272
TC
122
123sub _product {
124 my ($self, $id) = @_;
125
126 my $product = $self->{products}{$id};
127 unless ($product) {
128 require Products;
129 $product = Products->getByPkey($id)
130 or die "No product $id\n";
131 # FIXME
132 if ($product->generator ne "Generate::Product") {
133 require BSE::TB::Seminars;
134 $product = BSE::TB::Seminars->getByPkey($id)
135 or die "Not a product, not a seminar $id\n";
136 }
137
138 $self->{products}{$id} = $product;
139 }
140 return $product;
8757d2fb
TC
141}
142
143sub _session {
144 my ($self, $id) = @_;
145 my $session = $self->{sessions}{$id};
146 unless ($session) {
147 require BSE::TB::SeminarSessions;
148 $session = BSE::TB::SeminarSessions->getByPkey($id);
149 $self->{sessions}{$id} = $session;
150 }
11af7272 151
8757d2fb
TC
152 return $session;
153}
154
155=item cleanup()
156
157Clean up the cart, removing any items that are unreleased, expired or
158unlisted.
159
160For BSE use.
161
162=cut
163
164sub cleanup {
165 my ($self) = @_;
166
167 my @newitems;
168 for my $item ($self->items) {
169 my $product = $item->product;
170
171 if ($product->is_released && !$product->is_expired && $product->listed) {
172 push @newitems, $item;
173 }
174 }
175
176 $self->{items} = \@newitems;
177}
178
179=back
180
181=cut
182
183package BSE::Cart::Item;
184
185sub new {
186 my ($class, $raw_item, $index, $cart) = @_;
187
188 my $item = bless
189 {
190 %$raw_item,
191 index => $index,
192 cart => $cart,
193 }, $class;
194
195 Scalar::Util::weaken($item->{cart});
196
197 return $item;
198}
199
200=head2 Item Members
201
202=over
203
204=item product
205
206Returns the product for that line item.
207
208=cut
209
210sub product {
211 my $self = shift;
212
213 return $self->{cart}->_product($self->{productId});
214}
215
216=item price
217
218=cut
219
220sub price {
221 my ($self) = @_;
222
223 unless (defined $self->{calc_price}) {
88a03daa 224 $self->{calc_price} = $self->product->price(user => $self->{cart}{req}->siteuser);
8757d2fb
TC
225 }
226
227 return $self->{calc_price};
11af7272
TC
228}
229
230=item extended
231
232The extended price for the item.
233
8757d2fb
TC
234=cut
235
236sub extended {
237 my ($self, $base) = @_;
238
239 $base =~ /^(price|retailPrice|gst|wholesalePrice)$/
240 or return 0;
241
242 return self->$base() * $self->{units};
243}
244
245sub extended_retailPrice {
246 $_[0]->extended("price");
247}
248
249sub extended_wholesalePrice {
250 $_[0]->extended("wholesalePrice");
251}
252
253sub extended_gst {
254 $_[0]->extended("gst");
255}
256
257=item units
258
259The number of units.
260
261=cut
262
263sub units {
264 $_[0]{units};
265}
266
267=item session_id
268
269The seminar session id, if any.
270
271=cut
272
273sub session_id {
274 $_[0]{session_id};
275}
276
277=item tier_id
278
279The pricing tier id.
280
281=cut
282
283sub tier_id {
284 $_[0]{tier};
285}
286
11af7272
TC
287=item link
288
289A link to the product.
290
291=cut
292
8757d2fb 293sub link {
11af7272
TC
294 my ($self, $id) = @_;
295
8757d2fb 296 my $product = $self->product;
11af7272
TC
297 my $link = $product->link;
298 unless ($link =~ /^\w+:/) {
299 $link = BSE::Cfg->single->entryErr("site", "url") . $link;
300 }
301
302 return $link;
303}
304
305=item option_list
306
307Return a list of options for the item, each with:
308
309=over
310
311=item *
312
313id, name - the identifier for the option
314
315=item *
316
317value - the value of the option.
318
319=item *
320
321desc - the description of the option
322
323=item *
324
325display - display of the option value
326
327=back
328
329=cut
330
8757d2fb 331sub option_list {
11af7272
TC
332 my ($self, $index) = @_;
333
8757d2fb 334 return [ $self->product->option_descs(BSE::Cfg->single, $self->{options}) ];
11af7272
TC
335}
336
337=item option_text
338
339Display text for options for the item.
340
341=cut
342
8757d2fb 343sub option_text {
11af7272
TC
344 my ($self, $index) = @_;
345
8757d2fb 346 my $options = $self->option_list;
11af7272
TC
347 return join(", ", map "$_->{desc}: $_->{display}", @$options);
348}
349
350=item session
351
352The session object of the seminar session
353
354=cut
355
8757d2fb
TC
356sub session {
357 my ($self) = @_;
11af7272 358
8757d2fb
TC
359 $self->{session_id} or return;
360 return $self->{cart}->_session($self->{session_id});
361}
362
363
364my %product_keys;
365
366sub AUTOLOAD {
367 our $AUTOLOAD;
368 (my $name = $AUTOLOAD) =~ s/^.*:://;
369 unless (%product_keys) {
370 require Products;
371 %product_keys = map { $_ => 1 } Product->columns;
11af7272
TC
372 }
373
8757d2fb
TC
374 if ($product_keys{$name}) {
375 return $_[0]->product->$name();
376 }
377 else {
378 return "* unknown method $name *";
379 }
11af7272
TC
380}
381
3821;
383
384=back
385
386=head1 AUTHOR
387
388Tony Cook <tony@develop-help.com>
389
390=cut