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 my @items = $keeper->get_documents (
119 class => 'webshop::Basket',
120 status => [1,0],
121 order_id=> 0,
122 session => $opts{session},
123 );
124 foreach my $item ( @items ) {
125 $item->session(undef);
126 $item->uid( $opts{uid} );
127 $item->store;
128 }
129 @items = $keeper->get_documents (
130 class => 'webshop::Basket',
131 status => 1,
132 order_id=> 0,
133 uid => $opts{uid},
134 );
135 my $total = 0;
136 my $sum = 0;
137 foreach my $item ( @items ) {
138 $total += $item->number;
139 $sum += $item->number * $item->price;
140 }
141 my @plugins = split (/[\ |\t]+/, $state->{plugins});
142 363 ahitrov if ( grep { $_ eq 'session' } @plugins && ref $session ) {
143 $session->set ( basket_total => $total, basket_sum => $sum );
144 198 ahitrov }
145 warn "Merge end\n" if $DEBUG;
146 return ($total, $sum);
147 }
148
149
150 sub get_basket {
151 my $self = shift;
152 my (%opts) = @_;
153
154 return unless $opts{uid} || $opts{session} || $opts{order_id};
155
156 429 ahitrov my $with_products = delete $opts{with_products} || 0;
157 198 ahitrov my $uid = delete $opts{uid};
158 363 ahitrov my $session_id = delete $opts{session};
159 198 ahitrov unless ( exists $opts{order_id} && $opts{order_id} ) {
160 $opts{order_id} = 0;
161 if ( $uid ) {
162 $opts{uid} = $uid;
163 363 ahitrov } elsif ( $session_id ) {
164 $opts{session} = $session_id;
165 198 ahitrov }
166 }
167 $opts{status} = 1 unless exists $opts{status} && defined $opts{status};
168 429 ahitrov my @basket = $keeper->get_documents (
169 198 ahitrov class => 'webshop::Basket',
170 %opts,
171 );
172 my $total = 0;
173 my $sum = 0;
174 429 ahitrov my $items;
175 if ( $with_products ) {
176 my %ids = map { $_->item_id => 1 } @basket;
177 my @ids = keys %ids;
178 $items = @ids ? $keeper->get_documents (
179 id => \@ids,
180 class => $state->{webshop}->{item_document_class},
181 430 ahitrov status => 'positive',
182 429 ahitrov return_mode => 'hash_ref',
183 ) : {};
184 }
185 foreach my $bi ( @basket ) {
186 if ( $bi->status == 1 ) {
187 $total += $bi->number;
188 $sum += $bi->number * ($bi->price || 0);
189 198 ahitrov }
190 429 ahitrov if ( ref $items eq 'HASH' && exists $items->{$bi->item_id} ) {
191 $bi->{item} = $items->{$bi->item_id};
192 }
193 198 ahitrov }
194 429 ahitrov return \@basket;
195 198 ahitrov }
196
197
198 270 ahitrov sub basket_count {
199 my $self = shift;
200 my $basket = shift;
201 return (0,0) unless ref $basket eq 'ARRAY' && @$basket;
202
203 my $total = 0;
204 my $sum = 0;
205 foreach my $item ( @$basket ) {
206 if ( $item->status == 1 ) {
207 $total += $item->number;
208 $sum += $item->number * $item->price;
209 }
210 }
211 return ($total, $sum);
212 }
213
214
215 399 ahitrov sub wishlist_count {
216 my $self = shift;
217 my $basket = shift;
218 return (0,0) unless ref $basket eq 'ARRAY' && @$basket;
219
220 my $total = 0;
221 my $sum = 0;
222 foreach my $item ( @$basket ) {
223 if ( $item->status == 0 ) {
224 $total += $item->number;
225 $sum += $item->number * $item->price;
226 }
227 }
228 return ($total, $sum);
229 }
230
231
232 198 ahitrov sub clear_basket {
233 my $self = shift;
234 my (%opts) = @_;
235
236 return unless exists $opts{uid} || exists $opts{session};
237
238 my $table_name = webshop::SQL::Basket->db_table;
239 my $request = "delete from $table_name where order_id = 0 AND status = 1 AND";
240 my $dbh = $keeper->SQL;
241 my @vals;
242 if ( exists $opts{uid} && $opts{uid} ) {
243 $request .= " uid in (?)";
244 push @vals, $opts{uid};
245 } elsif ( exists $opts{session} ) {
246 $request .= " session in (?)";
247 push @vals, $opts{session};
248 }
249 warn "CLEAR: [$request]. VALS: [".join(',',@vals)."]\n" if $DEBUG;
250 my $statement = $dbh->prepare ($request);
251 $statement->execute( @vals ) || $log->error("DBI execute error on $request\n"."\ncalled with opts:\n".Data::Dumper::Dumper(\%opts));;
252 $statement->finish;
253
254 my @plugins = split (/[\ |\t]+/, $state->{plugins});
255 363 ahitrov if ( grep { $_ eq 'session' } @plugins && ref $session ) {
256 $session->set ( basket_total => 0, basket_sum => 0 );
257 198 ahitrov }
258 }
259
260
261 sub clear_wishlist {
262 my $self = shift;
263 my (%opts) = @_;
264
265 return unless exists $opts{uid} || exists $opts{session};
266
267 my $table_name = webshop::SQL::Basket->db_table;
268 my $request = "delete from $table_name where order_id = 0 AND status = 0 AND";
269 my $dbh = $keeper->SQL;
270 my @vals;
271 if ( exists $opts{uid} ) {
272 $request .= " uid in (?)";
273 push @vals, $opts{uid};
274 } elsif ( exists $opts{session} ) {
275 $request .= " session in (?)";
276 push @vals, $opts{session};
277 }
278 my $statement = $dbh->prepare ($request);
279 $statement->execute( @vals ) || $log->error("DBI execute error on $request\n"."\ncalled with opts:\n".Data::Dumper::Dumper(\%opts));;
280 $statement->finish;
281 }
282
283
284 ### Метод пересчета корзины
285 # Принимает на вход параметры:
286 # session => session_id пользователя
287 # uid => UID пользователя
288 # delete => массив или отдельный item_id
289 # renumber => ссылка на хеш вида item => number
290 #############################################################
291 sub recount {
292 my $self = shift;
293 my (%opts) = @_;
294
295 warn "Recount Started!!!\n" if $DEBUG;
296 return unless exists $opts{uid} || exists $opts{session} || exists $opts{order_id};
297 my $basket = $self->get_basket ( %opts );
298 return unless ref $basket eq 'ARRAY' && @$basket;
299
300 warn Dumper(\%opts) if $DEBUG;
301
302 my $total = 0;
303 my $sum = 0;
304 my @new_basket;
305 my $session_no_store = delete $opts{session_no_store};
306 foreach my $item ( @$basket ) {
307 my $delete = 0;
308 if ( exists $opts{renumber} && ref $opts{renumber} eq 'HASH' && exists $opts{renumber}{$item->id} && int($opts{renumber}{$item->id}) == 0 ) {
309 $delete = 1;
310 } elsif ( exists $opts{delete} && ref $opts{delete} eq 'ARRAY' ) {
311 $delete = 1 if grep { $_ == $item->id } @{ $opts{delete} };
312 } elsif ( exists $opts{delete} && $opts{delete} ) {
313 $delete = 1 if $item->id == $opts{delete};
314 }
315 if ( $delete ) {
316 warn "Item ID=".$item->id." DELETE\n" if $DEBUG;
317 $item->delete();
318 next;
319 } else {
320 my $store = 0;
321 if ( exists $opts{renumber} && ref $opts{renumber} eq 'HASH' && exists $opts{renumber}{$item->id} && $opts{renumber}{$item->id} != $item->number ) {
322 $item->number( $opts{renumber}{$item->id} );
323 $store = 1;
324 }
325 if ( exists $opts{price} && ref $opts{price} eq 'HASH' && exists $opts{price}{$item->id} && $opts{price}{$item->id} != $item->price ) {
326 $item->price( $opts{price}{$item->id} );
327 $store = 1;
328 }
329 $item->store if $store;
330 $total += $item->number;
331 $sum += $item->number * $item->price;
332 push @new_basket, $item;
333 }
334 }
335 my @plugins = split (/[\ |\t]+/, $state->{plugins});
336 363 ahitrov if ( !$session_no_store && grep { $_ eq 'session' } @plugins && ref $session ) {
337 $session->set ( basket_total => $total, basket_sum => $sum );
338 198 ahitrov }
339 return ($total, $sum, \@new_basket);
340 }
341
342
343 sub get_orders {
344 my $self = shift;
345 my (%opts) = @_;
346
347 my $list = delete $opts{list};
348 434 ahitrov my $count = delete $opts{count};
349 if ( $count ) {
350 delete $opts{order_by};
351 my $item_count = $keeper->get_documents (
352 class => 'webshop::Order',
353 count => 1,
354 %opts,
355 );
356 return $item_count;
357 198 ahitrov } else {
358 434 ahitrov $opts{order_by} ||= 'status';
359 my @items = $keeper->get_documents (
360 class => 'webshop::Order',
361 %opts,
362 );
363 if ( exists $opts{id} && defined $opts{id} && !ref $opts{id} ) {
364 if ( $list ) {
365 $items[0]->{list} = $self->get_order_list( order_id => $opts{id} );
366 }
367 return $items[0];
368 } else {
369 if ( $list ) {
370 map { $_->{list} = $self->get_order_list( order_id => $_->id ) } @items;
371 }
372 return \@items;
373 198 ahitrov }
374 }
375 }
376
377
378 sub get_order_list {
379 my $self = shift;
380 my (%opts) = @_;
381
382 return unless $opts{order_id};
383
384 $opts{status} = 1;
385 my @items = $keeper->get_documents (
386 class => 'webshop::Basket',
387 %opts,
388 );
389 my $total = 0;
390 my $sum = 0;
391 foreach my $item ( @items ) {
392 my $Item = $item->class->new( $keeper, $item->id );
393 $item->{item} = $Item;
394 if ( $item->status == 1 ) {
395 $total += $item->number;
396 $sum += $item->number * $item->price;
397 }
398 }
399 return \@items;
400 }
401
402 270 ahitrov
403 ### Метод приведения купонов для пользователя в момент логина
404 #############################################################
405 sub merge_coupons {
406 my $self = shift;
407 my (%opts) = @_;
408
409 warn "Merge (coupons) begin: ".Dumper(\%opts)."\n" if $DEBUG;
410 return unless $opts{uid} && $opts{session};
411
412 my @items = $keeper->get_links (
413 class => 'webshop::OrderCouponLink',
414 session => $opts{session},
415 source_id => 0,
416 );
417 my %merge_to = $keeper->get_links (
418 class => 'webshop::OrderCouponLink',
419 uid => $opts{uid},
420 source_id => 0,
421 return_mode => 'hash',
422 hash_by => 'dest_id',
423 );
424 foreach my $item ( @items ) {
425 if ( exists $merge_to{$item->dest_id} ) {
426 $item->delete;
427 } else {
428 $item->session( undef );
429 $item->uid( $opts{uid} );
430 $item->store;
431 }
432 }
433 }
434
435
436 424 ahitrov sub check_discount {
437 my $self = shift;
438 my (%opts) = @_;
439 270 ahitrov
440 432 ahitrov warn "Check discount begin:\n" if $DEBUG;
441 424 ahitrov my %dopts;
442 if ( exists $opts{uid} && $opts{uid} ) {
443 $dopts{uid} = [0,1];
444 } else {
445 $dopts{uid} = 0;
446 }
447 429 ahitrov my $basket = exists $opts{basket} ? $opts{basket} : $self->get_basket( %opts, with_products => 1 );
448 return 0 unless ref $basket eq 'ARRAY' && @$basket;
449 my @basket = grep { exists $_->{item} && $_->{item} } @$basket;
450 return 0 unless @basket;
451 424 ahitrov
452 425 ahitrov my $now = Contenido::DateTime->new;
453 424 ahitrov my @discounts = $keeper->get_documents(
454 class => 'webshop::Discount',
455 status => 1,
456 425 ahitrov interval=> [$now, $now],
457 %dopts,
458 424 ahitrov );
459 425 ahitrov return 0 unless @discounts;
460 my @summoned = sort { $b->min_sum <=> $a->min_sum } grep { $_->min_sum && $_->discount && $_->min_sum =~ /^\d+$/ } @discounts;
461 429 ahitrov my ($total, $sum) = $self->basket_count( \@basket );
462 425 ahitrov return 0 unless $sum;
463 my $result = 0;
464 foreach my $discount ( @summoned ) {
465 if ( $sum > $discount->min_sum ) {
466 429 ahitrov my $res = $discount->get_discount( basket => \@basket );
467 425 ahitrov $result = $res if $res > $result;
468 }
469 }
470 $result = 0 if $result >= $sum;
471 return $result;
472 424 ahitrov }
473
474
475 432 ahitrov sub check_coupons {
476 my $self = shift;
477 my (%opts) = @_;
478
479 warn "Check coupons begin:\n" if $DEBUG;
480 my %dopts;
481 if ( exists $opts{uid} && $opts{uid} ) {
482 $dopts{luid} = $opts{uid};
483 } else {
484 $dopts{lsession} = $opts{session};
485 }
486 my $basket = exists $opts{basket} ? $opts{basket} : $self->get_basket( %opts, with_products => 1 );
487 return (0, []) unless ref $basket eq 'ARRAY' && @$basket;
488 my @basket = grep { exists $_->{item} && $_->{item} } @$basket;
489 return (0, []) unless @basket;
490
491 my $now = Contenido::DateTime->new;
492 my @coupons = exists $opts{coupons} && ref $opts{coupons} eq 'ARRAY' ? @{$opts{coupons}} : $keeper->get_documents(
493 class => 'webshop::Coupon',
494 lclass => 'webshop::OrderCouponLink',
495 lsource => 0,
496 status => 1,
497 interval=> [$now->ymd, $now->ymd],
498 %dopts,
499 );
500 return (0, []) unless @coupons;
501 my @summoned = grep { $_->discount } @coupons;
502 warn "We count on ".scalar(@summoned)." coupons\n" if $DEBUG;
503 my ($total, $sum) = $self->basket_count( \@basket );
504 return (0, \@summoned) unless $sum;
505 my $result = 0;
506 foreach my $coupon ( @summoned ) {
507 my $res = $coupon->get_discount( basket => \@basket );
508 $coupon->{result} = $res;
509 }
510 @summoned = sort { $b->{result} <=> $a->{result} } @summoned;
511 foreach my $coupon ( @summoned ) {
512 if ( ($coupon->{result} > 0) && ($sum - $coupon->{result} > 0) ) {
513 $result += $coupon->{result};
514 $sum -= $coupon->{result};
515 $coupon->{afflict_order} = 1;
516 } else {
517 $coupon->{afflict_order} = 0;
518 }
519 }
520 return ($result, \@summoned);
521 }
522
523
524 198 ahitrov sub price_format {
525 my $self = shift;
526 my $price = shift;
527
528 $price = reverse $price;
529 $price =~ s/(\d{3})/$1\ /g;
530 $price = reverse $price;
531
532 return $price;
533 }
534
535
536 1;