Revision 853
Date:
2026/08/20 23:19:58
Author:
ahitrov
Revision Log:
Profile-coupon link and merge function
Files:
Legend:
Added
Removed
Modified
utf8/plugins/webshop/lib/webshop/Init.pm
18
18
webshop::SQL::Order
19
19
webshop::SQL::CouponsTable
20
20
webshop::SQL::CouponLinkTable
21
webshop::SQL::OrderCouponLinkTable
22
webshop::SQL::ProfileCouponLinkTable
21
23
22
24
webshop::Profile
23
25
…
…
37
39
webshop::PaymentSection
38
40
webshop::RegionSection
39
41
42
webshop::ProfileCouponLink
43
webshop::DiscountItemLink
40
44
webshop::CouponItemLink
41
webshop::DiscountItemLink
42
45
webshop::OrderCouponLink
46
webshop::ProfileCouponLink
43
47
44
48
webshop::Service::CDEK
45
49
));
…
…
47
51
sub init {
48
52
push @{ $state->{'available_documents'} }, qw(webshop::Basket webshop::Order webshop::Delivery webshop::Payment webshop::Coupon webshop::Discount webshop::Country webshop::Area webshop::Town webshop::Profile);
49
53
push @{ $state->{'available_sections'} }, qw(webshop::DeliverySection webshop::PaymentSection webshop::RegionSection);
50
push @{ $state->{'available_links'} }, qw(webshop::CouponItemLink webshop::OrderCouponLink);
54
push @{ $state->{'available_links'} }, qw(webshop::CouponItemLink webshop::OrderCouponLink webshop::ProfileCouponLink);
51
55
0;
52
56
}
53
57
utf8/plugins/webshop/lib/webshop/Keeper.pm
454
454
warn "Merge (coupons) begin: ".Dumper(\%opts)."\n" if $DEBUG;
455
455
return unless $opts{uid} && $opts{session};
456
456
457
my @items = $keeper->get_links (
457
my $merge_from = $keeper->get_links (
458
458
class => 'webshop::OrderCouponLink',
459
459
session => $opts{session},
460
460
source_id => 0,
461
return_mode => 'array_ref',
461
462
);
462
463
my $merge_to = $keeper->get_links (
463
464
class => 'webshop::OrderCouponLink',
464
465
uid => $opts{uid},
465
466
source_id => 0,
467
return_mode => 'hash_ref',
468
hash_by => 'dest_id',
469
);
470
my $profile_coupons_session = $keeper->get_links (
471
class => 'webshop::ProfileCouponLink',
472
session => $opts{session},
473
source_id => 0,
466
474
return_mode => 'array_ref',
467
475
);
468
my %cids = map{ $_->dest_id => 1 } (@items, @$merge_to);
469
my @cids = keys %cids;
470
my $coupons = @cids ? $keeper->get_documents(
471
class => 'webshop::Coupon',
472
id => \@cids,
476
my $profile_coupons_registered = $keeper->get_links (
477
class => 'webshop::ProfileCouponLink',
478
source_id => $opts{uid},
473
479
return_mode => 'hash_ref',
474
) : {};
475
my %merge_to;
476
foreach my $link ( @$merge_to ) {
477
my $coupon = $coupons->{$link->dest_id};
478
if ( !ref $coupon || !$coupon->is_accessible($opts{session}) ) {
479
$link->delete;
480
} elsif ( exists $merge_to{$link->dest_id} ) {
481
$link->delete;
482
} else {
483
$merge_to{$link->dest_id} = $link;
480
hash_by => 'dest_id',
481
);
482
483
### Merge coupon-order links
484
if ( ref $merge_from eq 'ARRAY' && @$merge_from ) {
485
foreach my $link ( @$merge_from ) {
486
if ( exists $merge_to->{$link->dest_id} ) {
487
$link->delete;
488
} else {
489
$link->session( undef );
490
$link->uid( $opts{uid} );
491
$link->store;
492
$merge_to->{$link->dest_id} = $link;
493
}
484
494
}
485
495
}
486
foreach my $item ( @items ) {
487
my $coupon = $coupons->{$item->dest_id};
488
if ( !ref $coupon || !$coupon->is_accessible($opts{session}) ) {
489
$item->delete;
490
} elsif ( exists $merge_to{$item->dest_id} ) {
491
$item->delete;
492
} else {
493
$item->session( undef );
494
$item->uid( $opts{uid} );
495
$item->store;
496
497
### Merge user-coupon links
498
if ( ref $profile_coupons_session eq 'ARRAY' && @$profile_coupons_session ) {
499
foreach my $link ( @$profile_coupons_session ) {
500
if ( exists $profile_coupons_registered->{$link->dest_id} ) {
501
$link->delete
502
} else {
503
$link->session( undef );
504
$link->source_id( $opts{uid} );
505
$link->store;
506
$profile_coupons_registered->{$link->dest_id} = $link;
507
}
496
508
}
497
509
}
510
511
my %cids = map { $_ => 1 } keys %$merge_to;
512
foreach my $cid ( keys %$profile_coupons_registered ) {
513
$cids{$cid} = 1;
514
}
515
my @cids = keys %cids;
516
517
if ( @cids ) {
518
my $coupons = @cids ? $keeper->get_documents(
519
class => 'webshop::Coupon',
520
id => \@cids,
521
return_mode => 'hash_ref',
522
) : {};
523
foreach my $link ( values %$merge_to ) {
524
unless ( exists $coupons->{$link->dest_id} ) {
525
$link->delete;
526
next;
527
}
528
my $coupon = $coupons->{$link->dest_id};
529
if ( !ref $coupon || !$coupon->is_accessible($opts{session}) ) {
530
$link->delete;
531
}
532
}
533
foreach my $link ( values %$profile_coupons_registered ) {
534
unless ( exists $coupons->{$link->dest_id} ) {
535
$link->delete;
536
next;
537
}
538
my $coupon = $coupons->{$link->dest_id};
539
if ( !ref $coupon || !$coupon->is_accessible($opts{session}) ) {
540
$link->delete;
541
}
542
}
543
}
544
498
545
}
499
546
500
547
utf8/plugins/webshop/lib/webshop/ProfileCouponLink.pm
1
package webshop::ProfileCouponLink;
2
3
use base 'Contenido::Link';
4
use Contenido::Globals;
5
6
sub class_name
7
{
8
return 'Пользовательские купоны';
9
}
10
11
sub class_description
12
{
13
return 'Купоны, принадлежащие пользователю';
14
}
15
16
sub extra_properties
17
{
18
return (
19
{ 'attr' => 'status', 'type' => 'status', 'rusname' => 'Статус купона пользователя',
20
'cases' => [
21
[0, 'недействительный'],
22
[1, 'действительный'],
23
],
24
},
25
);
26
}
27
28
sub class_table
29
{
30
return 'webshop::SQL::ProfileCouponLinkTable';
31
}
32
33
sub available_sources
34
{
35
return [ $state->{users}->profile_document_class ];
36
}
37
38
sub available_destinations
39
{
40
return [ qw(webshop::Coupon) ];
41
}
42
43
1;
utf8/plugins/webshop/lib/webshop/SQL/ProfileCouponLinkTable.pm
1
package webshop::SQL::ProfileCouponLinkTable;
2
3
use base 'SQL::LinkTable';
4
use Contenido::Globals;
5
6
sub db_table
7
{
8
return 'webshop_user_coupons';
9
}
10
11
my $available_filters = [qw(
12
13
_class_filter
14
_status_filter
15
_in_id_filter
16
_id_filter
17
_name_filter
18
_class_excludes_filter
19
_excludes_filter
20
_datetime_filter
21
_date_equal_filter
22
_date_filter
23
24
_dest_id_filter
25
_source_id_filter
26
_source_class_filter
27
_dest_class_filter
28
29
_session_filter
30
)];
31
32
sub available_filters {
33
return $available_filters;
34
}
35
36
37
# ----------------------------------------------------------------------------
38
# Свойства храним в массивах, потому что порядок важен!
39
# Это общие свойства - одинаковые для всех документов.
40
#
41
# attr - обязательный параметр, название атрибута;
42
# type - тип аттрибута, требуется для отображдения;
43
# rusname - русское название, опять же требуется для отображения;
44
# hidden - равен 1, когда
45
# readonly - инициализации при записи только без изменения в дальнейшем
46
# db_field - поле в таблице
47
# default - значение по умолчанию (поле всегда имеет это значение)
48
# ----------------------------------------------------------------------------
49
sub required_properties
50
{
51
my $self = shift;
52
53
my @parent_properties = $self->SUPER::required_properties;
54
return (
55
@parent_properties,
56
{ # ID Сессии
57
'attr' => 'session',
58
'type' => 'string',
59
'rusname' => 'ID Сессии пользователя',
60
'db_field' => 'session',
61
'db_type' => 'text',
62
},
63
);
64
}
65
66
########### FILTERS DESCRIPTION ###############################################################################
67
sub _session_filter {
68
my ($self,%opts)=@_;
69
return undef unless ( exists $opts{session} );
70
return &SQL::Common::_generic_text_filter('d.session', $opts{session});
71
}
72
73
1;
utf8/plugins/webshop/sql/TOAST/user_coupons.sql
1
CREATE TABLE webshop_user_coupons (
2
id integer DEFAULT nextval(('public.documents_id_seq'::text)::regclass) NOT NULL,
3
ctime timestamp without time zone DEFAULT now() NOT NULL,
4
mtime timestamp without time zone DEFAULT now() NOT NULL,
5
class text DEFAULT 'webshop::ProfileCouponLink'::text NOT NULL,
6
status smallint default 0 NOT NULL,
7
session text,
8
source_id integer,
9
source_class text,
10
dest_id integer,
11
dest_class text default 'webshop::Coupon'::text,
12
data text
13
);
14
15
CREATE INDEX webshop_user_coupons_session ON webshop_user_coupons USING btree (session) where session IS NOT NULL;
16
CREATE UNIQUE INDEX webshop_user_coupons_in_order ON webshop_user_coupons USING btree (source_id, dest_id) where source_id > 0;
17
create index webshop_user_coupons_source on webshop_user_coupons (source_id);
18
create index webshop_user_coupons_dest on webshop_user_coupons (dest_id);
Небольшая справка по веткам
cnddist – контейнер, в котором хранятся все дистрибутивы всех библиотек и программных пакетов, которые использовались при построении различных версий Contenido. Если какой-то библиотеки в данном хранилище нет, инсталлятор сделает попытку "подтянуть" ее с веба (например, с CPAN). Если библиотека слишком старая, есть очень большая вероятность, что ее там уже нет. Поэтому мы храним весь хлам от всех сборок. Если какой-то дистрибутив вдруг отсутствует в cnddist - напишите нам, мы положим его туда.
koi8 – отмирающая ветка, чей код, выдача и все внутренние библиотеки заточены на кодировку KOI8-R. Вносятся только те дополнения, которые касаются внешнего вида и функционала админки, баги ядра, обязательные обновления портов и мелочи, которые легко скопипастить. В дальнейшем планируется полная остановка поддержки по данной ветке.
utf8 – актуальная ветка, заточенная под UTF-8.
Внутри каждой ветки: core – исходники ядра; install – скрипт установки инсталляции; plugins – плагины; samples – "готовые к употреблению" проекты, которые можно поставить, запустить и посмотреть, как они работают.