used coupons are no longer deletable nor renamable
authorTony Cook <tony@develop-help.com>
Tue, 2 Jul 2013 11:29:11 +0000 (21:29 +1000)
committerTony Cook <tony@develop-help.com>
Sun, 21 Jul 2013 23:34:16 +0000 (09:34 +1000)
This makes incompatible changes to the orders table you need to run:

alter table orders drop coupon_code, drop coupon_code_discount_pc;

before updating the database

16 files changed:
schema/bse.sql
site/cgi-bin/bse.cfg
site/cgi-bin/modules/BSE/Cart.pm
site/cgi-bin/modules/BSE/Request/Base.pm
site/cgi-bin/modules/BSE/TB/Coupon.pm
site/cgi-bin/modules/BSE/TB/Order.pm
site/cgi-bin/modules/BSE/UI/AdminShop.pm
site/cgi-bin/modules/BSE/UI/Shop.pm
site/cgi-bin/modules/Squirrel/Table.pm
site/data/db/bse_msg_base.data
site/data/db/bse_msg_defaults.data
site/templates/admin/coupons/list.tmpl
site/templates/admin/order_detail.tmpl
site/templates/checkoutfinal_base.tmpl
site/templates/preload.tmpl
site/util/mysql.str

index 146fce59fa11d2e5b5d468b396d3b7a04bfc3860..8d83aa888eede482f3cc6c7ea1d6279d3544b327 100644 (file)
@@ -349,12 +349,13 @@ create table orders (
   -- 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;
index b538064413c1a4b65234d28beff215e09261a30e..04a8d46eb1ce66f37a65f7be51f195f8b841cf6c 100644 (file)
@@ -372,9 +372,9 @@ SQL
 
 [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
@@ -397,10 +397,9 @@ sql1params=1
 [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]
index c31bfbca4aaa0503d500c1ced591bc1c28b64c91..da0fb4ddcd97ce9690a6baae74867384688cb67b 100644 (file)
@@ -2,7 +2,7 @@ package BSE::Cart;
 use strict;
 use Scalar::Util;
 
-our $VERSION = "1.005";
+our $VERSION = "1.006";
 
 =head1 NAME
 
@@ -346,7 +346,6 @@ sub coupon_valid {
     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,
index 2e4b92b10ec153e10a2fca5eaaabd02672ced5b0..c8572bd73840ba667fd59238a9589856a2bbfa90 100644 (file)
@@ -5,7 +5,7 @@ use BSE::Cfg;
 use BSE::Util::HTML;
 use Carp qw(cluck confess);
 
-our $VERSION = "1.025";
+our $VERSION = "1.026";
 
 =head1 NAME
 
@@ -1756,6 +1756,10 @@ should already be that format.
 
 C<trim> - for plain text fields, trim leading and trailing whitespace.
 
+=item *
+
+C<readonly> - no values are stored.
+
 =back
 
 =cut
@@ -1768,8 +1772,11 @@ sub cgi_fields {
     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") {
index ec9f1fa5f9983325f46b6337936dbc906aaac89e..fcd9fbf1d4e718f05985e66ea7756f1f631d583f 100644 (file)
@@ -6,7 +6,7 @@ use BSE::TB::CouponTiers;
 
 =head1 NAME
 
-our $VERSION = "1.001";
+our $VERSION = "1.002";
 
 BSE::TB::Coupon - shop coupon objects
 
@@ -109,6 +109,9 @@ sub set_tiers {
 sub remove {
   my ($self) = @_;
 
+  $self->is_removable
+    or return;
+
   my @tiers = BSE::TB::CouponTiers->getBy2
     (
      [
@@ -167,8 +170,50 @@ sub is_valid {
   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 =
     (
@@ -252,6 +297,10 @@ sub 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");
 }
index 835e0ea62ce690288ff4c0b85ea41ae43cfa6cc7..7a81fe4aea3928f41298026bc211d60e80868081 100644 (file)
@@ -7,7 +7,7 @@ use vars qw/@ISA/;
 use Carp 'confess';
 use BSE::Shop::PaymentTypes;
 
-our $VERSION = "1.019";
+our $VERSION = "1.020";
 
 sub columns {
   return qw/id
@@ -30,7 +30,7 @@ sub columns {
            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 {
@@ -697,7 +697,7 @@ it's valid.
 sub coupon_valid {
   my ($self) = @_;
 
-  return $self->coupon_code ne "";
+  return defined($self->coupon_id);
 }
 
 =item coupon_active
@@ -758,4 +758,20 @@ sub product_cost_discount {
   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;
index 0d503f8a7a670870f48e7a7cf04d5fc75a596e69..80c7d12ade1e2cf47168b2b95e13dc5c47d5b790 100644 (file)
@@ -21,7 +21,7 @@ use BSE::CfgInfo qw(cfg_dist_image_uri);
 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 =
   (
@@ -1419,7 +1419,7 @@ sub req_coupon_edit {
   $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 || {});
 
@@ -1454,7 +1454,7 @@ sub req_coupon_save {
     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);
@@ -1527,6 +1527,11 @@ sub req_coupon_deleteform {
   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;
@@ -1553,6 +1558,11 @@ sub req_coupon_delete {
   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
index 231e603d87062ba4901aaa81954afac918707a12..62019d6671d427ee3bf52dbf9a7727d3894aa607 100644 (file)
@@ -18,7 +18,7 @@ use BSE::Countries qw(bse_country_code);
 use BSE::Util::Secure qw(make_secret);
 use BSE::Template;
 
-our $VERSION = "1.041";
+our $VERSION = "1.042";
 
 =head1 NAME
 
@@ -1842,10 +1842,10 @@ sub _fillout_order {
     }
   }
   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;
index 58062fe01172d341e86b464e1ca8475e5f9328fe..4676166c73e0277bd50633d584692ba4366c90ee 100644 (file)
@@ -1,6 +1,6 @@
 package Squirrel::Table;
 
-our $VERSION = "1.011";
+our $VERSION = "1.012";
 
 use Carp;
 use strict;
@@ -403,6 +403,44 @@ sub getCount {
   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.
index ab7877afc793008b8ade56ef76795fdf19d6213a..0b46ea5ba18bd6510bd5ce14c7c21f08d3d4d0bc 100644 (file)
@@ -236,6 +236,9 @@ description: Flashed when a coupon is saved (%1 coupon)
 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
 
index 194b4a27ee923238d6a11f6ce1c1848ee5c45ef4..31e1a375f20b8cee30aefeffd70f6e9a29c83cd0 100644 (file)
@@ -172,6 +172,9 @@ message: Saved coupon '%1:{code}s'
 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
 
index 54a0adfc9861dafded65a4bd1b6480a5f57a1f7c..677e5197c223d44a8805a0389b9795f439e0696c 100644 (file)
@@ -87,7 +87,7 @@
       <:.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>
index 6ba8964a4d18d10fa070123d55d57cf1610a7b42..7db60eb42a6e0cd3db514628678ad36c1e73dde0 100644 (file)
   <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>
index cf3aef6494c69de63315719c2ce7a9480591c224..26bffe5e7e93739c3e0ffe1db7ede897dbc83867 100644 (file)
@@ -79,9 +79,9 @@
   </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">&nbsp;</td>
       <td height="20" bgcolor="#666666">&nbsp;</td>
       <td align="CENTER" height="20" bgcolor="#666666" NOWRAP><font size="2" face="Verdana, Arial, Helvetica, sans-serif" color="#FFFFFF"> 
index e5815fbaa05ccd990675a4b71bc36db965b4681f..4f463813d4b6795e8653e2d778404135b8b27a8c 100644 (file)
@@ -222,7 +222,12 @@ Page <:= pages.page :> of <:= pages.pagecount :>
   <:.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 -:>
index 2bf7bfb180c827d2b4511c987d5e4b75aa1f3edb..3d9fa4a2d6b195c6f241609ae9eada6b15f6d65b 100644 (file)
@@ -676,10 +676,11 @@ Column freight_tracking;varchar(255);NO;;
 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