Revision 270
Date:
2013/02/01 20:48:45
Author:
ahitrov
Revision Log:
Coupon discounts workaround
Files:
Legend:
Added
Removed
Modified
utf8/plugins/webshop/comps/contenido/webshop/components/block_coupons.msn
1
<fieldset>
2
<legend>Актуальные купоны</legend>
3
4
<table width="100%" border="0" cellpadding="3" cellspacing="0" class="tlistdocs">
5
<tr class="<% $status == 1 ? 'inverted' : '' %>">
6
<td><a href="/contenido/webshop/coupons.html">Активные:</a></td>
7
<td><b><% $stats{1} || 0 %></b></td></tr>
8
<tr class="<% $status == 0 ? 'inverted' : '' %>">
9
<td><a href="/contenido/webshop/coupons.html?cst=0" style="<% $status == 0 ? '' : 'color:gray' %>">Неактивные:</a></td>
10
<td><b><% $stats{0} || 0 %></b></td></tr>
11
<tr class="<% $status == 3 ? 'inverted' : '' %>">
12
<td><a href="/contenido/webshop/coupons.html?cst=3" style="<% $status == 3 ? '' : 'color:black' %>">Использованные:</a></td>
13
<td><b><% $stats{3} || 0 %></b></td></tr>
14
<tr class="<% $status == 2 ? 'inverted' : '' %>">
15
<td><a href="/contenido/webshop/coupons.html?cst=2" style="<% $status == 2 ? '' : 'color:red' %>">Прототипы:</a></td>
16
<td><b><% $stats{2} || 0 %></b></td></tr>
17
<tr class="<% $status == 4 ? 'inverted' : '' %>">
18
<td><a href="/contenido/webshop/coupons.html?cst=4" style="<% $status == 4 ? '' : 'color:olive' %>">Обработанные:</a></td>
19
<td><b><% $stats{4} || 0 %></b></td></tr>
20
</table>
21
22
<table cellspacing="2" cellpadding="0" border="0" class="tform">
23
<tr><td height="3"></td></tr>
24
<tr><td colspan="2"><b><a href="/contenido/webshop/coupon.html">Зарегистрировать »</a></b></td></tr>
25
26
</table>
27
28
</fieldset>
29
<%args>
30
31
$status => undef
32
33
</%args>
34
<%init>
35
36
my $now = Contenido::DateTime->new;
37
my $sql = $keeper->SQL->prepare( 'select status, count(id) as cnt from webshop_coupons where ? <= etime group by status' );
38
$sql->execute( $now->ymd('-').' '.$now->hms );
39
40
my %stats;
41
while ( my $ln = $sql->fetchrow_hashref ) {
42
$stats{$ln->{status}} = $ln->{cnt};
43
}
44
# my $active = $keeper->get_documents (
45
# class => 'webshop::Coupon',
46
# status => 1,
47
# count => 1,
48
# interval => [$now, $now],
49
# );
50
# my $used = $keeper->get_documents (
51
# class => 'webshop::Coupon',
52
# status => 3,
53
# count => 1,
54
# interval => [$now, $now],
55
# );
56
57
</%init>
utf8/plugins/webshop/comps/contenido/webshop/components/coupon_browse.msn
1
<script type="text/javascript">
2
<!--
3
var aIDs = new Array (<% scalar @$documents ? join (',', map { $_->id } @$documents) : '' %>);
4
function delete_check () {
5
for ( var i = 0; i < aIDs.length; i++ ) {
6
var sFieldName = 'delete_' + aIDs[i] + '_id';
7
var oField = document.forms['section_browse'].elements[sFieldName];
8
if ( oField.checked ) {
9
oField.checked = 0;
10
} else {
11
oField.checked = 1;
12
}
13
}
14
}
15
//-->
16
</script>
17
<form name="section_browse" action="coupons.html" method="POST">
18
<table width="100%" border="0" cellpadding="4" cellspacing="0" class="tlistdocs">
19
<tr bgcolor="#efefef">
20
<th><a href="javascript:delete_check()" onclick="delete_check(); return false;"><img src="/contenido/i/actions/delete.gif" width="14" height="17" alt="Удаление документов" align="absmiddle" border="0" hspace="1"></a></th>
21
%
22
% foreach (@$columns) {
23
<th><% $_->{shortname} || $_->{rusname} %></th>
24
% }
25
%
26
</tr>
27
%
28
% unless (@$documents) {
29
<tr><td align="center" colspan="<% scalar @$columns %>">Документы не найдены</td></tr>
30
% }
31
% foreach my $document (@$documents) {
32
%
33
% next unless ref($document);
34
% my $document_access = $user->section_accesses($user, $document->section);
35
%
36
<tr valign="top">
37
<td nowrap>\
38
% if ($document_access == 2) {
39
% $delete_status = 1;
40
<input type="checkbox" name="<% 'delete_'.$document->id.'_id' %>">
41
% } else {
42
43
% }
44
</td>
45
%
46
% for my $col (@$columns) {
47
% if ($col->{attr} eq '_sort_') {
48
%
49
<td width="20px"><% $document->{sorder} %> <a
50
href="document_move.html?id=<% $document->{id} %>&move=up&s=<% $id %><% $params_unsection ? '&'.$params_unsection : '' %>"><img
51
src="/contenido/i/ico-up-9x10.gif" border=0 alt="Переместить документ на шаг вверх"></a> <a
52
href="document_move.html?id=<% $document->{id} %>&move=down&s=<% $id %><% $params_unsection ? '&'.$params_unsection : '' %>"><img
53
src="/contenido/i/ico-down-9x10.gif" border=0 alt="Переместить документ на шаг вниз"></a>\
54
%
55
% } elsif ($col->{attr} eq 'dtime') {
56
%
57
<td nowrap><& "/contenido/components/show_dtime.msn", dtime=>$document->{dtime} &>\
58
%
59
% } elsif ($col->{attr} eq 'name') {
60
%
61
<td><span<% $document->contenido_status_style ? ' style="' . $document->contenido_status_style . '"' : '' %> class="<% $document->status ? '':'hiddensect' %>">\
62
%
63
% my $name=$document->name ? $document->name : 'Безымянный документ N'.$document->id;
64
% if ($document_access == 2) {
65
%
66
<a<% $document->contenido_status_style ? ' style="' . $document->contenido_status_style . '"' : '' %> href="./coupon.html?id=<% $document->id %><% $params_unclassed ? '&'.$params_unclassed : '' %>"><% $name | h %> </a>\
67
%
68
% } else {
69
%
70
<% $name | h %> \
71
%
72
% }
73
%
74
</span>\
75
%
76
% } elsif ($col->{attr} eq 'id') {
77
%
78
<td><span class="<% $document->status ? '':'hiddensect' %>">\
79
% if ($document_access == 2) {
80
<a href="document.html?id=<% $document->id %>&class=<% $document->class %><% $params_unclassed ? '&'.$params_unclassed : '' %>"><% $document->id %> </a>\
81
% } else {
82
<% $document->id %> \
83
% }
84
</span>\
85
%
86
% } elsif ( exists $col->{inline} && $col->{inline} ) {
87
% $inline_status = 1;
88
% my $attr = $col->{attr};
89
% if ( $col->{type} =~ /^(string|integer|float)$/ && $col->{inline} ) {
90
% my $style = $col->{inline_style} ? $col->{inline_style} : ($col->{type} =~ /^(integer|float)$/ ? 'text-align:right; ' : '' );
91
<td><input type="text" name="<% 'update_'.$document->id.'_'.$attr %>" value="<% $document->$attr %>" style="<% $col->{inline_width} ? 'width:'.$col->{inline_width}.'px;' : 'width:65px; ' %> <% $col->{inline_style} || '' %>">
92
% } elsif ($col->{type} eq 'checkbox') {
93
% my $checked = $document->$attr ? ' checked' : '';
94
<td align="center"><input type="checkbox" name="<% 'update_'.$document->id.'_'.$attr %>"<% $checked %>>
95
% } elsif ($col->{type} eq 'select') {
96
% my $options = {};
97
% if ($toopi && (ref($toopi) eq 'HASH') && (exists($toopi->{$document->class}))) {
98
% %{ $options } = %{ $toopi->{$document->class} };
99
% }
100
% my $values = $options->{$col->{attr}};
101
<td><select name="<% 'update_'.$document->id.'_'.$attr %>">
102
% if ( ref $values eq 'ARRAY' ) {
103
% foreach my $val ( @$values ) {
104
% my $selected = $val eq $document->$attr ? ' selected' : '';
105
<option value="<% $val %>"<% $selected %>><% $val %>
106
% }
107
% }
108
</select>
109
% } elsif ($col->{type} eq 'status') {
110
% my $cases = $col->{cases};
111
% if ( ref $cases eq 'ARRAY' ) {
112
<td><select name="<% 'update_'.$document->id.'_'.$attr %>" style="<% $col->{inline_width} ? 'width:'.$col->{inline_width}.'px;' : '' %> <% $col->{inline_style} || '' %>">
113
% foreach my $case ( @$cases ) {
114
% my $selected = $case->[0] eq $document->$attr ? ' selected' : '';
115
<option value="<% $case->[0] %>"<% $selected %>><% $case->[1] %>
116
% }
117
</select>
118
% }
119
% }
120
%
121
% } elsif ($col->{attr} eq 'class') {
122
%
123
<td><% $document->class_name %> <font color="#999999">(<% $document->class %>)</font>\
124
%
125
% } elsif ($col->{type} eq 'datetime') {
126
%
127
<td nowrap><& "/contenido/components/show_dtime.msn", dtime=>$document->{$col->{attr}} &>\
128
%
129
% } elsif ($col->{attr} eq '_act_') {
130
%
131
<td nowrap>\
132
% if ($document_access == 2) {
133
%
134
<a href="./coupon.html?id=<% $document->id %><% $params_unclassed ? '&'.$params_unclassed : '' %>" title="Редактировать документ"><img
135
src="/contenido/i/actions/edit.gif" width="15" height="17" alt="Редактировать документ" align="absmiddle" border="0" hspace="1">редактировать</a>\
136
%#<a href="confirm.html?id=<% $document->id %>&action=documents_deletion&from=<% $section->id %>&class=<% $document->class %><% $params_unclassed ? '&'.$params_unclassed : '' %>" title="Удалить документ"><img
137
%# src="/contenido/i/actions/delete.gif" width="14" height="17" alt="Удалить документ" align="absmiddle" border="0" hspace="1"></a>\
138
<br>\
139
%
140
% } else {
141
\
142
% }
143
% if ( $inline_status ) {
144
<input type="hidden" name="update_<% $document->id %>_class" value="<% $document->class %>">
145
% }
146
% if ( $delete_status ) {
147
<input type="hidden" name="delete_<% $document->id %>_class" value="<% $document->class %>">
148
% }
149
%
150
% } else {
151
%
152
% if ($col->{type} eq 'date') {
153
% my $date=$document->{$col->{attr}};
154
% $date=~/(\d{4}-\d{2}-\d{2})/;
155
<td nowrap align="right"><% $1 || ' ' %>\
156
% } elsif ($col->{type} eq 'datetime') {
157
<td nowrap align="right"><% $document->{$col->{attr}} || ' ' %>\
158
% } elsif ($col->{type} eq 'integer') {
159
<td align="right"><% $document->{$col->{attr}} %> \
160
% } elsif ($col->{type} eq 'lookup' || $col->{type} eq 'pickup') {
161
<td align="left">\
162
% my $id = $document->{$col->{attr}};
163
% if ($id) {
164
% my ($doc)=$keeper->get_documents( ( ref($col->{lookup_opts}) ? %{$col->{lookup_opts}} : () ), id=>$id);
165
% if ($doc) {
166
<a href="document.html?id=<% $doc->id %>&class=<% $doc->class %><% $params_unclassed ? '&'.$params_unclassed : '' %>"><% $doc->name || $doc->id %></a> \
167
% } else {
168
<span class="hiddensect"><% $document->{$col->{attr}} %>???</span>\
169
% }
170
% } else {
171
<span class="hiddensect">NULL</span>\
172
% }
173
% } elsif ($col->{type} eq 'status') {
174
% my $status_map = ref $col->{cases} eq 'ARRAY' ? $col->{cases} : $keeper->default_status();
175
% my ($doc_status) = grep { $_->[0] eq $document->{$col->{attr}} } @$status_map;
176
% $doc_status ||= [$document->{$col->{attr}}, 'Неизвестный'];
177
<td nowrap><% $doc_status->[1].'('.$doc_status->[0].')' %>\
178
% } else {
179
<td><% defined($document->{$col->{attr}}) ? $document->{$col->{attr}} : ' ' %>\
180
% }
181
% }
182
</td>
183
%
184
% } #- for @columns
185
%
186
</tr>
187
% } #- foreach @documents
188
</table>
189
% if ( ref $filter eq 'HASH' ) {
190
% while ( my ($key, $value) = each %$filter ) {
191
% next if $key eq 's';
192
<input type="hidden" name="<% $key %>" value="<% $value |h %>">
193
% }
194
% }
195
% if ( $inline_status || $delete_status ) {
196
<div style="text-align:right; padding:10px 0;">
197
% if ( $inline_status ) {
198
<input type="submit" name="update" value="Сохранить изменения" class="input_btn">
199
% }
200
% if ( $delete_status ) {
201
<input type="submit" name="delete" value="Удалить выбранные" class="input_btn" onclick="return confirm('Все отмеченные позиции будут удалены');">
202
% }
203
</div>
204
% }
205
</form>
206
<%args>
207
208
$documents => undef
209
$columns => undef
210
$id => undef
211
$filter => undef
212
213
</%args>
214
<%init>
215
216
return unless ref $documents eq 'ARRAY';
217
return unless ref $columns eq 'ARRAY';
218
219
my $toopi = $project->documents();
220
my $inline_status = 0;
221
my $delete_status = 0;
222
my $params = ref $filter eq 'HASH' ? join ('&', map { $_.'='.$filter->{$_} } keys %$filter ) : '';
223
my $params_unclassed = ref $filter eq 'HASH' ? join ('&', map { $_.'='.$filter->{$_} } grep { $_ ne 'class' } keys %$filter ) : '';
224
my $params_unsection = ref $filter eq 'HASH' ? join ('&', map { $_.'='.$filter->{$_} } grep { $_ ne 's' } keys %$filter ) : '';
225
226
227
</%init>
utf8/plugins/webshop/comps/contenido/webshop/components/order_browse.msn
48
48
<td><span<% $style %> class="<% $document->status ? '':'hiddensect' %>"><% $a1.$document->id.$a2 %></span></td>
49
49
%
50
50
% for my $col (@$columns) {
51
% if ($col->{attr} eq 'dtime') {
51
% my $attr = $col->{attr};
52
% if ($attr eq 'dtime') {
52
53
%
53
54
<td nowrap><& "/contenido/components/show_dtime.msn", dtime=>$document->{dtime} &>\
54
55
% if ($document->{ctime} ne $document->{mtime}) {
…
…
56
57
<div style="color:<% $colortime %>;"><& "/contenido/components/show_dtime.msn", dtime=>$document->{mtime} &></div>\
57
58
% }
58
59
%
59
% } elsif ($col->{attr} eq 'name') {
60
% } elsif ($attr eq 'name') {
60
61
<td><span<% $style %> class="<% $document->status ? '':'hiddensect' %>">\
61
62
%
62
63
% my $name=$document->name ? $document->name : 'Безымянный документ N'.$document->id;
…
…
64
65
%
65
66
</span>\
66
67
%
67
% } elsif ($col->{attr} eq 'id') {
68
% } elsif ($attr eq 'id') {
68
69
%
69
70
<td><span class="<% $document->status ? '':'hiddensect' %>">\
70
71
% if ($document_access == 2) {
…
…
76
77
%
77
78
% } elsif ( exists $col->{inline} && $col->{inline} ) {
78
79
% $inline_status = 1;
79
% my $attr = $col->{attr};
80
80
% if ( $col->{type} =~ /^(string|integer|float)$/ && $col->{inline} ) {
81
81
% my $style = $col->{inline_style} ? $col->{inline_style} : ($col->{type} =~ /^(integer|float)$/ ? 'text-align:right; ' : '' );
82
82
<td><input type="text" name="<% 'update_'.$document->id.'_'.$attr %>" value="<% $document->$attr %>" style="<% $col->{inline_width} ? 'width:'.$col->{inline_width}.'px;' : 'width:65px; ' %> <% $col->{inline_style} || '' %>">
…
…
88
88
% if ($toopi && (ref($toopi) eq 'HASH') && (exists($toopi->{$document->class}))) {
89
89
% %{ $options } = %{ $toopi->{$document->class} };
90
90
% }
91
% my $values = $options->{$col->{attr}};
91
% my $values = $options->{$attr};
92
92
<td><select name="<% 'update_'.$document->id.'_'.$attr %>">
93
93
% if ( ref $values eq 'ARRAY' ) {
94
94
% foreach my $val ( @$values ) {
…
…
109
109
% }
110
110
% }
111
111
%
112
% } elsif ($col->{attr} eq 'class') {
112
% } elsif ($attr eq 'class') {
113
113
%
114
114
<td><% $document->class_name %> <font color="#999999">(<% $document->class %>)</font>\
115
115
%
116
116
% } elsif ($col->{type} eq 'datetime') {
117
117
%
118
<td nowrap><& "/contenido/components/show_dtime.msn", dtime=>$document->{$col->{attr}} &>\
118
<td nowrap><& "/contenido/components/show_dtime.msn", dtime=>$document->$attr &>\
119
119
%
120
% } elsif ($col->{attr} eq '_act_') {
120
% } elsif ($attr eq '_act_') {
121
121
% my $actions;
122
122
% if ( $document->$user_id == $user->id ) {
123
123
% $actions = join (' | ', map { '<a href="./'.$_->{href}.'?id='.$document->id.($params_unclassed ? '&'.$params_unclassed : '').'">'.$_->{name}.'</a>' } @actions);
…
…
144
144
% } else {
145
145
%
146
146
% if ($col->{type} eq 'date') {
147
% my $date=$document->{$col->{attr}};
147
% my $date=$document->$attr;
148
148
% $date=~/(\d{4}-\d{2}-\d{2})/;
149
149
<td nowrap align="right"><% $1 || ' ' %>\
150
150
% } elsif ($col->{type} eq 'datetime') {
151
<td nowrap align="right"><% $document->{$col->{attr}} || ' ' %>\
151
<td nowrap align="right"><% $document->$attr || ' ' %>\
152
152
% } elsif ($col->{type} eq 'integer') {
153
<td align="right"><% $document->{$col->{attr}} %> \
153
<td align="right"><% $document->$attr %> \
154
154
% } elsif ($col->{type} eq 'lookup') {
155
155
<td align="left">\
156
% my $id = $document->{$col->{attr}};
156
% my $id = $document->$attr;
157
157
% if ($id) {
158
158
% my ($doc)=$keeper->get_documents( ( ref($col->{lookup_opts}) ? %{$col->{lookup_opts}} : () ), id=>$id);
159
159
% if ($doc) {
160
<a href="document.html?id=<% $doc->id %>&class=<% $doc->class %><% $params_unclassed ? '&'.$params_unclassed : '' %>"><% $doc->name || $doc->id %></a> \
160
<a href="/contenido/document.html?id=<% $doc->id %>&class=<% $doc->class %><% $params_unclassed ? '&'.$params_unclassed : '' %>"><% $doc->name || $doc->id %></a> \
161
161
% } else {
162
<span class="hiddensect"><% $document->{$col->{attr}} %>???</span>\
162
<span class="hiddensect"><% $document->$attr %>???</span>\
163
163
% }
164
164
% } else {
165
165
<span class="hiddensect">NULL</span>\
166
166
% }
167
167
% } elsif ($col->{type} eq 'status') {
168
168
% my $status_map = ref $col->{cases} eq 'ARRAY' ? $col->{cases} : $keeper->default_status();
169
% my ($doc_status) = grep { $_->[0] eq $document->{$col->{attr}} } @$status_map;
170
% $doc_status ||= [$document->{$col->{attr}}, 'Неизвестный'];
169
% my ($doc_status) = grep { $_->[0] eq $document->$attr } @$status_map;
170
% $doc_status ||= [$document->$attr, 'Неизвестный'];
171
171
<td nowrap><% $doc_status->[1].'('.$doc_status->[0].')' %>\
172
172
% } else {
173
<td><% defined($document->{$col->{attr}}) ? $document->{$col->{attr}} : ' ' %>\
173
<td><% defined($document->$attr) ? $document->$attr : ' ' %>\
174
174
% }
175
175
% }
176
176
</td>
utf8/plugins/webshop/comps/contenido/webshop/components/order_list.msn
54
54
<td class="number" colspan="2"><b class="blue"><% $order->sum_delivery %></b></td>
55
55
</tr>
56
56
<tr>
57
<td colspan="5" align="right"><b>Скидка</b></td>
58
<td class="number" colspan="2"><b class="blue"><% $order->sum_discount || 0 %></b></td>
59
</tr>
60
<tr>
57
61
<td colspan="5" align="right"><b>Итого с доставкой</b></td>
58
<td class="number" colspan="2"><b class="blue"><% $total_sum + ($order->sum_delivery || 0) %></b></td>
62
<td class="number" colspan="2"><b class="blue"><% $order->sum_total %></b></td>
59
63
</tr>
60
64
61
65
</table>
…
…
69
73
% }
70
74
% }
71
75
<input type="hidden" name="id" value="<% $order->id %>">
76
</form>
72
77
78
% if ( @coupons ) {
79
<table width="100%" border="0" cellpadding="4" cellspacing="0" class="tlistdocs" bgcolor="white">
80
<tr bgcolor="#efefef">
81
<th>Использованные купоны</th>
82
<th>Скидка</th>
83
</tr>
73
84
74
</form>
85
% foreach my $coupon ( @coupons ) {
86
<tr>
87
<td><% $coupon->code %></td>
88
<td class="number" colspan="2"><b class="blue"><% $coupon->discount %></b></td>
89
</tr>
90
% }
91
92
</table>
93
% }
94
75
95
% } else {
76
96
<div style="color:red; font-size:110%; margin:20px 0;">Заказ пустой</div>
77
97
% }
…
…
93
113
my $total_sum = 0;
94
114
my $total_num = 0;
95
115
116
my @coupons = $keeper->get_documents (
117
class => 'webshop::Coupon',
118
lclass => 'webshop::OrderCouponLink',
119
lsource => $order->id,
120
) if $order->sum_discount;
121
96
122
</%init>
utf8/plugins/webshop/comps/contenido/webshop/coupon.html
1
<& "/contenido/components/header.msn" &>
2
3
<% spacer(h=>10) %>
4
5
% if ($error) {
6
<div align="center" style="font-size:110%; color:red;">
7
<% $error %>
8
</div>
9
<br><br>
10
% }
11
12
<!-- Форма для редактирования объекта -->
13
<a name="top"></a>
14
<form action="" method="POST" name="form" onSubmit="">
15
16
<table border="0" width="100%" cellspacing="0" cellpadding="6">
17
<tr>
18
<td style="font-size:110%;">
19
<b><% $object->id ? 'Редактирование' : 'Регистрация нового' %> купона</b>
20
</td>
21
<td align="right">
22
<input type="submit" value="Сохранить" class="input_btn">
23
</td>
24
</tr>
25
</table>
26
27
28
<center>
29
<table width="100%" cellpadding="0" cellspacing="0" border="0">
30
<tr>
31
<td bgcolor="#999999">
32
<table width="100%" cellpadding="0" cellspacing="1" border="0">
33
<tr><td valign="top" bgcolor="#ffffff" width="80%">
34
<center>
35
36
<table width="98%" cellpadding="1" cellspacing="0" border="0">
37
38
% foreach my $prop ( @props ) {
39
% next if $prop->{hidden} == 1;
40
% next if $prop->{attr} eq 'uid_proto' || $prop->{attr} eq 'uid_condition';
41
% my $name = $prop->{attr};
42
% my $type = $prop->{type};
43
<tr><td height="8"></td></tr>
44
<tr><td nowrap>
45
<table cellpadding="0" cellspacing="0" border="0">
46
<tr>
47
<td nowrap><b><% $prop->{rusname} %></b> /</td>
48
<td align="right" nowrap><font color="#888888" size="-1"> name="<% $prop->{attr} %>"</font></td>
49
% if( $prop->{readonly} ) {
50
<td align="right" nowrap> / <font color="#CC0000" size="-1">Значение нельзя изменить</font></td>
51
% }
52
</tr>
53
</table>
54
</td></tr>
55
% if ( $prop->{attr} eq 'uid' ) {
56
% #### Прототипирование
57
% ###################################################################
58
<script type="text/javascript">
59
<!--
60
function change_uid_proto ( oSelect ) {
61
var sUidDiv = document.getElementById('uid_block');
62
var sUidEq = document.getElementById('uid_equation');
63
if ( oSelect.value == '3' ) {
64
sUidDiv.style.display = 'block';
65
} else {
66
sUidDiv.style.display = 'none';
67
uid_undo();
68
}
69
if ( oSelect.value == '2' ) {
70
sUidEq.style.display = 'block';
71
} else {
72
sUidEq.style.display = 'none';
73
uid_undo();
74
}
75
}
76
//-->
77
</script>
78
<tr><td style="border:1px solid gray; padding:5px; background:#e0ffe0">
79
80
<select name="uid_proto" onchange="change_uid_proto(this)">
81
<option value="0">Купон доступен всем пользователям</option>
82
<option value="1" <% $object->uid_proto == 1 ? 'selected' : '' %>>Одноразовый купон, создается для всех зарегистрированных пользователей</option>
83
<option value="2" <% $object->uid_proto == 2 ? 'selected' : '' %>>Одноразовый купон, пользователи по выбору</option>
84
<option value="3" <% $object->uid_proto == 3 ? 'selected' : '' %>>Одноразовый купон для одного пользователя</option>
85
</select>
86
87
<div id="uid_block" style="padding-top:10px; display:<% $object->uid_proto == 3 ? 'block' : 'none' %>;">
88
<fieldset>
89
<legend>Выберите пользователя, нажав на знак бинокля</legend>
90
<& "/contenido/components/inputs/$type.msn", prop => $prop, object => $object, name => $name,
91
options => $options, id => ($object->id() || 0),
92
check => ($object->$name || $object->{$name}),
93
&>
94
</fieldset>
95
</div>
96
97
98
<div id="uid_equation" style="padding-top:10px; display:<% $object->uid_proto == 2 ? 'block' : 'none' %>;">
99
<fieldset>
100
<legend>Выберите условие:</legend>
101
102
<table width="100%" cellspacing="0" cellpadding="3">
103
104
<tr valign="top" bgcolor="white">
105
<td width="1%"><input type="radio" name="uid_condition" value="positive"
106
<% $object->uid_condition && $object->uid_condition eq 'loyal' ? 'checked' : '' %>></td>
107
<td width="49%">Все пользователи с позитивной историей</td>
108
<td width="50%"></td>
109
</tr>
110
111
<tr valign="top" bgcolor="#e0e0e0">
112
<td><input type="radio" name="uid_condition" value="total"
113
<% $object->uid_condition && $object->uid_condition =~ /^from/ ? 'checked' : '' %>></td>
114
<td>Все пользователи, купившие на сумму более:</td>
115
<td><input type="text" name="uid_condition.total" style="width:95%"
116
value="<% $object->uid_condition && $object->uid_condition =~ /^from (\d+)/ ? $1 : '' %>"></td>
117
</tr>
118
119
<tr valign="top" bgcolor="white">
120
<td><input type="radio" name="uid_condition" value="activity"
121
<% $object->uid_condition && $object->uid_condition =~ /^last/ ? 'checked' : '' %>></td>
122
<td>Все пользователи, проявлявшие активность за:</td>
123
% my $activity = $object->uid_condition && $object->uid_condition =~ /^last (\w+)/ ? $1 : '';
124
<td><select name="uid_condition.period">
125
<option value="month">месяц</option>
126
<option value="quarter" <% $activity eq 'quarter' ? 'selected' : '' %>>3 месяца</option>
127
<option value="half" <% $activity eq 'half' ? 'selected' : '' %>>6 месяцев</option>
128
<option value="year" <% $activity eq 'year' ? 'selected' : '' %>>год</option>
129
</select></td>
130
</tr>
131
132
</table>
133
</fieldset>
134
</div>
135
136
</div></td></tr>
137
% #### /Прототипирование
138
139
% } else {
140
<tr><td><& "/contenido/components/inputs/$type.msn", prop => $prop, object => $object, name => $name,
141
options => $options, id => ($object->id() || 0),
142
check => ($object->$name || $object->{$name}),
143
&></td></tr>
144
% }
145
% }
146
147
<tr>
148
<td><br>
149
% foreach my $prop ( @props ) {
150
% next if $prop->{hidden} != 1;
151
<input type="hidden" name="<% $prop->{attr} %>" value="<% html_escape($object->{ $prop->{attr} }) %>">
152
% }
153
<input type="hidden" name="save" value="1">
154
</td>
155
</tr>
156
</table>
157
158
</td>
159
<td valign="top" bgcolor="#efefef">
160
<% spacer(w=>270) %>
161
<div><iframe name="DocFinder" id="DocFinder" src="/contenido/find_document.html" frameborder="0"
162
marginheight="0" marginwidth="0" width="100%" height="0"></iframe></div>
163
164
</td>
165
</tr>
166
</table>
167
</center>
168
169
</td></tr></table>
170
</center>
171
172
<div align="center">
173
<input type="submit" value="Сохранить" class="input_btn">
174
% while ( my ($key, $value) = each %filter_params ) {
175
% next if exists $props{$key};
176
<input type="hidden" name="<% $key %>" value="<% $value %>">
177
% }
178
<input type="hidden" name="control_charset" value="Контроль">
179
<input type="submit" name="_save_and_leave" value="Сохранить и выйти" class="input_btn">
180
</div>
181
182
</form>
183
184
%#<pre><% Dumper($object) %></pre>
185
186
</body>
187
</html>
188
<%args>
189
190
$id => undef
191
$save => undef
192
193
</%args>
194
<%INIT>
195
196
my $error='';
197
198
my %filter_params;
199
my $return_params = join ('&', map { $_.'='.$filter_params{$_} } grep { $_ ne 's' } keys %filter_params );
200
201
my $object = $id ? webshop::Coupon->new( $keeper, $id ) : webshop::Coupon->new( $keeper );
202
my @props = $object->structure;
203
my %props = map { $_->{attr} => $_ } @props;
204
205
my $toopi = $project->documents();
206
my $options = {};
207
if ($toopi && (ref($toopi) eq 'HASH') && (exists($toopi->{ $object->class }))) {
208
%{ $options } = %{ $toopi->{ $object->class } };
209
}
210
211
if ( $save ) {
212
foreach my $prop ( @props ) {
213
my $name = $prop->{attr};
214
my $type = $prop->{type};
215
216
next if ($name eq 'sections' || $name eq 'id' || $name eq 'uid' || $name eq 'uid_condition');
217
218
if ( $name eq 'uid_proto' ) {
219
my $uid_proto = $ARGS{uid_proto} || 0;
220
$object->uid_proto( $uid_proto );
221
if ( $uid_proto == 3 ) {
222
$object->uid( $ARGS{uid} );
223
} elsif ( $uid_proto == 1 ) {
224
$object->uid( 0 );
225
$object->uid_condition( 'all' );
226
} elsif ( $uid_proto == 2 ) {
227
$object->uid( 0 );
228
if ( $ARGS{uid_condition} eq 'total' ) {
229
$object->uid_condition( 'from '.($ARGS{"uid_condition.total"} || 0) );
230
} elsif ( $ARGS{uid_condition} eq 'activity' ) {
231
$object->uid_condition( 'last '.($ARGS{"uid_condition.period"} || 'month') );
232
} else {
233
$object->uid_condition( 'loyal' );
234
}
235
}
236
} elsif ($m->comp_exists("/contenido/components/outputs/$type.msn")) {
237
$object->{$name} = $m->comp("/contenido/components/outputs/$type.msn", SETS => \%ARGS, name => $name, object => $object );
238
} else {
239
$object->{$name} = $m->comp('/contenido/components/filter.msn', str => $ARGS{$name} );
240
}
241
}
242
$object->pid( 0 ) unless defined $object->pid;
243
unless ( $object->id ) {
244
$object->status( 2 ) if $object->status && $object->uid_proto;
245
}
246
if ( !$object->code && ($object->uid_proto == 3 || $object->uid_proto == 0 ) ) {
247
my $code = `apg -q -d -a 1 -m 8 -x 8 -M nc -n 1`;
248
$object->code( $code );
249
}
250
unless ( $object->store ) {
251
$error="Ошибка сохранения ($keeper->{last_error})";
252
} else {
253
if ($ARGS{_save_and_leave}) {
254
$m->redirect('/contenido/webshop/coupons.html?cst='.$object->status);
255
} else {
256
$m->redirect('coupon.html?id='.$object->id.($return_params ? '&'.$return_params : ''));
257
}
258
}
259
}
260
261
</%INIT>
utf8/plugins/webshop/comps/contenido/webshop/coupons.html
1
<& "/contenido/components/header.msn" &>
2
3
<style>
4
<!--
5
.inverted td { font-weight:bold; color:white; background-color:#8093B0; }
6
.inverted td a { color:white; font-weight:bold; }
7
//-->
8
</style>
9
10
<% spacer( h=>10 ) %>
11
12
<table width="100%" cellspacing="0" cellpadding="0" border="0">
13
<tr valign="top">
14
<td width="35%">
15
16
<& /contenido/webshop/components/block_order_status_changer.msn &>
17
18
<& /contenido/webshop/components/block_coupons.msn, status => $cst &>
19
20
</td>
21
<td width="1%"> </td>
22
<td width="65%">
23
24
<fieldset>
25
<legend>Купоны со статусом "<span style="color:yellow"><% $current_status->[1] %></span>"</legend>
26
27
% if ( $total ) {
28
29
<div style="font-size:75%; font-family:Arial;">
30
<& /inc/pages_.msn, p=>$p, n=>$size, total=>$total, params=>\%filter_params, &>
31
<div style="height:5px"><spacer type="block" height="5"></div>
32
</div>
33
34
<& /contenido/webshop/components/coupon_browse.msn, documents => \@documents, columns => \@columns, filter => \%filter_params, status => $cst, %ARGS &>
35
36
<div style="font-size:75%; font-family:Arial;">
37
<& /inc/pages_.msn, p=>$p, n=>$size, total=>$total, params=>\%filter_params, &>
38
<div style="height:5px"><spacer type="block" height="5"></div>
39
</div>
40
41
% } else {
42
<h4 align="center"><i>---- Нет документов -----</i></h4>
43
% }
44
45
</td>
46
<td width="1%"> </td>
47
</tr>
48
</table>
49
50
</body>
51
</html>
52
<%args>
53
54
$cst => 1
55
$p => 1
56
$delete => undef
57
58
</%args>
59
<%init>
60
61
my %filter_params;
62
63
my (@documents, $total);
64
$filter_params{cst} = $cst if $cst != 1;
65
my $size = 40;
66
67
@documents = $keeper->get_documents(
68
class => 'webshop::Coupon',
69
status => $cst,
70
limit => $size,
71
offset => ($p-1)*$size,
72
order_by => 'dtime desc',
73
);
74
$total = $keeper->get_documents(
75
class => 'webshop::Coupon',
76
status => $cst,
77
count => 1,
78
);
79
my @structure = webshop::Coupon->new( $keeper->{webshop} )->structure;
80
my @columns = sort { $a->{column} <=> $b->{column} }
81
grep { $_->{column} } @structure;
82
push @columns, {attr => '_act_', rusname => 'Действия'};
83
my ($status_map) = grep { $_->{attr} eq 'status' } @structure;
84
my ($current_status) = grep { $_->[0] == $cst } @{$status_map->{cases}};
85
86
my $active_rights = $m->comp('/contenido/webshop/subs/user_rights.msn');
87
if ( !$active_rights && $delete ) {
88
my $return_params = join ('&', map { $_.'='.$filter_params{$_} } grep { $_ ne 's' } keys %filter_params );
89
my %deleted;
90
while ( my ($field, $value) = each %ARGS ) {
91
if ( $field =~ /^delete_(\d+)_(\w+)$/ ) {
92
my $oid = $1;
93
my $attr = $2;
94
$deleted{$oid}{$attr} = $value;
95
}
96
}
97
my %classes = map { $_->{class} => 1 } values %deleted;
98
foreach my $delete_class ( keys %classes ) {
99
my @ids;
100
while ( my ($oid, $attr) = each %deleted) {
101
push @ids, $oid if exists $attr->{id} && $attr->{id} && ($attr->{class} eq $delete_class);
102
}
103
my @objects = $keeper->get_documents (
104
id => \@ids,
105
class => $delete_class
106
) if @ids;
107
foreach my $object ( @objects ) {
108
my $document_access = $user->section_accesses($user, $object->section);
109
next unless $document_access == 2;
110
$object->delete;
111
}
112
}
113
$m->redirect("/contenido/webshop/".($return_params ? '?'.$return_params : ''));
114
}
115
116
</%init>
utf8/plugins/webshop/comps/contenido/webshop/index.html
7
7
//-->
8
8
</style>
9
9
10
<% spacer( h=>10 ) %>
11
10
12
<table width="100%" cellspacing="0" cellpadding="0" border="0">
11
13
<tr valign="top">
12
14
<td width="35%">
13
15
14
16
<& /contenido/webshop/components/block_order_status_changer.msn, status => $ost &>
15
17
18
<& /contenido/webshop/components/block_coupons.msn &>
19
16
20
</td>
17
21
<td width="1%"> </td>
18
22
<td width="65%">
utf8/plugins/webshop/lib/webshop/Coupon.pm
1
package webshop::Coupon;
2
3
use strict;
4
use warnings 'all';
5
6
use Contenido::Globals;
7
use base "Contenido::Document";
8
use Data::Dumper;
9
10
sub extra_properties
11
{
12
return (
13
{ 'attr' => 'class', 'column' => undef },
14
{ 'attr' => 'status', 'type' => 'status', 'rusname' => 'Статус',
15
'cases' => [
16
[0, 'Купон не активен'],
17
[1, 'Купон активен и разослан'],
18
[2, 'Прототип ждет обработки'],
19
[3, 'Купон использован'],
20
[4, 'Прототип обработан роботом'],
21
],
22
},
23
{ 'attr' => 'uid_proto','type' => 'status', 'rusname' => 'Прототип пользовательского доступа',
24
'cases' => [
25
[0, 'Купон доступен всем пользователям'],
26
[1, 'Персональный купон, создается для всех зарегистрированных пользователей'],
27
[2, 'Персональный купон, пользователи по выбору'],
28
[3, 'Персональный купон для одного пользователя'],
29
],
30
'rem' => 'Заполняется при создании прототипа купона',
31
},
32
{ 'attr' => 'uid_condition', 'type' => 'string', 'rusname' => 'Условие для прототипа' },
33
{ 'attr' => 'uid', 'type' => 'pickup', 'rusname' => 'Пользователь',
34
lookup_opts => { class => $state->{users}->profile_document_class, order_by => 'email', search_by => 'email' },
35
allow_null => 1, default => 0,
36
rem => '0 - действует для всех пользователей, многократно. id - для одного пользователя, однократно.'
37
},
38
{ 'attr' => 'groups', 'rusname' => 'Группы товаров',
39
lookup_opts => { class => $state->{webshop}->{item_section_class}, },
40
allow_null => 1,
41
rem => 'Список разделов, на содержимое которых распространяется скидка по купону',
42
},
43
{ 'attr' => 'discount', 'type' => 'string', 'rusname' => 'Скидка на сумму заказа (число или процент)', shortname => 'Скидка',
44
default => 0, column => 2 },
45
{ 'attr' => 'min_sum', 'type' => 'string', 'rusname' => 'Минимальная сумма, на которую действует скидка', default => 0 },
46
)
47
}
48
49
sub class_name
50
{
51
return 'Webshop: купон';
52
}
53
54
sub class_description
55
{
56
return 'Webshop: купон';
57
}
58
59
sub class_table
60
{
61
return 'webshop::SQL::CouponsTable';
62
}
63
64
sub contenido_status_style
65
{
66
my $self = shift;
67
if ( $self->status == 3 ) {
68
return 'color:black;';
69
} elsif ( $self->status == 2 ) {
70
return 'color:red;';
71
} elsif ( $self->status == 4 ) {
72
return 'color:olive;';
73
}
74
}
75
76
77
78
sub get_discount
79
{
80
my $self = shift;
81
82
my (%opts) = @_;
83
my $basket = delete $opts{basket};
84
return 0 unless exists $opts{uid} && $opts{uid} || exists $opts{session} && $opts{session};
85
return 0 unless $self->discount;
86
87
$basket ||= $keeper->{webshop}->get_basket ( %opts );
88
return 0 unless ref $basket eq 'ARRAY' && @$basket;
89
90
my ($number, $sum_total) = (0, 0);
91
map { $number += $_->number; $sum_total += $_->number * $_->price } @$basket;
92
warn "BASKET: $number Items of $sum_total Value\n";
93
94
my $discount_counted = 0;
95
my $items;
96
if ( $self->groups ) {
97
$items = $self->keeper->get_documents (
98
s => [$self->groups],
99
class => $state->{webshop}->{item_document_class},
100
light => 1,
101
return_mode => 'array_ref',
102
);
103
} else {
104
$items = $self->keeper->get_documents (
105
class => $state->{webshop}->{item_document_class},
106
lclass => 'webshop::CouponItemLink',
107
lsource => $self->id,
108
light => 1,
109
return_mode => 'array_ref',
110
);
111
}
112
113
if ( ref $items eq 'ARRAY' && @$items ) {
114
my $found_sum = 0;
115
foreach my $bi ( @$basket ) {
116
warn "BASKET: Item id [".$bi->item_id."]\n";
117
foreach ( @$items) {
118
warn "BASKET: Item [".$_->name."] id [".$_->id."]\n";
119
if ( $bi->item_id == $_->id ) {
120
$found_sum += $bi->number * $bi->price;
121
last;
122
}
123
}
124
}
125
warn "Sum found: [$found_sum]\n";
126
return $self->can_discount( $found_sum );
127
} elsif ( $self->groups ) {
128
return 0;
129
}
130
131
return $self->can_discount( $sum_total );
132
}
133
134
135
sub can_discount
136
{
137
my $self = shift;
138
my $sum = shift;
139
return 0 unless $sum;
140
141
my $discount = $self->discount;
142
my $min_sum = $self->min_sum || 0;
143
my $count = 0;
144
if ( $discount =~ /([\d\.]+)%/ ) {
145
my $proc = $1;
146
return 0 unless $proc;
147
$count = $sum / 100 * $proc;
148
} else {
149
$count = $discount;
150
}
151
my $rest = $sum - $count;
152
$count = 0 if $rest < $min_sum || $rest == 0;
153
return $count;
154
}
155
156
157
158
159
#sub table_links
160
#{
161
# return [
162
# { name => 'Города', class => 'webshop::Town', filter => 'pid', field => 'pid' },
163
# ];
164
#}
165
166
sub pre_store
167
{
168
my $self = shift;
169
170
my $default_section = $project->s_alias->{webshop_coupons} if ref $project->s_alias eq 'HASH';
171
if ( $default_section && !$self->sections ) {
172
$self->sections($default_section);
173
}
174
175
return 1;
176
}
177
178
1;
utf8/plugins/webshop/lib/webshop/CouponItemLink.pm
1
package webshop::CouponItemLink;
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
}
20
21
sub available_sources
22
{
23
return [ qw(webshop::Coupon) ];
24
}
25
26
sub available_destinations
27
{
28
my $self = shift;
29
return $state->{webshop}->{item_document_class};
30
}
31
32
1;
utf8/plugins/webshop/lib/webshop/Init.pm
14
14
Contenido::Init::load_classes(qw(
15
15
webshop::SQL::Basket
16
16
webshop::SQL::Order
17
webshop::SQL::CouponsTable
18
webshop::SQL::CouponLinkTable
17
19
18
20
webshop::Address
19
21
webshop::Basket
20
22
webshop::Order
21
23
webshop::Delivery
24
webshop::Coupon
22
25
23
26
webshop::Country
24
27
webshop::Area
…
…
26
29
27
30
webshop::DeliverySection
28
31
webshop::RegionSection
32
33
webshop::CouponItemLink
34
webshop::OrderCouponLink
29
35
));
30
36
31
37
sub init {
32
push @{ $state->{'available_documents'} }, qw(webshop::Basket webshop::Order webshop::Delivery webshop::Country webshop::Area webshop::Town);
38
push @{ $state->{'available_documents'} }, qw(webshop::Basket webshop::Order webshop::Delivery webshop::Coupon webshop::Country webshop::Area webshop::Town);
33
39
push @{ $state->{'available_sections'} }, qw(webshop::DeliverySection webshop::RegionSection);
40
push @{ $state->{'available_links'} }, qw(webshop::CouponItemLink webshop::OrderCouponLink);
34
41
0;
35
42
}
36
43
utf8/plugins/webshop/lib/webshop/Keeper.pm
137
137
}
138
138
139
139
140
sub basket_count {
141
my $self = shift;
142
my $basket = shift;
143
return (0,0) unless ref $basket eq 'ARRAY' && @$basket;
144
145
my $total = 0;
146
my $sum = 0;
147
foreach my $item ( @$basket ) {
148
if ( $item->status == 1 ) {
149
$total += $item->number;
150
$sum += $item->number * $item->price;
151
}
152
}
153
return ($total, $sum);
154
}
155
156
140
157
sub clear_basket {
141
158
my $self = shift;
142
159
my (%opts) = @_;
…
…
296
313
return \@items;
297
314
}
298
315
316
317
### Метод приведения купонов для пользователя в момент логина
318
#############################################################
319
sub merge_coupons {
320
my $self = shift;
321
my (%opts) = @_;
322
323
warn "Merge (coupons) begin: ".Dumper(\%opts)."\n" if $DEBUG;
324
return unless $opts{uid} && $opts{session};
325
326
my @items = $keeper->get_links (
327
class => 'webshop::OrderCouponLink',
328
session => $opts{session},
329
source_id => 0,
330
);
331
my %merge_to = $keeper->get_links (
332
class => 'webshop::OrderCouponLink',
333
uid => $opts{uid},
334
source_id => 0,
335
return_mode => 'hash',
336
hash_by => 'dest_id',
337
);
338
foreach my $item ( @items ) {
339
if ( exists $merge_to{$item->dest_id} ) {
340
$item->delete;
341
} else {
342
$item->session( undef );
343
$item->uid( $opts{uid} );
344
$item->store;
345
}
346
}
347
}
348
349
350
299
351
sub price_format {
300
352
my $self = shift;
301
353
my $price = shift;
utf8/plugins/webshop/lib/webshop/Order.pm
1
1
package webshop::Order;
2
2
3
3
use base "Contenido::Document";
4
use Contenido::Globals;
5
4
6
sub extra_properties
5
7
{
6
8
return (
…
…
29
31
manshow => 1, postshow => 1, facilshow => 1 },
30
32
{ 'attr' => 'sum', 'type' => 'string', 'rusname' => 'Сумма (total)', shortname => 'Сумма',
31
33
column => 5, postshow => 1, facilshow => 1 },
34
{ 'attr' => 'sum_discount', 'type' => 'string', 'rusname' => 'Сумма скидки', shortname => 'Скидка',
35
column => 6, postshow => 1, facilshow => 1 },
32
36
{ 'attr' => 'sum_delivery', 'type' => 'string', 'rusname' => 'Стоимость доставки', shortname => 'Доставка',
33
column => 6, postshow => 1, facilshow => 1 },
37
column => 7, postshow => 1, facilshow => 1 },
38
{ 'attr' => 'sum_total', 'type' => 'string', 'rusname' => 'Сумма общая', shortname => 'Total',
39
column => 8, virtual => 1, postshow => 1, facilshow => 1 },
34
40
{ 'attr' => 'contact', 'type' => 'string', 'rusname' => 'Контактное лицо', facilshow => 1 },
35
41
{ 'attr' => 'email', 'type' => 'string', 'rusname' => 'E-mail для связи', shortname => 'E-mail',
36
42
column => 3, postshow => 1, facilshow => 1, mandatory => 1, },
…
…
59
65
)
60
66
}
61
67
68
69
sub sum_total {
70
my $self = shift;
71
72
return ($self->sum || 0) - ($self->sum_discount || 0) + ($self->sum_delivery || 0);
73
}
74
75
62
76
sub sum_formatted {
63
77
my $self = shift;
64
78
…
…
73
87
sub total_formatted {
74
88
my $self = shift;
75
89
76
my $price = $self->sum + ($self->sum_delivery || 0);
77
$price = reverse $price;
78
$price =~ s/(\d{3})/$1\ /g;
79
$price = reverse $price;
80
81
return $price;
90
my $price = $self->sum_total;
91
return $keeper->{webshop}->price_format( $price );
82
92
}
83
93
84
94
…
…
117
127
foreach my $item ( @items ) {
118
128
$item->delete( attachments => 1 );
119
129
}
130
my $sql = $self->keeper->SQL->prepare('DELETE FROM webshop_order_coupons where source_id = ?');
131
$sql->execute( $self->id );
132
120
133
1;
121
134
}
122
135
utf8/plugins/webshop/lib/webshop/OrderCouponLink.pm
1
package webshop::OrderCouponLink;
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::OrderCouponLinkTable';
31
}
32
33
sub available_sources
34
{
35
return [ qw(webshop::Order) ];
36
}
37
38
sub available_destinations
39
{
40
return [ qw(webshop::Coupon) ];
41
}
42
43
1;
utf8/plugins/webshop/lib/webshop/SQL/CouponLinkTable.pm
1
package webshop::SQL::CouponLinkTable;
2
3
use base 'SQL::LinkTable';
4
5
sub db_table
6
{
7
return 'webshop_coupon_links';
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
_excludes_filter
19
_datetime_filter
20
_date_equal_filter
21
_date_filter
22
23
_dest_id_filter
24
_source_id_filter
25
_source_class_filter
26
_dest_class_filter
27
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 = $self->SUPER::required_properties;
52
return (
53
@parent_properties,
54
);
55
}
56
57
########### FILTERS DESCRIPTION ###############################################################################
58
59
1;
utf8/plugins/webshop/lib/webshop/SQL/CouponsTable.pm
1
package webshop::SQL::CouponsTable;
2
3
use base 'SQL::DocumentTable';
4
5
sub db_table
6
{
7
return 'webshop_coupons';
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
_link_filter
26
27
_uid_filter
28
_code_filter
29
)];
30
31
sub available_filters {
32
return $available_filters;
33
}
34
35
36
# ----------------------------------------------------------------------------
37
# Свойства храним в массивах, потому что порядок важен!
38
# Это общие свойства - одинаковые для всех документов.
39
#
40
# attr - обязательный параметр, название атрибута;
41
# type - тип аттрибута, требуется для отображдения;
42
# rusname - русское название, опять же требуется для отображения;
43
# hidden - равен 1, когда
44
# readonly - инициализации при записи только без изменения в дальнейшем
45
# db_field - поле в таблице
46
# default - значение по умолчанию (поле всегда имеет это значение)
47
# ----------------------------------------------------------------------------
48
sub required_properties
49
{
50
my $self = shift;
51
52
my @parent_properties = grep { $_->{attr} ne 'sections' && $_->{attr} ne 'dtime' } $self->SUPER::required_properties;
53
return (
54
@parent_properties,
55
{
56
'attr' => 'code',
57
'type' => 'string',
58
'rusname' => 'Код купона',
59
'shortname' => 'Код',
60
'column' => 1,
61
'rem' => 'Оставьте незаполненным для автоматической генерации',
62
'db_field' => 'code',
63
'db_type' => 'text',
64
},
65
{
66
'attr' => 'dtime',
67
'type' => 'datetime',
68
'rusname' => 'Начало действия купона',
69
'shortname' => 'Начало',
70
'column' => 4,
71
'db_field' => 'dtime',
72
'db_type' => 'timestamp',
73
'db_opts' => 'not null default now()',
74
'default' => 'CURRENT_TIMESTAMP',
75
},
76
{
77
'attr' => 'etime',
78
'type' => 'datetime',
79
'rusname' => 'Окончание действия купона',
80
'shortname' => 'Конец',
81
'column' => 5,
82
'db_field' => 'etime',
83
'db_type' => 'timestamp',
84
'db_opts' => 'not null default now()',
85
'default' => 'CURRENT_TIMESTAMP',
86
},
87
{
88
'attr' => 'pid',
89
'type' => 'integer',
90
'rusname' => 'ID прототипа',
91
'hidden' => 1,
92
'db_field' => 'pid',
93
'db_type' => 'integer',
94
'db_opts' => "not null default 0",
95
'default' => 0,
96
},
97
{ # User ID
98
'attr' => 'uid',
99
'type' => 'integer',
100
'rusname' => 'ID пользователя',
101
'db_field' => 'uid',
102
'db_type' => 'integer',
103
'db_opts' => "not null default 0",
104
'default' => 0,
105
},
106
{
107
'attr' => 'groups',
108
'type' => 'lookup_multi',
109
'rusname' => 'Группы товаров',
110
'db_field' => 'groups',
111
'db_type' => 'integer[]',
112
},
113
{
114
'attr' => 'sections',
115
'type' => 'sections_list',
116
'rusname' => 'Секции',
117
'hidden' => 1,
118
'db_field' => 'sections',
119
'db_type' => 'integer',
120
},
121
);
122
}
123
124
########### FILTERS DESCRIPTION ###############################################################################
125
sub _s_filter {
126
my ($self,%opts)=@_;
127
return undef unless ( exists $opts{s} );
128
return &SQL::Common::_generic_int_filter('d.sections', $opts{s});
129
}
130
131
sub _uid_filter {
132
my ($self,%opts)=@_;
133
return undef unless ( exists $opts{uid} );
134
return &SQL::Common::_generic_int_filter('d.uid', $opts{uid});
135
}
136
137
sub _code_filter {
138
my ($self,%opts)=@_;
139
return undef unless ( exists $opts{code} );
140
return &SQL::Common::_generic_name_filter('d.code', $opts{code}, undef, \%opts);
141
}
142
143
sub _interval_filter {
144
my ($self,%opts)=@_;
145
return undef unless ( exists $opts{interval} && ref $opts{interval} eq 'ARRAY' && scalar @{$opts{interval}} == 2 );
146
return undef unless ref $opts{interval}->[0] && ref $opts{interval}->[1];
147
return undef if DateTime->compare($opts{interval}->[0], $opts{interval}->[1]) == 1;
148
149
my $wheres = "d.dtime <= ? and d.etime >= ?";
150
my @values = ($opts{interval}->[1]->ymd('-'), $opts{interval}->[0]->ymd('-'));
151
152
return ($wheres, \@values);
153
}
154
155
sub _link_filter {
156
my ($self,%opts)=@_;
157
158
my @wheres=();
159
my @binds=();
160
161
# Связь определенного класса
162
if (exists($opts{lclass})) {
163
my ($where, $values) = SQL::Common::_generic_text_filter('l.class', $opts{lclass});
164
push (@wheres, $where);
165
push (@binds, ref($values) ? @$values:$values) if (defined $values);
166
}
167
168
my $lclass = $opts{lclass} || 'Contenido::Link';
169
my $link_table = $lclass->_get_table->db_table();
170
171
# Ограничение по статусу связи
172
if ( exists $opts{lstatus} ) {
173
my ($where, $values) = SQL::Common::_generic_int_filter('l.status', $opts{lstatus});
174
push (@wheres, $where);
175
push (@binds, ref($values) ? @$values:$values) if (defined $values);
176
}
177
178
# Ограничение по uid
179
if ( exists $opts{luid} ) {
180
my ($where, $values) = SQL::Common::_generic_int_filter('l.uid', $opts{luid});
181
push (@wheres, $where);
182
push (@binds, ref($values) ? @$values:$values) if (defined $values);
183
}
184
185
# Ограничение по сессии
186
if ( exists $opts{lsession} ) {
187
my ($where, $values) = SQL::Common::_generic_text_filter('l.session', $opts{lsession});
188
push (@wheres, $where);
189
push (@binds, ref($values) ? @$values:$values) if (defined $values);
190
}
191
192
# Связь с определенным документ(ом/тами) по цели линка
193
if ( exists $opts{ldest} ) {
194
my ($where, $values) = SQL::Common::_generic_int_filter('l.dest_id', $opts{ldest});
195
push (@wheres, $where);
196
push (@binds, ref($values) ? @$values:$values) if (defined $values);
197
if ($self->_single_class) {
198
return (\@wheres, \@binds, " join $link_table as l on l.source_id=d.id");
199
} else {
200
return (\@wheres, \@binds, " join $link_table as l on l.source_id=d.id and l.source_class=d.class");
201
}
202
}
203
# Связь с определенным документ(ом/тами) по источнику линка
204
if ( exists $opts{lsource} ) {
205
my ($where, $values) = SQL::Common::_generic_int_filter('l.source_id', $opts{lsource});
206
push (@wheres, $where);
207
push (@binds, ref($values) ? @$values:$values) if (defined $values);
208
if ($self->_single_class) {
209
return (\@wheres, \@binds, " join $link_table as l on l.dest_id=d.id");
210
} else {
211
return (\@wheres, \@binds, " join $link_table as l on l.dest_id=d.id and l.dest_class=d.class");
212
}
213
}
214
215
return (undef);
216
}
217
218
219
1;
utf8/plugins/webshop/lib/webshop/SQL/OrderCouponLinkTable.pm
1
package webshop::SQL::OrderCouponLinkTable;
2
3
use base 'SQL::LinkTable';
4
use Contenido::Globals;
5
6
sub db_table
7
{
8
return 'webshop_order_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
_uid_filter
30
_session_filter
31
)];
32
33
sub available_filters {
34
return $available_filters;
35
}
36
37
38
# ----------------------------------------------------------------------------
39
# Свойства храним в массивах, потому что порядок важен!
40
# Это общие свойства - одинаковые для всех документов.
41
#
42
# attr - обязательный параметр, название атрибута;
43
# type - тип аттрибута, требуется для отображдения;
44
# rusname - русское название, опять же требуется для отображения;
45
# hidden - равен 1, когда
46
# readonly - инициализации при записи только без изменения в дальнейшем
47
# db_field - поле в таблице
48
# default - значение по умолчанию (поле всегда имеет это значение)
49
# ----------------------------------------------------------------------------
50
sub required_properties
51
{
52
my $self = shift;
53
54
my @parent_properties = $self->SUPER::required_properties;
55
return (
56
@parent_properties,
57
{ # User ID
58
'attr' => 'uid',
59
'type' => 'pickup',
60
'rusname' => 'ID пользователя',
61
'lookup_opts' => {
62
class => $state->{users}->profile_document_class,
63
order_by => 'email',
64
search_by => 'email'
65
},
66
'db_field' => 'uid',
67
'db_type' => 'integer',
68
'db_opts' => "default 0",
69
'default' => 0,
70
},
71
{ # ID Сессии
72
'attr' => 'session',
73
'type' => 'string',
74
'rusname' => 'ID Сессии пользователя',
75
'db_field' => 'session',
76
'db_type' => 'text',
77
},
78
);
79
}
80
81
########### FILTERS DESCRIPTION ###############################################################################
82
sub _uid_filter {
83
my ($self,%opts)=@_;
84
return undef unless ( exists $opts{uid} );
85
return &SQL::Common::_generic_int_filter('d.uid', $opts{uid});
86
}
87
88
sub _session_filter {
89
my ($self,%opts)=@_;
90
return undef unless ( exists $opts{session} );
91
return &SQL::Common::_generic_text_filter('d.session', $opts{session});
92
}
93
94
1;
utf8/plugins/webshop/lib/webshop/State.pm.proto
20
20
# $self->{db_password} = '';
21
21
# $self->{db_port} = '';
22
22
$self->{profile_document_class} = '@PROFILE_DOCUMENT_CLASS@' || 'users::UserProfile';
23
$self->{item_document_class} = '@ITEM_DOCUMENT_CLASS@' || 'webshop::Item';
23
$self->{item_document_class} = '@ITEM_DOCUMENT_CLASS@' ? [qw( @ITEM_DOCUMENT_CLASS@ )] : ['webshop::Item'];
24
$self->{item_section_class} = '@ITEM_SECTION_CLASS@' ? [qw( @ITEM_SECTION_CLASS@ )] : ['webshop::ItemSection'];
24
25
25
26
$self->_init_();
26
27
$self;
utf8/plugins/webshop/sql/TOAST/coupon_links.sql
1
create table webshop_coupon_links
2
(
3
id integer not null primary key default nextval('public.documents_id_seq'::text),
4
class text not null,
5
ctime timestamp not null default now(),
6
mtime timestamp not null default now(),
7
status smallint not null default 1,
8
source_id integer not null,
9
source_class text not null default 'webshop::Coupon',
10
dest_id integer not null,
11
dest_class text not null,
12
data text
13
);
14
create index webshop_coupon_links_source on webshop_coupon_links (source_id);
15
create index webshop_coupon_links_dest on webshop_coupon_links (dest_id);
utf8/plugins/webshop/sql/TOAST/coupons.sql
1
CREATE TABLE webshop_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
dtime timestamp without time zone DEFAULT now() NOT NULL,
6
etime timestamp without time zone DEFAULT now() NOT NULL,
7
class text DEFAULT 'webshop::Coupon'::text NOT NULL,
8
status smallint default 0 NOT NULL,
9
sections integer,
10
pid integer default 0,
11
uid integer default 0,
12
groups integer[],
13
name text,
14
code text not null,
15
data text
16
);
17
18
CREATE INDEX webshop_coupons_sections ON webshop_coupons USING btree (sections);
19
CREATE INDEX webshop_coupons_uid ON webshop_coupons USING btree (uid);
20
CREATE INDEX webshop_coupons_code ON webshop_coupons USING btree (code);
utf8/plugins/webshop/sql/TOAST/order_coupons.sql
1
CREATE TABLE webshop_order_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::OrderCouponLink'::text NOT NULL,
6
status smallint default 0 NOT NULL,
7
uid integer default 0,
8
session text,
9
source_id integer,
10
source_class text default 'webshop::Order'::text,
11
dest_id integer,
12
dest_class text default 'webshop::Coupon'::text,
13
data text
14
);
15
16
CREATE INDEX webshop_order_coupons_user ON webshop_order_coupons USING btree (uid, session);
17
create index webshop_order_coupons_source on webshop_order_coupons (source_id);
18
create index webshop_order_coupons_dest on webshop_order_coupons (dest_id);
Небольшая справка по веткам
cnddist – контейнер, в котором хранятся все дистрибутивы всех библиотек и программных пакетов, которые использовались при построении различных версий Contenido. Если какой-то библиотеки в данном хранилище нет, инсталлятор сделает попытку "подтянуть" ее с веба (например, с CPAN). Если библиотека слишком старая, есть очень большая вероятность, что ее там уже нет. Поэтому мы храним весь хлам от всех сборок. Если какой-то дистрибутив вдруг отсутствует в cnddist - напишите нам, мы положим его туда.
koi8 – отмирающая ветка, чей код, выдача и все внутренние библиотеки заточены на кодировку KOI8-R. Вносятся только те дополнения, которые касаются внешнего вида и функционала админки, баги ядра, обязательные обновления портов и мелочи, которые легко скопипастить. В дальнейшем планируется полная остановка поддержки по данной ветке.
utf8 – актуальная ветка, заточенная под UTF-8.
Внутри каждой ветки: core – исходники ядра; install – скрипт установки инсталляции; plugins – плагины; samples – "готовые к употреблению" проекты, которые можно поставить, запустить и посмотреть, как они работают.