Revision 198
Date:
2012/03/15 18:29:29
Author:
ahitrov
Revision Log:
Simple webshop support plugin
Files:
Legend:
Added
Removed
Modified
utf8/plugins/webshop/comps/contenido/webshop/autohandler
1
<%init>
2
3
$r->content_type('text/html');
4
$m->call_next();
5
6
</%init>
utf8/plugins/webshop/comps/contenido/webshop/components/block_order_status_changer.msn
1
<fieldset>
2
<legend>Выбор заказов по статусу</legend>
3
4
<table width="100%" border="0" cellpadding="3" cellspacing="0" class="tlistdocs">
5
<tr bgcolor="#efefef">
6
<th align="center" width="1%">ID</th>
7
<th>Статус</th>
8
9
% foreach my $case ( @cases ) {
10
% next unless $case->[0];
11
% my $style = $status && $case->[0] == $status ? 'inverted' : '';
12
<tr class="<% $style %>">
13
<td><% $case->[0] %></td>
14
<td><table cellpadding="0" cellspacing="0" border="0">
15
<tr valign="top">
16
<td width="10"> </td>
17
<td><a href="./?ost=<% $case->[0] %>"><% Encode::encode('utf-8', ucfirst(Encode::decode('utf-8',$case->[1]))) %></a></td>
18
</tr>
19
</table>
20
</td>
21
</tr>
22
% }
23
24
</table>
25
26
</fieldset>
27
<%args>
28
29
$status => undef
30
31
</%args>
32
<%init>
33
34
my ($prop) = grep { $_->{attr} eq 'status' } webshop::Order->new( $keeper->{webshop} )->structure;
35
my @cases = @{$prop->{cases}};
36
37
</%init>
utf8/plugins/webshop/comps/contenido/webshop/components/object_form.msn
1
<!-- Форма для редактирования объекта -->
2
<a name="top"></a>
3
<form enctype="multipart/form-data" action="<% $PROTOS->{$proto}->[1] %>" method="POST" name="form" onSubmit="javascript:Save()">
4
<table border="0" width="100%" cellspacing="0" cellpadding="6">
5
<tr>
6
<td style="font-size:110%;">
7
<b><% (ref($object) && $object->id() ) ? 'Редактирование' : 'Создание' %> <% $PROTOS->{$proto}->[0] %> типа "<% $object->class_name() %>"</b>
8
</td>
9
<td align="right">
10
% unless ( $proto eq 'sections' && $object->id == 1 ) {
11
<input type="submit" value="Сохранить" class="input_btn">
12
% if (( $proto eq 'documents' ) || ( $proto eq 'sections' )) {
13
<input type="submit" name="_save_and_leave" value="Сохранить и выйти" class="input_btn">
14
<input type="submit" name="_save_and_again" value="Сохранить и создать новый" class="input_btn">
15
% }
16
% }
17
</td>
18
</tr>
19
</table>
20
21
% if ( $m->comp_exists ('/contenido/components/object_context_menu.msn') ) {
22
<div style="text-align:right;"><& '/contenido/components/object_context_menu.msn', object => $object &></div>
23
% }
24
25
<center>
26
<table width="100%" cellpadding="0" cellspacing="0" border="0">
27
<tr>
28
<td bgcolor="#999999">
29
<table width="100%" cellpadding="0" cellspacing="1" border="0">
30
<tr><td valign="top" width="80%" bgcolor="#ffffff">
31
<center>
32
<table width="98%" cellpadding="1" cellspacing="0" border="0">
33
34
<%perl>
35
36
my @properties = $object->structure();
37
38
for (0..$#properties)
39
{
40
41
my $prop = $properties[$_];
42
next if ($prop->{hidden} == 1) || ($prop->{type} eq 'image') || ($prop->{type} eq 'external') || ($prop->{type} =~ /^array/i) || ($prop->{type} =~ /^image/) || ($prop->{type} eq 'multimedia') || ($prop->{type} eq 'multimedia_new') || ($prop->{type} eq 'audio');
43
44
</%perl>
45
<tr><td height="8"></td></tr>
46
<tr><td nowrap>
47
<table cellpadding="0" cellspacing="0" border="0">
48
<tr>
49
% if( ( $prop->{type} eq 'flag' ) || ($prop->{type} eq 'checkbox') )
50
% {
51
<td><& "/contenido/components/inputs/checkbox.msn", prop => $prop, object=>$object, options=>$options, name => $prop->{attr}, check => $object->{$prop->{attr}}, id => $object->id() &></td>
52
% }
53
<td nowrap><b><% $prop->{rusname} %></b> /</td>
54
<td align="right" nowrap><font color="#888888" size="-1"> name="<% $prop->{attr} %>"</font></td>
55
% if( $prop->{readonly} )
56
% {
57
<td align="right" nowrap> / <font color="#CC0000" size="-1">Значение нельзя изменить</font></td>
58
% }
59
</tr>
60
</table>
61
</td></tr>
62
% if ($prop->{type} eq 'parent')
63
% {
64
<tr><td><& "/contenido/components/inputs/$prop->{type}.msn", prop => $prop, object=>$object, options=>$options, name => $prop->{attr}, check => ($object->{$prop->{attr} } || $prop->{default} || $sect_id ), id => $object->id() &></td></tr>
65
% }
66
% elsif( ( $prop->{type} ne 'flag' ) && ($prop->{type} ne 'checkbox') )
67
% {
68
<tr><td><& "/contenido/components/inputs/$prop->{type}.msn", prop => $prop, object=>$object, options => $options, name => $prop->{attr}, check => $object->{$prop->{attr}}, id => ($object->id() || 0) &></td></tr>
69
% }
70
% }
71
<tr>
72
<td><br>
73
74
% for (0..$#properties)
75
% {
76
% my $prop = $properties[$_];
77
% next if ($prop->{hidden} != 1);
78
% next if ($prop->{attr} eq 'id' && $clone);
79
<input type="hidden" name="<% $prop->{attr} %>" value="<% html_escape($object->{ $prop->{attr} }) %>">
80
% }
81
<input type="hidden" name="sect_id" value="<% $sect_id %>">
82
%#если класса в свойствах обьекта нет все равно надо его пробросить как hidden
83
% unless (grep {$_->{attr} eq 'class'} @properties) {
84
<input type="hidden" name="class" value="<% html_escape($object->{class}) %>">
85
% }
86
% if ( $clone ) {
87
<input type="hidden" name="clone" value="<% $object->id %>">
88
% }
89
<input type="hidden" name="save" value="1">
90
91
</td>
92
</tr>
93
</table>
94
</center>
95
96
</td>
97
<td valign="top" bgcolor="#efefef">
98
<div style="width:270px"><spacer type="block" width="270"></div>
99
<div><iframe name="DocFinder" id="DocFinder" src="/contenido/find_document.html" frameborder="0"
100
marginheight="0" marginwidth="0" width="100%" height="0"></iframe></div>
101
<table cellpadding="5" cellspacing="0" border="0"><tr><td>
102
<p><font size=+1><b><nobr>Мультимедиа-объекты</nobr> к <% $PROTOS->{$proto}->[2] %></b></font></p>
103
104
% for (0..$#properties)
105
% {
106
% my $prop = $properties[$_];
107
%
108
% if ($prop->{type} eq 'image')
109
% {
110
% my $IMAGE = $object->get_image( $prop->{attr} );
111
% if (ref($IMAGE) ne 'HASH') { next };
112
<& "/contenido/components/inputs/image.msn",
113
IMAGE => $IMAGE,
114
rusname => $prop->{rusname},
115
prop => $prop, object=>$object,
116
attr => $prop->{attr},
117
options => $options,
118
&>
119
120
% } elsif ($prop->{type} eq 'images')
121
% {
122
% my $IMAGES = $object->get_image( $prop->{attr} );
123
% if (ref($IMAGES) ne 'HASH') { next };
124
% my $MN = $IMAGES->{maxnumber}+0;
125
% for my $mn (1..$MN)
126
% {
127
% my $IMAGE = $IMAGES->{'image_'.$mn};
128
129
<& "/contenido/components/inputs/image.msn",
130
rusname => $prop->{rusname},
131
prop => $prop, object=>$object,
132
attr => $prop->{attr}.'_'.$mn,
133
IMAGE => $IMAGE,
134
options => $options,
135
&>
136
137
% }
138
% my $ME = exists $prop->{empty_slots} ? $prop->{empty_slots} : 5;
139
% $ME = ($ME + $IMAGES->{maxnumber}) >= 100 ? 0 : (100 - $IMAGES->{maxnumber} < $ME ? 100 - $IMAGES->{maxnumber} : $ME );
140
% for my $mn (1..$ME)
141
% {
142
143
144
<& "/contenido/components/inputs/image.msn",
145
IMAGE => {},
146
rusname => $prop->{rusname},
147
prop => $prop, object=>$object,
148
attr => $prop->{attr}.'_'.($MN+$mn),
149
options => $options,
150
&>
151
152
% }
153
% } elsif ($prop->{type} eq 'multimedia')
154
% {
155
% my $MULTI = $object->get_image( $prop->{attr} );
156
157
<& "/contenido/components/inputs/multimedia.msn",
158
rusname => $prop->{rusname},
159
prop => $prop, object=>$object,
160
attr => $prop->{attr},
161
MULTI => $MULTI,
162
options => $options,
163
&>
164
% } elsif ($prop->{type} eq 'multimedia_new')
165
% {
166
% my $MULTI = $object->get_image( $prop->{attr} );
167
168
<& "/contenido/components/inputs/multimedia_new.msn",
169
rusname => $prop->{rusname},
170
prop => $prop, object=>$object,
171
attr => $prop->{attr},
172
MULTI => $MULTI,
173
options => $options,
174
&>
175
% } elsif ($prop->{type} eq 'audio')
176
% {
177
% my $MULTI = $object->get_image( $prop->{attr} );
178
179
<& "/contenido/components/inputs/audio.msn",
180
rusname => $prop->{rusname},
181
prop => $prop, object=>$object,
182
attr => $prop->{attr},
183
MULTI => $MULTI,
184
options => $options,
185
&>
186
% }
187
% }
188
189
</table>
190
</td></tr></table>
191
192
193
</td></tr>
194
</table>
195
196
</td></tr></table>
197
</center>
198
199
<script>
200
<!--
201
function updateList(theFild,value,text) {
202
for (var i = 0; i < theFild.options.length; i++) {
203
if (theFild.options[i].value == value) {
204
return false;
205
}
206
}
207
var option_length = theFild.options.length++;
208
eval("theFild.options[option_length].value=value");
209
eval("theFild.options[option_length].text=text");
210
211
Save(); this.form.submit();
212
213
}
214
//-->
215
</script>
216
217
% unless ( $proto eq 'sections' && $object->id == 1 ) {
218
<div align="center"><input type="submit" value="Сохранить" class="input_btn">
219
%# <input type="submit" value="Сохранить изменения в <% $PROTOS->{$proto}->[3] %>" class="input_btn">
220
221
%#
222
%# Как же сохранять эти гребанные связи?
223
%# onClick="javascript:updateList(parent.opener.links.links,'link_6_20_DefaultLink','test');"
224
%#
225
226
227
% if ($object->id > 0 && !$clone) {
228
<input name="delete" type="button" value="Удалить" onClick="javascript:window.location='confirm.html?id=<% $object->id() %>&action=<% $proto %>_deletion&class=<% $object->class() %>'" class="input_btn">\
229
% }
230
% if ( ref $filter_params eq 'HASH' ) {
231
% while ( my ($key, $value) = each %$filter_params ) {
232
% next if grep { $_->{attr} eq $key } $object->structure();
233
<input type="hidden" name="<% $key %>" value="<% $value %>">
234
% }
235
% }
236
<input type="hidden" name="control_charset" value="Контроль">
237
% if (( $proto eq 'documents' ) || ( $proto eq 'sections' )) {
238
<input type="submit" name="_save_and_leave" value="Сохранить и выйти" class="input_btn">
239
% unless ( $clone ) {
240
<input type="submit" name="_save_and_again" value="Сохранить и создать новый" class="input_btn">
241
% }
242
% }
243
</div>
244
% }
245
</form>
246
247
<%ARGS>
248
249
$proto => 'documents'
250
$object => undef
251
$clone => undef
252
$sect_id => 1
253
$filter_params => undef
254
255
</%ARGS>
256
257
<%ONCE>
258
my $PROTOS = {
259
'documents' => ['документа','document.html','документу','документе'],
260
'sections' => ['секции','section.html','секции','секции'],
261
'links' => ['связи','link.html','связи','связи'],
262
'users' => ['пользователя','users.html','пользователю','пользователе'],
263
};
264
</%ONCE>
265
266
<%INIT>
267
return undef unless ref($object);
268
269
my $toopi = $project->$proto();
270
my $options = {};
271
if ($toopi && (ref($toopi) eq 'HASH') && (exists($toopi->{ $object->class }))) {
272
%{ $options } = %{ $toopi->{ $object->class } };
273
}
274
</%INIT>
utf8/plugins/webshop/comps/contenido/webshop/components/order_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="./" 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 'dtime') {
48
%
49
<td nowrap><& "/contenido/components/show_dtime.msn", dtime=>$document->{dtime} &>\
50
% if ($document->{ctime} ne $document->{mtime}) {
51
% my $colortime = '#c66';
52
<div style="color:<% $colortime %>;"><& "/contenido/components/show_dtime.msn", dtime=>$document->{mtime} &></div>\
53
% }
54
%
55
% } elsif ($col->{attr} eq 'name') {
56
% my $style = $document->contenido_status_style ? ' style="'.$document->contenido_status_style.'"' : '';
57
% my ($a1, $a2) = $href ? ('<a href="./'.$href.'?id='.$document->id.($params_unclassed ? '&'.$params_unclassed : '').'"'.$style.'>','</a>') : ('','');
58
<td><span<% $style %> class="<% $document->status ? '':'hiddensect' %>">\
59
%
60
% my $name=$document->name ? $document->name : 'Безымянный документ N'.$document->id;
61
<% $a1 %><% $name | h %><% $a2 %>\
62
%
63
</span>\
64
%
65
% } elsif ($col->{attr} eq 'id') {
66
%
67
<td><span class="<% $document->status ? '':'hiddensect' %>">\
68
% if ($document_access == 2) {
69
<a href="document.html?id=<% $document->id %>&class=<% $document->class %><% $params_unclassed ? '&'.$params_unclassed : '' %>"><% $document->id %> </a>\
70
% } else {
71
<% $document->id %> \
72
% }
73
</span>\
74
%
75
% } elsif ( exists $col->{inline} && $col->{inline} ) {
76
% $inline_status = 1;
77
% my $attr = $col->{attr};
78
% if ( $col->{type} =~ /^(string|integer|float)$/ && $col->{inline} ) {
79
% my $style = $col->{inline_style} ? $col->{inline_style} : ($col->{type} =~ /^(integer|float)$/ ? 'text-align:right; ' : '' );
80
<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} || '' %>">
81
% } elsif ($col->{type} eq 'checkbox') {
82
% my $checked = $document->$attr ? ' checked' : '';
83
<td align="center"><input type="checkbox" name="<% 'update_'.$document->id.'_'.$attr %>"<% $checked %>>
84
% } elsif ($col->{type} eq 'select') {
85
% my $options = {};
86
% if ($toopi && (ref($toopi) eq 'HASH') && (exists($toopi->{$document->class}))) {
87
% %{ $options } = %{ $toopi->{$document->class} };
88
% }
89
% my $values = $options->{$col->{attr}};
90
<td><select name="<% 'update_'.$document->id.'_'.$attr %>">
91
% if ( ref $values eq 'ARRAY' ) {
92
% foreach my $val ( @$values ) {
93
% my $selected = $val eq $document->$attr ? ' selected' : '';
94
<option value="<% $val %>"<% $selected %>><% $val %>
95
% }
96
% }
97
</select>
98
% } elsif ($col->{type} eq 'status') {
99
% my $cases = $col->{cases};
100
% if ( ref $cases eq 'ARRAY' ) {
101
<td><select name="<% 'update_'.$document->id.'_'.$attr %>" style="<% $col->{inline_width} ? 'width:'.$col->{inline_width}.'px;' : '' %> <% $col->{inline_style} || '' %>">
102
% foreach my $case ( @$cases ) {
103
% my $selected = $case->[0] eq $document->$attr ? ' selected' : '';
104
<option value="<% $case->[0] %>"<% $selected %>><% $case->[1] %>
105
% }
106
</select>
107
% }
108
% }
109
%
110
% } elsif ($col->{attr} eq 'class') {
111
%
112
<td><% $document->class_name %> <font color="#999999">(<% $document->class %>)</font>\
113
%
114
% } elsif ($col->{type} eq 'datetime') {
115
%
116
<td nowrap><& "/contenido/components/show_dtime.msn", dtime=>$document->{$col->{attr}} &>\
117
%
118
% } elsif ($col->{attr} eq '_act_') {
119
% my $actions;
120
% if ( $document->$user_id == $user->id ) {
121
% $actions = join (' | ', map { '<a href="./'.$_->{href}.'?id='.$document->id.($params_unclassed ? '&'.$params_unclassed : '').'">'.$_->{name}.'</a>' } @actions);
122
% } else {
123
% my $profile = exists $users{$document->$user_id} ? $users{$document->$user_id} : undef;
124
% unless ( ref $profile ) {
125
% $profile = $keeper->get_user_by_id( $document->$user_id );
126
% $users{$profile->id} = $profile if ref $profile;
127
% }
128
% if ( ref $profile ) {
129
% $actions = $profile->name;
130
% } else {
131
% $actions = join (' | ', map { '<a href="./'.$_->{href}.'?id='.$document->id.($params_unclassed ? '&'.$params_unclassed : '').'">'.$_->{name}.'</a>' } @actions);
132
% }
133
% }
134
<td nowrap><% $actions %>\
135
% if ( $inline_status ) {
136
<input type="hidden" name="update_<% $document->id %>_class" value="<% $document->class %>">
137
% }
138
% if ( $delete_status ) {
139
<input type="hidden" name="delete_<% $document->id %>_class" value="<% $document->class %>">
140
% }
141
%
142
% } else {
143
%
144
% if ($col->{type} eq 'date') {
145
% my $date=$document->{$col->{attr}};
146
% $date=~/(\d{4}-\d{2}-\d{2})/;
147
<td nowrap align="right"><% $1 || ' ' %>\
148
% } elsif ($col->{type} eq 'datetime') {
149
<td nowrap align="right"><% $document->{$col->{attr}} || ' ' %>\
150
% } elsif ($col->{type} eq 'integer') {
151
<td align="right"><% $document->{$col->{attr}} %> \
152
% } elsif ($col->{type} eq 'lookup') {
153
<td align="left">\
154
% my $id = $document->{$col->{attr}};
155
% if ($id) {
156
% my ($doc)=$keeper->get_documents( ( ref($col->{lookup_opts}) ? %{$col->{lookup_opts}} : () ), id=>$id);
157
% if ($doc) {
158
<a href="document.html?id=<% $doc->id %>&class=<% $doc->class %><% $params_unclassed ? '&'.$params_unclassed : '' %>"><% $doc->name || $doc->id %></a> \
159
% } else {
160
<span class="hiddensect"><% $document->{$col->{attr}} %>???</span>\
161
% }
162
% } else {
163
<span class="hiddensect">NULL</span>\
164
% }
165
% } elsif ($col->{type} eq 'status') {
166
% my $status_map = ref $col->{cases} eq 'ARRAY' ? $col->{cases} : $keeper->default_status();
167
% my ($doc_status) = grep { $_->[0] eq $document->{$col->{attr}} } @$status_map;
168
% $doc_status ||= [$document->{$col->{attr}}, 'Неизвестный'];
169
<td nowrap><% $doc_status->[1].'('.$doc_status->[0].')' %>\
170
% } else {
171
<td><% defined($document->{$col->{attr}}) ? $document->{$col->{attr}} : ' ' %>\
172
% }
173
% }
174
</td>
175
%
176
% } #- for @columns
177
%
178
</tr>
179
% } #- foreach @documents
180
</table>
181
% if ( ref $filter eq 'HASH' ) {
182
% while ( my ($key, $value) = each %$filter ) {
183
% next if $key eq 's';
184
<input type="hidden" name="<% $key %>" value="<% $value |h %>">
185
% }
186
% }
187
% if ( $inline_status || $delete_status ) {
188
<div style="text-align:right; padding:10px 0;">
189
% if ( $inline_status ) {
190
<input type="submit" name="update" value="Сохранить изменения" class="input_btn">
191
% }
192
% if ( $delete_status ) {
193
<input type="submit" name="delete" value="Удалить выбранные" class="input_btn" onclick="return confirm('Все отмеченные позиции будут удалены');">
194
% }
195
</div>
196
% }
197
</form>
198
<%args>
199
200
$section => undef
201
$documents => undef
202
$columns => undef
203
$id => undef
204
$filter => undef
205
$status => undef
206
207
</%args>
208
<%init>
209
210
return unless ref $documents eq 'ARRAY';
211
return unless ref $columns eq 'ARRAY';
212
213
@$columns = grep { $_->{attr} ne 'status' } @$columns;
214
my $toopi = $project->documents();
215
my $inline_status = 0;
216
my $delete_status = 0;
217
my $params = ref $filter eq 'HASH' ? join ('&', map { $_.'='.$filter->{$_} } keys %$filter ) : '';
218
my $params_unclassed = ref $filter eq 'HASH' ? join ('&', map { $_.'='.$filter->{$_} } grep { $_ ne 'class' } keys %$filter ) : '';
219
my $params_unsection = ref $filter eq 'HASH' ? join ('&', map { $_.'='.$filter->{$_} } grep { $_ ne 's' } keys %$filter ) : '';
220
221
my $active_rights = $m->comp('/contenido/webshop/subs/user_rights.msn');
222
223
my %users;
224
my ($href, @actions, $user_id);
225
if ( $status == 1 && (!$active_rights || $active_rights == 1) ) {
226
$href = 'take_care.html';
227
@actions = { href => $href, name => 'обработать' };
228
$user_id = 'manager_id';
229
} elsif ( $status == 2 && (!$active_rights || $active_rights == 2) ) {
230
$href = 'facility.html';
231
@actions = { href => $href, name => 'собрать' };
232
$user_id = 'vault_id';
233
} elsif ( $status == 3 && (!$active_rights || $active_rights == 3) ) {
234
$href = 'delivery.html';
235
@actions = { href => $href, name => 'доставить' };
236
$user_id = 'postman_id';
237
} elsif ( $status == 4 && !$active_rights ) {
238
$href = 'delivery.html';
239
@actions = { href => $href, name => 'просмотр' };
240
}
241
242
</%init>
utf8/plugins/webshop/comps/contenido/webshop/components/order_form.msn
1
<!-- Форма для редактирования объекта -->
2
<a name="top"></a>
3
% if ( $context eq 'facil' ) {
4
<b style="font-size:110%">Комплектация ЗАКАЗА</b>
5
% } elsif ( $context eq 'post' ) {
6
<b style="font-size:110%">Доставка ЗАКАЗА</b>
7
% } else {
8
<b style="font-size:110%">Просмотр и редактирование ЗАКАЗА</b>
9
% }
10
<center>
11
<table width="100%" cellpadding="0" cellspacing="0" border="0">
12
<tr>
13
<td bgcolor="#999999">
14
15
16
<table width="100%" cellpadding="0" cellspacing="1" border="0">
17
<tr><td valign="top" width="50%" bgcolor="#ffffff">
18
19
<form enctype="multipart/form-data" action="<% $action %>" method="POST" name="form" onSubmit="javascript:Save()">
20
21
<table border="0" width="100%" cellspacing="0" cellpadding="6">
22
<tr>
23
<td align="right">
24
<input type="submit" value="Сохранить" class="input_btn">
25
<input type="submit" name="_save_and_leave" value="Сохранить и выйти" class="input_btn">
26
<input type="submit" name="delete" value="Отменить" onClick="return confirm('Отменить заказ?');" class="input_btn"><br>
27
% if ( $context eq 'facil' ) {
28
<input type="submit" name="_ret_manager" value="Вернуть менеджеру" class="input_btn">
29
<input type="submit" name="_save_and_deliver" value="Отправить в доставку" class="input_btn">
30
% } elsif ( $context eq 'post' ) {
31
<input type="submit" name="_ret_manager" value="Вернуть менеджеру" class="input_btn">
32
<input type="submit" name="_ret_facility" value="Вернуть в доставку" class="input_btn">
33
<input type="submit" name="_save_and_deliver" value="Отметка о доставке" class="input_btn">
34
% } else {
35
<input type="submit" name="_save_and_complect" value="На комплектацию" class="input_btn">
36
<input type="submit" name="_save_and_deliver" value="Сразу в доставку" class="input_btn">
37
% }
38
39
<input type="hidden" name="control_charset" value="Контроль">
40
% if ( ref $filter_params eq 'HASH' ) {
41
% while ( my ($key, $value) = each %$filter_params ) {
42
% next if grep { $_->{attr} eq $key } $object->structure();
43
<input type="hidden" name="<% $key %>" value="<% $value %>">
44
% }
45
% }
46
</td>
47
</tr>
48
</table>
49
50
<center>
51
<table width="98%" cellpadding="1" cellspacing="0" border="0">
52
53
<%perl>
54
55
my @properties = $object->structure();
56
57
for (0..$#properties)
58
{
59
60
my $prop = $properties[$_];
61
my $field = $prop->{attr};
62
next if ($prop->{hidden} == 1) || ($prop->{type} eq 'external');
63
if ( $context eq 'facil' ) {
64
next unless $prop->{$context.'show'} || $prop->{$context."edit"};
65
} elsif ( $context eq 'post' ) {
66
next unless $prop->{$context.'show'} || $prop->{$context."edit"};
67
} else {
68
next if $prop->{manager_hidden};
69
}
70
71
</%perl>
72
<tr><td height="8"></td></tr>
73
<tr><td nowrap>
74
<table cellpadding="0" cellspacing="0" border="0">
75
<tr>
76
% if( ( $prop->{type} eq 'flag' ) || ($prop->{type} eq 'checkbox') ) {
77
<td><& "/contenido/components/inputs/checkbox.msn", prop => $prop, object=>$object, options=>$options, name => $prop->{attr}, check => $object->{$prop->{attr}}, id => $object->id() &></td>
78
% }
79
<td nowrap><b><% $prop->{rusname} %></b> /</td>
80
<td align="right" nowrap><font color="#888888" size="-1"> name="<% $prop->{attr} %>"</font></td>
81
% if( $prop->{readonly} ) {
82
<td align="right" nowrap> / <font color="#CC0000" size="-1">Значение нельзя изменить</font></td>
83
% }
84
</tr>
85
</table>
86
</td></tr>
87
% if ( ($context && $prop->{$context.'show'}) || $prop->{manshow} ) {
88
% if ( exists $prop->{lookup_opts} ) {
89
% my $doc = $keeper->get_document_by_id( $object->$field, class => $prop->{lookup_opts}{class} );
90
<tr><td><% $doc->name %></td></tr>
91
% } elsif ( $prop->{type} eq 'status' ) {
92
% my ($case) = grep { $_->[0] == $object->$field } @{$prop->{cases}};
93
<tr><td><% ref $case ? $case->[1] : '<span style="color:red;">Значение: '.$object->$field.'</span>' %></td></tr>
94
% } else {
95
<tr><td><% $object->$field %></td></tr>
96
% }
97
% } else {
98
% if ($prop->{type} eq 'parent') {
99
<tr><td><& "/contenido/components/inputs/$prop->{type}.msn", prop => $prop, object=>$object, options=>$options, name => $prop->{attr}, check => ($object->{$prop->{attr} } || $prop->{default} || $sect_id ), id => $object->id() &></td></tr>
100
% } elsif( ( $prop->{type} ne 'flag' ) && ($prop->{type} ne 'checkbox') ) {
101
<tr><td><& "/contenido/components/inputs/$prop->{type}.msn", prop => $prop, object=>$object, options => $options, name => $prop->{attr}, check => $object->{$prop->{attr}}, id => ($object->id() || 0) &></td></tr>
102
% }
103
% }
104
% }
105
<tr>
106
<td><br>
107
108
% for (0..$#properties) {
109
% my $prop = $properties[$_];
110
% next if ($prop->{hidden} != 1);
111
<input type="hidden" name="<% $prop->{attr} %>" value="<% html_escape($object->{ $prop->{attr} }) %>">
112
% }
113
<input type="hidden" name="sect_id" value="<% $sect_id %>">
114
%#если класса в свойствах обьекта нет все равно надо его пробросить как hidden
115
% unless (grep {$_->{attr} eq 'class'} @properties) {
116
<input type="hidden" name="class" value="<% html_escape($object->{class}) %>">
117
% }
118
<input type="hidden" name="save" value="1">
119
120
</td>
121
</tr>
122
</table>
123
</center>
124
125
</form>
126
127
</td>
128
<td valign="top" bgcolor="white" width="50%">
129
<% spacer(w=>270) %>
130
<div><iframe name="DocFinder" id="DocFinder" src="/contenido/find_document.html" frameborder="0"
131
marginheight="0" marginwidth="0" width="100%" height="0"></iframe></div>
132
<table width="100%" cellpadding="5" cellspacing="0" border="0"><tr><td>
133
<p><font size=+1><b><nobr>Список позиций в заказе:</nobr></b></font></p>
134
135
136
<& /contenido/webshop/components/order_list.msn, order => $object, filter_params => $filter_params, context => $context &>
137
138
139
140
</table>
141
</td></tr></table>
142
143
144
</td></tr>
145
</table>
146
147
</td></tr></table>
148
</center>
149
150
<script>
151
<!--
152
function updateList(theFild,value,text) {
153
for (var i = 0; i < theFild.options.length; i++) {
154
if (theFild.options[i].value == value) {
155
return false;
156
}
157
}
158
var option_length = theFild.options.length++;
159
eval("theFild.options[option_length].value=value");
160
eval("theFild.options[option_length].text=text");
161
162
Save(); this.form.submit();
163
164
}
165
//-->
166
</script>
167
168
169
<%ARGS>
170
171
$proto => 'documents'
172
$object => undef
173
$context => undef
174
$sect_id => 1
175
$filter_params => undef
176
177
</%ARGS>
178
179
<%ONCE>
180
my $PROTOS = {
181
'documents' => ['документа','document.html','документу','документе'],
182
'sections' => ['секции','section.html','секции','секции'],
183
'links' => ['связи','link.html','связи','связи'],
184
'users' => ['пользователя','users.html','пользователю','пользователе'],
185
};
186
</%ONCE>
187
188
<%INIT>
189
return undef unless ref($object);
190
191
my $action = $context eq 'facil' ? 'facility.html' : $context eq 'post' ? 'delivery.html' : 'take_care.html';
192
my $toopi = $project->$proto();
193
my $options = {};
194
if ($toopi && (ref($toopi) eq 'HASH') && (exists($toopi->{ $object->class }))) {
195
%{ $options } = %{ $toopi->{ $object->class } };
196
}
197
</%INIT>
utf8/plugins/webshop/comps/contenido/webshop/components/order_list.msn
1
<style>
2
3
.number { text-align:right; padding-right:5px; }
4
.blue { color:blue; }
5
input.text { border:1px solid gray; }
6
7
</style>
8
% if ( @basket ) {
9
<form action="recount.html" method="post">
10
<table width="100%" border="0" cellpadding="4" cellspacing="0" class="tlistdocs" bgcolor="white">
11
<tr bgcolor="#efefef">
12
<th><% $context ? ' ' : '<img src="/contenido/i/actions/delete.gif">' %></th>
13
<th>Артикул</th>
14
<th>Название</th>
15
<th>Цвет</th>
16
<th>Цена</th>
17
<th>Кол-во</th>
18
<th>Сумма</th>
19
</tr>
20
21
% foreach my $obj ( @basket ) {
22
% $total_num += $obj->number;
23
% $total_sum += $obj->number * $obj->price;
24
<tr><td>\
25
% if ( $context ) {
26
\
27
% } else {
28
<input type="checkbox" name="delete" value="<% $obj->id %>">\
29
% }
30
</td>
31
<td><% $obj->articul %></td>
32
<td><% $obj->name %></td>
33
<td><% $obj->colour %></td>
34
% if ( $context ) {
35
<td class="number"><% $obj->price %></td>
36
<td class="number"><% $obj->number %></td>
37
% } else {
38
<td class="number"><input type="text" class="text number" size="8" name="item_<% $obj->id %>.price" value="<% $obj->price %>"></td>
39
<td class="number"><input type="text" class="text number" size="5" name="item_<% $obj->id %>.num" value="<% $obj->number %>"></td>
40
% }
41
<td class="number"><% $obj->number * $obj->price %></td>
42
</tr>
43
% }
44
45
<tr>
46
<td colspan="5" align="right"><b>Итого товаров</b></td>
47
<td class="number"><b class="blue"><% $total_num %></b></td>
48
<td class="number"><b class="blue"><% $total_sum %></b></td>
49
</tr>
50
<tr>
51
<td colspan="5" align="right"><b>Доставка</b></td>
52
<td class="number" colspan="2"><b class="blue"><% $order->sum_delivery %></b></td>
53
</tr>
54
<tr>
55
<td colspan="5" align="right"><b>Итого с доставкой</b></td>
56
<td class="number" colspan="2"><b class="blue"><% $total_sum + ($order->sum_delivery || 0) %></b></td>
57
</tr>
58
59
</table>
60
% unless ( $context ) {
61
<div class="number"><input type="submit" name="update" value="Пересчитать" class="input_btn"></div>
62
% }
63
% if ( ref $filter_params eq 'HASH' ) {
64
% while ( my ($key, $value) = each %$filter_params ) {
65
% next if grep { $_->{attr} eq $key } $order->structure();
66
<input type="hidden" name="<% $key %>" value="<% $value %>">
67
% }
68
% }
69
<input type="hidden" name="id" value="<% $order->id %>">
70
71
72
</form>
73
% } else {
74
<div style="color:red; font-size:110%; margin:20px 0;">Заказ пустой</div>
75
% }
76
<%args>
77
78
$order => undef
79
$filter_params => undef
80
$context => undef
81
82
</%args>
83
<%init>
84
85
return unless ref $order;
86
my @basket = $keeper->get_documents(
87
class => 'webshop::Basket',
88
status => 1,
89
order_id => $order->id,
90
);
91
my $total_sum = 0;
92
my $total_num = 0;
93
94
</%init>
utf8/plugins/webshop/comps/contenido/webshop/components/set_properties.msn
1
<%ARGS>
2
3
$object => undef
4
$SETS => undef
5
$context=> undef
6
7
</%ARGS>
8
<%INIT>
9
10
use Contenido::File;
11
use vars qw($keeper $request );
12
13
$request->{local_codepage} = 'UTF8';
14
$request->{local_codepage} = 'WIN' if Convert::Cyrillic::cstocs('WIN', 'UTF8', $SETS->{control_charset}) eq 'Контроль';
15
$request->{local_codepage} = 'KOI8' if Convert::Cyrillic::cstocs('KOI8', 'UTF8', $SETS->{control_charset}) eq 'Контроль';
16
17
warn "Contenido Debug: Форма для редактирования пришла в кодировке ".$request->{local_codepage}."\n" if ($state->debug());
18
19
my @properties = $object->structure();
20
for (0..$#properties)
21
{
22
my $prop = $properties[$_];
23
my $name = $prop->{attr};
24
my $type = $prop->{type};
25
26
next if ($name eq 'sections');
27
next if ($name eq 'id');
28
29
next if $prop->{hidden};
30
next if $context =~ /(facil|post)/ && $prop->{$context.'show'};
31
next if !$context && ($prop->{manshow} || $prop->{manager_hidden});
32
33
if ( exists $SETS->{$name} ) {
34
if ($m->comp_exists("/contenido/components/outputs/$type.msn")) {
35
$object->{$name} = $m->comp("/contenido/components/outputs/$type.msn", SETS => $SETS, name => $name, object => $object );
36
} else {
37
$object->{$name} = $m->comp('/contenido/components/filter.msn', str => $SETS->{$name} );
38
}
39
}
40
41
}
42
43
# По идее - мы работаем с ссылкой, так что ничего не надо возвращать!
44
# O-ле! О-ле! О-ле!
45
return 1;
46
47
</%INIT>
utf8/plugins/webshop/comps/contenido/webshop/delivery.html
1
%# vim:syn=mason
2
<& "/contenido/components/header.msn" &>
3
<& "/contenido/components/naviline.msn", sect_id => $owner->id &>
4
5
% if ($error) {
6
<div align="center" style="font-size:110%; color:red;">
7
<% $error %>
8
</div>
9
<br><br>
10
% }
11
12
% if (!ref($document)) {
13
% if ($id) {
14
<div align="center" style="font-size:110%; color:red;">
15
Документ с идентификатором <% $id %> не найден
16
</div>
17
<br><br>
18
% } elsif ($class) {
19
<!-- Блок с выбором нового документа для создания -->
20
<table width="50%" border="0"><tr><td>
21
<fieldset>
22
<legend>Выберите тип документа для создания</legend>
23
<& "/contenido/components/new_objects_form.msn", proto => 'documents', sect_id => $owner->id &>
24
</fieldset>
25
</td></tr></table>
26
% } else {
27
<div align="center" style="font-size:110%; color:red;">
28
Неверный вызов документа!!! (отсутствуют id и class одновременно)
29
</div>
30
<br><br>
31
% }
32
33
% } else {
34
% if ( $document->postman_id == $user->id ) {
35
<& "/contenido/components/obj_list_js.msn", object => $document &>
36
<& "/contenido/webshop/components/order_form.msn",
37
context => 'post',
38
object => $document,
39
proto => 'documents',
40
sect_id => $owner->id,
41
clone => $clone,
42
filter_params => \%filter_params,
43
&>
44
% } else {
45
% my $profile = $keeper->get_user_by_id( $document->postman_id );
46
<div style="padding:10px;">
47
<b style="font-size:110%">Доставка ЗАКАЗА</b>
48
<p style="color:red"><b>Внимание!</b> Доставку заказа осуществляет <% $profile->name %></p>
49
<p><a href="./?ost=3"><< Вернуться в список доставки</a></p>
50
</div>
51
% }
52
% }
53
54
</body>
55
</html>
56
<%ARGS>
57
$p => 1
58
$class => 'webshop::Order'
59
$sect_id => undef
60
$s_alias => undef
61
$id => undef
62
$delete => undef
63
$save => undef
64
$clone => undef
65
$activate => undef
66
$deactivate => undef
67
</%ARGS>
68
<%INIT>
69
70
&abort404 unless $class;
71
my $error='';
72
### !!! При добавлении переменных в ARGS их надо внести в список исключений в структуре ниже
73
74
my $filter = $m->comp('/contenido/components/context.msn', name => 'filter');
75
76
my $document;
77
my $new;
78
my $now = Contenido::DateTime->new;
79
80
if ($id && ($id !~ /\D/) && ($id > 0)) {
81
$document = $keeper->get_document_by_id($id, class=>$class);
82
if ( $clone && exists $document->{'attributes'}->{'dtime'} ) {
83
$document->dtime(undef);
84
}
85
} elsif ( ($class) && (length($class)>0) && (! ref($document)) ) {
86
$document = new $class ($keeper);
87
$new = 1;
88
my @properties = $document->structure();
89
foreach my $prop ( @properties ) {
90
my $attr = $prop->{attr};
91
### !!! Если не стандартная переменная, то можем инициализировать
92
if ( exists $ARGS{$attr} && ! grep { $prop->{attr} eq $_ } qw( class sect_id id delete save clone s_alias activate deactivate p s use_section alpha alpha_search search_by search ) ) {
93
$document->$attr($ARGS{$attr});
94
}
95
}
96
}
97
&abort404 unless ref $document;
98
99
my @props = $document->structure();
100
my %filter_params;
101
if ($ARGS{use_section} && !grep { $_->{attr} eq 'use_section' } @props ) {
102
$filter_params{use_section} = $ARGS{use_section};
103
$filter_params{class} = $document->class;
104
}
105
$filter_params{alpha} = $ARGS{alpha} if $ARGS{alpha} && !grep { $_->{attr} eq 'alpha' } @props;
106
$filter_params{alpha_search} = $ARGS{alpha_search} if $ARGS{alpha_search} && !grep { $_->{attr} eq 'alpha_search' } @props;
107
$filter_params{search_by} = $ARGS{search_by} if $ARGS{search_by} && !grep { $_->{attr} eq 'search_by' } @props;
108
$filter_params{search} = $ARGS{search} if $ARGS{search} && !grep { $_->{attr} eq 'search' } @props;
109
$filter_params{s} = $ARGS{s} if $ARGS{s} && !grep { $_->{attr} eq 's' } @props;
110
$filter_params{p} = $p if $p > 1;
111
my $return_params = join ('&', map { $_.'='.$filter_params{$_} } grep { $_ ne 's' } keys %filter_params );
112
113
114
if ($s_alias) {
115
$sect_id = $project->s_alias->{$s_alias};
116
}
117
118
if ( (! $sect_id) && (ref($document)) && ($document->id) ) {
119
$sect_id = $document->section();
120
}
121
my $owner = $keeper->get_section_by_id ($sect_id || $Contenido::Section::ROOT || 1);
122
123
if (! ref($owner)) {
124
$owner = $keeper->get_section_by_id ($Contenido::Section::ROOT || 1);
125
}
126
if (! ref($owner)) {
127
warn "Contenido Die: Не могу найти корневую секцию\n";
128
return undef;
129
}
130
131
if ( ref $document && !$document->postman_id ) {
132
$document->postman_id( $user->id );
133
$document->store;
134
}
135
if (ref($document) && $document->id() && $document->section()) {
136
my $document_access = $user->section_accesses($user, $owner);
137
if ($document_access != 2) {
138
$m->clear_buffer;
139
$m->abort(403);
140
}
141
}
142
143
# Удаление...
144
if (defined($id) && ($id > 0) && $delete) {
145
$document->status( 5 );
146
$document->store;
147
148
$m->redirect("./?ost=3".($return_params ? '&'.$return_params : ''));
149
}
150
151
# Сохранение существующего документа или создание нового...
152
# Кстати, пока никак не обрабатываются связи...
153
elsif ( $save == 1 )
154
{
155
my $clonesource;
156
if ( $clone ) {
157
$clonesource = $keeper->get_document_by_id ($clone,
158
class => $document->class,
159
);
160
}
161
if ($m->comp('/contenido/webshop/components/set_properties.msn', object => $document, SETS => \%ARGS, context => 'post') != 1)
162
{
163
# Ошибка, надо бы обработать...
164
warn "Contenido Warning: Не могу установить значения полей!\n";
165
}
166
if ( $clone ) {
167
$m->comp('/contenido/components/clone_attachments.msn', object => $document, source => $clonesource );
168
$document->sections( $clonesource->sections );
169
} elsif ( $new ) {
170
$document->sections( $owner->id, $filter > 0 ? ($filter) : ());
171
}
172
173
if ( $ARGS{_save_and_deliver} ) {
174
$document->status(4);
175
$document->postman_id( $user->id );
176
$document->etime( $now->ymd('-').' '.$now->hms );
177
} elsif ( $ARGS{_ret_manager} ) {
178
$document->status(1);
179
} elsif ( $ARGS{_ret_facility} ) {
180
$document->status(2);
181
} elsif ( $ARGS{_save_and_leave} ) {
182
}
183
184
unless ($document->store()) {
185
# Ошибка, надо бы обработать...
186
$error="Ошибка сохранения ($keeper->{last_error})";
187
} else {
188
189
if ($ARGS{_save_and_deliver}) {
190
$m->redirect("./?ost=3");
191
} elsif ($ARGS{_ret_manager}) {
192
$m->redirect("./?ost=3");
193
} elsif ($ARGS{_ret_facility}) {
194
$m->redirect("./?ost=3");
195
} elsif ($ARGS{_save_and_leave}) {
196
$m->redirect("./?ost=1".($return_params ? '&'.$return_params : ''));
197
}
198
199
$m->redirect("delivery.html?id=".$document->id.($return_params ? '&'.$return_params : ''));
200
}
201
}
202
203
</%INIT>
utf8/plugins/webshop/comps/contenido/webshop/dhandler
1
<& $call, %ARGS &>
2
<%init>
3
4
my $call;
5
if ( $r->uri eq '/contenido/webshop/' ) {
6
$call = 'index.html';
7
} else {
8
&abort404;
9
}
10
11
</%init>
utf8/plugins/webshop/comps/contenido/webshop/facility.html
1
%# vim:syn=mason
2
<& "/contenido/components/header.msn" &>
3
<& "/contenido/components/naviline.msn", sect_id => $owner->id &>
4
5
% if ($error) {
6
<div align="center" style="font-size:110%; color:red;">
7
<% $error %>
8
</div>
9
<br><br>
10
% }
11
12
% if (!ref($document)) {
13
% if ($id) {
14
<div align="center" style="font-size:110%; color:red;">
15
Документ с идентификатором <% $id %> не найден
16
</div>
17
<br><br>
18
% } elsif ($class) {
19
<!-- Блок с выбором нового документа для создания -->
20
<table width="50%" border="0"><tr><td>
21
<fieldset>
22
<legend>Выберите тип документа для создания</legend>
23
<& "/contenido/components/new_objects_form.msn", proto => 'documents', sect_id => $owner->id &>
24
</fieldset>
25
</td></tr></table>
26
% } else {
27
<div align="center" style="font-size:110%; color:red;">
28
Неверный вызов документа!!! (отсутствуют id и class одновременно)
29
</div>
30
<br><br>
31
% }
32
33
% } else {
34
35
% if ( $document->vault_id == $user->id ) {
36
<& "/contenido/components/obj_list_js.msn", object => $document &>
37
<& "/contenido/webshop/components/order_form.msn",
38
context => 'facil',
39
object => $document,
40
proto => 'documents',
41
sect_id => $owner->id,
42
clone => $clone,
43
filter_params => \%filter_params,
44
&>
45
% } else {
46
% my $profile = $keeper->get_user_by_id( $document->vault_id );
47
<div style="padding:10px;">
48
<b style="font-size:110%">Комплектация ЗАКАЗА</b>
49
<p style="color:red"><b>Внимание!</b> Комплектацией заказа занимается <% $profile->name %></p>
50
<p><a href="./?ost=2"><< Вернуться в список комплектации</a></p>
51
</div>
52
% }
53
% }
54
55
</body>
56
</html>
57
<%ARGS>
58
$p => 1
59
$class => 'webshop::Order'
60
$sect_id => undef
61
$s_alias => undef
62
$id => undef
63
$delete => undef
64
$save => undef
65
$clone => undef
66
$activate => undef
67
$deactivate => undef
68
</%ARGS>
69
<%INIT>
70
71
&abort404 unless $class;
72
my $error='';
73
### !!! При добавлении переменных в ARGS их надо внести в список исключений в структуре ниже
74
75
my $filter = $m->comp('/contenido/components/context.msn', name => 'filter');
76
77
my $document;
78
my $new;
79
my $now = Contenido::DateTime->new;
80
81
if ($id && ($id !~ /\D/) && ($id > 0)) {
82
$document = $keeper->get_document_by_id($id, class=>$class);
83
if ( $clone && exists $document->{'attributes'}->{'dtime'} ) {
84
$document->dtime(undef);
85
}
86
} elsif ( ($class) && (length($class)>0) && (! ref($document)) ) {
87
$document = new $class ($keeper);
88
$new = 1;
89
my @properties = $document->structure();
90
foreach my $prop ( @properties ) {
91
my $attr = $prop->{attr};
92
### !!! Если не стандартная переменная, то можем инициализировать
93
if ( exists $ARGS{$attr} && ! grep { $prop->{attr} eq $_ } qw( class sect_id id delete save clone s_alias activate deactivate p s use_section alpha alpha_search search_by search ) ) {
94
$document->$attr($ARGS{$attr});
95
}
96
}
97
}
98
&abort404 unless ref $document;
99
100
my @props = $document->structure();
101
my %filter_params;
102
if ($ARGS{use_section} && !grep { $_->{attr} eq 'use_section' } @props ) {
103
$filter_params{use_section} = $ARGS{use_section};
104
$filter_params{class} = $document->class;
105
}
106
$filter_params{alpha} = $ARGS{alpha} if $ARGS{alpha} && !grep { $_->{attr} eq 'alpha' } @props;
107
$filter_params{alpha_search} = $ARGS{alpha_search} if $ARGS{alpha_search} && !grep { $_->{attr} eq 'alpha_search' } @props;
108
$filter_params{search_by} = $ARGS{search_by} if $ARGS{search_by} && !grep { $_->{attr} eq 'search_by' } @props;
109
$filter_params{search} = $ARGS{search} if $ARGS{search} && !grep { $_->{attr} eq 'search' } @props;
110
$filter_params{s} = $ARGS{s} if $ARGS{s} && !grep { $_->{attr} eq 's' } @props;
111
$filter_params{p} = $p if $p > 1;
112
my $return_params = join ('&', map { $_.'='.$filter_params{$_} } grep { $_ ne 's' } keys %filter_params );
113
114
115
if ($s_alias) {
116
$sect_id = $project->s_alias->{$s_alias};
117
}
118
119
if ( (! $sect_id) && (ref($document)) && ($document->id) ) {
120
$sect_id = $document->section();
121
}
122
my $owner = $keeper->get_section_by_id ($sect_id || $Contenido::Section::ROOT || 1);
123
124
if (! ref($owner)) {
125
$owner = $keeper->get_section_by_id ($Contenido::Section::ROOT || 1);
126
}
127
if (! ref($owner)) {
128
warn "Contenido Die: Не могу найти корневую секцию\n";
129
return undef;
130
}
131
132
if ( ref $document ) {
133
$document->vault_id( $user->id );
134
$document->store;
135
}
136
if (ref($document) && $document->id() && $document->section()) {
137
my $document_access = $user->section_accesses($user, $owner);
138
if ($document_access != 2) {
139
$m->clear_buffer;
140
$m->abort(403);
141
}
142
}
143
144
# Удаление...
145
if (defined($id) && ($id > 0) && $delete) {
146
$document->status( 5 );
147
$document->store;
148
149
$m->redirect("./?ost=2".($return_params ? '&'.$return_params : ''));
150
}
151
152
# Сохранение существующего документа или создание нового...
153
# Кстати, пока никак не обрабатываются связи...
154
elsif ( $save == 1 )
155
{
156
if ($m->comp('/contenido/webshop/components/set_properties.msn', object => $document, SETS => \%ARGS, context => 'facil') != 1)
157
{
158
# Ошибка, надо бы обработать...
159
warn "Contenido Warning: Не могу установить значения полей!\n";
160
}
161
162
if ( $ARGS{_save_and_deliver} ) {
163
$document->vault_id( $user->id );
164
$document->ftime( $now->ymd('-').' '.$now->hms );
165
$document->status(3);
166
} elsif ( $ARGS{_ret_manager} ) {
167
$document->status(1);
168
} elsif ( $ARGS{_save_and_leave} ) {
169
$document->vault_id( undef );
170
$document->status(1);
171
}
172
173
unless ($document->store()) {
174
# Ошибка, надо бы обработать...
175
$error="Ошибка сохранения ($keeper->{last_error})";
176
} else {
177
178
if ($ARGS{_save_and_deliver}) {
179
$m->redirect("./?ost=3");
180
} elsif ($ARGS{_ret_manager}) {
181
$m->redirect("./?ost=2");
182
} elsif ($ARGS{_save_and_leave}) {
183
$m->redirect("./?ost=2");
184
} elsif ($ARGS{_save_and_leave}) {
185
$m->redirect("./?ost=1".($return_params ? '&'.$return_params : ''));
186
}
187
188
$m->redirect("take_care.html?id=".$document->id.($return_params ? '&'.$return_params : ''));
189
}
190
}
191
192
</%INIT>
utf8/plugins/webshop/comps/contenido/webshop/index.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
<table width="100%" cellspacing="0" cellpadding="0" border="0">
11
<tr valign="top">
12
<td width="35%">
13
14
<& /contenido/webshop/components/block_order_status_changer.msn, status => $ost &>
15
16
</td>
17
<td width="1%"> </td>
18
<td width="65%">
19
20
<fieldset>
21
<legend>Заказы со статусом "<span style="color:yellow"><% $current_status->[1] %></span>"</legend>
22
23
% if ( $total ) {
24
25
<div style="font-size:75%; font-family:Arial;">
26
<& /inc/pages_.msn, p=>$p, n=>$size, total=>$total, params=>\%filter_params, &>
27
<div style="height:5px"><spacer type="block" height="5"></div>
28
</div>
29
30
<& /contenido/webshop/components/order_browse.msn, documents => \@documents, columns => \@columns, filter => \%filter_params, status => $ost, %ARGS &>
31
32
<div style="font-size:75%; font-family:Arial;">
33
<& /inc/pages_.msn, p=>$p, n=>$size, total=>$total, params=>\%filter_params, &>
34
<div style="height:5px"><spacer type="block" height="5"></div>
35
</div>
36
37
% } else {
38
<h4 align="center"><i>---- Нет документов -----</i></h4>
39
% }
40
41
42
</td>
43
<td width="1%"> </td>
44
</tr>
45
</table>
46
47
</body>
48
</html>
49
<%args>
50
51
$ost => 1
52
$p => 1
53
54
</%args>
55
<%init>
56
57
my %filter_params;
58
59
my (@documents, $total);
60
$filter_params{ost} = $ost if $ost != 1;
61
my $size = 40;
62
63
@documents = $keeper->get_documents(
64
class => 'webshop::Order',
65
status => $ost,
66
limit => $size,
67
offset => ($p-1)*$size,
68
);
69
$total = $keeper->get_documents(
70
class => 'webshop::Order',
71
status => $ost,
72
count => 1,
73
);
74
my @structure = webshop::Order->new( $keeper->{webshop} )->structure;
75
my @columns = sort { $a->{column} <=> $b->{column} }
76
grep { $_->{column} } @structure;
77
push @columns, {attr => '_act_', rusname => 'Действия'};
78
my ($status_map) = grep { $_->{attr} eq 'status' } @structure;
79
my ($current_status) = grep { $_->[0] == $ost } @{$status_map->{cases}};
80
81
</%init>
utf8/plugins/webshop/comps/contenido/webshop/recount.html
1
<pre><% Dumper(\%ARGS) %></pre>
2
<%args>
3
4
$delete => []
5
$update => undef
6
$id => undef
7
8
</%args>
9
<%init>
10
11
&abort404 unless $id && $id =~ /^\d+$/;
12
13
my $order = $keeper->{webshop}->get_orders( id => $id );
14
&abort404 unless ref $order;
15
16
if ( $update ) {
17
my @delete = ref $delete ? @$delete : ();
18
my (%renumber, %reprice);
19
while ( my ($key, $value) = each %ARGS ) {
20
if ( $key =~ /item_(\d+)\.num/ ) {
21
my $id = $1;
22
$renumber{$id} = $value if $value =~ /^\d+$/;
23
} elsif ( $key =~ /item_(\d+)\.price/ ) {
24
my $id = $1;
25
$reprice{$id} = $value if $value =~ /^[\d\.]+$/;
26
}
27
}
28
my ($total, $sum, $new_basket) = $keeper->{webshop}->recount(
29
order_id=> $order->id,
30
delete => \@delete,
31
price => \%reprice,
32
session_no_store=>1,
33
renumber => \%renumber,
34
);
35
$order->total( $total );
36
$order->sum( $sum );
37
$order->store;
38
$m->redirect('/contenido/webshop/take_care.html?id='.$id);
39
}
40
41
</%init>
utf8/plugins/webshop/comps/contenido/webshop/subs/user_rights.msn
1
<%init>
2
3
if ( grep { $_->{attr} eq 'type' } $user->structure ) {
4
return $user->type;
5
} else {
6
return 0;
7
}
8
9
</%init>
utf8/plugins/webshop/comps/contenido/webshop/take_care.html
1
%# vim:syn=mason
2
<& "/contenido/components/header.msn" &>
3
<& "/contenido/components/naviline.msn", sect_id => $owner->id &>
4
5
% if ($error) {
6
<div align="center" style="font-size:110%; color:red;">
7
<% $error %>
8
</div>
9
<br><br>
10
% }
11
12
% if (!ref($document)) {
13
% if ($id) {
14
<div align="center" style="font-size:110%; color:red;">
15
Документ с идентификатором <% $id %> не найден
16
</div>
17
<br><br>
18
% } elsif ($class) {
19
<!-- Блок с выбором нового документа для создания -->
20
<table width="50%" border="0"><tr><td>
21
<fieldset>
22
<legend>Выберите тип документа для создания</legend>
23
<& "/contenido/components/new_objects_form.msn", proto => 'documents', sect_id => $owner->id &>
24
</fieldset>
25
</td></tr></table>
26
% } else {
27
<div align="center" style="font-size:110%; color:red;">
28
Неверный вызов документа!!! (отсутствуют id и class одновременно)
29
</div>
30
<br><br>
31
% }
32
33
% } else {
34
35
<& "/contenido/components/obj_list_js.msn", object => $document &>
36
<& "/contenido/webshop/components/order_form.msn",
37
object => $document,
38
proto => 'documents',
39
sect_id => $owner->id,
40
filter_params => \%filter_params,
41
&>
42
% }
43
44
</body>
45
</html>
46
<%ARGS>
47
$p => 1
48
$class => 'webshop::Order'
49
$sect_id => undef
50
$s_alias => undef
51
$id => undef
52
$delete => undef
53
$save => undef
54
</%ARGS>
55
<%INIT>
56
57
&abort404 unless $class;
58
my $error='';
59
### !!! При добавлении переменных в ARGS их надо внести в список исключений в структуре ниже
60
61
my $filter = $m->comp('/contenido/components/context.msn', name => 'filter');
62
63
my $document;
64
my $new;
65
my $now = Contenido::DateTime->new;
66
67
if ($id && ($id !~ /\D/) && ($id > 0)) {
68
$document = $keeper->get_document_by_id($id, class=>$class);
69
} elsif ( ($class) && (length($class)>0) && (! ref($document)) ) {
70
$document = new $class ($keeper);
71
$new = 1;
72
my @properties = $document->structure();
73
foreach my $prop ( @properties ) {
74
my $attr = $prop->{attr};
75
### !!! Если не стандартная переменная, то можем инициализировать
76
if ( exists $ARGS{$attr} && ! grep { $prop->{attr} eq $_ } qw( class sect_id id delete save s_alias p s use_section alpha alpha_search search_by search ) ) {
77
$document->$attr($ARGS{$attr});
78
}
79
}
80
}
81
&abort404 unless ref $document;
82
83
my @props = $document->structure();
84
my %filter_params;
85
if ($ARGS{use_section} && !grep { $_->{attr} eq 'use_section' } @props ) {
86
$filter_params{use_section} = $ARGS{use_section};
87
$filter_params{class} = $document->class;
88
}
89
$filter_params{alpha} = $ARGS{alpha} if $ARGS{alpha} && !grep { $_->{attr} eq 'alpha' } @props;
90
$filter_params{alpha_search} = $ARGS{alpha_search} if $ARGS{alpha_search} && !grep { $_->{attr} eq 'alpha_search' } @props;
91
$filter_params{search_by} = $ARGS{search_by} if $ARGS{search_by} && !grep { $_->{attr} eq 'search_by' } @props;
92
$filter_params{search} = $ARGS{search} if $ARGS{search} && !grep { $_->{attr} eq 'search' } @props;
93
$filter_params{s} = $ARGS{s} if $ARGS{s} && !grep { $_->{attr} eq 's' } @props;
94
$filter_params{p} = $p if $p > 1;
95
my $return_params = join ('&', map { $_.'='.$filter_params{$_} } grep { $_ ne 's' } keys %filter_params );
96
97
98
if ($s_alias) {
99
$sect_id = $project->s_alias->{$s_alias};
100
}
101
102
if ( (! $sect_id) && (ref($document)) && ($document->id) ) {
103
$sect_id = $document->section();
104
}
105
my $owner = $keeper->get_section_by_id ($sect_id || $Contenido::Section::ROOT || 1);
106
107
if (! ref($owner)) {
108
$owner = $keeper->get_section_by_id ($Contenido::Section::ROOT || 1);
109
}
110
if (! ref($owner)) {
111
warn "Contenido Die: Не могу найти корневую секцию\n";
112
return undef;
113
}
114
115
if ( ref $document ) {
116
$document->manager_id( $user->id );
117
$document->store;
118
}
119
if (ref($document) && $document->id() && $document->section()) {
120
my $document_access = $user->section_accesses($user, $owner);
121
if ($document_access != 2) {
122
$m->clear_buffer;
123
$m->abort(403);
124
}
125
}
126
127
# Удаление...
128
if (defined($id) && ($id > 0) && $delete) {
129
$document->status( 5 );
130
$document->store;
131
132
$m->redirect("./?ost=1".($return_params ? '&'.$return_params : ''));
133
}
134
135
# Сохранение существующего документа или создание нового...
136
# Кстати, пока никак не обрабатываются связи...
137
elsif ( $save == 1 )
138
{
139
if ($m->comp('/contenido/webshop/components/set_properties.msn', object => $document, SETS => \%ARGS) != 1)
140
{
141
# Ошибка, надо бы обработать...
142
warn "Contenido Warning: Не могу установить значения полей!\n";
143
}
144
if ( $new ) {
145
$document->sections( $owner->id, $filter > 0 ? ($filter) : ());
146
}
147
148
if ( $ARGS{_save_and_deliver} ) {
149
$document->manager_id( $user->id );
150
$document->ftime( $now->ymd('-').' '.$now->hms );
151
$document->status(3);
152
} elsif ( $ARGS{_save_and_complect} ) {
153
$document->manager_id( $user->id );
154
$document->btime( $now->ymd('-').' '.$now->hms );
155
$document->status(2);
156
} elsif ( $ARGS{_save_and_leave} ) {
157
$document->manager_id( undef );
158
$document->status(1);
159
}
160
161
unless ($document->store()) {
162
# Ошибка, надо бы обработать...
163
$error="Ошибка сохранения ($keeper->{last_error})";
164
} else {
165
166
if ($ARGS{_save_and_complect}) {
167
$m->redirect("./?ost=2");
168
} elsif ($ARGS{_save_and_deliver}) {
169
$m->redirect("./?ost=3");
170
} elsif ($ARGS{_save_and_leave}) {
171
$m->redirect("./?ost=1".($return_params ? '&'.$return_params : ''));
172
}
173
174
$m->redirect("take_care.html?id=".$document->id.($return_params ? '&'.$return_params : ''));
175
}
176
}
177
178
</%INIT>
utf8/plugins/webshop/config.proto
1
#############################################################################
2
#
3
# Параметры данного шаблона необходимо ВРУЧНУЮ добавить в config.mk проекта
4
# и привести в соответствие с требованиями проекта
5
#
6
#############################################################################
7
8
PLUGINS += webshop
9
10
11
#############################################################################
12
# Параметр испфльзуется для переопределения на уровне проекта таблицы или класса
13
# для хранения описания товара
14
15
ITEM_DOCUMENT_CLASS = webshop::Item
16
REWRITE += ITEM_DOCUMENT_CLASS
17
18
ITEM_DOCUMENT_TABLE = webshop::SQL::Items
19
REWRITE += ITEM_DOCUMENT_TABLE
20
utf8/plugins/webshop/lib/webshop/Address.pm
1
package webshop::Address;
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' => 'delivery', 'type' => 'lookup', 'rusname' => 'Тип доставки',
15
'lookup_opts' => {
16
'class' => 'webshop::Delivery',
17
},
18
},
19
{ 'attr' => 'name', 'type' => 'string', 'rusname' => 'Контактное лицо' },
20
{ 'attr' => 'phone', 'type' => 'string', 'rusname' => 'Телефон для связи' },
21
{ 'attr' => 'zipcode', 'type' => 'string', 'rusname' => 'Почтовый индекс' },
22
{ 'attr' => 'town', 'type' => 'string', 'rusname' => 'Город' },
23
{ 'attr' => 'metro', 'type' => 'string', 'rusname' => 'Ближайшее метро' },
24
{ 'attr' => 'address', 'type' => 'text', 'rusname' => 'Адрес доставки', rows => 5 },
25
{ 'attr' => 'timeline', 'type' => 'string', 'rusname' => 'Предпочтительное время' },
26
{ 'attr' => 'description', 'type' => 'text', 'rusname' => 'Описание для курьера', rows => 5 },
27
)
28
}
29
30
sub class_name
31
{
32
return 'Заказ';
33
}
34
35
sub class_description
36
{
37
return 'Заказ';
38
}
39
40
sub class_table
41
{
42
return 'webshop::SQL::AddressTable';
43
}
44
45
1;
utf8/plugins/webshop/lib/webshop/Apache.pm
1
package webshop::Apache;
2
3
use strict;
4
use warnings 'all';
5
6
use webshop::State;
7
use Contenido::Globals;
8
9
10
sub child_init {
11
# встраиваем keeper плагина в keeper проекта
12
$keeper->{webshop} = webshop::Keeper->new($state->webshop);
13
}
14
15
sub request_init {
16
}
17
18
sub child_exit {
19
}
20
21
1;
utf8/plugins/webshop/lib/webshop/Basket.pm
1
package webshop::Basket;
2
3
use base "Contenido::Document";
4
sub extra_properties
5
{
6
return (
7
{ 'attr' => 'status', 'type' => 'status', 'rusname' => 'Статус заказа',
8
'cases' => [
9
[0, 'Элемент wish-листа'],
10
[1, 'Элемент корзины'],
11
],
12
},
13
{ 'attr' => 'articul', 'type' => 'string', 'rusname' => 'Артикул' },
14
{ 'attr' => 'colour', 'type' => 'string', 'rusname' => 'Цвет' },
15
{ 'attr' => 'discount_flag', 'type' => 'string', 'rusname' => 'Наличие скидки' },
16
{ 'attr' => 'discount', 'type' => 'text', 'rusname' => 'Формула скидки', rows => 15 },
17
)
18
}
19
20
sub price_formatted {
21
my $self = shift;
22
23
my $price = $self->price;
24
$price = reverse $price;
25
$price =~ s/(\d{3})/$1\ /g;
26
$price = reverse $price;
27
28
return $price;
29
}
30
31
sub total_formatted {
32
my $self = shift;
33
34
my $price = $self->price * ($self->number || 0);
35
$price = reverse $price;
36
$price =~ s/(\d{3})/$1\ /g;
37
$price = reverse $price;
38
39
return $price;
40
}
41
42
sub class_name
43
{
44
return 'Позиция в корзине';
45
}
46
47
sub class_description
48
{
49
return 'Позиция в корзине';
50
}
51
52
sub class_table
53
{
54
return 'webshop::SQL::Basket';
55
}
56
57
1;
utf8/plugins/webshop/lib/webshop/Delivery.pm
1
package webshop::Delivery;
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
{ 'attr' => 'dtime', 'hidden' => 1 },
14
{ 'attr' => 'price', 'type' => 'string', 'rusname' => 'Стоимость доставки (NN или NN%)', default => 0 },
15
{ 'attr' => 'abstr', 'type' => 'text', 'rusname' => 'Краткое описание' },
16
{ 'attr' => 'fields', 'type' => 'status_multi', 'rusname' => 'Поля, необходимые к заполнению',
17
'cases' => [
18
['timeline','Время заказа'],
19
['zipcode','Почтовый индекс'],
20
['town','Город'],
21
['address','Адрес'],
22
['metro','Ближайшее метро'],
23
],
24
},
25
)
26
}
27
28
sub class_name
29
{
30
return 'Способ доставки';
31
}
32
33
sub class_description
34
{
35
return 'Способ доставки';
36
}
37
38
sub pre_store
39
{
40
my $self = shift;
41
42
my $default_section = $project->s_alias->{delivery} if ref $project->s_alias eq 'HASH';
43
my $sections = $self->{sections};
44
if ( $default_section ) {
45
if ( ref $sections eq 'ARRAY' && scalar @$sections ) {
46
my @new_sects = grep { $_ != $default_section } @$sections;
47
push @new_sects, $default_section;
48
$self->sections(@new_sects);
49
} elsif ( $sections && !ref $sections && $sections != $default_section ) {
50
my @new_sects = ($default_section, $sections);
51
$self->sections(@new_sects);
52
} else {
53
$self->sections($default_section);
54
}
55
}
56
57
return 1;
58
}
59
60
1;
utf8/plugins/webshop/lib/webshop/DeliverySection.pm
1
package webshop::DeliverySection;
2
3
use Contenido::Section;
4
@ISA = ('Contenido::Section');
5
6
sub extra_properties
7
{
8
return (
9
{ 'attr' => 'brief', 'type' => 'text', 'rusname' => 'Описание секции' },
10
{ 'attr' => 'default_document_class', 'default' => 'webshop::Delivery' },
11
{ 'attr' => '_sorted', 'default' => 1 },
12
{ 'attr' => 'filters', 'hidden' => 1 },
13
{ 'attr' => 'order_by', 'hidden' => 1 },
14
)
15
}
16
17
sub class_name
18
{
19
return 'Cпособы доставки';
20
}
21
22
sub class_description
23
{
24
return 'Cпособы доставки';
25
}
26
27
1;
utf8/plugins/webshop/lib/webshop/Init.pm
1
package webshop::Init;
2
3
use strict;
4
use warnings 'all';
5
6
use Contenido::Globals;
7
use webshop::Apache;
8
use webshop::Keeper;
9
10
11
# загрузка всех необходимых плагину классов
12
# webshop::SQL::SomeTable
13
# webshop::SomeClass
14
Contenido::Init::load_classes(qw(
15
webshop::SQL::Basket
16
webshop::SQL::Order
17
18
webshop::Address
19
webshop::Basket
20
webshop::Order
21
webshop::Delivery
22
23
webshop::DeliverySection
24
));
25
26
sub init {
27
push @{ $state->{'available_documents'} }, ('webshop::Basket', 'webshop::Order', 'webshop::Delivery');
28
push @{ $state->{'available_sections'} }, 'webshop::DeliverySection';
29
0;
30
}
31
32
1;
utf8/plugins/webshop/lib/webshop/Keeper.pm
1
package webshop::Keeper;
2
3
use strict;
4
use warnings 'all';
5
use base qw(Contenido::Keeper);
6
7
use Data::Dumper;
8
use Contenido::Globals;
9
use webshop::Basket;
10
use webshop::SQL::Basket;
11
12
sub add_item {
13
my $self = shift;
14
my $object = shift;
15
my (%options) = @_;
16
17
return unless ref $object;
18
return unless $object->item_id && $object->number;
19
return unless $object->uid || $object->session;
20
21
22
my %opts;
23
if ( $object->uid ) {
24
$opts{uid} = $object->uid;
25
} elsif ( $object->session ) {
26
$opts{session} = $object->session;
27
}
28
my @items = $keeper->get_documents (
29
class => 'webshop::Basket',
30
status => [0,1],
31
order_id=> 0,
32
%opts,
33
);
34
my $total = 0;
35
my $sum = 0;
36
my $found = 0;
37
if ( @items ) {
38
foreach my $item ( @items ) {
39
if ( $object->item_id == $item->item_id && $object->color_id == $item->color_id ) {
40
$item->number($item->number + $object->number);
41
$item->status(1);
42
$item->store;
43
$found = 1;
44
}
45
$total += $item->number;
46
$sum += $item->number * $item->price;
47
}
48
}
49
unless ( $found ) {
50
$total += $object->number;
51
$sum += $object->number * $object->price;
52
$object->order_id(0);
53
$object->store;
54
}
55
my @plugins = split (/[\ |\t]+/, $state->{plugins});
56
if ( grep { $_ eq 'session' } @plugins ) {
57
$keeper->{session}->store_value ( basket_total => $total, basket_sum => $sum );
58
}
59
return ($total, $sum);
60
}
61
62
### Метод приведения корзины для пользователя в момент логина
63
#############################################################
64
sub merge_basket {
65
my $self = shift;
66
my (%opts) = @_;
67
68
warn "Merge begin: ".Dumper(\%opts)."\n" if $DEBUG;
69
return unless $opts{uid} && $opts{session};
70
71
my @items = $keeper->get_documents (
72
class => 'webshop::Basket',
73
status => [1,0],
74
order_id=> 0,
75
session => $opts{session},
76
);
77
foreach my $item ( @items ) {
78
$item->session(undef);
79
$item->uid( $opts{uid} );
80
$item->store;
81
}
82
@items = $keeper->get_documents (
83
class => 'webshop::Basket',
84
status => 1,
85
order_id=> 0,
86
uid => $opts{uid},
87
);
88
my $total = 0;
89
my $sum = 0;
90
foreach my $item ( @items ) {
91
$total += $item->number;
92
$sum += $item->number * $item->price;
93
}
94
my @plugins = split (/[\ |\t]+/, $state->{plugins});
95
if ( grep { $_ eq 'session' } @plugins ) {
96
$keeper->{session}->store_value ( basket_total => $total, basket_sum => $sum );
97
}
98
warn "Merge end\n" if $DEBUG;
99
return ($total, $sum);
100
}
101
102
103
sub get_basket {
104
my $self = shift;
105
my (%opts) = @_;
106
107
return unless $opts{uid} || $opts{session} || $opts{order_id};
108
109
my $uid = delete $opts{uid};
110
my $session = delete $opts{session};
111
unless ( exists $opts{order_id} && $opts{order_id} ) {
112
$opts{order_id} = 0;
113
if ( $uid ) {
114
$opts{uid} = $uid;
115
} elsif ( $session ) {
116
$opts{session} = $session;
117
}
118
}
119
$opts{status} = 1 unless exists $opts{status} && defined $opts{status};
120
my @items = $keeper->get_documents (
121
class => 'webshop::Basket',
122
%opts,
123
);
124
my $total = 0;
125
my $sum = 0;
126
foreach my $item ( @items ) {
127
if ( $item->status == 1 ) {
128
$total += $item->number;
129
$sum += $item->number * $item->price;
130
}
131
}
132
my @plugins = split (/[\ |\t]+/, $state->{plugins});
133
if ( grep { $_ eq 'session' } @plugins ) {
134
$keeper->{session}->store_value ( basket_total => $total, basket_sum => $sum );
135
}
136
return \@items;
137
}
138
139
140
sub clear_basket {
141
my $self = shift;
142
my (%opts) = @_;
143
144
return unless exists $opts{uid} || exists $opts{session};
145
146
my $table_name = webshop::SQL::Basket->db_table;
147
my $request = "delete from $table_name where order_id = 0 AND status = 1 AND";
148
my $dbh = $keeper->SQL;
149
my @vals;
150
if ( exists $opts{uid} && $opts{uid} ) {
151
$request .= " uid in (?)";
152
push @vals, $opts{uid};
153
} elsif ( exists $opts{session} ) {
154
$request .= " session in (?)";
155
push @vals, $opts{session};
156
}
157
warn "CLEAR: [$request]. VALS: [".join(',',@vals)."]\n" if $DEBUG;
158
my $statement = $dbh->prepare ($request);
159
$statement->execute( @vals ) || $log->error("DBI execute error on $request\n"."\ncalled with opts:\n".Data::Dumper::Dumper(\%opts));;
160
$statement->finish;
161
162
my @plugins = split (/[\ |\t]+/, $state->{plugins});
163
if ( grep { $_ eq 'session' } @plugins ) {
164
$keeper->{session}->store_value ( basket_total => 0, basket_sum => 0 );
165
}
166
}
167
168
169
sub clear_wishlist {
170
my $self = shift;
171
my (%opts) = @_;
172
173
return unless exists $opts{uid} || exists $opts{session};
174
175
my $table_name = webshop::SQL::Basket->db_table;
176
my $request = "delete from $table_name where order_id = 0 AND status = 0 AND";
177
my $dbh = $keeper->SQL;
178
my @vals;
179
if ( exists $opts{uid} ) {
180
$request .= " uid in (?)";
181
push @vals, $opts{uid};
182
} elsif ( exists $opts{session} ) {
183
$request .= " session in (?)";
184
push @vals, $opts{session};
185
}
186
my $statement = $dbh->prepare ($request);
187
$statement->execute( @vals ) || $log->error("DBI execute error on $request\n"."\ncalled with opts:\n".Data::Dumper::Dumper(\%opts));;
188
$statement->finish;
189
}
190
191
192
### Метод пересчета корзины
193
# Принимает на вход параметры:
194
# session => session_id пользователя
195
# uid => UID пользователя
196
# delete => массив или отдельный item_id
197
# renumber => ссылка на хеш вида item => number
198
#############################################################
199
sub recount {
200
my $self = shift;
201
my (%opts) = @_;
202
203
warn "Recount Started!!!\n" if $DEBUG;
204
return unless exists $opts{uid} || exists $opts{session} || exists $opts{order_id};
205
my $basket = $self->get_basket ( %opts );
206
return unless ref $basket eq 'ARRAY' && @$basket;
207
208
warn Dumper(\%opts) if $DEBUG;
209
210
my $total = 0;
211
my $sum = 0;
212
my @new_basket;
213
my $session_no_store = delete $opts{session_no_store};
214
foreach my $item ( @$basket ) {
215
my $delete = 0;
216
if ( exists $opts{renumber} && ref $opts{renumber} eq 'HASH' && exists $opts{renumber}{$item->id} && int($opts{renumber}{$item->id}) == 0 ) {
217
$delete = 1;
218
} elsif ( exists $opts{delete} && ref $opts{delete} eq 'ARRAY' ) {
219
$delete = 1 if grep { $_ == $item->id } @{ $opts{delete} };
220
} elsif ( exists $opts{delete} && $opts{delete} ) {
221
$delete = 1 if $item->id == $opts{delete};
222
}
223
if ( $delete ) {
224
warn "Item ID=".$item->id." DELETE\n" if $DEBUG;
225
$item->delete();
226
next;
227
} else {
228
my $store = 0;
229
if ( exists $opts{renumber} && ref $opts{renumber} eq 'HASH' && exists $opts{renumber}{$item->id} && $opts{renumber}{$item->id} != $item->number ) {
230
$item->number( $opts{renumber}{$item->id} );
231
$store = 1;
232
}
233
if ( exists $opts{price} && ref $opts{price} eq 'HASH' && exists $opts{price}{$item->id} && $opts{price}{$item->id} != $item->price ) {
234
$item->price( $opts{price}{$item->id} );
235
$store = 1;
236
}
237
$item->store if $store;
238
$total += $item->number;
239
$sum += $item->number * $item->price;
240
push @new_basket, $item;
241
}
242
}
243
my @plugins = split (/[\ |\t]+/, $state->{plugins});
244
if ( !$session_no_store && grep { $_ eq 'session' } @plugins ) {
245
$keeper->{session}->store_value ( basket_total => $total, basket_sum => $sum );
246
}
247
return ($total, $sum, \@new_basket);
248
}
249
250
251
sub get_orders {
252
my $self = shift;
253
my (%opts) = @_;
254
255
my $list = delete $opts{list};
256
$opts{order_by} ||= 'status';
257
my @items = $keeper->get_documents (
258
class => 'webshop::Order',
259
%opts,
260
);
261
if ( exists $opts{id} && defined $opts{id} && !ref $opts{id} ) {
262
if ( $list ) {
263
$items[0]->{list} = $self->get_order_list( order_id => $opts{id} );
264
}
265
return $items[0];
266
} else {
267
if ( $list ) {
268
map { $_->{list} = $self->get_order_list( order_id => $_->id ) } @items;
269
}
270
return \@items;
271
}
272
}
273
274
275
sub get_order_list {
276
my $self = shift;
277
my (%opts) = @_;
278
279
return unless $opts{order_id};
280
281
$opts{status} = 1;
282
my @items = $keeper->get_documents (
283
class => 'webshop::Basket',
284
%opts,
285
);
286
my $total = 0;
287
my $sum = 0;
288
foreach my $item ( @items ) {
289
my $Item = $item->class->new( $keeper, $item->id );
290
$item->{item} = $Item;
291
if ( $item->status == 1 ) {
292
$total += $item->number;
293
$sum += $item->number * $item->price;
294
}
295
}
296
return \@items;
297
}
298
299
sub price_format {
300
my $self = shift;
301
my $price = shift;
302
303
$price = reverse $price;
304
$price =~ s/(\d{3})/$1\ /g;
305
$price = reverse $price;
306
307
return $price;
308
}
309
310
311
1;
utf8/plugins/webshop/lib/webshop/Order.pm
1
package webshop::Order;
2
3
use base "Contenido::Document";
4
sub extra_properties
5
{
6
return (
7
{ 'attr' => 'status', 'type' => 'status', 'rusname' => 'Статус заказа', facilshow => 1, postshow => 1,
8
'cases' => [
9
[0, 'открыт'],
10
[1, 'обрабатывается'],
11
[2, 'комплектуется'],
12
[3, 'в доставке'],
13
[4, 'доставлен'],
14
[5, 'отклонен'],
15
],
16
},
17
{ 'attr' => 'delivery', 'type' => 'lookup', 'rusname' => 'Тип доставки', facilshow => 1, postshow => 1,
18
'lookup_opts' => {
19
'class' => 'webshop::Delivery',
20
},
21
},
22
{ 'attr' => 'btime', 'type' => 'datetime', 'rusname' => 'Заказ отдан на комплектацию',
23
postshow => 1, facilshow => 1 },
24
{ 'attr' => 'ftime', 'type' => 'datetime', 'rusname' => 'Заказ передан в доставку',
25
manager_hidden => 1, postshow => 1, faciledit => 1 },
26
{ 'attr' => 'etime', 'type' => 'datetime', 'rusname' => 'Заказ доставлен',
27
manager_hidden => 1, postedit => 1, facilshow => 1 },
28
{ 'attr' => 'num', 'type' => 'integer', 'rusname' => 'Количество позиций',
29
manshow => 1, postshow => 1, facilshow => 1 },
30
{ 'attr' => 'sum', 'type' => 'string', 'rusname' => 'Сумма (total)', shortname => 'Сумма',
31
column => 5, postshow => 1, facilshow => 1 },
32
{ 'attr' => 'sum_delivery', 'type' => 'string', 'rusname' => 'Стоимость доставки', shortname => 'Доставка',
33
column => 6, postshow => 1, facilshow => 1 },
34
{ 'attr' => 'contact', 'type' => 'string', 'rusname' => 'Контактное лицо', facilshow => 1 },
35
{ 'attr' => 'email', 'type' => 'string', 'rusname' => 'E-mail для связи', shortname => 'E-mail',
36
column => 3, postshow => 1, facilshow => 1 },
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 },
41
{ 'attr' => 'metro', 'type' => 'string', 'rusname' => 'Ближайшее метро', postshow => 1 },
42
{ 'attr' => 'timeline', 'type' => 'string', 'rusname' => 'Предпочтительное время', postshow => 1 },
43
{ 'attr' => 'description', 'type' => 'text', 'rusname' => 'Описание для курьера', rows => 5, postshow => 1 },
44
{ 'attr' => 'facility_comment', 'type' => 'text', 'rusname' => 'Описание проблем с комплектацией', rows => 5, faciledit => 1 },
45
{ 'attr' => 'delivery_comment', 'type' => 'text', 'rusname' => 'Описание проблем с доставкой', rows => 5, postedit => 1 },
46
{ 'attr' => 'address', 'type' => 'text', 'rusname' => 'Адрес доставки', rows => 5, postedit => 1, facilshow => 1 },
47
)
48
}
49
50
sub sum_formatted {
51
my $self = shift;
52
53
my $price = $self->sum;
54
$price = reverse $price;
55
$price =~ s/(\d{3})/$1\ /g;
56
$price = reverse $price;
57
58
return $price;
59
}
60
61
sub total_formatted {
62
my $self = shift;
63
64
my $price = $self->sum + ($self->sum_delivery || 0);
65
$price = reverse $price;
66
$price =~ s/(\d{3})/$1\ /g;
67
$price = reverse $price;
68
69
return $price;
70
}
71
72
73
sub reject {
74
my $self = shift;
75
76
my $query = $self->{keeper}->SQL->prepare("delete from basket where order_id = ? and uid = ?");
77
$query->execute( $self->id, $self->uid );
78
$self->delete;
79
}
80
81
82
sub class_name
83
{
84
return 'Заказ';
85
}
86
87
sub class_description
88
{
89
return 'Заказ';
90
}
91
92
sub class_table
93
{
94
return 'webshop::SQL::Order';
95
}
96
97
98
sub post_delete
99
{
100
my $self = shift;
101
my @items = $self->keeper->get_documents(
102
class => 'webshop::Basket',
103
order_id => $self->id,
104
);
105
foreach my $item ( @items ) {
106
$item->delete( attachments => 1 );
107
}
108
1;
109
}
110
111
1;
utf8/plugins/webshop/lib/webshop/SQL/AddressTable.pm
1
package webshop::SQL::AddressTable;
2
3
use base 'SQL::DocumentTable';
4
5
sub db_table
6
{
7
return 'adresses';
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
_uid_filter
27
)];
28
29
sub available_filters {
30
return $available_filters;
31
}
32
33
34
# ----------------------------------------------------------------------------
35
# Свойства храним в массивах, потому что порядок важен!
36
# Это общие свойства - одинаковые для всех документов.
37
#
38
# attr - обязательный параметр, название атрибута;
39
# type - тип аттрибута, требуется для отображдения;
40
# rusname - русское название, опять же требуется для отображения;
41
# hidden - равен 1, когда
42
# readonly - инициализации при записи только без изменения в дальнейшем
43
# db_field - поле в таблице
44
# default - значение по умолчанию (поле всегда имеет это значение)
45
# ----------------------------------------------------------------------------
46
sub required_properties
47
{
48
my $self = shift;
49
50
my @parent_properties = $self->SUPER::required_properties;
51
return (
52
@parent_properties,
53
{ # User ID
54
'attr' => 'uid',
55
'type' => 'string',
56
'rusname' => 'ID пользователя',
57
# 'hidden' => 1,
58
'db_field' => 'uid',
59
'db_type' => 'integer',
60
'db_opts' => "not null default 0",
61
'default' => 0,
62
},
63
);
64
}
65
66
########### FILTERS DESCRIPTION ###############################################################################
67
sub _uid_filter {
68
my ($self,%opts)=@_;
69
return undef unless ( exists $opts{uid} );
70
return &SQL::Common::_generic_int_filter('d.uid', $opts{uid});
71
}
72
73
1;
utf8/plugins/webshop/lib/webshop/SQL/Basket.pm
1
package webshop::SQL::Basket;
2
3
use base 'SQL::DocumentTable';
4
5
sub db_table
6
{
7
return 'basket';
8
}
9
10
11
sub available_filters {
12
my @available_filters = qw(
13
14
_class_filter
15
_status_filter
16
_in_id_filter
17
_id_filter
18
_name_filter
19
_class_excludes_filter
20
_sfilter_filter
21
_excludes_filter
22
_datetime_filter
23
_date_equal_filter
24
_date_filter
25
_previous_days_filter
26
_s_filter
27
28
_color_filter
29
_session_filter
30
_uid_filter
31
_order_id_filter
32
_item_filter
33
);
34
35
return \@available_filters;
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
{
58
'attr' => 'uid',
59
'type' => 'integer',
60
'rusname' => 'Идентификатор пользователя',
61
'lookup_opts' => {
62
'class' => $self->profile_class(),
63
'search_by' => 'login',
64
},
65
'db_field' => 'uid',
66
'db_type' => 'integer',
67
'db_opts' => "default 0",
68
},
69
{ # ID товара
70
'attr' => 'item_id',
71
'type' => 'integer',
72
'rusname' => 'ID товара',
73
'hidden' => 1,
74
'db_field' => 'item_id',
75
'db_type' => 'integer',
76
'db_opts' => "not null",
77
},
78
{
79
'attr' => 'item_class',
80
'type' => 'string',
81
'rusname' => 'Класс товарной позиции',
82
'hidden' => 1,
83
'db_field' => 'item_class',
84
'db_type' => 'text',
85
},
86
{
87
'attr' => 'item_table',
88
'type' => 'string',
89
'rusname' => 'Класс таблицы товарной позиции',
90
'hidden' => 1,
91
'db_field' => 'item_table',
92
'db_type' => 'text',
93
},
94
{
95
'attr' => 'number',
96
'type' => 'integer',
97
'rusname' => 'Количество позиций',
98
'db_field' => 'number',
99
'db_type' => 'integer',
100
'db_opts' => "default 0",
101
},
102
{
103
'attr' => 'price',
104
'type' => 'string',
105
'rusname' => 'Цена позиции',
106
'db_field' => 'price',
107
'db_type' => 'float',
108
'db_opts' => "default 0",
109
},
110
{ # ID заказа
111
'attr' => 'order_id',
112
'type' => 'integer',
113
'rusname' => 'ID заказа',
114
'hidden' => 1,
115
'db_field' => 'order_id',
116
'db_type' => 'integer',
117
'db_opts' => "default 0",
118
},
119
{ # ID цвета
120
'attr' => 'color_id',
121
'type' => 'integer',
122
'rusname' => 'ID цвета',
123
'hidden' => 1,
124
'db_field' => 'color_id',
125
'db_type' => 'integer',
126
},
127
{ # ID Сессии
128
'attr' => 'session',
129
'type' => 'string',
130
'rusname' => 'ID Сессии пользователя',
131
'db_field' => 'session',
132
'db_type' => 'text',
133
},
134
);
135
}
136
137
138
sub profile_class () {
139
my $self = shift;
140
return ref $self->{keeper} ? $self->{keeper}{webshop}{state}->profile_document_class : undef;
141
}
142
143
144
########### FILTERS DESCRIPTION ###############################################################################
145
sub _uid_filter {
146
my ($self,%opts)=@_;
147
return undef unless ( exists $opts{uid} );
148
return &SQL::Common::_generic_int_filter('d.uid', $opts{uid});
149
}
150
151
sub _session_filter {
152
my ($self,%opts)=@_;
153
return undef unless ( exists $opts{session} );
154
return &SQL::Common::_generic_text_filter('d.session', $opts{session});
155
}
156
157
sub _item_filter {
158
my ($self,%opts)=@_;
159
return undef unless ( exists $opts{item} );
160
return &SQL::Common::_generic_int_filter('d.item_id', $opts{item});
161
}
162
163
sub _color_filter {
164
my ($self,%opts)=@_;
165
return undef unless ( exists $opts{color} );
166
return &SQL::Common::_generic_int_filter('d.color_id', $opts{color});
167
}
168
169
sub _order_id_filter {
170
my ($self,%opts)=@_;
171
return undef unless ( exists $opts{order_id} );
172
return &SQL::Common::_generic_int_filter('d.order_id', $opts{order_id});
173
}
174
175
176
1;
utf8/plugins/webshop/lib/webshop/SQL/Order.pm
1
package webshop::SQL::Order;
2
3
use base 'SQL::DocumentTable';
4
5
sub db_table
6
{
7
return 'orders';
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
_uid_filter
27
)];
28
29
sub available_filters {
30
return $available_filters;
31
}
32
33
my @required_properties = (
34
{ # Идентификатор документа, сквозной по всем типам...
35
'attr' => 'id',
36
'type' => 'integer',
37
'rusname' => 'Идентификатор документа',
38
'hidden' => 1,
39
'readonly' => 1,
40
'auto' => 1,
41
'db_field' => 'id',
42
'db_type' => 'integer',
43
'db_opts' => "not null default nextval('public.documents_id_seq'::text)",
44
},
45
{ # Класс документа...
46
'attr' => 'class',
47
'type' => 'string',
48
'rusname' => 'Класс документа',
49
'hidden' => 1,
50
'readonly' => 1,
51
'db_field' => 'class',
52
'db_type' => 'varchar(48)',
53
'db_opts' => 'not null',
54
},
55
{ # Название (не используется)
56
'attr' => 'name',
57
'type' => 'string',
58
'rusname' => 'Имя заказчика',
59
'column' => 2,
60
'db_field' => 'name',
61
'db_type' => 'varchar(255)',
62
},
63
{ # User ID
64
'attr' => 'uid',
65
'type' => 'string',
66
'rusname' => 'ID пользователя',
67
# 'hidden' => 1,
68
'db_field' => 'uid',
69
'db_type' => 'integer',
70
'db_opts' => "not null default 0",
71
'default' => 0,
72
},
73
{ # User ID
74
'attr' => 'manager_id',
75
'type' => 'string',
76
'rusname' => 'ID менеджера',
77
'hidden' => 1,
78
'db_field' => 'manager_id',
79
'db_type' => 'integer',
80
'db_opts' => "not null default 0",
81
'default' => 0,
82
},
83
{ # User ID
84
'attr' => 'vault_id',
85
'type' => 'string',
86
'rusname' => 'ID специалиста комплектации',
87
'hidden' => 1,
88
'db_field' => 'vault_id',
89
'db_type' => 'integer',
90
'db_opts' => "not null default 0",
91
'default' => 0,
92
},
93
{ # User ID
94
'attr' => 'postman_id',
95
'type' => 'string',
96
'rusname' => 'ID курьера',
97
'hidden' => 1,
98
'db_field' => 'postman_id',
99
'db_type' => 'integer',
100
'db_opts' => "not null default 0",
101
'default' => 0,
102
},
103
{ # Время создания документа, служебное поле...
104
'attr' => 'ctime',
105
'type' => 'datetime',
106
'rusname' => 'Время создания',
107
'readonly' => 1,
108
'auto' => 1,
109
'hidden' => 1,
110
'db_field' => 'ctime',
111
'db_type' => 'timestamp',
112
'db_opts' => 'not null default now()',
113
'default' => 'CURRENT_TIMESTAMP',
114
},
115
{ # Время модификации документа, служебное поле...
116
'attr' => 'mtime',
117
'type' => 'datetime',
118
'rusname' => 'Время модификации',
119
'hidden' => 1,
120
'auto' => 1,
121
'db_field' => 'mtime',
122
'db_type' => 'timestamp',
123
'db_opts' => 'not null default now()',
124
'default' => 'CURRENT_TIMESTAMP',
125
},
126
{ # Дата и время документа...
127
'attr' => 'dtime',
128
'type' => 'datetime',
129
'rusname' => 'Заказ подтвержден',
130
'column' => 1,
131
'postshow' => 1,
132
'facilshow' => 1,
133
'db_field' => 'dtime',
134
'db_type' => 'timestamp',
135
'db_opts' => 'not null default now()',
136
'default' => 'CURRENT_TIMESTAMP',
137
},
138
{ # Массив секций, обрабатывается специальным образом...
139
'attr' => 'sections',
140
'type' => 'sections_list',
141
'rusname' => 'Секции',
142
'hidden' => 1,
143
'db_field' => 'sections',
144
'db_type' => 'integer[]',
145
},
146
{ # Одно поле статуса является встроенным...
147
'attr' => 'status',
148
'type' => 'status',
149
'postshow' => 1,
150
'facilshow' => 1,
151
'column' => 7,
152
'rusname' => 'Статус',
153
'db_field' => 'status',
154
'db_type' => 'integer',
155
},
156
);
157
158
# ----------------------------------------------------------------------------
159
# Свойства храним в массивах, потому что порядок важен!
160
# Это общие свойства - одинаковые для всех документов.
161
#
162
# attr - обязательный параметр, название атрибута;
163
# type - тип аттрибута, требуется для отображдения;
164
# rusname - русское название, опять же требуется для отображения;
165
# hidden - равен 1, когда
166
# readonly - инициализации при записи только без изменения в дальнейшем
167
# db_field - поле в таблице
168
# default - значение по умолчанию (поле всегда имеет это значение)
169
# ----------------------------------------------------------------------------
170
sub required_properties
171
{
172
return @required_properties;
173
}
174
175
########### FILTERS DESCRIPTION ###############################################################################
176
sub _uid_filter {
177
my ($self,%opts)=@_;
178
return undef unless ( exists $opts{uid} );
179
return &SQL::Common::_generic_int_filter('d.uid', $opts{uid});
180
}
181
182
1;
utf8/plugins/webshop/lib/webshop/State.pm.proto
1
package webshop::State;
2
3
use strict;
4
use warnings 'all';
5
use vars qw($AUTOLOAD);
6
7
8
sub new {
9
my ($proto) = @_;
10
my $class = ref($proto) || $proto;
11
my $self = {};
12
bless $self, $class;
13
14
# зашитая конфигурация плагина
15
$self->{db_type} = 'none';
16
$self->{db_keepalive} = 0;
17
# $self->{db_host} = '';
18
# $self->{db_name} = '';
19
# $self->{db_user} = '';
20
# $self->{db_password} = '';
21
# $self->{db_port} = '';
22
$self->{profile_document_class} = '@PROFILE_DOCUMENT_CLASS@' || 'users::UserProfile';
23
$self->{item_document_class} = '@ITEM_DOCUMENT_CLASS@' || 'webshop::Item';
24
25
$self->_init_();
26
$self;
27
}
28
29
sub info {
30
my $self = shift;
31
return unless ref $self;
32
33
for (sort keys %{$self->{attributes}}) {
34
my $la = length $_;
35
warn "\t$_".("\t" x (2-int($la/8))).": $self->{$_}\n";
36
}
37
}
38
39
sub _init_ {
40
my $self = shift;
41
42
# зашитая конфигурация плагина
43
$self->{attributes}->{$_} = 'SCALAR' for qw(
44
profile_document_class
45
item_document_class
46
db_type
47
db_keepalive
48
);
49
# db_keepalive
50
# db_host
51
# db_port
52
# db_name
53
# db_user
54
# db_password
55
}
56
57
sub AUTOLOAD {
58
my $self = shift;
59
my $attribute = $AUTOLOAD;
60
61
$attribute =~ s/.*:://;
62
return unless $attribute =~ /[^A-Z]/; # Отключаем методы типа DESTROY
63
64
if (!exists $self->{attributes}->{$attribute}) {
65
warn "Contenido Error (webshop::State): Вызов метода, для которого не существует обрабатываемого свойства: ->$attribute()\n";
66
return;
67
}
68
69
$self->{$attribute} = shift @_ if $#_>=0;
70
$self->{$attribute};
71
}
72
73
1;
utf8/plugins/webshop/sql/TOAST/adresses.sql
1
CREATE TABLE adresses (
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
class text DEFAULT 'webshop::Address'::text NOT NULL,
7
status smallint default 0 NOT NULL,
8
sections integer,
9
uid integer not null,
10
name text,
11
data text
12
);
13
14
CREATE INDEX adresses_sections ON adresses USING btree (sections);
15
CREATE INDEX adresses_uid ON adresses USING btree (uid);
utf8/plugins/webshop/sql/TOAST/basket.sql
1
CREATE TABLE basket (
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
class text DEFAULT 'webshop::Basket'::text NOT NULL,
7
status smallint default 0 NOT NULL,
8
sections integer[],
9
uid integer,
10
item_id integer not null,
11
item_class text,
12
item_table text,
13
order_id integer,
14
color_id integer,
15
session text,
16
number integer default 0,
17
price float default 0,
18
name text,
19
data text
20
);
21
22
CREATE INDEX basket_order_id ON basket USING btree (order_id);
23
CREATE INDEX basket_sections ON basket USING gist (sections);
24
CREATE INDEX basket_session ON basket USING btree (session) where session is not null;
25
CREATE INDEX basket_uid ON basket USING btree (uid);
utf8/plugins/webshop/sql/TOAST/orders.sql
1
CREATE TABLE orders (
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
class text DEFAULT 'webshop::Order'::text NOT NULL,
7
status smallint default 0 NOT NULL,
8
sections integer[],
9
uid integer,
10
manager_id integer,
11
vault_id integer,
12
postman_id integer,
13
session text,
14
name text,
15
data text
16
);
17
18
CREATE INDEX orders_sections ON orders USING gist (sections);
19
CREATE INDEX orders_session ON orders USING btree (session) where session is not null;
20
CREATE INDEX orders_uid ON orders USING btree (uid) where uid is not null;
Небольшая справка по веткам
cnddist – контейнер, в котором хранятся все дистрибутивы всех библиотек и программных пакетов, которые использовались при построении различных версий Contenido. Если какой-то библиотеки в данном хранилище нет, инсталлятор сделает попытку "подтянуть" ее с веба (например, с CPAN). Если библиотека слишком старая, есть очень большая вероятность, что ее там уже нет. Поэтому мы храним весь хлам от всех сборок. Если какой-то дистрибутив вдруг отсутствует в cnddist - напишите нам, мы положим его туда.
koi8 – отмирающая ветка, чей код, выдача и все внутренние библиотеки заточены на кодировку KOI8-R. Вносятся только те дополнения, которые касаются внешнего вида и функционала админки, баги ядра, обязательные обновления портов и мелочи, которые легко скопипастить. В дальнейшем планируется полная остановка поддержки по данной ветке.
utf8 – актуальная ветка, заточенная под UTF-8.
Внутри каждой ветки: core – исходники ядра; install – скрипт установки инсталляции; plugins – плагины; samples – "готовые к употреблению" проекты, которые можно поставить, запустить и посмотреть, как они работают.