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">Зарегистрировать &raquo;</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 &nbsp;
    43 % }
    44 </td>
    45 %
    46 % for my $col (@$columns) {
    47 % if ($col->{attr} eq '_sort_') {
    48 %
    49 <td width="20px"><% $document->{sorder} %>&nbsp;<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>&nbsp;<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 %>&nbsp;</a>\
    67 %
    68 % } else {
    69 %
    70 <% $name | h %>&nbsp;\
    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 %>&nbsp;</a>\
    81 % } else {
    82 <% $document->id %>&nbsp;\
    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 %>&nbsp;<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 &nbsp;\
    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 || '&nbsp;' %>\
    156 % } elsif ($col->{type} eq 'datetime') {
    157 <td nowrap align="right"><% $document->{$col->{attr}} || '&nbsp;' %>\
    158 % } elsif ($col->{type} eq 'integer') {
    159 <td align="right"><% $document->{$col->{attr}} %>&nbsp;\
    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>&nbsp;\
    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}} : '&nbsp;' %>\
    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 %>&nbsp;<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 || '&nbsp;' %>\
    150 150 % } elsif ($col->{type} eq 'datetime') {
    151 <td nowrap align="right"><% $document->{$col->{attr}} || '&nbsp;' %>\
    151 <td nowrap align="right"><% $document->$attr || '&nbsp;' %>\
    152 152 % } elsif ($col->{type} eq 'integer') {
    153 <td align="right"><% $document->{$col->{attr}} %>&nbsp;\
    153 <td align="right"><% $document->$attr %>&nbsp;\
    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>&nbsp;\
    160 <a href="/contenido/document.html?id=<% $doc->id %>&class=<% $doc->class %><% $params_unclassed ? '&'.$params_unclassed : '' %>"><% $doc->name || $doc->id %></a>&nbsp;\
    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}} : '&nbsp;' %>\
    173 <td><% defined($document->$attr) ? $document->$attr : '&nbsp;' %>\
    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>&nbsp;/</td>
    48 <td align="right" nowrap><font color="#888888" size="-1">&nbsp;name="<% $prop->{attr} %>"</font></td>
    49 % if( $prop->{readonly} ) {
    50 <td align="right" nowrap>&nbsp;/&nbsp;<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%">&nbsp;</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%">&nbsp;</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%">&nbsp;</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 – "готовые к употреблению" проекты, которые можно поставить, запустить и посмотреть, как они работают.