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 – "готовые к употреблению" проекты, которые можно поставить, запустить и посмотреть, как они работают.