File Coverage

blib/lib/Dancer2/Plugin/Cart.pm
Criterion Covered Total %
statement 275 302 91.0
branch 44 76 57.8
condition 7 36 19.4
subroutine 29 30 96.6
pod 11 13 84.6
total 366 457 80.0


line stmt bran cond sub pod time code
1             package Dancer2::Plugin::Cart;
2             # ABSTRACT: Cart Plugin for Dancer2 app.
3 7     7   1965817 use strict;
  7         13  
  7         155  
4 7     7   23 use warnings;
  7         6  
  7         127  
5 7     7   3131 use Dancer2::Plugin;
  7         113501  
  7         41  
6 7     7   32467 use Dancer2::Plugin::Cart::InlineViews;
  7         11  
  7         164  
7 7     7   3973 use JSON;
  7         46147  
  7         23  
8             our $VERSION = '0.0013'; #Version
9              
10              
11             BEGIN{
12             has 'product_list' => (
13             is => 'ro',
14             from_config => 1,
15 1         351 default => sub { [] }
16 7     7   2131 );
17              
18             has 'products_view_template' => (
19             is => 'ro',
20             from_config => 'views.products',
21             default => sub {}
22 7         2628 );
23              
24             has 'cart_view_template' => (
25             is => 'ro',
26             from_config => 'views.products',
27             default => sub {}
28 7         1782 );
29              
30             has 'cart_receipt_template' => (
31             is => 'ro',
32             from_config => 'views.receipt',
33             default => sub {}
34 7         1747 );
35              
36             has 'cart_checkout_template' => (
37             is => 'ro',
38             from_config => 'views.checkout',
39             default => sub {}
40 7         1751 );
41              
42             has 'shipping_view_template' => (
43             is => 'ro',
44             from_config => 'views.shipping',
45             default => sub {}
46 7         1818 );
47              
48             has 'billing_view_template' => (
49             is => 'ro',
50             from_config => 'views.billing',
51             default => sub {}
52 7         1784 );
53              
54             has 'review_view_template' => (
55             is => 'ro',
56             from_config => 'views.review',
57             default => sub {}
58 7         1663 );
59              
60             has 'receipt_view_template' => (
61             is => 'ro',
62             from_config => 'views.receipt',
63             default => sub {}
64 7         1629 );
65              
66             has 'default_routes' => (
67             is => 'ro',
68             from_config => 1,
69 6         2015 default => sub { '1' }
70 7         1671 );
71              
72             has 'excluded_routes' => (
73             is => 'ro',
74             from_config => 1,
75 6         4087 default => sub { [] }
76 7         1636 );
77              
78 7         1547 plugin_keywords qw/
79             products
80             cart
81             cart_add
82             cart_add_item
83             clear_cart
84             subtotal
85             billing
86             shipping
87             checkout
88             close_cart
89             adjustments
90             /;
91              
92 7         4133 plugin_hooks qw/
93             products
94             before_cart
95             after_cart
96             validate_cart_add_params
97             before_cart_add
98             after_cart_add
99             before_cart_add_item
100             after_cart_add_item
101             validate_shipping_params
102             before_shipping
103             after_shipping
104             validate_billing_params
105             before_billing
106             after_billing
107             validate_checkout_params
108             before_checkout
109             checkout
110             after_checkout
111             before_close_cart
112             after_close_cart
113             before_clear_cart
114             after_clear_cart
115             before_subtotal
116             after_subtotal
117             adjustments
118             /;
119             }
120              
121             sub BUILD {
122 6     6 0 11044 my $self = shift;
123             #Create a session
124 6         62 my $settings = $self->app->config;
125 6         145 my $excluded_routes = $self->excluded_routes;
126              
127 6 50       19 if( $self->default_routes ){
128             $self->app->add_route(
129             method => 'get',
130             regexp => '/products',
131             code => sub {
132 2     2   186098 my $app = shift;
133             #generate session if didn't exists
134 2         13 $app->session;
135 2   50     13430 my $template = $self->products_view_template || 'products' ;
136 2 50       16 if( -e $self->app->config->{views}.'/'.$template.'.tt' ) {
137 0         0 $app->template( $template, {
138             product_list => $self->products
139             },
140             {
141             layout => 'cart.tt'
142             });
143             }
144             else{
145 2         95 _products_view({ product_list => $self->products });
146             }
147             },
148 6 50       8 )if !grep { $_ eq 'products' }@{$excluded_routes};
  0         0  
  6         70  
149              
150             $self->app->add_route(
151             method => 'get',
152             regexp => '/cart',
153             code => sub {
154 3     3   28535 my $app = shift;
155 3         11 my $cart = $self->cart;
156             #Generate session if didn't exists
157 3         39 $app->session;
158 3   50     38 my $template = $self->cart_view_template || 'cart/cart' ;
159 3         24 my $page = "";
160 3 50       10 if( -e $self->app->config->{views}.'/'.$template.'.tt' ) {
161 0         0 $page = $app->template( $template, {
162             ec_cart => $app->session->read('ec_cart'),
163             },
164             {
165             layout => 'cart.tt'
166             } );
167             }
168             else{
169 3         166 $page = _cart_view({ ec_cart => $app->session->read('ec_cart') });
170             }
171 3         47 my $ec_cart = $app->session->read('ec_cart');
172 3 50       64 delete $ec_cart->{add}->{error} if $ec_cart->{add}->{error};
173 3         38 $app->session->write( 'ec_cart', $ec_cart );
174 3         175 $page;
175             }
176 6 50       17339 )if !grep { $_ eq 'cart' }@{$excluded_routes};
  0         0  
  6         76  
177              
178             $self->app->add_route(
179             method => 'post',
180             regexp => '/cart/add',
181             code => sub {
182 3     3   41735 my $app = shift;
183 3         13 $self->cart_add;
184 3         165 $app->redirect('/cart');
185             }
186 6 50       1493 )if !grep { $_ eq 'cart/add' }@{$excluded_routes};
  0         0  
  6         51  
187              
188              
189             $self->app->add_route(
190             method => 'get',
191             regexp => '/cart/clear',
192             code => sub {
193 0     0   0 my $app = shift;
194 0         0 $self->clear_cart;
195 0         0 $app->redirect('/cart');
196             }
197 6 50       1452 )if !grep { $_ eq 'cart/clear' }@{$excluded_routes};
  0         0  
  6         49  
198              
199             $self->app->add_route(
200             method => 'get',
201             regexp => '/cart/shipping',
202             code => sub {
203 1     1   10737 my $app = shift;
204 1         4 my $cart = $self->cart;
205 1   50     5 my $template = $self->shipping_view_template || 'shipping';
206 1         2 my $page = "";
207 1 50       3 if( -e $app->config->{views}.'/cart/'.$template.'.tt' ) {
208 0         0 $page = $app->template ( 'cart/'.$template, {
209             ec_cart => $app->session->read('ec_cart'),
210             },
211             {
212             layout => 'cart.tt'
213             });
214             }
215             else{
216 1         56 $page = _shipping_view({ ec_cart => $app->session->read('ec_cart') });
217             }
218 1         16 my $ec_cart = $app->session->read('ec_cart');
219 1 50       23 delete $ec_cart->{shipping}->{error} if $ec_cart->{shipping}->{error};
220 1         13 $app->session->write( 'ec_cart', $ec_cart );
221 1         59 $page;
222             }
223 6 50       1455 )if !grep { $_ eq 'cart/shipping' }@{$excluded_routes};
  0         0  
  6         52  
224            
225             $self->app->add_route(
226             method => 'post',
227             regexp => '/cart/shipping',
228             code => sub {
229 2     2   19557 my $app = shift;
230 2         9 $self->shipping;
231 1         136 $app->redirect('/cart/billing');
232             }
233 6 50       1357 )if !grep { $_ eq 'cart/shipping' }@{$excluded_routes};
  0         0  
  6         49  
234              
235             $self->app->add_route(
236             method => 'get',
237             regexp => '/cart/billing',
238             code => sub {
239 1     1   11929 my $app = shift;
240 1         6 my $cart = $self->cart;
241 1   50     5 my $template = $self->billing_view_template || 'billing' ;
242 1         2 my $page = "";
243 1 50       4 if( -e $app->config->{views}.'/cart/'.$template.'.tt' ) {
244 0         0 $page = $app->template( 'cart/'.$template, {
245             ec_cart => $app->session->read('ec_cart'),
246             },
247             {
248             layout => 'cart.tt'
249             }
250             );
251             }
252             else{
253 1         55 $page = _billing_view({ ec_cart => $app->session->read('ec_cart') });
254             }
255 1         17 my $ec_cart = $app->session->read('ec_cart');
256 1 50       23 delete $ec_cart->{billing}->{error} if $ec_cart->{billing}->{error};
257 1         13 $app->session->write( 'ec_cart', $ec_cart );
258 1         59 $page;
259             }
260 6 50       1412 )if !grep { $_ eq 'cart/billing' }@{$excluded_routes};
  0         0  
  6         51  
261              
262             $self->app->add_route(
263             method => 'post',
264             regexp => '/cart/billing',
265             code => sub {
266 1     1   9861 my $app = shift;
267 1         6 $self->billing;
268 1         135 $app->redirect('/cart/review');
269             }
270 6 50       1422 )if !grep { $_ eq 'cart/billing' }@{$excluded_routes};
  0         0  
  6         50  
271            
272             $self->app->add_route(
273             method => 'get',
274             regexp => '/cart/review',
275             code => sub {
276 1     1   11615 my $app = shift;
277 1         5 my $cart = $self->cart;
278 1         2 my $page = "";
279 1   50     4 my $template = $self->review_view_template || 'review' ;
280 1 50       4 if( -e $app->config->{views}.'/cart/'.$template.'.tt' ) {
281 0         0 $page = $app->template( 'cart/'.$template,{
282             ec_cart => $app->session->read('ec_cart'),
283             },
284             {
285             layout => 'cart.tt'
286             });
287             }
288             else{
289 1         54 $page = _review_view( { ec_cart => $app->session->read('ec_cart') } );
290             }
291 1         16 my $ec_cart = $app->session->read('ec_cart');
292 1 50       23 delete $ec_cart->{checkout}->{error} if $ec_cart->{checkout}->{error};
293 1         13 $app->session->write('ec_cart',$ec_cart);
294 1         59 $page;
295             }
296 6 50       1372 )if !grep { $_ eq 'cart/review' }@{$excluded_routes};
  0         0  
  6         49  
297              
298             $self->app->add_route(
299             method => 'post',
300             regexp => '/cart/checkout',
301             code => sub {
302 1     1   11746 my $app = shift;
303 1         6 $self->checkout;
304 1         133 $app->redirect('/cart/receipt');
305             }
306 6 50       1398 )if !grep { $_ eq 'cart/receipt' }@{$excluded_routes};
  0         0  
  6         55  
307              
308             $self->app->add_route(
309             method => 'get',
310             regexp => '/cart/receipt',
311             code => sub {
312 1     1   10786 my $app = shift;
313 1   50     5 my $template = $self->receipt_view_template || 'receipt' ;
314 1         3 my $page = "";
315 1         16 my $ec_cart = $app->session->read('ec_cart');
316 1 50       799 if( -e $app->config->{views}.'/cart/'.$template.'.tt' ) {
317 0         0 $page = $app->template( 'cart/'.$template,
318             {
319             ec_cart => $ec_cart
320             },
321             {
322             layout => 'cart.tt'
323             });
324             }
325             else{
326 1         39 $page = _receipt_view({ ec_cart => $ec_cart });
327             }
328 1         18 $app->session->delete('ec_cart');
329 1         77 $page;
330             }
331 6 50       1422 )if !grep { $_ eq 'cart/receipt' }@{$excluded_routes};
  0         0  
  6         62  
332             }
333             };
334              
335              
336             sub products {
337 8     8 1 10 my ( $self ) = @_;
338 8         20 my $app = $self->app;
339 8         21 my $ec_cart = $self->cart;
340 8 50       71 if ( $self->product_list ){
341 8         1373 $ec_cart->{products} = $self->product_list;
342 8         128 $app->session->write( 'ec_cart', $ec_cart );
343             }
344 8         578 $app->execute_hook('plugin.cart.products');
345 8         2252 $ec_cart = $self->cart;
346 8         30 return $ec_cart->{products};
347             }
348              
349             sub cart_add_item {
350 9     9 1 161455 my ( $self, $product ) = @_;
351 9         37 my $app = $self->app;
352 9         15 my $index = 0;
353 9         24 my $ec_cart = $self->cart;
354 9 50       28 $ec_cart->{cart}->{items} = [] unless $ec_cart->{cart}->{items};
355 9         12 foreach my $cart_product ( @{$ec_cart->{cart}->{items}} ){
  9         22  
356 6 100       25 if( $cart_product->{ec_sku} eq $product->{ec_sku} ){
357 3         5 $cart_product->{ec_quantity} += $product->{ec_quantity};
358 3         7 $cart_product->{ec_subtotal} = $cart_product->{ec_quantity} * $cart_product->{ec_price};
359 3 100       12 if( $cart_product->{ec_quantity} <= 0 ){
360 1         2 splice @{$ec_cart->{cart}->{items}}, $index, 1;
  1         2  
361             }
362 3         42 $app->session->write( 'ec_cart', $ec_cart );
363 3         173 return $cart_product;
364             }
365 3         7 $index++;
366             }
367            
368 6         8 foreach my $product_item ( @{$self->products} ){
  6         26  
369 18 100       51 if( $product_item->{ec_sku} eq $product->{ec_sku} ){
370 5         7 foreach my $k (keys %{ $product_item }) {
  5         16  
371 10         17 $product->{$k} = $product_item->{$k};
372             }
373 5         19 $product->{ec_subtotal} = $product->{ec_quantity} * $product->{ec_price};
374             }
375             }
376 6         10 push @{$ec_cart->{cart}->{items}}, $product;
  6         16  
377 6         83 $app->session->write( 'ec_cart', $ec_cart );
378            
379 6         372 return $product;
380             };
381              
382             sub cart {
383 41     41 1 210545 my ( $self ) = @_;
384 41         94 my $app = $self->app;
385 41         635 $app->execute_hook('plugin.cart.before_cart');
386 41         6109 my $ec_cart = $app->session->read('ec_cart');
387 41 100       31032 $ec_cart->{cart}->{items} = [] unless $ec_cart->{cart}->{items};
388 41         562 $app->session->write('ec_cart', $ec_cart);
389 41         4449 $self->subtotal;
390 41         100 $self->adjustments;
391 41         5912 $self->total;
392 41         509 $ec_cart = $app->session->read('ec_cart');
393 41         1328 $app->execute_hook('plugin.cart.after_cart');
394 41         6065 $ec_cart = $app->session->read('ec_cart');
395 41         863 return $ec_cart;
396             };
397              
398              
399             sub subtotal{
400 41     41 1 53 my ($self, $params) = @_;
401 41         88 my $app = $self->app;
402              
403 41         133 $self->execute_hook ('plugin.cart.before_subtotal');
404 41         2581 my $ec_cart = $app->session->read('ec_cart');
405 41         786 my $subtotal = 0;
406 41         47 foreach my $item_subtotal ( @{ $ec_cart->{cart}->{items} } ){
  41         90  
407 34 100       94 $subtotal += $item_subtotal->{ec_subtotal} if $item_subtotal->{ec_subtotal};
408             }
409 41         54 $ec_cart->{cart}->{subtotal} = $subtotal;
410 41         529 $app->session->write('ec_cart',$ec_cart);
411 41         2278 $self->execute_hook ('plugin.cart.after_subtotal');
412 41         2187 $ec_cart = $app->session->read('ec_cart');
413 41         797 $ec_cart->{cart}->{subtotal};
414             }
415              
416              
417             sub clear_cart {
418 1     1 1 11135 my ($self, $params ) = @_;
419 1         5 $self->execute_hook ('plugin.cart.before_clear_cart');
420 1         66 $self->app->session->delete('ec_cart');
421 1         890 $self->execute_hook ('plugin.cart.after_clear_cart');
422             }
423              
424              
425             sub cart_add {
426 3     3 1 4 my ($self, $params) = @_;
427              
428 3         9 my $app = $self->app;
429 3         12 my $form_params = { $app->request->params };
430 3         34 my $product = undef;
431            
432             #Add params to ec_cart session
433 3         44 my $ec_cart = $app->session->read( 'ec_cart' );
434 3         2374 $ec_cart->{add}->{form} = $form_params;
435 3         39 $app->session->write( 'ec_cart', $ec_cart );
436              
437             #Param validation
438 3         222 $app->execute_hook( 'plugin.cart.validate_cart_add_params' );
439 3         454 $ec_cart = $app->session->read('ec_cart');
440            
441 3 50       63 if ( $ec_cart->{add}->{error} ){
442 0   0     0 $self->app->redirect( $app->request->referer || $app->request->uri );
443             }
444             else{
445             #Cart operations before add product to the cart.
446 3         72 $app->execute_hook( 'plugin.cart.before_cart_add' );
447 3         3100 $ec_cart = $app->session->read('ec_cart');
448              
449 3 50       64 if ( $ec_cart->{add}->{error} ){
450 0   0     0 $self->app->redirect( $app->request->referer || $app->request->uri );
451             }
452             else{
453 3         46 $app->execute_hook( 'plugin.cart.before_cart_add_item' );
454             $product = $self->cart_add_item({
455             ec_sku => $ec_cart->{add}->{form}->{'ec_sku'},
456 3         432 ec_quantity => $ec_cart->{add}->{form}->{'ec_quantity'},
457             }
458             );
459 3         53 $app->execute_hook( 'plugin.cart.after_cart_add_item' );
460              
461             #Cart operations after adding product to the cart
462 3         470 $app->execute_hook( 'plugin.cart.after_cart_add' );
463 3         1075 $ec_cart = $app->session->read('ec_cart');
464 3         60 delete $ec_cart->{add};
465 3         37 $app->session->write( 'ec_cart', $ec_cart );
466             }
467             }
468             }
469              
470             sub shipping {
471 2     2 1 3 my $self = shift;
472 2         7 my $app = $self->app;
473 2         10 my $params = { $app->request->params };
474             #Add params to ec_cart session
475 2         66 my $ec_cart = $app->session->read( 'ec_cart' );
476 2         1552 $ec_cart->{shipping}->{form} = $params;
477 2         26 $app->session->write( 'ec_cart', $ec_cart );
478 2         150 $app->execute_hook( 'plugin.cart.validate_shipping_params' );
479 2         2323 $ec_cart = $app->session->read('ec_cart');
480 2 100       45 if ( $ec_cart->{shipping}->{error} ){
481 1   33     10 $app->redirect( $app->request->referer || $app->request->uri );
482             }
483             else{
484 1         16 $app->execute_hook( 'plugin.cart.before_shipping' );
485 1         151 my $ec_cart = $app->session->read('ec_cart');
486              
487 1 50       23 if ( $ec_cart->{shipping}->{error} ){
488            
489 0   0     0 $app->redirect( ''.$app->request->referer || $app->request->uri );
490             }
491 1         15 $app->execute_hook( 'plugin.cart.after_shipping' );
492             }
493             }
494              
495             sub billing{
496 1     1 1 1 my $self = shift;
497 1         4 my $app = $self->app;
498 1         5 my $params = { $app->request->params };
499             #Add params to ec_cart session
500 1         25 my $ec_cart = $app->session->read( 'ec_cart' );
501 1         771 $ec_cart->{billing}->{form} = $params;
502 1         14 $app->session->write( 'ec_cart', $ec_cart );
503 1         75 $app->execute_hook( 'plugin.cart.validate_billing_params' );
504 1         152 $ec_cart = $app->session->read('ec_cart');
505 1 50       22 if ( $ec_cart->{billing}->{error} ){
506 0   0     0 $app->redirect( $app->request->referer || $app->request->uri );
507             }
508             else{
509 1         16 $app->execute_hook( 'plugin.cart.before_billing' );
510 1         143 my $ec_cart = $app->session->read('ec_cart');
511              
512 1 50       23 if ( $ec_cart->{billing}->{error} ){
513 0   0     0 $app->redirect( $app->request->referer || $app->request->uri );
514             }
515 1         17 $app->execute_hook( 'plugin.cart.after_billing' );
516             }
517             }
518              
519             sub checkout{
520 1     1 1 1 my $self = shift;
521 1         4 my $app = $self->app;
522              
523 1         6 my $params = ($app->request->params);
524 1         23 $app->execute_hook( 'plugin.cart.validate_checkout_params' );
525 1         147 my $ec_cart = $app->session->read('ec_cart');
526              
527 1 50       770 if ( $ec_cart->{checkout}->{error} ){
528 0   0     0 $app->redirect( $app->request->referer || $app->request->uri );
529             }
530             else{
531 1         19 $app->execute_hook( 'plugin.cart.checkout' );
532 1         152 $ec_cart = $app->session->read('ec_cart');
533 1 50       23 if ( $ec_cart->{checkout}->{error} ){
534 0   0     0 $app->redirect( $app->request->referer || $app->request->uri );
535             }
536 1         4 $self->close_cart;
537 1         149 $app->execute_hook( 'plugin.cart.after_checkout' );
538             }
539             }
540              
541             sub close_cart{
542 1     1 1 2 my ($self, $params) = @_;
543 1         3 my $app = $self->app;
544 1         5 my $ec_cart = $self->cart;
545 1 50       2 return { error => 'Cart without items' } unless @{$ec_cart->{cart}->{items}} > 0;
  1         5  
546 1         16 $app->execute_hook( 'plugin.cart.before_close_cart' );
547 1         175 $ec_cart->{cart}->{session} = $app->session->id;
548 1         21 $ec_cart->{cart}->{status} = 1;
549 1         13 $app->session->write('ec_cart', $ec_cart );
550 1         71 $app->execute_hook( 'plugin.cart.after_close_cart' );
551             }
552              
553             sub adjustments {
554 41     41 1 47 my ($self, $params) = @_;
555 41         77 my $app = $self->app;
556 41         521 my $ec_cart = $app->session->read('ec_cart');
557 41         904 my $default_adjustments = [
558             {
559             description => 'Discounts',
560             value => '0'
561             },
562             {
563             description => 'Shipping',
564             value => '0'
565             },
566             {
567             description => 'Taxes',
568             value => '0'
569             },
570             ];
571 41         62 $ec_cart->{cart}->{adjustments} = $default_adjustments;
572 41         571 $app->session->write( 'ec_cart', $ec_cart );
573 41         2768 $app->execute_hook('plugin.cart.adjustments');
574             }
575              
576              
577             sub total {
578 41     41 0 54 my ($self) = shift;
579 41         72 my $app = $self->app;
580 41         41 my $total = 0;
581 41         525 my $ec_cart = $app->session->read('ec_cart');
582 41         799 $total += $ec_cart->{cart}->{subtotal};
583 41         45 foreach my $adjustment ( @{$ec_cart->{cart}->{adjustments}}){
  41         87  
584 123         153 $total += $adjustment->{value};
585             }
586 41         54 $ec_cart->{cart}->{total} = $total;
587 41         507 $app->session->write('ec_cart', $ec_cart );
588 41         2263 return $total;
589             }
590              
591              
592              
593             1;
594             __END__