package webshop::Discount;
use strict;
use warnings 'all';
use Contenido::Globals;
use base "Contenido::Document";
use Data::Dumper;
sub extra_properties
{
return (
{ 'attr' => 'code', 'hidden' => 1, 'column' => undef },
{ 'attr' => 'class', 'column' => undef },
{ 'attr' => 'dtime', 'rusname' => 'Начало действия скидки' },
{ 'attr' => 'etime', 'rusname' => 'Окончание действия скидки' },
{ 'attr' => 'status', 'type' => 'status', 'rusname' => 'Статус',
'cases' => [
[0, 'Скидка не активна'],
[1, 'Скидка активна'],
],
},
{ 'attr' => 'mechanic', 'type' => 'status', 'rusname' => 'Механика',
'cases' => [
['simple', 'Простая скидка'],
['two_as_one', 'Скидка на каждую вторую покупку'],
],
},
{ 'attr' => 'uid', 'type' => 'status', 'rusname' => 'Доступность для пользователей',
'cases' => [
[0, 'Скидка доступна всем пользователям'],
[1, 'Скидка доступна только зарегистрированным пользователям'],
],
},
{ 'attr' => 'trigger_groups', 'rusname' => 'Триггер: группы товаров',
lookup_opts => { class => $state->{webshop}->{item_section_class}, },
allow_null => 1,
rem => 'Список разделов, позиции из которых активируют скидку.',
},
{ 'attr' => 'groups', 'rusname' => 'Группы товаров',
lookup_opts => { class => $state->{webshop}->{item_section_class}, },
allow_null => 1,
rem => 'Список разделов, на содержимое которых распространяется скидка по купону.',
},
{ 'attr' => 'min_sum', 'type' => 'string', 'rusname' => 'Порог суммы, с которого действует скидка',
default => 0, column => 6, shortname => 'Сумма заказа' },
{ 'attr' => 'field', 'type' => 'string', 'rusname' => 'Название ценовой колонки', shortname => 'Колонка',
column => 7,
rem => 'Используется название, отмеченное серым цветом в "name=priceNN"' },
{ 'attr' => 'discount', 'type' => 'string', 'rusname' => 'Размер скидки (число или процент)', shortname => 'Скидка',
default => 0, column => 8,
rem => 'Данное поле будет задействовано, если не указана ценовая колонка.', },
)
}
sub class_name
{
return 'Webshop: скидка';
}
sub class_description
{
return 'Webshop: скидка';
}
sub class_table
{
return 'webshop::SQL::CouponsTable';
}
sub contenido_status_style
{
my $self = shift;
if ( $self->status == 3 ) {
return 'color:black;';
} elsif ( $self->status == 2 ) {
return 'color:red;';
} elsif ( $self->status == 4 ) {
return 'color:olive;';
}
}
sub table_links
{
return [
# { name => 'Купоны', class => 'webshop::Coupon', filter => 'pid', field => 'pid' },
];
}
sub get_discount
{
my $self = shift;
my (%opts) = @_;
return 0 unless exists $opts{basket} || exists $opts{uid} && $opts{uid} || exists $opts{session} && $opts{session};
return 0 unless $self->discount;
my $basket = delete $opts{basket};
$basket = $keeper->{webshop}->get_basket ( %opts ) unless $basket;
return 0 unless ref $basket eq 'ARRAY' && @$basket;
my @basket = grep { exists $_->{item} && $_->{item} } @$basket;
return 0 unless @basket;
my ($number, $sum_total) = (0, 0);
map { $number += $_->number; $sum_total += $_->number * $_->{item}->price } @basket;
warn "BASKET: $number Items of $sum_total Value\n" if $DEBUG;
my %item_props = map { $_->{attr} => $_ } $basket[0]->{item}->structure;
if ( exists $item_props{special_price} ) {
@basket = grep { !$_->{item}->special_price } @basket;
return 0 unless @basket;
}
my $discount_counted = 0;
my $items;
if ( $self->groups ) {
$items = $self->keeper->get_documents (
s => [$self->groups],
class => $state->{webshop}->{item_document_class},
ids => 1,
return_mode => 'array_ref',
);
return 0 unless ref $items eq 'ARRAY' && @$items;
} else {
$items = $self->keeper->get_documents (
class => $state->{webshop}->{item_document_class},
lclass => 'webshop::DiscountItemLink',
lsource => $self->id,
ids => 1,
return_mode => 'array_ref',
);
}
if ( ref $items eq 'ARRAY' && @$items ) {
my %items = map { $_ => 1 } @$items;
@basket = grep { exists $items{$_->item_id} } @basket;
return 0 unless @basket;
}
if ( $self->mechanic eq 'two_as_one' ) {
$discount_counted = $self->mechanic_two_as_one( \@basket );
} else {
$discount_counted = $self->mechanic_simple( \@basket );
}
return wantarray ? ($discount_counted, \@basket) : $discount_counted;
}
sub mechanic_simple {
my $self = shift;
my $basket = shift;
return 0 unless ref $basket eq 'ARRAY' && @$basket;
my $discount = 0;
my $price_field = $self->field;
$price_field =~ s/\s//sg;
$price_field ||= 'price';
my $value = $self->discount;
$value =~ s/\s//sg;
return 0 unless $value;
my ($proc, $fixed);
if ( $value =~ /([\d\.]+)\s*%/ ) {
$proc = $1;
} elsif ( $value =~ /^\s*([\d\.]+)\s*$/ ) {
$fixed = $1;
} else {
return 0;
}
foreach my $bi ( @$basket ) {
my $item_discount = 0;
if ( $proc ) {
$item_discount += $bi->{item}->$price_field / 100 * $proc;
} else {
$item_discount += $bi->{item}->$price_field > $fixed ? $fixed : 0;
}
my $sum = ($bi->{item}->$price_field - $item_discount) * $bi->number;
$bi->{sum_discounted}{$self->id}{sum_show} = $sum;
$bi->{sum_discounted}{$self->id}{sum_bill} = $sum;
$bi->{sum_discounted}{$self->id}{discount} = $item_discount * $bi->number;
$discount += $bi->{sum_discounted}{$self->id}{discount};
}
return $discount;
}
sub mechanic_two_as_one {
my $self = shift;
my $basket = shift;
return 0 unless ref $basket eq 'ARRAY';
my @new_basket;
foreach my $bi ( @$basket ) {
$bi->{sum_discounted} = {};
for ( my $i = 0; $i < $bi->number; $i++ ) {
push @new_basket, $bi;
}
}
return 0 if scalar @new_basket <= 1;
my $price_field = $self->field;
$price_field =~ s/\s//sg;
$price_field ||= 'price';
my $value = $self->discount;
$value =~ s/\s//sg;
$value ||= '100%';
my ($proc, $fixed);
if ( $value =~ /([\d\.]+)\s*%/ ) {
$proc = $1;
} elsif ( $value =~ /^\s*([\d\.]+)\s*$/ ) {
$fixed = $1;
} else {
return 0;
}
my $discount = 0;
my @sorted = sort { $b->{item}->$price_field <=> $a->{item}->$price_field } @new_basket;
my $border = int(scalar(@sorted) / 2) + scalar(@sorted) % 2;
my $sum = 0;
for( my $i = 0; $i < scalar(@sorted); $i++ ) {
my $bi = $sorted[$i];
$sum += $bi->{item}->$price_field;
if ( $i >= $border ) {
my $item_discount = 0;
if ( $proc ) {
$item_discount = $bi->{item}->$price_field * ($proc / 100);
} else {
$item_discount = $bi->{item}->$price_field > $fixed ? $fixed : 0;
}
$bi->{sum_discounted}{$self->id}{sum_show} += $bi->{item}->$price_field - $item_discount;
$bi->{sum_discounted}{$self->id}{discount} += $item_discount;
$discount += $item_discount;
} else {
$bi->{sum_discounted}{$self->id}{sum_show} += $bi->{item}->$price_field;
}
}
my $avg_discount = ($sum - $discount) / $sum;
foreach my $bi ( @sorted ) {
my $bill_price = $bi->{item}->$price_field * $avg_discount;
$bi->{sum_discounted}{$self->id}{sum_bill} += $bill_price;
}
return $discount;
}
sub count_sum_discount
{
my $self = shift;
my $sum = shift;
return 0 unless $sum;
my $discount = $self->discount;
my $count = 0;
if ( $discount =~ /([\d\.]+)%/ ) {
my $proc = $1;
return 0 unless $proc;
$count = $sum / 100 * $proc;
} else {
$count = $discount;
}
$count = 0 if $sum <= $count;
return $count;
}
sub pre_store
{
my $self = shift;
my $default_section = $project->s_alias->{webshop_discounts} if ref $project->s_alias eq 'HASH';
if ( $default_section && !$self->sections ) {
$self->sections($default_section);
}
if ( $self->discount ) {
$self->{discount} =~ s/[^\d\.%]//sg;
}
return 1;
}
sub post_delete
{
my $self = shift;
my $sql = $self->keeper->SQL->prepare('DELETE FROM webshop_coupon_links where source_id = ?');
$sql->execute( $self->id );
1;
}
1;