Line # Revision Author
1 198 ahitrov package webshop::Keeper;
2
3 use strict;
4 use warnings 'all';
5 use base qw(Contenido::Keeper);
6
7 use Data::Dumper;
8 use Contenido::Globals;
9 use webshop::Basket;
10 use webshop::SQL::Basket;
11
12 sub add_item {
13 my $self = shift;
14 my $object = shift;
15 my (%options) = @_;
16
17 return unless ref $object;
18 return unless $object->item_id && $object->number;
19 return unless $object->uid || $object->session;
20
21
22 my %opts;
23 if ( $object->uid ) {
24 $opts{uid} = $object->uid;
25 } elsif ( $object->session ) {
26 $opts{session} = $object->session;
27 }
28 my @items = $keeper->get_documents (
29 class => 'webshop::Basket',
30 399 ahitrov status => 1,
31 198 ahitrov order_id=> 0,
32 %opts,
33 );
34 my $total = 0;
35 my $sum = 0;
36 my $found = 0;
37 if ( @items ) {
38 foreach my $item ( @items ) {
39 399 ahitrov if ( $object->item_id == $item->item_id && $object->color_id == $item->color_id &&
40 ((!$object->size_id && !$object->size) || ($object->size_id && $object->size_id == $item->size_id) || ($object->size && $object->size eq $item->size)) ) {
41 $item->number( $item->number + $object->number );
42 $item->price( $object->price );
43 198 ahitrov $item->store;
44 $found = 1;
45 }
46 $total += $item->number;
47 $sum += $item->number * $item->price;
48 }
49 }
50 unless ( $found ) {
51 $total += $object->number;
52 $sum += $object->number * $object->price;
53 $object->order_id(0);
54 $object->store;
55 }
56 my @plugins = split (/[\ |\t]+/, $state->{plugins});
57 363 ahitrov if ( grep { $_ eq 'session' } @plugins && ref $session ) {
58 $session->set ( basket_total => $total, basket_sum => $sum );
59 198 ahitrov }
60 return ($total, $sum);
61 }
62
63 391 ahitrov sub add_wishlist {
64 my $self = shift;
65 my $object = shift;
66 my (%options) = @_;
67
68 return unless ref $object;
69 return unless $object->item_id && $object->number;
70 return unless $object->uid || $object->session;
71
72
73 my %opts;
74 if ( $object->uid ) {
75 $opts{uid} = $object->uid;
76 } elsif ( $object->session ) {
77 $opts{session} = $object->session;
78 }
79 my @items = $keeper->get_documents (
80 class => 'webshop::Basket',
81 status => 0,
82 order_id=> 0,
83 %opts,
84 );
85 my $total = 0;
86 my $sum = 0;
87 my $found = 0;
88 if ( @items ) {
89 foreach my $item ( @items ) {
90 399 ahitrov if ( $object->item_id == $item->item_id && $object->color_id == $item->color_id &&
91 ((!$object->size_id && !$object->size) || ($object->size_id && $object->size_id == $item->size_id) || ($object->size && $object->size eq $item->size)) ) {
92 391 ahitrov $item->number($item->number + $object->number);
93 $item->store;
94 $found = 1;
95 }
96 $total += $item->number;
97 $sum += $item->number * $item->price;
98 }
99 }
100 unless ( $found ) {
101 $total += $object->number;
102 $sum += $object->number * $object->price;
103 $object->order_id(0);
104 $object->store;
105 }
106 return ($total, $sum);
107 }
108
109 198 ahitrov ### Метод приведения корзины для пользователя в момент логина
110 #############################################################
111 sub merge_basket {
112 my $self = shift;
113 my (%opts) = @_;
114
115 warn "Merge begin: ".Dumper(\%opts)."\n" if $DEBUG;
116 return unless $opts{uid} && $opts{session};
117
118 517 ahitrov my @items_session = $keeper->get_documents (
119 198 ahitrov class => 'webshop::Basket',
120 status => [1,0],
121 session => $opts{session},
122 517 ahitrov order_id => 0,
123 198 ahitrov );
124 517 ahitrov my @items_user = $keeper->get_documents (
125 198 ahitrov class => 'webshop::Basket',
126 471 ahitrov status => [1,0],
127 198 ahitrov order_id=> 0,
128 uid => $opts{uid},
129 );
130 471 ahitrov my ($basket_total, $basket_sum, $wishlist_total, $wishlist_sum) = (0,0,0,0);
131 517 ahitrov foreach my $item ( @items_user ) {
132 my ($item_from_session) = grep {
133 $_->item_id == $item->item_id
134 && $_->status == $item->status
135 && ($_->color_id || 0) == ($item->color_id || 0)
136 && ($_->size_id || 0) == ($item->size_id || 0)
137 && ($_->colour || '') eq ($item->colour || '')
138 && ($_->size || '') eq ($item->size || '')
139 } @items_session;
140 if ( ref $item_from_session ) {
141 warn "Merge basket: Found identical item id=".$item_from_session->id."\n" if $DEBUG;
142 $item->number( $item->number + $item_from_session->number );
143 $item->price( $item_from_session->price );
144 $item->store;
145 $item_from_session->status(-1);
146 $item_from_session->delete;
147 }
148 471 ahitrov if ( $item->status == 1 ) {
149 $basket_total += $item->number;
150 $basket_sum += $item->number * $item->price;
151 } else {
152 $wishlist_total += $item->number;
153 $wishlist_sum += $item->number * $item->price;
154 }
155 198 ahitrov }
156 517 ahitrov foreach my $item ( @items_session ) {
157 if ( $item->status > 0 ) {
158 $item->session(undef);
159 $item->uid( $opts{uid} );
160 $item->store;
161 if ( $item->status == 1 ) {
162 $basket_total += $item->number;
163 $basket_sum += $item->number * $item->price;
164 } else {
165 $wishlist_total += $item->number;
166 $wishlist_sum += $item->number * $item->price;
167 }
168 }
169 }
170 471 ahitrov warn "Merge end\n" if $DEBUG;
171 return ($basket_total, $basket_sum, $wishlist_total, $wishlist_sum);
172 198 ahitrov }
173
174
175 sub get_basket {
176 my $self = shift;
177 my (%opts) = @_;
178
179 return unless $opts{uid} || $opts{session} || $opts{order_id};
180
181 429 ahitrov my $with_products = delete $opts{with_products} || 0;
182 449 ahitrov my $product_status = exists $opts{product_status} ? delete $opts{product_status} : 'positive';
183 198 ahitrov my $uid = delete $opts{uid};
184 363 ahitrov my $session_id = delete $opts{session};
185 198 ahitrov unless ( exists $opts{order_id} && $opts{order_id} ) {
186 $opts{order_id} = 0;
187 if ( $uid ) {
188 $opts{uid} = $uid;
189 363 ahitrov } elsif ( $session_id ) {
190 $opts{session} = $session_id;
191 198 ahitrov }
192 }
193 $opts{status} = 1 unless exists $opts{status} && defined $opts{status};
194 429 ahitrov my @basket = $keeper->get_documents (
195 198 ahitrov class => 'webshop::Basket',
196 %opts,
197 );
198 my $total = 0;
199 my $sum = 0;
200 429 ahitrov my $items;
201 if ( $with_products ) {
202 my %ids = map { $_->item_id => 1 } @basket;
203 my @ids = keys %ids;
204 $items = @ids ? $keeper->get_documents (
205 id => \@ids,
206 class => $state->{webshop}->{item_document_class},
207 449 ahitrov status => $product_status,
208 429 ahitrov return_mode => 'hash_ref',
209 ) : {};
210 }
211 foreach my $bi ( @basket ) {
212 if ( $bi->status == 1 ) {
213 $total += $bi->number;
214 $sum += $bi->number * ($bi->price || 0);
215 198 ahitrov }
216 429 ahitrov if ( ref $items eq 'HASH' && exists $items->{$bi->item_id} ) {
217 $bi->{item} = $items->{$bi->item_id};
218 }
219 198 ahitrov }
220 429 ahitrov return \@basket;
221 198 ahitrov }
222
223
224 270 ahitrov sub basket_count {
225 my $self = shift;
226 my $basket = shift;
227 return (0,0) unless ref $basket eq 'ARRAY' && @$basket;
228 572 ahitrov my (%opts) = @_;
229 my $with_products = delete $opts{with_products};
230 270 ahitrov
231 my $total = 0;
232 my $sum = 0;
233 foreach my $item ( @$basket ) {
234 if ( $item->status == 1 ) {
235 $total += $item->number;
236 509 ahitrov if ( exists $item->{item} ) {
237 $sum += $item->number * $item->{item}->price;
238 572 ahitrov } elsif ( !$with_products ) {
239 509 ahitrov $sum += $item->number * $item->price;
240 }
241 270 ahitrov }
242 }
243 return ($total, $sum);
244 }
245
246
247 399 ahitrov sub wishlist_count {
248 my $self = shift;
249 my $basket = shift;
250 return (0,0) unless ref $basket eq 'ARRAY' && @$basket;
251
252 my $total = 0;
253 my $sum = 0;
254 foreach my $item ( @$basket ) {
255 if ( $item->status == 0 ) {
256 $total += $item->number;
257 $sum += $item->number * $item->price;
258 }
259 }
260 return ($total, $sum);
261 }
262
263
264 198 ahitrov sub clear_basket {
265 my $self = shift;
266 my (%opts) = @_;
267
268 return unless exists $opts{uid} || exists $opts{session};
269
270 my $table_name = webshop::SQL::Basket->db_table;
271 my $request = "delete from $table_name where order_id = 0 AND status = 1 AND";
272 my $dbh = $keeper->SQL;
273 my @vals;
274 if ( exists $opts{uid} && $opts{uid} ) {
275 $request .= " uid in (?)";
276 push @vals, $opts{uid};
277 } elsif ( exists $opts{session} ) {
278 $request .= " session in (?)";
279 push @vals, $opts{session};
280 }
281 warn "CLEAR: [$request]. VALS: [".join(',',@vals)."]\n" if $DEBUG;
282 my $statement = $dbh->prepare ($request);
283 $statement->execute( @vals ) || $log->error("DBI execute error on $request\n"."\ncalled with opts:\n".Data::Dumper::Dumper(\%opts));;
284 $statement->finish;
285
286 my @plugins = split (/[\ |\t]+/, $state->{plugins});
287 363 ahitrov if ( grep { $_ eq 'session' } @plugins && ref $session ) {
288 $session->set ( basket_total => 0, basket_sum => 0 );
289 198 ahitrov }
290 }
291
292
293 sub clear_wishlist {
294 my $self = shift;
295 my (%opts) = @_;
296
297 return unless exists $opts{uid} || exists $opts{session};
298
299 my $table_name = webshop::SQL::Basket->db_table;
300 my $request = "delete from $table_name where order_id = 0 AND status = 0 AND";
301 my $dbh = $keeper->SQL;
302 my @vals;
303 if ( exists $opts{uid} ) {
304 $request .= " uid in (?)";
305 push @vals, $opts{uid};
306 } elsif ( exists $opts{session} ) {
307 $request .= " session in (?)";
308 push @vals, $opts{session};
309 }
310 my $statement = $dbh->prepare ($request);
311 $statement->execute( @vals ) || $log->error("DBI execute error on $request\n"."\ncalled with opts:\n".Data::Dumper::Dumper(\%opts));;
312 $statement->finish;
313 }
314
315
316 ### Метод пересчета корзины
317 # Принимает на вход параметры:
318 # session => session_id пользователя
319 # uid => UID пользователя
320 # delete => массив или отдельный item_id
321 # renumber => ссылка на хеш вида item => number
322 #############################################################
323 sub recount {
324 my $self = shift;
325 my (%opts) = @_;
326
327 warn "Recount Started!!!\n" if $DEBUG;
328 return unless exists $opts{uid} || exists $opts{session} || exists $opts{order_id};
329 my $basket = $self->get_basket ( %opts );
330 return unless ref $basket eq 'ARRAY' && @$basket;
331
332 warn Dumper(\%opts) if $DEBUG;
333
334 my $total = 0;
335 my $sum = 0;
336 my @new_basket;
337 my $session_no_store = delete $opts{session_no_store};
338 foreach my $item ( @$basket ) {
339 my $delete = 0;
340 if ( exists $opts{renumber} && ref $opts{renumber} eq 'HASH' && exists $opts{renumber}{$item->id} && int($opts{renumber}{$item->id}) == 0 ) {
341 $delete = 1;
342 } elsif ( exists $opts{delete} && ref $opts{delete} eq 'ARRAY' ) {
343 $delete = 1 if grep { $_ == $item->id } @{ $opts{delete} };
344 } elsif ( exists $opts{delete} && $opts{delete} ) {
345 $delete = 1 if $item->id == $opts{delete};
346 }
347 if ( $delete ) {
348 warn "Item ID=".$item->id." DELETE\n" if $DEBUG;
349 $item->delete();
350 next;
351 } else {
352 my $store = 0;
353 if ( exists $opts{renumber} && ref $opts{renumber} eq 'HASH' && exists $opts{renumber}{$item->id} && $opts{renumber}{$item->id} != $item->number ) {
354 $item->number( $opts{renumber}{$item->id} );
355 $store = 1;
356 }
357 if ( exists $opts{price} && ref $opts{price} eq 'HASH' && exists $opts{price}{$item->id} && $opts{price}{$item->id} != $item->price ) {
358 $item->price( $opts{price}{$item->id} );
359 $store = 1;
360 }
361 $item->store if $store;
362 $total += $item->number;
363 $sum += $item->number * $item->price;
364 push @new_basket, $item;
365 }
366 }
367 my @plugins = split (/[\ |\t]+/, $state->{plugins});
368 363 ahitrov if ( !$session_no_store && grep { $_ eq 'session' } @plugins && ref $session ) {
369 $session->set ( basket_total => $total, basket_sum => $sum );
370 198 ahitrov }
371 return ($total, $sum, \@new_basket);
372 }
373
374
375 sub get_orders {
376 my $self = shift;
377 my (%opts) = @_;
378
379 my $list = delete $opts{list};
380 434 ahitrov my $count = delete $opts{count};
381 if ( $count ) {
382 delete $opts{order_by};
383 my $item_count = $keeper->get_documents (
384 class => 'webshop::Order',
385 count => 1,
386 %opts,
387 );
388 return $item_count;
389 198 ahitrov } else {
390 434 ahitrov $opts{order_by} ||= 'status';
391 my @items = $keeper->get_documents (
392 class => 'webshop::Order',
393 %opts,
394 );
395 if ( exists $opts{id} && defined $opts{id} && !ref $opts{id} ) {
396 if ( $list ) {
397 $items[0]->{list} = $self->get_order_list( order_id => $opts{id} );
398 }
399 return $items[0];
400 } else {
401 if ( $list ) {
402 map { $_->{list} = $self->get_order_list( order_id => $_->id ) } @items;
403 }
404 return \@items;
405 198 ahitrov }
406 }
407 }
408
409
410 sub get_order_list {
411 my $self = shift;
412 my (%opts) = @_;
413
414 return unless $opts{order_id};
415
416 $opts{status} = 1;
417 my @items = $keeper->get_documents (
418 class => 'webshop::Basket',
419 %opts,
420 );
421 my $total = 0;
422 my $sum = 0;
423 foreach my $item ( @items ) {
424 my $Item = $item->class->new( $keeper, $item->id );
425 $item->{item} = $Item;
426 if ( $item->status == 1 ) {
427 $total += $item->number;
428 $sum += $item->number * $item->price;
429 }
430 }
431 return \@items;
432 }
433
434 270 ahitrov
435 ### Метод приведения купонов для пользователя в момент логина
436 #############################################################
437 sub merge_coupons {
438 my $self = shift;
439 my (%opts) = @_;
440
441 warn "Merge (coupons) begin: ".Dumper(\%opts)."\n" if $DEBUG;
442 return unless $opts{uid} && $opts{session};
443
444 my @items = $keeper->get_links (
445 class => 'webshop::OrderCouponLink',
446 session => $opts{session},
447 source_id => 0,
448 );
449 my %merge_to = $keeper->get_links (
450 class => 'webshop::OrderCouponLink',
451 uid => $opts{uid},
452 source_id => 0,
453 return_mode => 'hash',
454 hash_by => 'dest_id',
455 );
456 foreach my $item ( @items ) {
457 if ( exists $merge_to{$item->dest_id} ) {
458 $item->delete;
459 } else {
460 $item->session( undef );
461 $item->uid( $opts{uid} );
462 $item->store;
463 }
464 }
465 }
466
467
468 424 ahitrov sub check_discount {
469 my $self = shift;
470 my (%opts) = @_;
471 270 ahitrov
472 432 ahitrov warn "Check discount begin:\n" if $DEBUG;
473 424 ahitrov my %dopts;
474 if ( exists $opts{uid} && $opts{uid} ) {
475 $dopts{uid} = [0,1];
476 } else {
477 $dopts{uid} = 0;
478 }
479 429 ahitrov my $basket = exists $opts{basket} ? $opts{basket} : $self->get_basket( %opts, with_products => 1 );
480 return 0 unless ref $basket eq 'ARRAY' && @$basket;
481 my @basket = grep { exists $_->{item} && $_->{item} } @$basket;
482 return 0 unless @basket;
483 424 ahitrov
484 425 ahitrov my $now = Contenido::DateTime->new;
485 424 ahitrov my @discounts = $keeper->get_documents(
486 class => 'webshop::Discount',
487 status => 1,
488 425 ahitrov interval=> [$now, $now],
489 %dopts,
490 424 ahitrov );
491 425 ahitrov return 0 unless @discounts;
492 my @summoned = sort { $b->min_sum <=> $a->min_sum } grep { $_->min_sum && $_->discount && $_->min_sum =~ /^\d+$/ } @discounts;
493 429 ahitrov my ($total, $sum) = $self->basket_count( \@basket );
494 425 ahitrov return 0 unless $sum;
495 my $result = 0;
496 foreach my $discount ( @summoned ) {
497 if ( $sum > $discount->min_sum ) {
498 429 ahitrov my $res = $discount->get_discount( basket => \@basket );
499 425 ahitrov $result = $res if $res > $result;
500 }
501 }
502 $result = 0 if $result >= $sum;
503 return $result;
504 424 ahitrov }
505
506
507 432 ahitrov sub check_coupons {
508 my $self = shift;
509 my (%opts) = @_;
510
511 warn "Check coupons begin:\n" if $DEBUG;
512 my %dopts;
513 if ( exists $opts{uid} && $opts{uid} ) {
514 $dopts{luid} = $opts{uid};
515 } else {
516 $dopts{lsession} = $opts{session};
517 }
518 my $basket = exists $opts{basket} ? $opts{basket} : $self->get_basket( %opts, with_products => 1 );
519 return (0, []) unless ref $basket eq 'ARRAY' && @$basket;
520 my @basket = grep { exists $_->{item} && $_->{item} } @$basket;
521 return (0, []) unless @basket;
522
523 my $now = Contenido::DateTime->new;
524 my @coupons = exists $opts{coupons} && ref $opts{coupons} eq 'ARRAY' ? @{$opts{coupons}} : $keeper->get_documents(
525 class => 'webshop::Coupon',
526 lclass => 'webshop::OrderCouponLink',
527 lsource => 0,
528 status => 1,
529 interval=> [$now->ymd, $now->ymd],
530 %dopts,
531 );
532 return (0, []) unless @coupons;
533 my @summoned = grep { $_->discount } @coupons;
534 warn "We count on ".scalar(@summoned)." coupons\n" if $DEBUG;
535 my ($total, $sum) = $self->basket_count( \@basket );
536 return (0, \@summoned) unless $sum;
537 my $result = 0;
538 foreach my $coupon ( @summoned ) {
539 my $res = $coupon->get_discount( basket => \@basket );
540 $coupon->{result} = $res;
541 }
542 @summoned = sort { $b->{result} <=> $a->{result} } @summoned;
543 foreach my $coupon ( @summoned ) {
544 if ( ($coupon->{result} > 0) && ($sum - $coupon->{result} > 0) ) {
545 $result += $coupon->{result};
546 $sum -= $coupon->{result};
547 $coupon->{afflict_order} = 1;
548 } else {
549 $coupon->{afflict_order} = 0;
550 }
551 }
552 return ($result, \@summoned);
553 }
554
555
556 198 ahitrov sub price_format {
557 my $self = shift;
558 my $price = shift;
559
560 572 ahitrov if ( defined $price ) {
561 $price = reverse $price;
562 $price =~ s/(\d{3})/$1\ /g;
563 $price = reverse $price;
564 }
565 198 ahitrov
566 return $price;
567 }
568
569
570 1;