line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
# Nitesi::Cart - Nitesi cart class |
2
|
|
|
|
|
|
|
|
3
|
|
|
|
|
|
|
package Nitesi::Cart; |
4
|
|
|
|
|
|
|
|
5
|
2
|
|
|
2
|
|
55293
|
use strict; |
|
2
|
|
|
|
|
6
|
|
|
2
|
|
|
|
|
85
|
|
6
|
2
|
|
|
2
|
|
10
|
use warnings; |
|
2
|
|
|
|
|
4
|
|
|
2
|
|
|
|
|
67
|
|
7
|
|
|
|
|
|
|
|
8
|
2
|
|
|
2
|
|
12
|
use constant CART_DEFAULT => 'main'; |
|
2
|
|
|
|
|
4
|
|
|
2
|
|
|
|
|
5396
|
|
9
|
|
|
|
|
|
|
|
10
|
|
|
|
|
|
|
=head1 NAME |
11
|
|
|
|
|
|
|
|
12
|
|
|
|
|
|
|
Nitesi::Cart - Cart class for Nitesi Shop Machine |
13
|
|
|
|
|
|
|
|
14
|
|
|
|
|
|
|
=head1 DESCRIPTION |
15
|
|
|
|
|
|
|
|
16
|
|
|
|
|
|
|
Generic cart class for L. |
17
|
|
|
|
|
|
|
|
18
|
|
|
|
|
|
|
=head2 CART ITEMS |
19
|
|
|
|
|
|
|
|
20
|
|
|
|
|
|
|
Each item in the cart has at least the following attributes: |
21
|
|
|
|
|
|
|
|
22
|
|
|
|
|
|
|
=over 4 |
23
|
|
|
|
|
|
|
|
24
|
|
|
|
|
|
|
=item sku |
25
|
|
|
|
|
|
|
|
26
|
|
|
|
|
|
|
Unique item identifier. |
27
|
|
|
|
|
|
|
|
28
|
|
|
|
|
|
|
=item name |
29
|
|
|
|
|
|
|
|
30
|
|
|
|
|
|
|
Item name. |
31
|
|
|
|
|
|
|
|
32
|
|
|
|
|
|
|
=item quantity |
33
|
|
|
|
|
|
|
|
34
|
|
|
|
|
|
|
Item quantity. |
35
|
|
|
|
|
|
|
|
36
|
|
|
|
|
|
|
=item price |
37
|
|
|
|
|
|
|
|
38
|
|
|
|
|
|
|
Item price. |
39
|
|
|
|
|
|
|
|
40
|
|
|
|
|
|
|
=back |
41
|
|
|
|
|
|
|
|
42
|
|
|
|
|
|
|
=head1 CONSTRUCTOR |
43
|
|
|
|
|
|
|
|
44
|
|
|
|
|
|
|
=head2 new |
45
|
|
|
|
|
|
|
|
46
|
|
|
|
|
|
|
=cut |
47
|
|
|
|
|
|
|
|
48
|
|
|
|
|
|
|
sub new { |
49
|
6
|
|
|
6
|
1
|
722
|
my ($class, $self, %args); |
50
|
|
|
|
|
|
|
|
51
|
6
|
|
|
|
|
10
|
$class = shift; |
52
|
6
|
|
|
|
|
15
|
%args = @_; |
53
|
|
|
|
|
|
|
|
54
|
6
|
|
|
|
|
20
|
my $time = time; |
55
|
|
|
|
|
|
|
|
56
|
6
|
|
|
|
|
54
|
$self = {error => '', items => [], modifiers => [], |
57
|
|
|
|
|
|
|
costs => [], subtotal => 0, total => 0, |
58
|
|
|
|
|
|
|
cache_subtotal => 1, cache_total => 1, |
59
|
|
|
|
|
|
|
created => $time, last_modified => $time, |
60
|
|
|
|
|
|
|
}; |
61
|
|
|
|
|
|
|
|
62
|
6
|
50
|
|
|
|
21
|
if ($args{name}) { |
63
|
0
|
|
|
|
|
0
|
$self->{name} = $args{name}; |
64
|
|
|
|
|
|
|
} |
65
|
|
|
|
|
|
|
else { |
66
|
6
|
|
|
|
|
13
|
$self->{name} = CART_DEFAULT; |
67
|
|
|
|
|
|
|
} |
68
|
|
|
|
|
|
|
|
69
|
6
|
|
|
|
|
13
|
for my $ts (qw/created last_modified/) { |
70
|
12
|
100
|
|
|
|
35
|
if (exists $args{$ts}) { |
71
|
1
|
|
|
|
|
4
|
$self->{$ts} = $args{$ts}; |
72
|
|
|
|
|
|
|
} |
73
|
|
|
|
|
|
|
} |
74
|
|
|
|
|
|
|
|
75
|
6
|
50
|
|
|
|
17
|
if ($args{modifiers}) { |
76
|
0
|
|
|
|
|
0
|
$self->{modifiers} = $args{modifiers}; |
77
|
|
|
|
|
|
|
} |
78
|
|
|
|
|
|
|
|
79
|
6
|
100
|
|
|
|
18
|
if ($args{run_hooks}) { |
80
|
2
|
|
|
|
|
4
|
$self->{run_hooks} = $args{run_hooks}; |
81
|
|
|
|
|
|
|
} |
82
|
|
|
|
|
|
|
|
83
|
6
|
|
|
|
|
15
|
bless $self, $class; |
84
|
|
|
|
|
|
|
|
85
|
6
|
|
|
|
|
21
|
$self->init(%args); |
86
|
|
|
|
|
|
|
|
87
|
6
|
|
|
|
|
19
|
return $self; |
88
|
|
|
|
|
|
|
} |
89
|
|
|
|
|
|
|
|
90
|
|
|
|
|
|
|
=head2 init |
91
|
|
|
|
|
|
|
|
92
|
|
|
|
|
|
|
Initializer which receives the constructor arguments, but does nothing. |
93
|
|
|
|
|
|
|
May be overridden in a subclass. |
94
|
|
|
|
|
|
|
|
95
|
|
|
|
|
|
|
=cut |
96
|
|
|
|
|
|
|
|
97
|
|
|
|
|
|
|
sub init { |
98
|
6
|
|
|
6
|
1
|
8
|
return 1; |
99
|
|
|
|
|
|
|
}; |
100
|
|
|
|
|
|
|
|
101
|
|
|
|
|
|
|
=head1 METHODS |
102
|
|
|
|
|
|
|
|
103
|
|
|
|
|
|
|
=head2 items |
104
|
|
|
|
|
|
|
|
105
|
|
|
|
|
|
|
Returns items in the cart. |
106
|
|
|
|
|
|
|
|
107
|
|
|
|
|
|
|
=cut |
108
|
|
|
|
|
|
|
|
109
|
|
|
|
|
|
|
sub items { |
110
|
7
|
|
|
7
|
1
|
235
|
my ($self) = shift; |
111
|
|
|
|
|
|
|
|
112
|
7
|
|
|
|
|
18
|
return $self->{items}; |
113
|
|
|
|
|
|
|
} |
114
|
|
|
|
|
|
|
|
115
|
|
|
|
|
|
|
=head2 subtotal |
116
|
|
|
|
|
|
|
|
117
|
|
|
|
|
|
|
Returns subtotal of the cart. |
118
|
|
|
|
|
|
|
|
119
|
|
|
|
|
|
|
=cut |
120
|
|
|
|
|
|
|
|
121
|
|
|
|
|
|
|
sub subtotal { |
122
|
7
|
|
|
7
|
1
|
12
|
my ($self) = shift; |
123
|
|
|
|
|
|
|
|
124
|
7
|
100
|
|
|
|
18
|
if ($self->{cache_subtotal}) { |
125
|
3
|
|
|
|
|
8
|
return $self->{subtotal}; |
126
|
|
|
|
|
|
|
} |
127
|
|
|
|
|
|
|
|
128
|
4
|
|
|
|
|
7
|
$self->{subtotal} = 0; |
129
|
|
|
|
|
|
|
|
130
|
4
|
|
|
|
|
6
|
for my $item (@{$self->{items}}) { |
|
4
|
|
|
|
|
10
|
|
131
|
6
|
|
|
|
|
36
|
$self->{subtotal} += $item->{price} * $item->{quantity}; |
132
|
|
|
|
|
|
|
} |
133
|
|
|
|
|
|
|
|
134
|
4
|
|
|
|
|
6
|
$self->{cache_subtotal} = 1; |
135
|
|
|
|
|
|
|
|
136
|
4
|
|
|
|
|
11
|
return $self->{subtotal}; |
137
|
|
|
|
|
|
|
} |
138
|
|
|
|
|
|
|
|
139
|
|
|
|
|
|
|
=head2 total |
140
|
|
|
|
|
|
|
|
141
|
|
|
|
|
|
|
Returns total of the cart. |
142
|
|
|
|
|
|
|
|
143
|
|
|
|
|
|
|
=cut |
144
|
|
|
|
|
|
|
|
145
|
|
|
|
|
|
|
sub total { |
146
|
8
|
|
|
8
|
1
|
435
|
my ($self) = shift; |
147
|
8
|
|
|
|
|
10
|
my ($subtotal); |
148
|
|
|
|
|
|
|
|
149
|
8
|
100
|
|
|
|
23
|
if ($self->{cache_total}) { |
150
|
1
|
|
|
|
|
3
|
return $self->{total}; |
151
|
|
|
|
|
|
|
} |
152
|
|
|
|
|
|
|
|
153
|
7
|
|
|
|
|
17
|
$self->{total} = $subtotal = $self->subtotal(); |
154
|
|
|
|
|
|
|
|
155
|
|
|
|
|
|
|
# calculate costs |
156
|
7
|
|
|
|
|
20
|
$self->{total} += $self->_calculate($subtotal); |
157
|
|
|
|
|
|
|
|
158
|
7
|
|
|
|
|
9
|
$self->{cache_total} = 1; |
159
|
|
|
|
|
|
|
|
160
|
7
|
|
|
|
|
19
|
return $self->{total}; |
161
|
|
|
|
|
|
|
} |
162
|
|
|
|
|
|
|
|
163
|
|
|
|
|
|
|
=head2 add $item |
164
|
|
|
|
|
|
|
|
165
|
|
|
|
|
|
|
Add item to the cart. Returns item in case of success. |
166
|
|
|
|
|
|
|
|
167
|
|
|
|
|
|
|
The item is a hash (reference) which is subject to the following |
168
|
|
|
|
|
|
|
conditions: |
169
|
|
|
|
|
|
|
|
170
|
|
|
|
|
|
|
=over 4 |
171
|
|
|
|
|
|
|
|
172
|
|
|
|
|
|
|
=item sku |
173
|
|
|
|
|
|
|
|
174
|
|
|
|
|
|
|
Item identifier is required. |
175
|
|
|
|
|
|
|
|
176
|
|
|
|
|
|
|
=item name |
177
|
|
|
|
|
|
|
|
178
|
|
|
|
|
|
|
Item name is required. |
179
|
|
|
|
|
|
|
|
180
|
|
|
|
|
|
|
=item quantity |
181
|
|
|
|
|
|
|
|
182
|
|
|
|
|
|
|
Item quantity is optional and has to be a natural number greater |
183
|
|
|
|
|
|
|
than zero. Default for quantity is 1. |
184
|
|
|
|
|
|
|
|
185
|
|
|
|
|
|
|
=item price |
186
|
|
|
|
|
|
|
|
187
|
|
|
|
|
|
|
Item price is required and a positive number. |
188
|
|
|
|
|
|
|
|
189
|
|
|
|
|
|
|
Price is required, because you want to maintain the price that was valid at the time of adding to the cart. Should the price in the shop change in the meantime, it will maintain this price. If you would like to update the pages, you have to do it before loading the cart page on your shop. |
190
|
|
|
|
|
|
|
|
191
|
|
|
|
|
|
|
|
192
|
|
|
|
|
|
|
B Add 5 BMX2012 products to the cart |
193
|
|
|
|
|
|
|
|
194
|
|
|
|
|
|
|
$cart->add( sku => 'BMX2012', quantity => 5, price => 200); |
195
|
|
|
|
|
|
|
|
196
|
|
|
|
|
|
|
B Add a BMX2012 product to the cart. |
197
|
|
|
|
|
|
|
|
198
|
|
|
|
|
|
|
$cart->add( sku => 'BMX2012', price => 200); |
199
|
|
|
|
|
|
|
|
200
|
|
|
|
|
|
|
=back |
201
|
|
|
|
|
|
|
|
202
|
|
|
|
|
|
|
=cut |
203
|
|
|
|
|
|
|
|
204
|
|
|
|
|
|
|
sub add { |
205
|
12
|
|
|
12
|
1
|
1063
|
my $self = shift; |
206
|
12
|
|
|
|
|
14
|
my (%item, $ret); |
207
|
|
|
|
|
|
|
|
208
|
12
|
50
|
|
|
|
27
|
if (ref($_[0])) { |
209
|
|
|
|
|
|
|
# copy item |
210
|
12
|
|
|
|
|
481
|
%item = %{$_[0]}; |
|
12
|
|
|
|
|
47
|
|
211
|
|
|
|
|
|
|
} |
212
|
|
|
|
|
|
|
else { |
213
|
0
|
|
|
|
|
0
|
%item = @_; |
214
|
|
|
|
|
|
|
} |
215
|
|
|
|
|
|
|
|
216
|
|
|
|
|
|
|
# run hooks before validating item |
217
|
12
|
|
|
|
|
35
|
$self->_run_hook('before_cart_add_validate', $self, \%item); |
218
|
|
|
|
|
|
|
|
219
|
|
|
|
|
|
|
# validate item |
220
|
12
|
50
|
66
|
|
|
104
|
unless (exists $item{sku} && defined $item{sku} && $item{sku} =~ /\S/) { |
|
|
|
66
|
|
|
|
|
221
|
1
|
|
|
|
|
3
|
$self->{error} = 'Item added without SKU.'; |
222
|
1
|
|
|
|
|
2
|
return; |
223
|
|
|
|
|
|
|
} |
224
|
|
|
|
|
|
|
|
225
|
11
|
50
|
66
|
|
|
97
|
unless (exists $item{name} && defined $item{name} && $item{name} =~ /\S/) { |
|
|
|
66
|
|
|
|
|
226
|
1
|
|
|
|
|
4
|
$self->{error} = "Item $item{sku} added without a name."; |
227
|
1
|
|
|
|
|
4
|
return; |
228
|
|
|
|
|
|
|
} |
229
|
|
|
|
|
|
|
|
230
|
10
|
100
|
66
|
|
|
74
|
if (exists $item{quantity} && defined $item{quantity}) { |
231
|
3
|
50
|
33
|
|
|
24
|
unless ($item{quantity} =~ /^(\d+)$/ && $item{quantity} > 0) { |
232
|
0
|
|
|
|
|
0
|
$self->{error} = "Item $item{sku} added with invalid quantity $item{quantity}."; |
233
|
0
|
|
|
|
|
0
|
return; |
234
|
|
|
|
|
|
|
} |
235
|
|
|
|
|
|
|
} |
236
|
|
|
|
|
|
|
else { |
237
|
7
|
|
|
|
|
15
|
$item{quantity} = 1; |
238
|
|
|
|
|
|
|
} |
239
|
|
|
|
|
|
|
|
240
|
10
|
50
|
66
|
|
|
153
|
unless (exists $item{price} && defined $item{price} |
|
|
|
66
|
|
|
|
|
|
|
|
33
|
|
|
|
|
241
|
|
|
|
|
|
|
&& $item{price} =~ /^(\d+)(\.\d+)?$/ && $item{price} > 0) { |
242
|
1
|
|
|
|
|
4
|
$self->{error} = "Item $item{sku} added with invalid price."; |
243
|
1
|
|
|
|
|
4
|
return; |
244
|
|
|
|
|
|
|
} |
245
|
|
|
|
|
|
|
|
246
|
|
|
|
|
|
|
# run hooks before adding item to cart |
247
|
9
|
|
|
|
|
622
|
$self->_run_hook('before_cart_add', $self, \%item); |
248
|
|
|
|
|
|
|
|
249
|
9
|
100
|
|
|
|
23
|
if (exists $item{error}) { |
250
|
|
|
|
|
|
|
# one of the hooks denied the item |
251
|
1
|
|
|
|
|
3
|
$self->{error} = $item{error}; |
252
|
1
|
|
|
|
|
5
|
return; |
253
|
|
|
|
|
|
|
} |
254
|
|
|
|
|
|
|
|
255
|
|
|
|
|
|
|
# clear cache flags |
256
|
8
|
|
|
|
|
17
|
$self->{cache_subtotal} = $self->{cache_total} = 0; |
257
|
|
|
|
|
|
|
|
258
|
8
|
100
|
|
|
|
20
|
unless ($ret = $self->_combine(\%item)) { |
259
|
7
|
|
|
|
|
8
|
push @{$self->{items}}, \%item; |
|
7
|
|
|
|
|
14
|
|
260
|
7
|
|
|
|
|
14
|
$self->{last_modified} = time; |
261
|
|
|
|
|
|
|
} |
262
|
|
|
|
|
|
|
|
263
|
|
|
|
|
|
|
# run hooks after adding item to cart |
264
|
8
|
|
|
|
|
18
|
$self->_run_hook('after_cart_add', $self, \%item, $ret); |
265
|
|
|
|
|
|
|
|
266
|
8
|
|
|
|
|
25
|
return \%item; |
267
|
|
|
|
|
|
|
} |
268
|
|
|
|
|
|
|
|
269
|
|
|
|
|
|
|
=head2 remove $sku |
270
|
|
|
|
|
|
|
|
271
|
|
|
|
|
|
|
Remove item from the cart. Takes SKU of item to identify the item. |
272
|
|
|
|
|
|
|
|
273
|
|
|
|
|
|
|
=cut |
274
|
|
|
|
|
|
|
|
275
|
|
|
|
|
|
|
sub remove { |
276
|
3
|
|
|
3
|
1
|
261
|
my ($self, $arg) = @_; |
277
|
3
|
|
|
|
|
4
|
my ($pos, $found, $item); |
278
|
|
|
|
|
|
|
|
279
|
3
|
|
|
|
|
4
|
$pos = 0; |
280
|
|
|
|
|
|
|
|
281
|
|
|
|
|
|
|
# run hooks before locating item |
282
|
3
|
|
|
|
|
5
|
$self->_run_hook('before_cart_remove_validate', $self, $arg); |
283
|
|
|
|
|
|
|
|
284
|
3
|
|
|
|
|
3
|
for $item (@{$self->{items}}) { |
|
3
|
|
|
|
|
7
|
|
285
|
4
|
100
|
|
|
|
10
|
if ($item->{sku} eq $arg) { |
286
|
3
|
|
|
|
|
4
|
$found = 1; |
287
|
3
|
|
|
|
|
6
|
last; |
288
|
|
|
|
|
|
|
} |
289
|
1
|
|
|
|
|
2
|
$pos++; |
290
|
|
|
|
|
|
|
} |
291
|
|
|
|
|
|
|
|
292
|
3
|
50
|
|
|
|
7
|
if ($found) { |
293
|
|
|
|
|
|
|
# run hooks before adding item to cart |
294
|
3
|
|
|
|
|
5
|
$item = $self->{items}->[$pos]; |
295
|
|
|
|
|
|
|
|
296
|
3
|
|
|
|
|
7
|
$self->_run_hook('before_cart_remove', $self, $item); |
297
|
|
|
|
|
|
|
|
298
|
3
|
100
|
|
|
|
7
|
if (exists $item->{error}) { |
299
|
|
|
|
|
|
|
# one of the hooks denied removing the item |
300
|
1
|
|
|
|
|
3
|
$self->{error} = $item->{error}; |
301
|
1
|
|
|
|
|
3
|
return; |
302
|
|
|
|
|
|
|
} |
303
|
|
|
|
|
|
|
|
304
|
|
|
|
|
|
|
# clear cache flags |
305
|
2
|
|
|
|
|
3
|
$self->{cache_subtotal} = $self->{cache_total} = 0; |
306
|
|
|
|
|
|
|
|
307
|
|
|
|
|
|
|
# removing item from our array |
308
|
2
|
|
|
|
|
2
|
splice(@{$self->{items}}, $pos, 1); |
|
2
|
|
|
|
|
6
|
|
309
|
|
|
|
|
|
|
|
310
|
2
|
|
|
|
|
4
|
$self->{last_modified} = time; |
311
|
|
|
|
|
|
|
|
312
|
2
|
|
|
|
|
4
|
$self->_run_hook('after_cart_remove', $self, $item); |
313
|
2
|
|
|
|
|
6
|
return 1; |
314
|
|
|
|
|
|
|
} |
315
|
|
|
|
|
|
|
|
316
|
|
|
|
|
|
|
# item missing |
317
|
0
|
|
|
|
|
0
|
$self->{error} = "Missing item $arg."; |
318
|
|
|
|
|
|
|
|
319
|
0
|
|
|
|
|
0
|
return; |
320
|
|
|
|
|
|
|
} |
321
|
|
|
|
|
|
|
|
322
|
|
|
|
|
|
|
=head2 update |
323
|
|
|
|
|
|
|
|
324
|
|
|
|
|
|
|
Update quantity of items in the cart. |
325
|
|
|
|
|
|
|
|
326
|
|
|
|
|
|
|
Parameters are pairs of SKUs and quantities, e.g. |
327
|
|
|
|
|
|
|
|
328
|
|
|
|
|
|
|
$cart->update(9780977920174 => 5, |
329
|
|
|
|
|
|
|
9780596004927 => 3); |
330
|
|
|
|
|
|
|
|
331
|
|
|
|
|
|
|
Triggers before_cart_update and after_cart_update hooks. |
332
|
|
|
|
|
|
|
|
333
|
|
|
|
|
|
|
A quantity of zero is equivalent to removing this item, |
334
|
|
|
|
|
|
|
so in this case the remove hooks will be invoked instead |
335
|
|
|
|
|
|
|
of the update hooks. |
336
|
|
|
|
|
|
|
|
337
|
|
|
|
|
|
|
=cut |
338
|
|
|
|
|
|
|
|
339
|
|
|
|
|
|
|
sub update { |
340
|
3
|
|
|
3
|
1
|
796
|
my ($self, @args) = @_; |
341
|
3
|
|
|
|
|
5
|
my ($ref, $sku, $qty, $item, $new_item); |
342
|
|
|
|
|
|
|
|
343
|
3
|
|
|
|
|
9
|
while (@args > 0) { |
344
|
4
|
|
|
|
|
6
|
$sku = shift @args; |
345
|
4
|
|
|
|
|
6
|
$qty = shift @args; |
346
|
|
|
|
|
|
|
|
347
|
4
|
50
|
|
|
|
11
|
unless ($item = $self->find($sku)) { |
348
|
0
|
|
|
|
|
0
|
die "Item for $sku not found in cart.\n"; |
349
|
|
|
|
|
|
|
} |
350
|
|
|
|
|
|
|
|
351
|
4
|
100
|
|
|
|
10
|
if ($qty == 0) { |
352
|
|
|
|
|
|
|
# remove item instead |
353
|
1
|
|
|
|
|
3
|
$self->remove($sku); |
354
|
1
|
|
|
|
|
6
|
next; |
355
|
|
|
|
|
|
|
} |
356
|
|
|
|
|
|
|
|
357
|
|
|
|
|
|
|
# jump to next item if quantity stays the same |
358
|
3
|
100
|
|
|
|
12
|
next if $qty == $item->{quantity}; |
359
|
|
|
|
|
|
|
|
360
|
|
|
|
|
|
|
# run hook before updating the cart |
361
|
2
|
|
|
|
|
6
|
$new_item = {quantity => $qty}; |
362
|
|
|
|
|
|
|
|
363
|
2
|
|
|
|
|
5
|
$self->_run_hook('before_cart_update', $self, $item, $new_item); |
364
|
|
|
|
|
|
|
|
365
|
2
|
50
|
|
|
|
7
|
if (exists $new_item->{error}) { |
366
|
|
|
|
|
|
|
# one of the hooks denied the item |
367
|
0
|
|
|
|
|
0
|
$self->{error} = $new_item->{error}; |
368
|
0
|
|
|
|
|
0
|
return; |
369
|
|
|
|
|
|
|
} |
370
|
|
|
|
|
|
|
|
371
|
2
|
|
|
|
|
3
|
$self->{last_modified} = time; |
372
|
|
|
|
|
|
|
|
373
|
2
|
|
|
|
|
5
|
$self->_run_hook('after_cart_update', $self, $item, $new_item); |
374
|
|
|
|
|
|
|
|
375
|
2
|
|
|
|
|
8
|
$item->{quantity} = $qty; |
376
|
|
|
|
|
|
|
} |
377
|
|
|
|
|
|
|
} |
378
|
|
|
|
|
|
|
|
379
|
|
|
|
|
|
|
=head2 clear |
380
|
|
|
|
|
|
|
|
381
|
|
|
|
|
|
|
Removes all items from the cart. |
382
|
|
|
|
|
|
|
|
383
|
|
|
|
|
|
|
=cut |
384
|
|
|
|
|
|
|
|
385
|
|
|
|
|
|
|
sub clear { |
386
|
2
|
|
|
2
|
1
|
483
|
my ($self) = @_; |
387
|
|
|
|
|
|
|
|
388
|
|
|
|
|
|
|
# run hook before clearing the cart |
389
|
2
|
|
|
|
|
6
|
$self->_run_hook('before_cart_clear', $self); |
390
|
|
|
|
|
|
|
|
391
|
2
|
|
|
|
|
3
|
$self->{items} = []; |
392
|
|
|
|
|
|
|
|
393
|
|
|
|
|
|
|
# run hook after clearing the cart |
394
|
2
|
|
|
|
|
9
|
$self->_run_hook('after_cart_clear', $self); |
395
|
|
|
|
|
|
|
|
396
|
|
|
|
|
|
|
# reset subtotal/total |
397
|
2
|
|
|
|
|
4
|
$self->{subtotal} = 0; |
398
|
2
|
|
|
|
|
3
|
$self->{total} = 0; |
399
|
2
|
|
|
|
|
3
|
$self->{cache_subtotal} = 1; |
400
|
2
|
|
|
|
|
2
|
$self->{cache_total} = 1; |
401
|
|
|
|
|
|
|
|
402
|
2
|
|
|
|
|
4
|
$self->{last_modified} = time; |
403
|
|
|
|
|
|
|
|
404
|
2
|
|
|
|
|
5
|
return; |
405
|
|
|
|
|
|
|
} |
406
|
|
|
|
|
|
|
|
407
|
|
|
|
|
|
|
=head2 find |
408
|
|
|
|
|
|
|
|
409
|
|
|
|
|
|
|
Searches for an cart item with the given SKU. |
410
|
|
|
|
|
|
|
Returns cart item in case of sucess. |
411
|
|
|
|
|
|
|
|
412
|
|
|
|
|
|
|
if ($item = $cart->find(9780977920174)) { |
413
|
|
|
|
|
|
|
print "Quantity: $item->{quantity}.\n"; |
414
|
|
|
|
|
|
|
} |
415
|
|
|
|
|
|
|
|
416
|
|
|
|
|
|
|
=cut |
417
|
|
|
|
|
|
|
|
418
|
|
|
|
|
|
|
sub find { |
419
|
4
|
|
|
4
|
1
|
5
|
my ($self, $sku) = @_; |
420
|
|
|
|
|
|
|
|
421
|
4
|
|
|
|
|
4
|
for my $cartitem (@{$self->{items}}) { |
|
4
|
|
|
|
|
10
|
|
422
|
5
|
100
|
|
|
|
12
|
if ($sku eq $cartitem->{sku}) { |
423
|
4
|
|
|
|
|
13
|
return $cartitem; |
424
|
|
|
|
|
|
|
} |
425
|
|
|
|
|
|
|
} |
426
|
|
|
|
|
|
|
|
427
|
0
|
|
|
|
|
0
|
return; |
428
|
|
|
|
|
|
|
} |
429
|
|
|
|
|
|
|
|
430
|
|
|
|
|
|
|
=head2 quantity |
431
|
|
|
|
|
|
|
|
432
|
|
|
|
|
|
|
Returns the sum of the quantity of all items in the shopping cart, |
433
|
|
|
|
|
|
|
which is commonly used as number of items. If you have 5 apples and 6 pears it will return 11. |
434
|
|
|
|
|
|
|
|
435
|
|
|
|
|
|
|
print 'Items in your cart: ', $cart->quantity, "\n"; |
436
|
|
|
|
|
|
|
|
437
|
|
|
|
|
|
|
=cut |
438
|
|
|
|
|
|
|
|
439
|
|
|
|
|
|
|
sub quantity { |
440
|
5
|
|
|
5
|
1
|
1253
|
my $self = shift; |
441
|
5
|
|
|
|
|
7
|
my $qty = 0; |
442
|
|
|
|
|
|
|
|
443
|
5
|
|
|
|
|
7
|
for my $item (@{$self->{items}}) { |
|
5
|
|
|
|
|
12
|
|
444
|
7
|
|
|
|
|
17
|
$qty += $item->{quantity}; |
445
|
|
|
|
|
|
|
} |
446
|
|
|
|
|
|
|
|
447
|
5
|
|
|
|
|
12
|
return $qty; |
448
|
|
|
|
|
|
|
} |
449
|
|
|
|
|
|
|
|
450
|
|
|
|
|
|
|
=head2 created |
451
|
|
|
|
|
|
|
|
452
|
|
|
|
|
|
|
Returns the time (epoch) when the cart was created. |
453
|
|
|
|
|
|
|
|
454
|
|
|
|
|
|
|
=cut |
455
|
|
|
|
|
|
|
|
456
|
|
|
|
|
|
|
sub created { |
457
|
1
|
|
|
1
|
1
|
233
|
my ($self) = @_; |
458
|
|
|
|
|
|
|
|
459
|
1
|
|
|
|
|
3
|
return $self->{created}; |
460
|
|
|
|
|
|
|
} |
461
|
|
|
|
|
|
|
|
462
|
|
|
|
|
|
|
=head2 last_modified |
463
|
|
|
|
|
|
|
|
464
|
|
|
|
|
|
|
Returns the time (epoch) when the cart was last modified. |
465
|
|
|
|
|
|
|
|
466
|
|
|
|
|
|
|
=cut |
467
|
|
|
|
|
|
|
|
468
|
|
|
|
|
|
|
sub last_modified { |
469
|
5
|
|
|
5
|
1
|
937
|
my ($self) = @_; |
470
|
|
|
|
|
|
|
|
471
|
5
|
|
|
|
|
18
|
return $self->{last_modified}; |
472
|
|
|
|
|
|
|
} |
473
|
|
|
|
|
|
|
|
474
|
|
|
|
|
|
|
=head2 count |
475
|
|
|
|
|
|
|
|
476
|
|
|
|
|
|
|
Returns the number of different items in the shopping cart. If you have 5 apples and 6 pears it will return 2 (2 different items). |
477
|
|
|
|
|
|
|
|
478
|
|
|
|
|
|
|
=cut |
479
|
|
|
|
|
|
|
|
480
|
|
|
|
|
|
|
sub count { |
481
|
5
|
|
|
5
|
1
|
283
|
my $self = shift; |
482
|
|
|
|
|
|
|
|
483
|
5
|
|
|
|
|
7
|
return scalar(@{$self->{items}}); |
|
5
|
|
|
|
|
18
|
|
484
|
|
|
|
|
|
|
} |
485
|
|
|
|
|
|
|
|
486
|
|
|
|
|
|
|
=head2 apply_cost |
487
|
|
|
|
|
|
|
|
488
|
|
|
|
|
|
|
Apply cost to cart. apply_cost is a generic method typicaly used for taxes, discounts, coupons, gift certificates,... |
489
|
|
|
|
|
|
|
|
490
|
|
|
|
|
|
|
B Absolute cost |
491
|
|
|
|
|
|
|
|
492
|
|
|
|
|
|
|
Uses absolute value for amount. Amount 5 is 5 units of currency used (ie. $5). |
493
|
|
|
|
|
|
|
|
494
|
|
|
|
|
|
|
$cart->apply_cost(amount => 5, name => 'shipping', label => 'Shipping'); |
495
|
|
|
|
|
|
|
|
496
|
|
|
|
|
|
|
B Relative cost |
497
|
|
|
|
|
|
|
|
498
|
|
|
|
|
|
|
Uses percentage instead of value for amount. Amount 0.19 in example is 19%. |
499
|
|
|
|
|
|
|
|
500
|
|
|
|
|
|
|
relative is a boolean value (0/1). |
501
|
|
|
|
|
|
|
|
502
|
|
|
|
|
|
|
$cart->apply_cost(amount => 0.19, name => 'tax', label => 'VAT', relative => 1); |
503
|
|
|
|
|
|
|
|
504
|
|
|
|
|
|
|
B Inclusive cost |
505
|
|
|
|
|
|
|
|
506
|
|
|
|
|
|
|
Same as relative cost, but it assumes that tax was included in the subtotal already, and only displays it (19% of subtotal value in example). Inclusive is a boolean value (0/1). |
507
|
|
|
|
|
|
|
|
508
|
|
|
|
|
|
|
$cart->apply_cost(amount => 0.19, name => 'tax', label => 'Sales Tax', relative => 1, inclusive => 1); |
509
|
|
|
|
|
|
|
|
510
|
|
|
|
|
|
|
=cut |
511
|
|
|
|
|
|
|
|
512
|
|
|
|
|
|
|
sub apply_cost { |
513
|
4
|
|
|
4
|
1
|
29
|
my ($self, %args) = @_; |
514
|
|
|
|
|
|
|
|
515
|
4
|
|
|
|
|
5
|
push @{$self->{costs}}, \%args; |
|
4
|
|
|
|
|
16
|
|
516
|
|
|
|
|
|
|
|
517
|
4
|
100
|
|
|
|
15
|
unless ($args{inclusive}) { |
518
|
|
|
|
|
|
|
# clear cache for total |
519
|
3
|
|
|
|
|
11
|
$self->{cache_total} = 0; |
520
|
|
|
|
|
|
|
} |
521
|
|
|
|
|
|
|
} |
522
|
|
|
|
|
|
|
|
523
|
|
|
|
|
|
|
=head2 clear_cost |
524
|
|
|
|
|
|
|
|
525
|
|
|
|
|
|
|
It removes all the costs previously applied (using apply_cost). Used typically if you have free shipping or something similar, you can clear the costs. |
526
|
|
|
|
|
|
|
|
527
|
|
|
|
|
|
|
=cut |
528
|
|
|
|
|
|
|
|
529
|
|
|
|
|
|
|
sub clear_cost { |
530
|
4
|
|
|
4
|
1
|
1090
|
my $self = shift; |
531
|
|
|
|
|
|
|
|
532
|
4
|
|
|
|
|
9
|
$self->{costs} = []; |
533
|
|
|
|
|
|
|
|
534
|
4
|
|
|
|
|
136
|
$self->{cache_total} = 0; |
535
|
|
|
|
|
|
|
} |
536
|
|
|
|
|
|
|
|
537
|
|
|
|
|
|
|
=head2 cost |
538
|
|
|
|
|
|
|
|
539
|
|
|
|
|
|
|
Returns particular cost by position or by name. |
540
|
|
|
|
|
|
|
|
541
|
|
|
|
|
|
|
B Return tax value by name |
542
|
|
|
|
|
|
|
|
543
|
|
|
|
|
|
|
$cart->cost('tax'); |
544
|
|
|
|
|
|
|
|
545
|
|
|
|
|
|
|
Returns value of the tax (absolute value in your currency, not percantage) |
546
|
|
|
|
|
|
|
|
547
|
|
|
|
|
|
|
B Return tax value by position |
548
|
|
|
|
|
|
|
|
549
|
|
|
|
|
|
|
$cart->cost(0); |
550
|
|
|
|
|
|
|
|
551
|
|
|
|
|
|
|
Returns the cost that was first applied to subtotal. By increasing the number you can retrieve other costs applied. |
552
|
|
|
|
|
|
|
|
553
|
|
|
|
|
|
|
=cut |
554
|
|
|
|
|
|
|
|
555
|
|
|
|
|
|
|
sub cost { |
556
|
6
|
|
|
6
|
1
|
1644
|
my ($self, $loc) = @_; |
557
|
6
|
|
|
|
|
8
|
my ($cost, $ret); |
558
|
|
|
|
|
|
|
|
559
|
6
|
50
|
|
|
|
13
|
if (defined $loc) { |
560
|
6
|
100
|
|
|
|
26
|
if ($loc =~ /^\d+/) { |
|
|
50
|
|
|
|
|
|
561
|
|
|
|
|
|
|
# cost by position |
562
|
3
|
|
|
|
|
7
|
$cost = $self->{costs}->[$loc]; |
563
|
|
|
|
|
|
|
} |
564
|
|
|
|
|
|
|
elsif ($loc =~ /\S/) { |
565
|
|
|
|
|
|
|
# cost by name |
566
|
3
|
|
|
|
|
3
|
for my $c (@{$self->{costs}}) { |
|
3
|
|
|
|
|
7
|
|
567
|
3
|
50
|
|
|
|
8
|
if ($c->{name} eq $loc) { |
568
|
3
|
|
|
|
|
7
|
$cost = $c; |
569
|
|
|
|
|
|
|
} |
570
|
|
|
|
|
|
|
} |
571
|
|
|
|
|
|
|
} |
572
|
|
|
|
|
|
|
} |
573
|
|
|
|
|
|
|
|
574
|
6
|
50
|
|
|
|
13
|
if (defined $cost) { |
575
|
6
|
|
|
|
|
14
|
$ret = $self->_calculate($self->{subtotal}, $cost, 1); |
576
|
|
|
|
|
|
|
} |
577
|
|
|
|
|
|
|
|
578
|
6
|
|
|
|
|
26
|
return $ret; |
579
|
|
|
|
|
|
|
} |
580
|
|
|
|
|
|
|
|
581
|
|
|
|
|
|
|
=head2 id |
582
|
|
|
|
|
|
|
|
583
|
|
|
|
|
|
|
Get or set id of the cart. This can be used for subclasses, |
584
|
|
|
|
|
|
|
e.g. primary key value for carts in the database. |
585
|
|
|
|
|
|
|
|
586
|
|
|
|
|
|
|
=cut |
587
|
|
|
|
|
|
|
|
588
|
|
|
|
|
|
|
sub id { |
589
|
0
|
|
|
0
|
1
|
0
|
my $self = shift; |
590
|
|
|
|
|
|
|
|
591
|
0
|
0
|
|
|
|
0
|
if (@_ > 0) { |
592
|
0
|
|
|
|
|
0
|
$self->{id} = $_[0]; |
593
|
|
|
|
|
|
|
} |
594
|
|
|
|
|
|
|
|
595
|
0
|
|
|
|
|
0
|
return $self->{id}; |
596
|
|
|
|
|
|
|
} |
597
|
|
|
|
|
|
|
|
598
|
|
|
|
|
|
|
=head2 name |
599
|
|
|
|
|
|
|
|
600
|
|
|
|
|
|
|
Get or set the name of the cart. |
601
|
|
|
|
|
|
|
|
602
|
|
|
|
|
|
|
=cut |
603
|
|
|
|
|
|
|
|
604
|
|
|
|
|
|
|
sub name { |
605
|
2
|
|
|
2
|
1
|
411
|
my $self = shift; |
606
|
|
|
|
|
|
|
|
607
|
2
|
100
|
|
|
|
7
|
if (@_ > 0) { |
608
|
1
|
|
|
|
|
2
|
my $old_name = $self->{name}; |
609
|
|
|
|
|
|
|
|
610
|
1
|
|
|
|
|
6
|
$self->_run_hook('before_cart_rename', $self, $old_name, $_[0]); |
611
|
|
|
|
|
|
|
|
612
|
1
|
|
|
|
|
1
|
$self->{name} = $_[0]; |
613
|
1
|
|
|
|
|
2
|
$self->{last_modified} = time; |
614
|
|
|
|
|
|
|
|
615
|
1
|
|
|
|
|
4
|
$self->_run_hook('after_cart_rename', $self, $old_name, $_[0]); |
616
|
|
|
|
|
|
|
} |
617
|
|
|
|
|
|
|
|
618
|
2
|
|
|
|
|
10
|
return $self->{name}; |
619
|
|
|
|
|
|
|
} |
620
|
|
|
|
|
|
|
|
621
|
|
|
|
|
|
|
=head2 error |
622
|
|
|
|
|
|
|
|
623
|
|
|
|
|
|
|
Returns last error. |
624
|
|
|
|
|
|
|
|
625
|
|
|
|
|
|
|
=cut |
626
|
|
|
|
|
|
|
|
627
|
|
|
|
|
|
|
sub error { |
628
|
10
|
|
|
10
|
1
|
342
|
my $self = shift; |
629
|
|
|
|
|
|
|
|
630
|
10
|
|
|
|
|
40
|
return $self->{error}; |
631
|
|
|
|
|
|
|
} |
632
|
|
|
|
|
|
|
|
633
|
|
|
|
|
|
|
=head2 seed $item_ref |
634
|
|
|
|
|
|
|
|
635
|
|
|
|
|
|
|
Seeds items within the cart from $item_ref. |
636
|
|
|
|
|
|
|
|
637
|
|
|
|
|
|
|
B |
638
|
|
|
|
|
|
|
|
639
|
|
|
|
|
|
|
$cart->seed([ |
640
|
|
|
|
|
|
|
{ sku => 'BMX2015', price => 20, quantity = 1 }, |
641
|
|
|
|
|
|
|
{ sku => 'KTM2018', price => 400, quantity = 5 }, |
642
|
|
|
|
|
|
|
{ sku => 'DBF2020', price => 200, quantity = 5 }, |
643
|
|
|
|
|
|
|
]); |
644
|
|
|
|
|
|
|
|
645
|
|
|
|
|
|
|
=cut |
646
|
|
|
|
|
|
|
|
647
|
|
|
|
|
|
|
sub seed { |
648
|
1
|
|
|
1
|
1
|
63
|
my ($self, $item_ref) = @_; |
649
|
|
|
|
|
|
|
|
650
|
1
|
50
|
|
|
|
3
|
@{$self->{items}} = @{$item_ref || []}; |
|
1
|
|
|
|
|
4
|
|
|
1
|
|
|
|
|
7
|
|
651
|
|
|
|
|
|
|
|
652
|
|
|
|
|
|
|
# clear cache flags |
653
|
1
|
|
|
|
|
3
|
$self->{cache_subtotal} = $self->{cache_total} = 0; |
654
|
|
|
|
|
|
|
|
655
|
1
|
|
|
|
|
2
|
$self->{last_modified} = time; |
656
|
|
|
|
|
|
|
|
657
|
1
|
|
|
|
|
4
|
return $self->{items}; |
658
|
|
|
|
|
|
|
} |
659
|
|
|
|
|
|
|
|
660
|
|
|
|
|
|
|
sub _combine { |
661
|
8
|
|
|
8
|
|
11
|
my ($self, $item) = @_; |
662
|
|
|
|
|
|
|
|
663
|
8
|
|
|
|
|
9
|
ITEMS: for my $cartitem (@{$self->{items}}) { |
|
8
|
|
|
|
|
19
|
|
664
|
4
|
100
|
|
|
|
15
|
if ($item->{sku} eq $cartitem->{sku}) { |
665
|
1
|
|
|
|
|
2
|
for my $mod (@{$self->{modifiers}}) { |
|
1
|
|
|
|
|
2
|
|
666
|
0
|
0
|
|
|
|
0
|
next ITEMS unless($item->{$mod} eq $cartitem->{$mod}); |
667
|
|
|
|
|
|
|
} |
668
|
|
|
|
|
|
|
|
669
|
1
|
|
|
|
|
3
|
$cartitem->{'quantity'} += $item->{'quantity'}; |
670
|
1
|
|
|
|
|
2
|
$item->{'quantity'} = $cartitem->{'quantity'}; |
671
|
|
|
|
|
|
|
|
672
|
1
|
|
|
|
|
4
|
return 1; |
673
|
|
|
|
|
|
|
} |
674
|
|
|
|
|
|
|
} |
675
|
|
|
|
|
|
|
|
676
|
7
|
|
|
|
|
22
|
return 0; |
677
|
|
|
|
|
|
|
} |
678
|
|
|
|
|
|
|
|
679
|
|
|
|
|
|
|
sub _calculate { |
680
|
13
|
|
|
13
|
|
20
|
my ($self, $subtotal, $costs, $display) = @_; |
681
|
13
|
|
|
|
|
14
|
my ($cost_ref, $sum); |
682
|
|
|
|
|
|
|
|
683
|
13
|
100
|
|
|
|
37
|
if (ref $costs eq 'HASH') { |
|
|
50
|
|
|
|
|
|
684
|
6
|
|
|
|
|
8
|
$cost_ref = [$costs]; |
685
|
|
|
|
|
|
|
} |
686
|
|
|
|
|
|
|
elsif (ref $costs eq 'ARRAY') { |
687
|
0
|
|
|
|
|
0
|
$cost_ref = $costs; |
688
|
|
|
|
|
|
|
} |
689
|
|
|
|
|
|
|
else { |
690
|
7
|
|
|
|
|
13
|
$cost_ref = $self->{costs}; |
691
|
|
|
|
|
|
|
} |
692
|
|
|
|
|
|
|
|
693
|
13
|
|
|
|
|
26
|
$sum = 0; |
694
|
|
|
|
|
|
|
|
695
|
13
|
|
|
|
|
21
|
for my $calc (@$cost_ref) { |
696
|
10
|
100
|
100
|
|
|
30
|
if ($calc->{inclusive} && ! $display) { |
697
|
1
|
|
|
|
|
3
|
next; |
698
|
|
|
|
|
|
|
} |
699
|
|
|
|
|
|
|
|
700
|
9
|
100
|
|
|
|
16
|
if ($calc->{relative}) { |
701
|
6
|
|
|
|
|
20
|
$sum += $subtotal * $calc->{amount}; |
702
|
|
|
|
|
|
|
} |
703
|
|
|
|
|
|
|
else { |
704
|
3
|
|
|
|
|
8
|
$sum += $calc->{amount}; |
705
|
|
|
|
|
|
|
} |
706
|
|
|
|
|
|
|
} |
707
|
|
|
|
|
|
|
|
708
|
13
|
|
|
|
|
38
|
return $sum; |
709
|
|
|
|
|
|
|
} |
710
|
|
|
|
|
|
|
|
711
|
|
|
|
|
|
|
sub _run_hook { |
712
|
47
|
|
|
47
|
|
89
|
my ($self, $name, @args) = @_; |
713
|
47
|
|
|
|
|
45
|
my $ret; |
714
|
|
|
|
|
|
|
|
715
|
47
|
100
|
|
|
|
108
|
if ($self->{run_hooks}) { |
716
|
21
|
|
|
|
|
44
|
$ret = $self->{run_hooks}->($name, @args); |
717
|
|
|
|
|
|
|
} |
718
|
|
|
|
|
|
|
|
719
|
47
|
|
|
|
|
168
|
return $ret; |
720
|
|
|
|
|
|
|
} |
721
|
|
|
|
|
|
|
|
722
|
|
|
|
|
|
|
=head1 AUTHOR |
723
|
|
|
|
|
|
|
|
724
|
|
|
|
|
|
|
Stefan Hornburg (Racke), |
725
|
|
|
|
|
|
|
|
726
|
|
|
|
|
|
|
=head1 LICENSE AND COPYRIGHT |
727
|
|
|
|
|
|
|
|
728
|
|
|
|
|
|
|
Copyright 2011-2013 Stefan Hornburg (Racke) . |
729
|
|
|
|
|
|
|
|
730
|
|
|
|
|
|
|
This program is free software; you can redistribute it and/or modify it |
731
|
|
|
|
|
|
|
under the terms of either: the GNU General Public License as published |
732
|
|
|
|
|
|
|
by the Free Software Foundation; or the Artistic License. |
733
|
|
|
|
|
|
|
|
734
|
|
|
|
|
|
|
See http://dev.perl.org/licenses/ for more information. |
735
|
|
|
|
|
|
|
|
736
|
|
|
|
|
|
|
=cut |
737
|
|
|
|
|
|
|
|
738
|
|
|
|
|
|
|
1; |