Revision 589 (by ahitrov, 2016/09/12 09:48:55) Coupon discount refactored

package webshop::Coupon;

use strict;
use warnings 'all';

use Contenido::Globals;
use base "Contenido::Document";
use Data::Dumper;

sub extra_properties
{
	return (
		{ 'attr' => 'class',				'column' => undef },
		{ 'attr' => 'status',	'type' => 'status',     'rusname' => 'Статус',
			'cases' => [
					[0, 'Купон не активен'],
					[1, 'Купон активен и разослан'],
					[2, 'Прототип ждет обработки'],
					[3, 'Купон использован'],
					[4, 'Прототип обработан роботом'],
				],
		},
		{ 'attr' => 'uid_proto','type' => 'status',	'rusname' => 'Прототип пользовательского доступа',
			'cases' => [
					[0, 'Купон доступен всем пользователям'],
					[1, 'Персональный купон, создается для всех зарегистрированных пользователей'],
					[2, 'Персональный купон, пользователи по выбору'],
					[3, 'Персональный купон для одного пользователя'],
				],
				'rem'	=> 'Заполняется при создании прототипа купона',
		},
		{ 'attr' => 'uid_condition',	'type' => 'string',	'rusname' => 'Условие для прототипа' },
		{ 'attr' => 'uid',	'type' => 'pickup',	'rusname' => 'Пользователь',
				lookup_opts => { class => $state->{users}->profile_document_class, order_by => 'email', search_by => 'email' },
				allow_null	=> 1, default	=> 0,
				rem	=> '0 - действует для всех пользователей, многократно. id - для одного пользователя, однократно.'
		},
		{ 'attr' => 'groups',				'rusname' => 'Группы товаров',
				lookup_opts => { class => $state->{webshop}->{item_section_class}, },
				allow_null => 1,
				rem	=> 'Список разделов, на содержимое которых распространяется скидка по купону',
		},
		{ 'attr' => 'discount',	'type' => 'string',	'rusname' => 'Скидка на сумму заказа (число или процент)', shortname => 'Скидка',
				default => 0, column => 2 },
		{ 'attr' => 'min_sum',	'type' => 'string',	'rusname' => 'Минимальная сумма, на которую действует скидка', default => 0 },
	)
}

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 = exists $opts{basket} ? delete $opts{basket} : $keeper->{webshop}->get_basket ( %opts, with_products => 1 );
    return 0	unless ref $basket eq 'ARRAY' && @$basket;

    my ($number, $sum_total) = (0, 0);
    my @basket = grep { exists $_->{item} && $_->{item} } @$basket;
    return 0	unless @basket;
    map { my $price = $_->{item}->price; $number += $_->number; $sum_total += $_->number * $price } @basket;
    warn "BASKET: $number Items of $sum_total Value\n";

    my $discount_counted = 0;
    my $items = $self->keeper->get_documents (
			class	=> $state->{webshop}->{item_document_class},
			lclass	=> 'webshop::CouponItemLink',
			lsource	=> $self->id,
			light	=> 1,
			return_mode	=> 'hash_ref',
		);
    
    if ( $self->groups ) {
	my %groups = map { $_ => 1 } $self->groups;
	@basket = grep { scalar (grep { exists $groups{$_} } $_->sections) > 0 || exists $items->{$_->{item}->id} } @basket;
    } elsif ( keys %$items ) {
	@basket = grep { exists $items->{$_->{item}->id} } @basket;
    }
    return 0	unless @basket;

    my $found_sum = 0;
    foreach my $bi ( @basket ) {
	warn "BASKET: Basket item id [".$bi->item_id."]\n"				if $DEBUG;
	next	if $bi->{item}->special_price;
	next	unless $bi->{item}->price > 0 && $bi->{item}->storage;
	$found_sum += $bi->number * $bi->{item}->price;
    }
    return $found_sum > 0 ? $self->can_discount( $found_sum ) : 0;
}


sub can_discount
{
    my $self = shift;
    my $sum = shift;
    return 0	unless $sum;

    my $discount = $self->discount;
    my $min_sum = $self->min_sum || 0;
    my $count = 0;
    if ( $discount =~ /([\d\.]+)%/ ) {
	my $proc = $1;
	return 0	unless $proc;
	$count = $sum / 100 * $proc;
    } else {
	$count = $discount;
    }
    my $rest = $sum - $count;
    warn "Min Sum: $min_sum. Rest: $rest\n"		if $DEBUG;
    $count = 0		if ($min_sum && $sum < $min_sum) || $rest <= 0;
    warn "Count: $count\n"				if $DEBUG;
    return $count;
}




#sub table_links
#{
#	return [
#		{ name => 'Города', class => 'webshop::Town', filter => 'pid', field => 'pid' },
#	];
#}

sub pre_store
{
	my $self = shift;

	my $default_section = $project->s_alias->{webshop_coupons}	if ref $project->s_alias eq 'HASH';
	if ( $default_section && !$self->sections ) {
		$self->sections($default_section);
	}

	return 1;
}


sub post_delete
{
    my $self = shift;

    my $sql = $self->keeper->SQL->prepare('DELETE FROM webshop_coupons where pid = ?');
    $sql->execute( $self->id );
    $sql = $self->keeper->SQL->prepare('DELETE FROM webshop_coupon_links where source_id = ?');
    $sql->execute( $self->id );
    $sql = $self->keeper->SQL->prepare('DELETE FROM webshop_order_coupons where dest_id = ?');
    $sql->execute( $self->id );

    1;
}

1;

Небольшая справка по веткам

cnddist – контейнер, в котором хранятся все дистрибутивы всех библиотек и программных пакетов, которые использовались при построении различных версий Contenido. Если какой-то библиотеки в данном хранилище нет, инсталлятор сделает попытку "подтянуть" ее с веба (например, с CPAN). Если библиотека слишком старая, есть очень большая вероятность, что ее там уже нет. Поэтому мы храним весь хлам от всех сборок. Если какой-то дистрибутив вдруг отсутствует в cnddist - напишите нам, мы положим его туда.

koi8 – отмирающая ветка, чей код, выдача и все внутренние библиотеки заточены на кодировку KOI8-R. Вносятся только те дополнения, которые касаются внешнего вида и функционала админки, баги ядра, обязательные обновления портов и мелочи, которые легко скопипастить. В дальнейшем планируется полная остановка поддержки по данной ветке.

utf8 – актуальная ветка, заточенная под UTF-8.

Внутри каждой ветки: core – исходники ядра; install – скрипт установки инсталляции; plugins – плагины; samples – "готовые к употреблению" проекты, которые можно поставить, запустить и посмотреть, как они работают.