Revision 694
Date:
2018/08/13 17:27:52
Author:
ahitrov
Revision Log:
Bug fixes
Files:
Legend:
Added
Removed
Modified
utf8/plugins/money/config.proto
11
11
DREAMKAS_ID =
12
12
DREAMKAS_SECRET = 123
13
13
DREAMKAS_CURRENCY_CODE = RUB
14
DREAMKAS_DEVICE_ID = NNNNN # ID кассового аппарата в системе Dreamkas
14
15
DREAMKAS_TAX_MODE = DEFAULT # DEFAULT or SIMPLE or SIMPLE_WO or ENVD or AGRICULT or PATENT
15
16
DREAMKAS_TAX_NDS = NDS_NO_TAX # NDS_NO_TAX or NDS_0 or NDS_10 or NDS_18 or NDS_20 or NDS_10_CALCULATED or NDS_18_CALCULATED
16
17
DREAMKAS_TEST_MODE = 1 # 0 - для боевого режима
utf8/plugins/money/lib/money/Check.pm
1
package money::Check;
2
3
use base "Contenido::Document";
4
sub extra_properties
5
{
6
return (
7
{ 'attr' => 'status', 'type' => 'status', 'rusname' => 'Статус тестирования',
8
'cases' => [
9
[0, 'Реальный чек'],
10
[1, 'Тестовый чек'],
11
],
12
},
13
)
14
}
15
16
sub class_name
17
{
18
return 'Money: онлайн-чек';
19
}
20
21
sub class_description
22
{
23
return 'Money: онлайн-чек';
24
}
25
26
sub class_table
27
{
28
return 'money::SQL::ChecksTable';
29
}
30
31
1;
utf8/plugins/money/lib/money/Init.pm
12
12
# money::SQL::SomeTable
13
13
# money::SomeClass
14
14
Contenido::Init::load_classes(qw(
15
money::SQL::MovementsTable
16
money::Movement
15
money::SQL::ChecksTable
16
money::Check
17
17
18
18
money::MovementSection
19
19
20
20
money::Provider::Base
21
money::Provider::Dreamkas
21
22
));
22
23
23
24
sub init {
utf8/plugins/money/lib/money/Movement.pm
1
package money::Movement;
2
3
use base "Contenido::Document";
4
sub extra_properties
5
{
6
return (
7
{ 'attr' => 'status', 'type' => 'status', 'rusname' => 'Статус тестирования',
8
'cases' => [
9
[0, 'Реальный чек'],
10
[1, 'Тестовый чек'],
11
],
12
},
13
)
14
}
15
16
sub class_name
17
{
18
return 'Money: онлайн-чек';
19
}
20
21
sub class_description
22
{
23
return 'Money: онлайн-чек';
24
}
25
26
sub class_table
27
{
28
return 'money::SQL::MovementsTable';
29
}
30
31
1;
utf8/plugins/money/lib/money/MovementSection.pm
19
19
20
20
sub class_description
21
21
{
22
return 'Money: Секция транзакций';чеков
22
return 'Money: Секция чеков';
23
23
}
24
24
25
25
1;
utf8/plugins/money/lib/money/Provider/Dreamkas.pm
74
74
75
75
$self->{currency} = $state->{money}{$prefix."_currency_code"};
76
76
77
$self->{$base_url} = 'https://'. ($self->{test_mode} ? 'private-anon-f6c2f7b545-kabinet.apiary-mock.com' : 'kabinet.dreamkas.ru').'/api';
77
$self->{base_url} = 'https://'. ($self->{test_mode} ? 'private-anon-f6c2f7b545-kabinet.apiary-mock.com' : 'kabinet.dreamkas.ru').'/api';
78
78
$self->{result} = {};
79
79
80
80
bless $self, $class;
…
…
189
189
190
190
my $MM;
191
191
if ( exists $opts->{order} ) {
192
$MM = $self->_GetLastMoneyMovement( $opts->{order}->id );
192
$MM = $self->_GetLastMoneyCheck( $opts->{order}->id );
193
193
}
194
194
if ( ref $MM && $MM->session_id && $MM->name eq $opts->{type} ) {
195
195
$self->{result}{money_movement} = $MM;
196
196
return $self;
197
197
}
198
198
unless ( $MM ) {
199
$MM = money::Movement->new( $keeper );
199
$MM = money::Check->new( $keeper );
200
200
$MM->name( $opts->{type} );
201
201
$MM->provider( $self->{prefix} );
202
202
$MM->status( $self->{test_mode} );
…
…
220
220
foreach my $bi ( @{$opts->{basket}} ) {
221
221
my $item = $bi->{item};
222
222
next unless ref $item;
223
my $price = int($bi->{item}->price * 100)
223
my $price = int($bi->{item}->price * 100);
224
224
my $pos = {
225
225
name => $bi->name,
226
226
type => 'COUNTABLE',
…
…
253
253
my $attributes = {};
254
254
if ( exists $opts->{email} && $opts->{email} ) {
255
255
if ( ref $opts->{email} ) {
256
$arrtibutes->{email} = $opts->{email}->name;
256
$attributes->{email} = $opts->{email}->name;
257
257
} else {
258
$arrtibutes->{email} = $opts->{email};
258
$attributes->{email} = $opts->{email};
259
259
}
260
260
}
261
261
if ( exists $opts->{phone} && $opts->{phone} ) {
262
262
if ( ref $opts->{phone} ) {
263
$arrtibutes->{phone} = $opts->{phone}->name;
263
$attributes->{phone} = $opts->{phone}->name;
264
264
} else {
265
$arrtibutes->{phone} = $opts->{phone};
265
$attributes->{phone} = $opts->{phone};
266
266
}
267
267
}
268
268
$data->{attributes} = $attributes;
…
…
286
286
$data->{payments}{type} = 'CASH';
287
287
}
288
288
289
my $api_url = '/api/receipts';
289
my $api_url = 'receipts';
290
290
291
291
$self->_MakeRequest( $api_url, 'post', $data );
292
292
if ( $self->{result}{code} == 202 ) {
…
…
346
346
} elsif ( exists $opts->{money_movement} ) {
347
347
$MM = $opts->{money_movement};
348
348
} elsif ( $opts->{operation_id} ) {
349
($MM) = $self->_GetMMByOperationId( $opts->{operation_id} );
349
($MM) = $self->_GetCheckByOperationId( $opts->{operation_id} );
350
350
}
351
351
unless ( ref $MM ) {
352
352
$self->{result}{error} = 'Не найден объект "движение денежных средств". Проверьте входные параметры';
353
353
return $self;
354
354
}
355
355
356
my $api_url = '/api/operations/'.$MM->session_id;
356
my $api_url = 'operations/'.$MM->session_id;
357
357
358
358
$self->_MakeRequest( $api_url, 'get' );
359
359
if ( $self->{result}{code} == 200 ) {
…
…
388
388
$body = encode_json( $body );
389
389
}
390
390
391
my $req = URI->new( $self->{host}.($url =~ /^\// ? '' : '/').$url );
391
my $req = URI->new( $self->{base_url}.($url =~ /^\// ? '' : '/').$url );
392
392
my $res;
393
393
if ( $type eq 'post' ) {
394
394
$res = $ua->post( $req, Content => $body );
…
…
401
401
code => $res->code,
402
402
status => $res->status_line,
403
403
content => JSON::XS->new->decode( $res->decoded_content ),
404
}
404
};
405
405
return $self;
406
406
}
407
407
408
sub _GetLastMoneyMovement {
408
sub _GetLastMoneyCheck {
409
409
my $self = shift;
410
410
my $order_id = shift;
411
411
my ($mm) = $keeper->get_documents(
412
class => 'money::Movement',
412
class => 'money::Check',
413
413
limit => 1,
414
order_id => $order_id
414
provider => $self->{prefix},
415
order_id => $order_id,
415
416
order_by => 'id desc',
416
417
);
417
418
return $mm;
418
419
}
419
420
420
sub _GetMMByOperationId {
421
sub _GetCheckByOperationId {
421
422
my $self = shift;
422
423
my $op_id = shift;
423
424
my ($mm) = $keeper->get_documents(
424
class => 'money::Movement',
425
class => 'money::Check',
425
426
limit => 1,
427
provider => $self->{prefix},
426
428
session_id => $op_id,
427
429
);
428
430
return $mm;
utf8/plugins/money/lib/money/SQL/ChecksTable.pm
1
package money::SQL::ChecksTable;
2
3
use base 'SQL::DocumentTable';
4
5
sub db_table
6
{
7
return 'money_movements';
8
}
9
10
sub db_id_sequence {
11
return 'money_movements_id_seq';
12
}
13
14
sub available_filters {
15
my @available_filters = qw(
16
17
_class_filter
18
_status_filter
19
_in_id_filter
20
_id_filter
21
_name_filter
22
_class_excludes_filter
23
_sfilter_filter
24
_excludes_filter
25
_datetime_filter
26
_date_equal_filter
27
_date_filter
28
_previous_days_filter
29
30
_provider_filter
31
_session_id_filter
32
_order_id_filter
33
_success_filter
34
_name_exact_filter
35
);
36
37
return \@available_filters;
38
}
39
40
# ----------------------------------------------------------------------------
41
# Свойства храним в массивах, потому что порядок важен!
42
# Это общие свойства - одинаковые для всех документов.
43
#
44
# attr - обязательный параметр, название атрибута;
45
# type - тип аттрибута, требуется для отображдения;
46
# rusname - русское название, опять же требуется для отображения;
47
# hidden - равен 1, когда
48
# readonly - инициализации при записи только без изменения в дальнейшем
49
# db_field - поле в таблице
50
# default - значение по умолчанию (поле всегда имеет это значение)
51
# ----------------------------------------------------------------------------
52
sub required_properties
53
{
54
my $self = shift;
55
56
my @parent_properties = grep { $_->{attr} ne 'sections' } $self->SUPER::required_properties;
57
return (
58
@parent_properties,
59
{
60
'attr' => 'provider',
61
'type' => 'string',
62
'rusname' => 'Провайдер',
63
'db_field' => 'provider',
64
'db_type' => 'text',
65
},
66
{
67
'attr' => 'session_id',
68
'type' => 'string',
69
'rusname' => 'Ключ сессии',
70
'db_field' => 'session_id',
71
'db_type' => 'text',
72
},
73
{ # ID заказа
74
'attr' => 'order_id',
75
'type' => 'integer',
76
'rusname' => 'ID заказа',
77
'db_field' => 'order_id',
78
'db_type' => 'integer',
79
'db_opts' => "not null",
80
},
81
{
82
'attr' => 'currency_code',
83
'type' => 'string',
84
'rusname' => 'ID валюты',
85
'db_field' => 'currency_code',
86
'db_type' => 'varchar(4)',
87
},
88
{
89
'attr' => 'sum',
90
'type' => 'string',
91
'rusname' => 'Сумма чека',
92
'db_field' => 'sum',
93
'db_type' => 'float',
94
},
95
{ # Результат транзакции
96
'attr' => 'success',
97
'type' => 'checkbox',
98
'rusname' => 'Транзакция прошла успешно',
99
'db_field' => 'success',
100
'db_type' => 'smallint',
101
'db_opts' => "default 0",
102
},
103
);
104
}
105
106
107
########### FILTERS DESCRIPTION ###############################################################################
108
sub _order_id_filter {
109
my ($self,%opts)=@_;
110
return undef unless ( exists $opts{order_id} );
111
return &SQL::Common::_generic_int_filter('d.order_id', $opts{order_id});
112
}
113
114
sub _success_filter {
115
my ($self,%opts)=@_;
116
return undef unless ( exists $opts{success} );
117
return &SQL::Common::_generic_int_filter('d.success', $opts{success});
118
}
119
120
sub _provider_filter {
121
my ($self,%opts)=@_;
122
return undef unless ( exists $opts{provider} );
123
return &SQL::Common::_generic_text_filter('d.provider', $opts{provider});
124
}
125
126
sub _session_id_filter {
127
my ($self,%opts)=@_;
128
return undef unless ( exists $opts{session_id} );
129
return &SQL::Common::_generic_int_filter('d.session_id', $opts{session_id});
130
}
131
132
sub _name_exact_filter {
133
my ($self,%opts)=@_;
134
return undef unless ( exists $opts{name_exact} );
135
return &SQL::Common::_generic_text_filter('d.name', $opts{name_exact});
136
}
137
138
1;
utf8/plugins/money/lib/money/SQL/MovementsTable.pm
1
package money::SQL::MovementsTable;
2
3
use base 'SQL::DocumentTable';
4
5
sub db_table
6
{
7
return 'money_movements';
8
}
9
10
sub db_id_sequence {
11
return 'money_movements_id_seq';
12
}
13
14
sub available_filters {
15
my @available_filters = qw(
16
17
_class_filter
18
_status_filter
19
_in_id_filter
20
_id_filter
21
_name_filter
22
_class_excludes_filter
23
_sfilter_filter
24
_excludes_filter
25
_datetime_filter
26
_date_equal_filter
27
_date_filter
28
_previous_days_filter
29
30
_provider_filter
31
_session_id_filter
32
_order_id_filter
33
_success_filter
34
_name_exact_filter
35
);
36
37
return \@available_filters;
38
}
39
40
# ----------------------------------------------------------------------------
41
# Свойства храним в массивах, потому что порядок важен!
42
# Это общие свойства - одинаковые для всех документов.
43
#
44
# attr - обязательный параметр, название атрибута;
45
# type - тип аттрибута, требуется для отображдения;
46
# rusname - русское название, опять же требуется для отображения;
47
# hidden - равен 1, когда
48
# readonly - инициализации при записи только без изменения в дальнейшем
49
# db_field - поле в таблице
50
# default - значение по умолчанию (поле всегда имеет это значение)
51
# ----------------------------------------------------------------------------
52
sub required_properties
53
{
54
my $self = shift;
55
56
my @parent_properties = grep { $_->{attr} ne 'sections' } $self->SUPER::required_properties;
57
return (
58
@parent_properties,
59
{
60
'attr' => 'provider',
61
'type' => 'string',
62
'rusname' => 'Провайдер',
63
'db_field' => 'provider',
64
'db_type' => 'text',
65
},
66
{
67
'attr' => 'session_id',
68
'type' => 'string',
69
'rusname' => 'Ключ сессии',
70
'db_field' => 'session_id',
71
'db_type' => 'text',
72
},
73
{ # ID заказа
74
'attr' => 'order_id',
75
'type' => 'integer',
76
'rusname' => 'ID заказа',
77
'db_field' => 'order_id',
78
'db_type' => 'integer',
79
'db_opts' => "not null",
80
},
81
{
82
'attr' => 'currency_code',
83
'type' => 'string',
84
'rusname' => 'ID валюты',
85
'db_field' => 'currency_code',
86
'db_type' => 'varchar(4)',
87
},
88
{
89
'attr' => 'sum',
90
'type' => 'string',
91
'rusname' => 'Сумма чека',
92
'db_field' => 'sum',
93
'db_type' => 'float',
94
},
95
{ # Результат транзакции
96
'attr' => 'success',
97
'type' => 'checkbox',
98
'rusname' => 'Транзакция прошла успешно',
99
'db_field' => 'success',
100
'db_type' => 'smallint',
101
'db_opts' => "default 0",
102
},
103
);
104
}
105
106
107
########### FILTERS DESCRIPTION ###############################################################################
108
sub _order_id_filter {
109
my ($self,%opts)=@_;
110
return undef unless ( exists $opts{order_id} );
111
return &SQL::Common::_generic_int_filter('d.order_id', $opts{order_id});
112
}
113
114
sub _success_filter {
115
my ($self,%opts)=@_;
116
return undef unless ( exists $opts{success} );
117
return &SQL::Common::_generic_int_filter('d.success', $opts{success});
118
}
119
120
sub _provider_filter {
121
my ($self,%opts)=@_;
122
return undef unless ( exists $opts{provider} );
123
return &SQL::Common::_generic_text_filter('d.provider', $opts{provider});
124
}
125
126
sub _session_id_filter {
127
my ($self,%opts)=@_;
128
return undef unless ( exists $opts{session_id} );
129
return &SQL::Common::_generic_int_filter('d.session_id', $opts{session_id});
130
}
131
132
sub _name_exact_filter {
133
my ($self,%opts)=@_;
134
return undef unless ( exists $opts{name_exact} );
135
return &SQL::Common::_generic_text_filter('d.name', $opts{name_exact});
136
}
137
138
1;
utf8/plugins/money/sql/TOAST/money_checks.sql
1
create sequence money_checks_id_seq;
2
select setval('money_checks_id_seq', 1, true);
3
4
create table money_checks
5
(
6
id integer not null primary key default nextval('public.documents_id_seq'::text),
7
class text not null,
8
ctime timestamp not null default now(),
9
mtime timestamp not null default now(),
10
dtime timestamp not null default now(),
11
status smallint not null default 0,
12
provider text,
13
session_id text,
14
name text,
15
order_id integer not null,
16
currency_code varchar(4),
17
sum float,
18
success smallint default 0,
19
data text
20
);
21
CREATE INDEX money_checks_sessions ON money_checks USING btree (provider, session_id) WHERE session_id is not null;
22
CREATE INDEX money_checks_orders ON money_checks USING btree (order_id);
utf8/plugins/money/sql/TOAST/money_movement.sql
1
create sequence money_movements_id_seq;
2
select setval('money_movements_id_seq', 1, true);
3
4
create table money_movements
5
(
6
id integer not null primary key default nextval('public.documents_id_seq'::text),
7
class text not null,
8
ctime timestamp not null default now(),
9
mtime timestamp not null default now(),
10
dtime timestamp not null default now(),
11
status smallint not null default 0,
12
provider text,
13
session_id text,
14
name text,
15
order_id integer not null,
16
currency_code varchar(4),
17
sum float,
18
success smallint default 0,
19
data text
20
);
21
CREATE INDEX money_movements_sessions ON money_movements USING btree (provider, session_id) WHERE session_id is not null;
22
CREATE INDEX money_movements_orders ON money_movements USING btree (order_id);
Небольшая справка по веткам
cnddist – контейнер, в котором хранятся все дистрибутивы всех библиотек и программных пакетов, которые использовались при построении различных версий Contenido. Если какой-то библиотеки в данном хранилище нет, инсталлятор сделает попытку "подтянуть" ее с веба (например, с CPAN). Если библиотека слишком старая, есть очень большая вероятность, что ее там уже нет. Поэтому мы храним весь хлам от всех сборок. Если какой-то дистрибутив вдруг отсутствует в cnddist - напишите нам, мы положим его туда.
koi8 – отмирающая ветка, чей код, выдача и все внутренние библиотеки заточены на кодировку KOI8-R. Вносятся только те дополнения, которые касаются внешнего вида и функционала админки, баги ядра, обязательные обновления портов и мелочи, которые легко скопипастить. В дальнейшем планируется полная остановка поддержки по данной ветке.
utf8 – актуальная ветка, заточенная под UTF-8.
Внутри каждой ветки: core – исходники ядра; install – скрипт установки инсталляции; plugins – плагины; samples – "готовые к употреблению" проекты, которые можно поставить, запустить и посмотреть, как они работают.