-- true if the order was paid manually
paid_manually integer not null default 0,
- coupon_code varchar(40) not null default '',
+ coupon_id integer null,
coupon_code_discount_pc real not null default 0,
primary key (id),
index order_cchash(ccNumberHash),
- index order_userId(userId, orderDate)
+ index order_userId(userId, orderDate),
+ index order_coupon(coupon_id)
);
DROP TABLE IF EXISTS order_item;
[report bse_coupon_orders]
sql1=<<SQL
-select id, billFirstName as "First Name", billLastName as "Last Name", format(total/100.0, 2) as "$ Total"
-from orders
-where coupon_code = ?
+select o.id, o.billFirstName as "First Name", o.billLastName as "Last Name", format(o.total/100.0, 2) as "$ Total"
+from orders o
+where o.coupon_id = ?
SQL
sql1params=1
param1=used_coupon_code,Coupon Code
[report datatype used_coupon_code]
type=sql
sql=<<SQL
-select coupon_code as "id", coupon_code as "label"
-from orders
-where coupon_code <> ''
-group by coupon_code
+select distinct c.id as "id", c.code as "label"
+from bse_coupons c
+where c.id in (select coupon_id from orders)
SQL
[valid child types]
use strict;
use Scalar::Util;
-our $VERSION = "1.005";
+our $VERSION = "1.006";
=head1 NAME
if (length $self->{coupon_code}) {
require BSE::TB::Coupons;
my ($coupon) = BSE::TB::Coupons->getBy(code => $self->{coupon_code});
- print STDERR "Searching for coupon '$self->{coupon_code}'\n";
my %check =
(
coupon => $coupon,
use BSE::Util::HTML;
use Carp qw(cluck confess);
-our $VERSION = "1.025";
+our $VERSION = "1.026";
=head1 NAME
C<trim> - for plain text fields, trim leading and trailing whitespace.
+=item *
+
+C<readonly> - no values are stored.
+
=back
=cut
or confess "Missing fields parameter";
my $cgi = $self->cgi;
+ FIELD:
for my $name (keys %$fields) {
my $field = $fields->{$name};
+ $field->{readonly}
+ and next FIELD;
my $value;
if ($field->{htmltype} eq "checkbox") {
if ($field->{type} eq "int") {
=head1 NAME
-our $VERSION = "1.001";
+our $VERSION = "1.002";
BSE::TB::Coupon - shop coupon objects
sub remove {
my ($self) = @_;
+ $self->is_removable
+ or return;
+
my @tiers = BSE::TB::CouponTiers->getBy2
(
[
return $self->is_released && !$self->is_expired;
}
+=item is_removable
+
+Return true if the coupon can be removed.
+
+=cut
+
+sub is_removable {
+ my ($self) = @_;
+
+ require BSE::TB::Orders;
+ return !BSE::TB::Orders->getExists([ coupon_id => $self->id ]);
+}
+
+=item is_renamable
+
+Return true if the name can be changed.
+
+This is currently equivalent to is_removable().
+
+=cut
+
+sub is_renamable {
+ my ($self) = @_;
+
+ return $self->is_removable;
+}
+
+=item set_code($code)
+
+Set the coupon code. Requires that is_renamable() be true.
+
+=cut
+
+sub set_code {
+ my ($self, $code) = @_;
+
+ $self->is_renamable
+ or return;
+
+ $self->{code} = $code;
+}
+
sub fields {
- my ($class) = @_;
+ my ($self) = @_;
my %fields =
(
},
);
+ if (ref $self && !$self->is_renamable) {
+ $fields{code}{readonly} = 1;
+ }
+
require BSE::Validate;
return BSE::Validate::bse_configure_fields(\%fields, BSE::Cfg->single, "bse coupon validation");
}
use Carp 'confess';
use BSE::Shop::PaymentTypes;
-our $VERSION = "1.019";
+our $VERSION = "1.020";
sub columns {
return qw/id
delivStreet2 billStreet2 purchase_order shipping_method
shipping_name shipping_trace
paypal_token paypal_tran_id freight_tracking stage ccPAN
- paid_manually coupon_code coupon_code_discount_pc/;
+ paid_manually coupon_id coupon_code_discount_pc/;
}
sub table {
sub coupon_valid {
my ($self) = @_;
- return $self->coupon_code ne "";
+ return defined($self->coupon_id);
}
=item coupon_active
return $self->total_cost - $self->discounted_product_cost;
}
+=item coupon
+
+Return the coupon used for this order, if any.
+
+=cut
+
+sub coupon {
+ my ($self) = @_;
+
+ $self->coupon_id
+ or return;
+
+ require BSE::TB::Coupons;
+ return BSE::TB::Coupons->getByPkey($self->coupon_id);
+}
+
1;
use BSE::Util::SQL qw/now_sqldate sql_to_date date_to_sql sql_date sql_datetime/;
use BSE::Util::Valid qw/valid_date/;
-our $VERSION = "1.022";
+our $VERSION = "1.023";
my %actions =
(
$req->message($errors);
require BSE::TB::Coupons;
- $req->set_variable(fields => BSE::TB::Coupon->fields);
+ $req->set_variable(fields => $coupon->fields);
$req->set_variable(coupon => $coupon);
$req->set_variable(errors => $errors || {});
or return $result;
require BSE::TB::Coupons;
- my $fields = BSE::TB::Coupon->fields;
+ my $fields = $coupon->fields;
my %errors;
$req->validate(fields => $fields, errors => \%errors,
rules => BSE::TB::Coupon->rules);
my $coupon = $self->_get_coupon_id($req, \$result)
or return $result;
+ unless ($coupon->is_removable) {
+ $req->flash_error("msg:bse/admin/shop/coupons/not_deletable", [ $coupon ]);
+ return $self->req_coupon_list($req);
+ }
+
my %acts = $req->admin_tags;
require BSE::TB::Coupons;
my $coupon = $self->_get_coupon_id($req, \$result)
or return $result;
+ unless ($coupon->is_removable) {
+ $req->flash_error("msg:bse/admin/shop/coupons/not_deletable", [ $coupon ]);
+ return $self->req_coupon_list($req);
+ }
+
my $code = $coupon->code;
$req->audit
use BSE::Util::Secure qw(make_secret);
use BSE::Template;
-our $VERSION = "1.041";
+our $VERSION = "1.042";
=head1 NAME
}
}
if ($cart->coupon_active) {
- $values->{coupon_code} = $cart->coupon_code;
+ $values->{coupon_id} = $cart->coupon->id;
}
else {
- $values->{coupon_code} = "";
+ $values->{coupon_id} = undef;
}
$values->{coupon_code_discount_pc} = $cart->coupon_code_discount_pc;
$values->{total} = $cart->total;
package Squirrel::Table;
-our $VERSION = "1.011";
+our $VERSION = "1.012";
use Carp;
use strict;
return $row->[0];
}
+=item getExists($query)
+
+Return true if any matching records exist.
+
+=cut
+
+sub getExists {
+ my ($self, $query) = @_;
+
+ my $table_name = $self->rowClass->table
+ or confess "No table_name defined";
+
+ my @db_cols = $self->rowClass->db_columns;
+ my @code_cols = $self->rowClass->columns;
+ my %map;
+ @map{@code_cols} = @db_cols;
+ my ($where, @args) = $self->_where_clause(\%map, @$query);
+
+ my $sql = "select exists(select " . $self->rowClass->primary .
+ "\nfrom $table_name";
+ $where and $sql .= "\nwhere $where";
+ $sql .= ")";
+
+ $dh ||= BSE::DB->single;
+ my $sth = $dh->dbh->prepare($sql)
+ or confess "Cann't prepare generated $sql: ", $dh->dbh->errstr;
+
+ $sth->execute(@args)
+ or confess "Cannot execute $sql: ", $sth->errstr;
+
+ my $row = $sth->fetchrow_arrayref
+ or return;
+
+ $sth->finish;
+
+ return $row->[0];
+}
+
=item getBy2($query, $opts)
Dynamically build a query.
id: bse/admin/shop/coupons/delete
description: Flashed when a coupon is deleted (%1 coupon code)
+id: bse/admin/shop/coupons/not_deletable
+description: Flashed when the user attempts to delete an undeletable coupon (%1 coupon)
+
id: bse/admin/logon/
description: Logon tool messages
id: bse/admin/shop/coupons/delete
message: Deleted coupon '%1:s'
+id: bse/admin/shop/coupons/not_deletable
+message: Coupon '%1:{code}s' has been used in an order and may not be removed
+
id: bse/admin/logon/logoff
message: Administrative user '%1:s' logged off
<:.if request.user_can("bse_shop_coupon_edit") -:>
<a href="<:= cfg.admin_url2("shopadmin", "coupon_edit", { "id": coupon.id }) :>">Edit</a>
<:.end if -:>
- <:.if request.user_can("bse_shop_coupon_delete") -:>
+ <:.if request.user_can("bse_shop_coupon_delete") and coupon.is_removable -:>
<a href="<:= cfg.admin_url2("shopadmin", "coupon_deleteform", { "id": coupon.id }) :>">Delete</a>
<:.end if -:>
</td>
<td colspan="7" class="col_label_right">Total Price of goods:</td>
<td class="col_extension"><:= bse.number("money", order.total_cost) -:></td>
</tr>
-<:.if order.coupon_code ne "" -:>
+<:.if order.coupon -:>
<tr>
- <td>Coupon code <b><:= order.coupon_code -:></b></td>
+ <td>Coupon code <b><:= order.coupon.code -:></b></td>
<td colspan="6" class="col_label_right">Discount:</td>
<td class="col_extension">(<:= bse.number("money", order.product_cost_discount) -:>)</td>
</tr>
</tr>
</table>
<table width="100%" border="0" cellspacing="0" cellpadding="0">
-<:.if order.coupon_code ne "" -:>
+<:.if order.coupon -:>
<tr>
- <td>Coupon code: <:= order.coupon_code -:></td>
+ <td>Coupon code: <:= order.coupon.code -:></td>
<td height="20"> </td>
<td height="20" bgcolor="#666666"> </td>
<td align="CENTER" height="20" bgcolor="#666666" NOWRAP><font size="2" face="Verdana, Arial, Helvetica, sans-serif" color="#FFFFFF">
<:.if field.is_hash -:>
<div>
<label for="<:= name | html :>"><:= field.nolabel ? "" : field.description | html :>:</label>
- <span><:.call "input", "name":name -:><:.call "error_img", "field":name :>
+ <span>
+ <:-.if field.readonly -:>
+<:-.call "display", "name":name -:>
+ <:.else -:>
+<:-.call "input", "name":name -:><:.call "error_img", "field":name :>
+ <:-.end if -:>
<:-.if field.units -:>
<:-= field.units | html -:>
<:-.end if -:>
Column stage;varchar(20);NO;;
Column ccPAN;varchar(4);NO;;
Column paid_manually;int(11);NO;0;
-Column coupon_code;varchar(40);NO;;
+Column coupon_id;int(11);YES;NULL;
Column coupon_code_discount_pc;double;NO;0;
Index PRIMARY;1;[id]
Index order_cchash;0;[ccNumberHash]
+Index order_coupon;0;[coupon_id]
Index order_userId;0;[userId;orderDate]
Table other_parents
Engine InnoDB