Revision 259
Date:
2012/12/05 08:06:03
Author:
ahitrov
Revision Log:
Area-sensitive delivery.
Files:
Legend:
Added
Removed
Modified
utf8/plugins/webshop/lib/webshop/Address.pm
17
17
},
18
18
},
19
19
{ 'attr' => 'name', 'type' => 'string', 'rusname' => 'Контактное лицо' },
20
{ 'attr' => 'phone', 'type' => 'string', 'rusname' => 'Телефон для связи' },
21
{ 'attr' => 'zipcode', 'type' => 'string', 'rusname' => 'Почтовый индекс' },
22
{ 'attr' => 'town', 'type' => 'string', 'rusname' => 'Город' },
20
{ 'attr' => 'phone', 'type' => 'string', 'rusname' => 'Телефон для связи',
21
mandatory => 1, rel => 'Не указан телефон' },
22
{ 'attr' => 'zipcode', 'type' => 'string', 'rusname' => 'Почтовый индекс',
23
mandatory => 1, rel => 'Не указан почтовый индекс' },
24
{ 'attr' => 'area', 'type' => 'lookup', 'rusname' => 'Регион',
25
lookup_opts => { class => 'webshop::Area', order_by => 'name' },
26
allow_null => 1, mandatory => 1, rel => 'Не выбран регион'
27
},
28
{ 'attr' => 'town_id', 'type' => 'lookup', 'rusname' => 'Город',
29
lookup_opts => { class => 'webshop::Town', order_by => 'name' },
30
allow_null => 1, mandatory => 1, rel => 'Не выбран город'
31
},
32
{ 'attr' => 'town', 'type' => 'string', 'rusname' => 'Город',
33
mandatory => 1, rel => 'Не указан город' },
23
34
{ 'attr' => 'metro', 'type' => 'string', 'rusname' => 'Ближайшее метро' },
24
{ 'attr' => 'address', 'type' => 'text', 'rusname' => 'Адрес доставки', rows => 5 },
25
{ 'attr' => 'timeline', 'type' => 'string', 'rusname' => 'Предпочтительное время' },
35
{ 'attr' => 'address', 'type' => 'text', 'rusname' => 'Адрес доставки', rows => 5,
36
mandatory => 1, rel => 'Не заполнен адрес доставки' },
37
{ 'attr' => 'timeline', 'type' => 'string', 'rusname' => 'Предпочтительное время',
38
mandatory => 1, rel => 'Вы не указали предпочтительное время получения заказа' },
26
39
{ 'attr' => 'description', 'type' => 'text', 'rusname' => 'Описание для курьера', rows => 5 },
27
40
)
28
41
}
29
42
30
43
sub class_name
31
44
{
32
return 'Заказ';
45
return 'Webshop: адрес доставки';
33
46
}
34
47
35
48
sub class_description
36
49
{
37
return 'Заказ';
50
return 'Webshop: адрес доставки';
38
51
}
39
52
40
53
sub class_table
…
…
42
55
return 'webshop::SQL::AddressTable';
43
56
}
44
57
58
sub pre_store
59
{
60
my $self = shift;
61
62
if ( $self->town_id && !$self->town ) {
63
my $town = webshop::Town->new( $keeper, $self->town_id );
64
if ( ref $town ) {
65
$self->town( $town->name );
66
}
67
}
68
69
return 1;
70
}
71
45
72
1;
utf8/plugins/webshop/lib/webshop/Area.pm
1
package webshop::Area;
2
3
use Contenido::Globals;
4
use base "Contenido::Document";
5
sub extra_properties
6
{
7
return (
8
{ 'attr' => 'status', 'type' => 'status', 'rusname' => 'Статус',
9
'cases' => [
10
[0, 'Запись не активна'],
11
[1, 'Запись активна'],
12
],
13
},
14
{ 'attr' => 'pid', 'type' => 'lookup', 'rusname' => 'Страна',
15
lookup_opts => { class => 'webshop::Country', order_by => 'name' },
16
allow_null => 1,
17
},
18
)
19
}
20
21
sub class_name
22
{
23
return 'Webshop: регион';
24
}
25
26
sub class_description
27
{
28
return 'Webshop: region';
29
}
30
31
sub class_table
32
{
33
return 'webshop::SQL::RegionsTable';
34
}
35
36
sub table_links
37
{
38
return [
39
{ name => 'Города', class => 'webshop::Town', filter => 'pid', field => 'pid' },
40
];
41
}
42
43
sub pre_store
44
{
45
my $self = shift;
46
47
unless ( $self->pid ) {
48
my $count = $self->keeper->get_documents (
49
class => 'webshop::Country',
50
count => 1,
51
);
52
if ( $count == 1 ) {
53
my ($country) = $self->keeper->get_documents (
54
class => 'webshop::Country',
55
);
56
$self->pid( $country->id );
57
}
58
}
59
60
my $default_section = $project->s_alias->{webshop_area} if ref $project->s_alias eq 'HASH';
61
my $sections = $self->{sections};
62
if ( $default_section ) {
63
if ( ref $sections eq 'ARRAY' && scalar @$sections ) {
64
my @new_sects = grep { $_ != $default_section } @$sections;
65
push @new_sects, $default_section;
66
$self->sections(@new_sects);
67
} elsif ( $sections && !ref $sections && $sections != $default_section ) {
68
my @new_sects = ($default_section, $sections);
69
$self->sections(@new_sects);
70
} else {
71
$self->sections($default_section);
72
}
73
}
74
75
return 1;
76
}
77
78
1;
utf8/plugins/webshop/lib/webshop/Country.pm
1
package webshop::Country;
2
3
use Contenido::Globals;
4
use base "Contenido::Document";
5
sub extra_properties
6
{
7
return (
8
{ 'attr' => 'status', 'type' => 'status', 'rusname' => 'Статус',
9
'cases' => [
10
[0, 'Запись активна'],
11
[1, 'Запись не активна'],
12
],
13
},
14
{ 'attr' => 'pid', 'hidden' => 1 },
15
)
16
}
17
18
sub class_name
19
{
20
return 'Webshop: страна';
21
}
22
23
sub class_description
24
{
25
return 'Webshop: страна';
26
}
27
28
sub class_table
29
{
30
return 'webshop::SQL::RegionsTable';
31
}
32
33
sub table_links
34
{
35
return [
36
{ name => 'Регионы', class => 'webshop::Area', filter => 'pid', field => 'pid' },
37
];
38
}
39
40
sub pre_store
41
{
42
my $self = shift;
43
44
my $default_section = $project->s_alias->{webshop_country} if ref $project->s_alias eq 'HASH';
45
my $sections = $self->{sections};
46
if ( $default_section ) {
47
if ( ref $sections eq 'ARRAY' && scalar @$sections ) {
48
my @new_sects = grep { $_ != $default_section } @$sections;
49
push @new_sects, $default_section;
50
$self->sections(@new_sects);
51
} elsif ( $sections && !ref $sections && $sections != $default_section ) {
52
my @new_sects = ($default_section, $sections);
53
$self->sections(@new_sects);
54
} else {
55
$self->sections($default_section);
56
}
57
}
58
59
return 1;
60
}
61
62
63
1;
utf8/plugins/webshop/lib/webshop/Delivery.pm
12
12
[0, 'Не активна'],
13
13
[1, 'Действует'],
14
14
],
15
},
15
},
16
16
{ 'attr' => 'dtime', 'hidden' => 1 },
17
{ 'attr' => 'price', 'type' => 'string', 'rusname' => 'Стоимость доставки (NN или NN%)', default => 0 },
17
{ 'attr' => 'price', 'type' => 'string', 'rusname' => 'Стоимость доставки (число)', default => 0 },
18
18
{ 'attr' => 'price_modify', 'type' => 'cost_level', 'rusname' => 'Стоимость доставки с учетом стоимости заказа' },
19
{ 'attr' => 'price_region', 'type' => 'cost_region', 'rusname' => 'Стоимость доставки с учетом региона' },
19
20
{ 'attr' => 'abstr', 'type' => 'text', 'rusname' => 'Краткое описание' },
20
21
{ 'attr' => 'fields', 'type' => 'status_multi', 'rusname' => 'Поля, необходимые к заполнению',
21
22
'cases' => [
22
23
['timeline','Время заказа'],
23
24
['zipcode','Почтовый индекс'],
25
['country','Страна'],
26
['area','Регион из списка'],
27
['town_id','Город из списка'],
24
28
['town','Город'],
25
29
['address','Адрес'],
26
30
['metro','Ближайшее метро'],
…
…
32
36
sub price_actual
33
37
{
34
38
my $self = shift;
35
my $sum = shift;
39
my (%opts) = @_;
40
41
my $sum = delete $opts{sum};
42
my $region_id = exists $opts{region} && ref $opts{region} ? $opts{region}->id : exists $opts{region_id} ? $opts{region_id} : undef;
36
43
my $price = $self->price;
37
$price = defined $price && $price =~ /^\d+$/ ? $price : 0;
44
$price = defined $price && $price =~ /^\d+$/ ? $price : 'не определена';
38
45
39
if ( $sum ) {
46
if ( $region_id ) {
47
if ( $self->price_region ) {
48
my $region_price = $json->decode( $self->price_region );
49
if ( ref $region_price eq 'HASH' && exists $region_price->{$region_id} ) {
50
$price = $region_price->{$region_id};
51
}
52
}
53
} elsif ( $sum ) {
40
54
if ( $self->price_modify ) {
41
my $modify = $json->decode( $self->price_modify );
42
if ( ref $modify eq 'ARRAY' && @$modify ) {
43
foreach my $mod ( @$modify ) {
55
my $price_modify = $json->decode( $self->price_modify );
56
if ( ref $price_modify eq 'ARRAY' && @$price_modify ) {
57
foreach my $mod ( @$price_modify ) {
44
58
if ( $mod->{level} <= $sum ) {
45
59
$price = $mod->{cost};
46
60
}
47
61
}
48
62
}
49
63
}
50
return $price;
51
} else {
52
return $price;
53
64
}
65
return $price;
54
66
}
55
67
68
sub get_fields
69
{
70
my $self = shift;
71
my $fields = $self->fields;
72
my @fields = @$fields if $fields && ref $fields eq 'ARRAY';
73
74
return @fields;
75
}
76
77
sub check_field
78
{
79
my $self = shift;
80
my $field = shift;
81
return unless $field;
82
my $fields = $self->fields;
83
my %fields = map { $_ => 1 } @$fields if ref $fields eq 'ARRAY' && @$fields;
84
85
return exists $fields{$field} ? 1 : 0;
86
}
87
56
88
sub class_name
57
89
{
58
90
return 'Способ доставки';
utf8/plugins/webshop/lib/webshop/DeliverySection.pm
1
1
package webshop::DeliverySection;
2
2
3
use Contenido::Section;
4
@ISA = ('Contenido::Section');
3
use base 'Contenido::Section';
5
4
6
5
sub extra_properties
7
6
{
utf8/plugins/webshop/lib/webshop/Init.pm
20
20
webshop::Order
21
21
webshop::Delivery
22
22
23
webshop::Country
24
webshop::Area
25
webshop::Town
26
23
27
webshop::DeliverySection
28
webshop::RegionSection
24
29
));
25
30
26
31
sub init {
27
push @{ $state->{'available_documents'} }, ('webshop::Basket', 'webshop::Order', 'webshop::Delivery');
28
push @{ $state->{'available_sections'} }, 'webshop::DeliverySection';
32
push @{ $state->{'available_documents'} }, qw(webshop::Basket webshop::Order webshop::Delivery webshop::Country webshop::Area webshop::Town);
33
push @{ $state->{'available_sections'} }, qw(webshop::DeliverySection webshop::RegionSection);
29
34
0;
30
35
}
31
36
utf8/plugins/webshop/lib/webshop/Order.pm
33
33
column => 6, postshow => 1, facilshow => 1 },
34
34
{ 'attr' => 'contact', 'type' => 'string', 'rusname' => 'Контактное лицо', facilshow => 1 },
35
35
{ 'attr' => 'email', 'type' => 'string', 'rusname' => 'E-mail для связи', shortname => 'E-mail',
36
column => 3, postshow => 1, facilshow => 1 },
36
column => 3, postshow => 1, facilshow => 1, mandatory => 1, },
37
37
{ 'attr' => 'phone', 'type' => 'string', 'rusname' => 'Телефон для связи', shortname => 'Тел.',
38
column => 4, postshow => 1, facilshow => 1 },
39
{ 'attr' => 'zipcode', 'type' => 'string', 'rusname' => 'Почтовый индекс', postshow => 1 },
40
{ 'attr' => 'town', 'type' => 'string', 'rusname' => 'Город', postshow => 1, facilshow => 1 },
38
column => 4, postshow => 1, facilshow => 1,
39
mandatory => 1, rel => 'Не указан телефон' },
40
{ 'attr' => 'zipcode', 'type' => 'string', 'rusname' => 'Почтовый индекс', postshow => 1,
41
mandatory => 1, rel => 'Не указан почтовый индекс' },
42
{ 'attr' => 'area', 'type' => 'lookup', 'rusname' => 'Регион',
43
lookup_opts => { class => 'webshop::Area', order_by => 'name' },
44
allow_null => 1, mandatory => 1, rel => 'Не выбран регион'
45
},
46
{ 'attr' => 'town_id', 'type' => 'lookup', 'rusname' => 'Город',
47
lookup_opts => { class => 'webshop::Town', order_by => 'name' },
48
allow_null => 1, mandatory => 1, rel => 'Не выбран город'
49
},
50
{ 'attr' => 'town', 'type' => 'string', 'rusname' => 'Город', postshow => 1, facilshow => 1,
51
mandatory => 1, rel => 'Не указан город' },
41
52
{ 'attr' => 'metro', 'type' => 'string', 'rusname' => 'Ближайшее метро', postshow => 1 },
42
{ 'attr' => 'timeline', 'type' => 'string', 'rusname' => 'Предпочтительное время', postshow => 1 },
53
{ 'attr' => 'timeline', 'type' => 'string', 'rusname' => 'Предпочтительное время', postshow => 1, },
43
54
{ 'attr' => 'description', 'type' => 'text', 'rusname' => 'Описание для курьера', rows => 5, postshow => 1 },
44
55
{ 'attr' => 'facility_comment', 'type' => 'text', 'rusname' => 'Описание проблем с комплектацией', rows => 5, faciledit => 1 },
45
56
{ 'attr' => 'delivery_comment', 'type' => 'text', 'rusname' => 'Описание проблем с доставкой', rows => 5, postedit => 1 },
46
{ 'attr' => 'address', 'type' => 'text', 'rusname' => 'Адрес доставки', rows => 5, postedit => 1, facilshow => 1 },
57
{ 'attr' => 'address', 'type' => 'text', 'rusname' => 'Адрес доставки', rows => 5, postedit => 1, facilshow => 1,
58
mandatory => 1, rel => 'Не заполнен адрес доставки' },
47
59
)
48
60
}
49
61
utf8/plugins/webshop/lib/webshop/RegionSection.pm
1
package webshop::RegionSection;
2
3
use base 'Contenido::Section';
4
5
sub extra_properties
6
{
7
return (
8
{ 'attr' => 'brief', 'type' => 'text', 'rusname' => 'Описание секции' },
9
# { 'attr' => 'default_document_class', 'default' => 'webshop::Delivery' },
10
# { 'attr' => '_sorted', 'default' => 1 },
11
{ 'attr' => 'filters', 'hidden' => 1 },
12
)
13
}
14
15
sub class_name
16
{
17
return 'География доставки';
18
}
19
20
sub class_description
21
{
22
return 'География доставки';
23
}
24
25
1;
utf8/plugins/webshop/lib/webshop/SQL/RegionsTable.pm
1
package webshop::SQL::RegionsTable;
2
3
use base 'SQL::DocumentTable';
4
5
sub db_table
6
{
7
return 'webshop_regions';
8
}
9
10
my $available_filters = [qw(
11
12
_class_filter
13
_status_filter
14
_in_id_filter
15
_id_filter
16
_name_filter
17
_class_excludes_filter
18
_sfilter_filter
19
_excludes_filter
20
_datetime_filter
21
_date_equal_filter
22
_date_filter
23
_previous_days_filter
24
_s_filter
25
26
_pid_filter
27
_alias_filter
28
)];
29
30
sub available_filters {
31
return $available_filters;
32
}
33
34
35
# ----------------------------------------------------------------------------
36
# Свойства храним в массивах, потому что порядок важен!
37
# Это общие свойства - одинаковые для всех документов.
38
#
39
# attr - обязательный параметр, название атрибута;
40
# type - тип аттрибута, требуется для отображдения;
41
# rusname - русское название, опять же требуется для отображения;
42
# hidden - равен 1, когда
43
# readonly - инициализации при записи только без изменения в дальнейшем
44
# db_field - поле в таблице
45
# default - значение по умолчанию (поле всегда имеет это значение)
46
# ----------------------------------------------------------------------------
47
sub required_properties
48
{
49
my $self = shift;
50
51
my @parent_properties = grep { $_->{attr} ne 'dtime' } $self->SUPER::required_properties;
52
return (
53
@parent_properties,
54
{ # Parent ID
55
'attr' => 'pid',
56
'type' => 'integer',
57
'rusname' => 'ID родительского региона',
58
'db_field' => 'pid',
59
'db_type' => 'integer',
60
'db_opts' => "default 0",
61
'default' => 0,
62
},
63
{
64
'attr' => 'alias',
65
'type' => 'string',
66
'rusname' => 'Web-alias региона',
67
'db_field' => 'alias',
68
'db_type' => 'text',
69
},
70
{
71
'attr' => 'price',
72
'type' => 'string',
73
'rusname' => 'Стоимость доставки',
74
'db_field' => 'price',
75
'db_type' => 'text',
76
},
77
);
78
}
79
80
########### FILTERS DESCRIPTION ###############################################################################
81
sub _pid_filter {
82
my ($self,%opts)=@_;
83
return undef unless ( exists $opts{pid} );
84
return &SQL::Common::_generic_int_filter('d.pid', $opts{pid});
85
}
86
87
sub _alias_filter {
88
my ($self,%opts)=@_;
89
return undef unless ( exists $opts{alias} );
90
return &SQL::Common::_generic_text_filter('d.alias', $opts{alias});
91
}
92
93
1;
utf8/plugins/webshop/lib/webshop/Town.pm
1
package webshop::Town;
2
3
use Contenido::Globals;
4
use base "Contenido::Document";
5
sub extra_properties
6
{
7
return (
8
{ 'attr' => 'status', 'type' => 'status', 'rusname' => 'Статус',
9
'cases' => [
10
[0, 'Запись не активна'],
11
[1, 'Запись активна'],
12
],
13
},
14
{ 'attr' => 'pid', 'type' => 'lookup', 'rusname' => 'Регион',
15
lookup_opts => { class => 'webshop::Area', order_by => 'name' },
16
allow_null => 1,
17
},
18
)
19
}
20
21
sub class_name
22
{
23
return 'Webshop: город';
24
}
25
26
sub class_description
27
{
28
return 'Webshop: город';
29
}
30
31
sub class_table
32
{
33
return 'webshop::SQL::RegionsTable';
34
}
35
36
sub pre_store
37
{
38
my $self = shift;
39
40
my $default_section = $project->s_alias->{webshop_town} if ref $project->s_alias eq 'HASH';
41
my $sections = $self->{sections};
42
if ( $default_section ) {
43
if ( ref $sections eq 'ARRAY' && scalar @$sections ) {
44
my @new_sects = grep { $_ != $default_section } @$sections;
45
push @new_sects, $default_section;
46
$self->sections(@new_sects);
47
} elsif ( $sections && !ref $sections && $sections != $default_section ) {
48
my @new_sects = ($default_section, $sections);
49
$self->sections(@new_sects);
50
} else {
51
$self->sections($default_section);
52
}
53
}
54
55
return 1;
56
}
57
58
1;
Небольшая справка по веткам
cnddist – контейнер, в котором хранятся все дистрибутивы всех библиотек и программных пакетов, которые использовались при построении различных версий Contenido. Если какой-то библиотеки в данном хранилище нет, инсталлятор сделает попытку "подтянуть" ее с веба (например, с CPAN). Если библиотека слишком старая, есть очень большая вероятность, что ее там уже нет. Поэтому мы храним весь хлам от всех сборок. Если какой-то дистрибутив вдруг отсутствует в cnddist - напишите нам, мы положим его туда.
koi8 – отмирающая ветка, чей код, выдача и все внутренние библиотеки заточены на кодировку KOI8-R. Вносятся только те дополнения, которые касаются внешнего вида и функционала админки, баги ядра, обязательные обновления портов и мелочи, которые легко скопипастить. В дальнейшем планируется полная остановка поддержки по данной ветке.
utf8 – актуальная ветка, заточенная под UTF-8.
Внутри каждой ветки: core – исходники ядра; install – скрипт установки инсталляции; plugins – плагины; samples – "готовые к употреблению" проекты, которые можно поставить, запустить и посмотреть, как они работают.