BSE tiered product pricing
authorTony Cook <tony@develop-help.com>
Fri, 20 May 2011 10:17:47 +0000 (20:17 +1000)
committerTony Cook <tony@develop-help.com>
Fri, 27 May 2011 05:42:18 +0000 (15:42 +1000)
22 files changed:
MANIFEST
schema/bse.sql
site/cgi-bin/modules/Article.pm
site/cgi-bin/modules/BSE/Edit/Article.pm
site/cgi-bin/modules/BSE/Edit/Product.pm
site/cgi-bin/modules/BSE/TB/PriceTier.pm [new file with mode: 0644]
site/cgi-bin/modules/BSE/TB/PriceTierPrice.pm [new file with mode: 0644]
site/cgi-bin/modules/BSE/TB/PriceTierPrices.pm [new file with mode: 0644]
site/cgi-bin/modules/BSE/TB/PriceTiers.pm [new file with mode: 0644]
site/cgi-bin/modules/BSE/TB/SiteUserGroups.pm
site/cgi-bin/modules/BSE/UI/Shop.pm
site/cgi-bin/modules/BSE/Util/DynamicTags.pm
site/cgi-bin/modules/Product.pm
site/cgi-bin/modules/Products.pm
site/data/db/sql_statements.data
site/templates/admin/edit_product.tmpl
site/templates/cart_base.tmpl
site/templates/catalog.tmpl
site/templates/checkoutnew_base.tmpl
site/templates/checkoutpay_base.tmpl
site/templates/shopitem.tmpl
site/util/mysql.str

index 66f27c4..2b07d23 100644 (file)
--- a/MANIFEST
+++ b/MANIFEST
@@ -188,6 +188,10 @@ site/cgi-bin/modules/BSE/TB/OrderItems.pm
 site/cgi-bin/modules/BSE/TB/Orders.pm
 site/cgi-bin/modules/BSE/TB/OwnedFile.pm
 site/cgi-bin/modules/BSE/TB/OwnedFiles.pm
+site/cgi-bin/modules/BSE/TB/PriceTier.pm
+site/cgi-bin/modules/BSE/TB/PriceTierPrice.pm
+site/cgi-bin/modules/BSE/TB/PriceTierPrices.pm
+site/cgi-bin/modules/BSE/TB/PriceTiers.pm
 site/cgi-bin/modules/BSE/TB/ProductOption.pm
 site/cgi-bin/modules/BSE/TB/ProductOptions.pm
 site/cgi-bin/modules/BSE/TB/ProductOptionValue.pm
index 0a7e243..3ee6a62 100644 (file)
@@ -1228,4 +1228,32 @@ create table bse_selected_files (
   display_order integer not null default -1,
 
   unique only_one(owner_id, owner_type, file_id)
-) type = InnoDB;
\ No newline at end of file
+) type = InnoDB;
+
+drop table if exists bse_price_tiers;
+create table bse_price_tiers (
+  id integer not null auto_increment primary key,
+
+  description text not null,
+
+  group_id integer null,
+
+  from_date date null,
+  to_date date null,
+
+  display_order integer null null
+);
+
+drop table if exists bse_price_tier_prices;
+
+create table bse_price_tier_prices (
+  id integer not null auto_increment primary key,
+
+  tier_id integer not null,
+  product_id integer not null,
+
+  retailPrice integer not null,
+
+  unique tier_product(tier_id, product_id)
+);
+
index 43958df..9893b91 100644 (file)
@@ -57,6 +57,12 @@ sub update_dynamic {
   # conditional in case something strange is in the config file
   my $dynamic = $cfg->entry('basic', 'all_dynamic', 0) ? 1 : 0;
 
+  if (!$dynamic && $self->generator =~ /\bCatalog\b/) {
+    require Products;
+    my @tiers = Products->pricing_tiers;
+    @tiers and $dynamic = 1;
+  }
+
   $dynamic or $dynamic = $self->{force_dynamic};
 
   $dynamic or $dynamic = $self->is_access_controlled;
index 06bed00..93df5c9 100644 (file)
@@ -13,7 +13,7 @@ use BSE::Util::ContentType qw(content_type);
 use DevHelp::Date qw(dh_parse_date dh_parse_sql_date);
 use constant MAX_FILE_DISPLAYNAME_LENGTH => 255;
 
-our $VERSION = "1.005";
+our $VERSION = "1.007";
 
 =head1 NAME
 
@@ -1536,6 +1536,12 @@ sub make_link {
   $link;
 }
 
+sub save_columns {
+  my ($self, $table_object) = @_;
+
+  return $table_object->rowClass->columns;
+}
+
 sub save_new {
   my ($self, $req, $article, $articles) = @_;
 
@@ -1545,7 +1551,7 @@ sub save_new {
   my $cgi = $req->cgi;
   my %data;
   my $table_object = $self->table_object($articles);
-  my @columns = $table_object->rowClass->columns;
+  my @columns = $self->save_columns($table_object);
   $self->save_thumbnail($cgi, undef, \%data);
   for my $name (@columns) {
     $data{$name} = $cgi->param($name) 
@@ -1682,8 +1688,11 @@ sub save_new {
       or $data{$col} = $self->default_value($req, \%data, $col);
   }
 
-  shift @columns;
-  $article = $table_object->add(@data{@columns});
+  my @cols = $table_object->rowClass->columns;
+  shift @cols;
+  $article = $table_object->add(@data{@cols});
+
+  $self->save_new_more($req, $article, \%data);
 
   # we now have an id - generate the links
 
@@ -1759,6 +1768,16 @@ sub _article_data {
   return $article_data;
 }
 
+sub save_more {
+  my ($self, $req, $article, $data) = @_;
+  # nothing to do here
+}
+
+sub save_new_more {
+  my ($self, $req, $article, $data) = @_;
+  # nothing to do here
+}
+
 =item save
 
 Error codes:
@@ -1795,7 +1814,9 @@ sub save {
   my $old_dynamic = $article->is_dynamic;
   my $cgi = $req->cgi;
   my %data;
-  for my $name ($article->columns) {
+  my $table_object = $self->table_object($articles);
+  my @save_cols = $self->save_columns($table_object);
+  for my $name (@save_cols) {
     $data{$name} = $cgi->param($name) 
       if defined($cgi->param($name)) and $name ne 'id' && $name ne 'parentid'
        && $req->user_can("edit_field_edit_$name", $article);
@@ -1956,6 +1977,8 @@ sub save {
     }
   }
 
+  $self->save_more($req, $article, \%data);
+
   if ($req->is_ajax) {
     return $req->json_content
       (
index 469bbbb..a5263d6 100644 (file)
@@ -9,7 +9,7 @@ use BSE::Util::HTML;
 use BSE::CfgInfo 'product_options';
 use BSE::Util::Tags qw(tag_hash);
 
-our $VERSION = "1.001";
+our $VERSION = "1.004";
 
 =head1 NAME
 
@@ -146,6 +146,70 @@ sub tag_dboption_move {
   return BSE::Arrows::make_arrows($req->cfg, $down_url, $up_url, $refresh, $args, id => $my_id, id_prefix => "prodoptmove");
 }
 
+sub tag_tier_price {
+  my ($self, $rtier, $rprices, $product) = @_;
+
+  unless ($rprices->{loaded}) {
+    %$rprices = map { $_->tier_id => $_ } $product->prices
+      if $product->{id};
+    $rprices->{loaded} = 1;
+  }
+
+  $$rtier or return '** no current tier **';
+
+  exists $rprices->{$$rtier->id}
+    or return '';
+
+  return $rprices->{$$rtier->id}->retailPrice;
+}
+
+sub save_more {
+  my ($self, $req, $article, $data) = @_;
+
+  $self->_save_price_tiers($req, $article, $data);
+  $self->SUPER::save_more($req, $article, $data);
+}
+
+sub save_new_more {
+  my ($self, $req, $article, $data) = @_;
+
+  $self->_save_price_tiers($req, $article, $data);
+  $self->SUPER::save_new_more($req, $article, $data);
+}
+
+sub _save_price_tiers {
+  my ($self, $req, $article, $data) = @_;
+
+  $data->{save_pricing_tiers}
+    or return;
+
+  $req->user_can('edit_field_edit_retailPrice', $data)
+    or return;
+
+  my @tiers = Products->pricing_tiers;
+  my %prices;
+  for my $tier (@tiers) {
+    my $key = "tier_price_" . $tier->id;
+    if (exists $data->{$key} && $data->{$key} =~ /\S/) {
+      $prices{$tier->id} = $data->{$key} * 100;
+    }
+  }
+  $article->set_prices(\%prices);
+}
+
+sub save_columns {
+  my ($self, $table_object) = @_;
+
+  my @cols = $self->SUPER::save_columns($table_object);
+  my @tiers = Products->pricing_tiers;
+  if (@tiers) {
+    push @cols, "save_pricing_tiers";
+    push @cols, map { "tier_price_" . $_->id } @tiers;
+  }
+
+  return @cols;
+}
+
 =head1 Edit tags
 
 These a tags available on admin/edit_* pages specific to products.
@@ -183,6 +247,19 @@ by default.
 
 dboptionsjson - returns the product options as JSON.
 
+=item *
+
+iterator begin price_tiers ... price_tier I<field> ... iterator end price_tiers
+
+Iterate over the configured price tiers.
+
+=item *
+
+tier_price
+
+Return the price at the current price_tier.  Returns an empty string
+if there's no price at this tier.
+
 =back
 
 =cut
@@ -202,6 +279,9 @@ sub low_edit_tags {
   my $dboption_value_index;
   my $current_option_value;
   my $it = BSE::Util::Iterate->new;
+  my @tiers;
+  my $price_tier;
+  my %prices;
   return 
     (
      product => [ $tag_hash, $article ],
@@ -240,6 +320,15 @@ sub low_edit_tags {
       tag_dboptionvalue_move =>
       $self, $req, $article, \@dboption_values, \$dboption_value_index
      ],
+     $it->make
+     (
+      single => "price_tier",
+      plural => "price_tiers",
+      code => [ pricing_tiers => "Products" ],
+      data => \@tiers,
+      store => \$price_tier,
+     ),
+     tier_price => [ tag_tier_price => $self, \$price_tier, \%prices, $article ],
     );
 }
 
@@ -323,6 +412,18 @@ sub _validate_common {
     }
   }
 
+  if ($data->{save_pricing_tiers}) {
+    my @tiers = Products->pricing_tiers;
+    for my $tier (@tiers) {
+      my $key = "tier_price_" . $tier->id;
+      my $value = $data->{$key};
+      defined $value or next;
+      if ($value =~ /\S/ && $value !~ /^\d+(\.\d{1,2})?\s*/) {
+       $errors->{$key} = 'Pricing tier "' . $tier->description . '" price invalid';
+      }
+    }
+  }
+
   return !keys %$errors;
 }
 
diff --git a/site/cgi-bin/modules/BSE/TB/PriceTier.pm b/site/cgi-bin/modules/BSE/TB/PriceTier.pm
new file mode 100644 (file)
index 0000000..c4510d1
--- /dev/null
@@ -0,0 +1,68 @@
+package BSE::TB::PriceTier;
+use strict;
+use base 'Squirrel::Row';
+
+our $VERSION = "1.000";
+
+sub columns {
+  return qw(id description group_id from_date to_date display_order);
+}
+
+sub table {
+  return "bse_price_tiers";
+}
+
+sub defaults {
+  return
+    (
+     group_id => undef,
+     from_date => undef,
+     to_date => undef,
+     display_order => time,
+    );
+}
+
+=item match($user, $date)
+
+Match the C<$user> and C<$date> against the contraints for this tier.
+
+Returns true if all constraints pass.
+
+=cut
+
+sub match {
+  my ($self, $user, $date) = @_;
+
+  if (my $from = $self->from_date) {
+    $date lt $from and return;
+  }
+
+  if (my $to = $self->to_date) {
+    $date gt $to and return;
+  }
+
+  if ($self->group_id) {
+    $user or return;
+
+    require BSE::TB::SiteUserGroups;
+    my $group = BSE::TB::SiteUserGroups->getById($self->group_id);
+    unless ($group) {
+      require BSE::TB::AuditLog;
+      BSE::TB::AuditLog->log
+         (
+          component => "shop:pricetier:match",
+          level => "crit",
+          actor => "S",
+          msg => "Unknown group id " . $self->group_id . " in price tier " . $self->id,
+          object => $self,
+         );
+      return;
+    }
+    $group->contains_user($user)
+      or return;
+  }
+
+  return 1;
+}
+
+1;
diff --git a/site/cgi-bin/modules/BSE/TB/PriceTierPrice.pm b/site/cgi-bin/modules/BSE/TB/PriceTierPrice.pm
new file mode 100644 (file)
index 0000000..b7529e9
--- /dev/null
@@ -0,0 +1,15 @@
+package BSE::TB::PriceTierPrice;
+use strict;
+use base 'Squirrel::Row';
+
+our $VERSION = "1.000";
+
+sub table {
+  return "bse_price_tier_prices";
+}
+
+sub columns {
+  return qw(id tier_id product_id retailPrice);
+}
+
+1;
diff --git a/site/cgi-bin/modules/BSE/TB/PriceTierPrices.pm b/site/cgi-bin/modules/BSE/TB/PriceTierPrices.pm
new file mode 100644 (file)
index 0000000..1835d4e
--- /dev/null
@@ -0,0 +1,12 @@
+package BSE::TB::PriceTierPrices;
+use strict;
+use base 'Squirrel::Table';
+use BSE::TB::PriceTierPrice;
+
+our $VERSION = "1.000";
+
+sub rowClass {
+  return "BSE::TB::PriceTierPrice";
+}
+
+1;
diff --git a/site/cgi-bin/modules/BSE/TB/PriceTiers.pm b/site/cgi-bin/modules/BSE/TB/PriceTiers.pm
new file mode 100644 (file)
index 0000000..c14b6eb
--- /dev/null
@@ -0,0 +1,10 @@
+package BSE::TB::PriceTiers;
+use strict;
+use base 'Squirrel::Table';
+use BSE::TB::PriceTier;
+
+our $VERSION = "1.000";
+
+sub rowClass { 'BSE::TB::PriceTier' }
+
+1;
index db49b00..84eac4e 100644 (file)
@@ -3,7 +3,7 @@ use strict;
 use base 'Squirrel::Table';
 use BSE::TB::SiteUserGroup;
 
-our $VERSION = "1.000";
+our $VERSION = "1.001";
 
 use constant SECT_QUERY_GROUPS => "Query Groups";
 use constant SECT_QUERY_GROUP_PREFIX => 'Query group ';
@@ -75,6 +75,12 @@ sub getByName {
   }
 }
 
+sub getById {
+  my ($self, $id) = @_;
+
+  return $id > 0 ? $self->getByPkey($id) : $self->getQueryGroup(BSE::Cfg->single, $id);
+}
+
 package BSE::TB::SiteUserQueryGroup;
 use constant OWNER_TYPE => "G";
 use Carp qw(confess);
index 24a9210..9ea005c 100644 (file)
@@ -17,7 +17,7 @@ use BSE::Shipping;
 use BSE::Countries qw(bse_country_code);
 use BSE::Util::Secure qw(make_secret);
 
-our $VERSION = "1.009";
+our $VERSION = "1.010";
 
 use constant MSG_SHOP_CART_FULL => 'Your shopping cart is full, please remove an item and try adding an item again';
 
@@ -204,7 +204,7 @@ sub req_add {
       { 
        productId => $product->{id}, 
        units => $quantity, 
-       price=>$product->{retailPrice},
+       price=>$product->price(user => scalar $req->siteuser),
        options=>$options,
        %$extras,
       };
@@ -273,7 +273,7 @@ sub req_addsingle {
       { 
        productId => $addid, 
        units => $quantity, 
-       price=>$product->{retailPrice},
+       price=>$product->price(user => scalar $req->siteuser),
        options=>$options,
        %$extras,
       };
@@ -401,7 +401,7 @@ sub req_addmultiple {
        { 
         productId => $product->{id},
         units => $addition->{quantity}, 
-        price=>$product->{retailPrice},
+        price=>$product->price(user => scalar $req->siteuser),
         options=>[],
         %{$addition->{extras}},
        };
@@ -1643,7 +1643,8 @@ sub _build_items {
       for my $col (@prodcols) {
        $work{$col} = $product->$col() unless exists $work{$col};
       }
-      $work{extended_retailPrice} = $work{units} * $work{retailPrice};
+      $work{price} = $product->price(user => scalar $req->siteuser);
+      $work{extended_retailPrice} = $work{units} * $work{price};
       $work{extended_gst} = $work{units} * $work{gst};
       $work{extended_wholesale} = $work{units} * $work{wholesalePrice};
       
index 7432fbd..c97561d 100644 (file)
@@ -6,7 +6,7 @@ use base 'BSE::ThumbLow';
 use base 'BSE::TagFormats';
 use BSE::CfgInfo qw(custom_class);
 
-our $VERSION = "1.005";
+our $VERSION = "1.006";
 
 sub new {
   my ($class, $req) = @_;
@@ -59,6 +59,8 @@ sub tags {
      dyncatmsg => [ tag_dyncatmsg => $self, $req ],
      $self->dyn_iterator("userfiles", "userfile"),
      $self->dyn_iterator_obj("paidfiles", "paidfile"),
+     price => [ tag_price => $self ],
+     ifTieredPricing => [ tag_ifTieredPricing => $self ],
      $self->_custom_tags,
     );
 }
@@ -1102,6 +1104,77 @@ sub tag_dynmove {
   return make_arrows($self->{req}->cfg, $down_url, $up_url, $refresh_to, $img_prefix);
 }
 
+=item price
+
+Return the price of a product.
+
+One of two parameters:
+
+=over
+
+=item *
+
+I<product> - the product to fetch the price for.  This can be a name
+or [] evaluating to a product id.
+
+=item *
+
+I<field> - "price" to fetch the price, "discount" to fetch the
+difference from the base price, "discountpc" to fetch the discount in
+percent (whole number).  Returns the price if no I<field> is
+specified.
+
+=back
+
+=cut
+
+sub tag_price {
+  my ($self, $args, $acts, $func, $templater) = @_;
+
+  my ($id, $field) = $templater->get_parms($args, $acts);
+  $field ||= "price";
+
+  my $work;
+  if ($id =~ /^[0-9]+$/) {
+    require Products;
+    $work = Products->getByPkey($id)
+      or return "** unknown product $id **";
+  }
+  else {
+    $work = $self->{req}->get_article($id)
+      or return "** unknown product name $id **";
+  }
+
+  my ($price, $tier) = $work->price(user => scalar $self->{req}->siteuser);
+
+  if ($field eq "price") {
+    return $price;
+  }
+  elsif ($field eq "discount") {
+    return $work->retailPrice - $price;
+  }
+  elsif ($field eq "discountpc") {
+    $work->retailPrice or return "";
+    return sprintf("%.0f", ($work->retailPrice - $price) / $work->retailPrice * 100);
+  }
+  else {
+    return "** unknown field $field **";
+  }
+}
+
+=item ifTieredPricing
+
+Conditional to check if there's tiered pricing.
+
+=cut
+
+sub tag_ifTieredPricing {
+  require Products;
+  my @tiers = Products->pricing_tiers;
+
+  return scalar @tiers;
+}
+
 1;
 
 =head1 NAME
index 9ce03ed..cdc9bdf 100644 (file)
@@ -5,7 +5,7 @@ use Articles;
 use vars qw/@ISA/;
 @ISA = qw/Article/;
 
-our $VERSION = "1.000";
+our $VERSION = "1.001";
 
 # subscription_usage values
 use constant SUBUSAGE_START_ONLY => 1;
@@ -154,6 +154,9 @@ sub remove {
   # remove any wishlist items
   BSE::DB->run(bseRemoveProductFromWishlists => $self->id);
 
+  # remove any tiered prices
+  BSE::DB->run(bseRemoveProductPrices => $self->id);
+
   return $self->SUPER::remove($cfg);
 }
 
@@ -165,6 +168,97 @@ sub has_sale_files {
   return $row->{have_sale_files};
 }
 
+sub prices {
+  my ($self) = @_;
+
+  require BSE::TB::PriceTierPrices;
+  my @prices = BSE::TB::PriceTierPrices->getBy(product_id => $self->id);
+}
+
+=item set_prices($prices)
+
+Set tiered pricing for the product.
+
+I<$prices> is a hashref mapping tier ids to prices in cents.
+
+If a tier doesn't have a price in I<$prices> it's removed from the
+product.
+
+=cut
+
+sub set_prices {
+  my ($self, $prices) = @_;
+
+  my %current = map { $_->tier_id => $_ } $self->prices;
+  for my $tier_id (keys %$prices) {
+    my $current = delete $current{$tier_id};
+    if ($current) {
+      $current->set_retailPrice($prices->{$tier_id});
+      $current->save;
+    }
+    else {
+      BSE::TB::PriceTierPrices->make
+         (
+          tier_id => $tier_id,
+          product_id => $self->id,
+          retailPrice => $prices->{$tier_id},
+         );
+    }
+  }
+
+  # remove any spares
+  for my $price (values %current) {
+    $price->remove;
+  }
+}
+
+=item price(user => $user, date => $sql_date)
+
+=item price(user => $user)
+
+Return the retail price depending on the user and date
+and optionally the tier object (in list context).
+
+If no tier matches then the undef is returned at the tier object.
+
+=cut
+
+sub price {
+  my ($self, %opts) = @_;
+
+  my $user = delete $opts{user};
+  my $date = delete $opts{date} || BSE::Util::SQL::now_sqldate();
+  my @tiers = Products->pricing_tiers;
+  my %prices = map { $_->tier_id => $_ } $self->prices;
+
+  my $price;
+  my $found_tier;
+  for my $tier (@tiers) {
+    if ($prices{$tier->id}
+       && $tier->match($user, $date)) {
+      $price = $prices{$tier->id}->retailPrice;
+      $found_tier = $tier;
+      last;
+    }
+  }
+
+  defined $price or $price = $self->retailPrice;
+
+  return wantarray ? ( $price, $found_tier ) : $price;
+}
+
+sub update_dynamic {
+  my ($self, $cfg) = @_;
+
+  my @tiers = Products->pricing_tiers;
+  if (@tiers) {
+    $self->set_cached_dynamic(1);
+    return;
+  }
+
+  return $self->SUPER::update_dynamic($cfg);
+}
+
 package BSE::CfgProductOption;
 use strict;
 
index 98a960f..da38e94 100644 (file)
@@ -5,7 +5,7 @@ use vars qw(@ISA $VERSION);
 @ISA = qw(Squirrel::Table);
 use Product;
 
-our $VERSION = "1.000";
+our $VERSION = "1.001";
 
 sub rowClass {
   return 'Product';
@@ -51,4 +51,17 @@ sub visible_step_children {
   return Products->getSpecial(visibleStep => $id, $today);
 }
 
+{
+  my $tiers;
+  sub pricing_tiers {
+    unless ($tiers) {
+      require BSE::TB::PriceTiers;
+      $tiers = [ sort { $a->display_order <=> $b->display_order }
+                BSE::TB::PriceTiers->all ];
+    }
+
+    return @$tiers;
+  }
+}
+
 1;
index 0ddcbc2..94621b3 100644 (file)
@@ -1,5 +1,5 @@
 --
-# VERSION=1.001
+# VERSION=1.002
 name: bse_siteuserSeminarBookingsDetail
 sql_statement: <<SQL
 select ar.*, pr.*, se.*, ss.*, sb.*,
@@ -457,3 +457,8 @@ delete from bse_selected_files
    and owner_id = ?
 SQL
 
+name: bseRemoveProductPrices
+sql_statement: <<SQL
+delete from bse_price_tier_prices
+where product_id = ?
+SQL
index 5c397a8..e390b4f 100644 (file)
@@ -1,4 +1,4 @@
-<:wrap admin/xbase.tmpl title => "Shop Administration", menuitem=>edit, showtitle=>1 :>
+<:wrap admin/xbase.tmpl title => "Shop Administration", menuitem=>"edit", showtitle=>"1" :>
 <:ifMessage:>
 <p><b><:message:></b></p>
 <:or:><:eif:> 
               (0.00)<:or:><:money product retailPrice:><:eif:> </td>
             <td class="help"><:help product retail:> <:error_img retailPrice:></td>
           </tr>
+<:if Price_tiers:>
+          <tr>
+           <th><:cfg "product field" tier_prices "Tier prices":>:</th>
+           <td>
+<input type="hidden" name="save_pricing_tiers" value="1" />
+           <table class="editform editformtiny">
+<:iterator begin price_tiers:>
+<tr>
+  <th><:price_tier description:></th>
+  <td>$<:if FieldPerm retailPrice:><input type="text" name="tier_price_<:price_tier id:>" value="<:ifEq [tier_price] "":><:oldi [cat "tier_price_" [price_tier id]] 0:><:or:><:oldi [cat "tier_price_" [price_tier id]] 0 money tier_price:><:eif:>" size="7" /> (0.00)<:or FieldPerm:><:ifEq [tier_price] "":>-<:or:><:money tier_price:><:eif:><:eif FieldPerm:></td>
+  <td class="help"><:error_img [cat "tier_price_" [price_tier id]]:></td>
+
+</tr>
+<:iterator end price_tiers:>
+           </table>
+           </td>
+<td class="help"><:help product tier_price:></td>
+         </tr>
+<:or Price_tiers:><:eif Price_tiers:>
           <tr> 
             <th>Wholesale price:</th>
             <td>$ 
index 44d749a..c9020b0 100644 (file)
@@ -52,8 +52,7 @@
             <td nowrap align="center"> 
               <input type="text" name="quantity_<:index:>" size="2" value="<:item units:>">
             </td>
-            <td align="right"> <font face="Verdana, Arial, Helvetica, sans-serif" size="-2"><b>$<:money 
-              item retailPrice:></b></font></td>
+            <td align="right"> <font face="Verdana, Arial, Helvetica, sans-serif" size="-2"><b>$<:money item price:></b></font></td>
             <td nowrap> 
               <input type="submit" name="delete_<:index:>" value="Remove">
             </td>
index e93fa26..5c5d16b 100644 (file)
          <td><font face="Verdana, Arial, Helvetica, sans-serif" size="-2" color="#FFFFFF"><b>Qty:</b></font></td>
 <:or:><:eif:>
           </tr>
+<:if Dynamic:>
+          <:iterator begin dynallprods:> 
+          <tr valign="middle" align="center" bgcolor="#FFFFFF"> 
+            <td width="100%" align="left"> &nbsp;<font face="Verdana, Arial, Helvetica, sans-serif" size="-2"><a href="<:url dynallprod:>"><:dynallprod 
+              description:></a></font> <:dynmoveallprod:></td>
+<:if Dynallprod retailPrice:>
+            <td align="right"> <font face="Verdana, Arial, Helvetica, sans-serif" size="-2"><b>$<:money price dynallprod:></b></font></td>
+<:ifAnd [cfg shop order_from_catalog] [ifEq [ifDynAnyProductOptions dynallprod] "0"]:>
+<td nowrap="nowrap"><font face="Verdana, Arial, Helvetica, sans-serif" size="-2"><input type="text" name="qty<:dynallprod id:>" size="4" /><input type="image" src="/images/store/quick_add.gif" alt="+" name="a_addsingle<:dynallprod id:>" /></font></td>
+<:or:><:eif:>
+<:or Dynallprod:>
+            <td align="center"> <font face="Verdana, Arial, Helvetica, sans-serif" size="-2" color="#999999"><b>TBA</b></font></td>
+<:eif Dynallprod:>
+          </tr>
+
+          <:iterator end dynallprods:> 
+<:or Dynamic:>
           <:iterator begin allprods:> 
           <tr valign="middle" align="center" bgcolor="#FFFFFF"> 
             <td width="100%" align="left"> &nbsp;<font face="Verdana, Arial, Helvetica, sans-serif" size="-2"><a href="<:url allprod:>"><:allprod 
@@ -58,6 +75,7 @@
           </tr>
 
           <:iterator end allprods:> 
+<:eif Dynamic:>
 <:ifCfg shop order_from_catalog:>
 <tr>
   <td colspan="3" align="right" bgcolor="#FFFFFF"><font face="Verdana, Arial, Helvetica, sans-serif" size="-2"><input type="submit" name="a_addmultiple" value="Add" /></font></td>
index 8b519fb..f38b858 100644 (file)
@@ -90,7 +90,7 @@ function BSE_validateForm {
           <td nowrap align="center"><font face="Verdana, Arial, Helvetica, sans-serif" size="-2"><:item 
             units:></font></td>
           <td align="right"> <font face="Verdana, Arial, Helvetica, sans-serif" size="-2"><b>$<: 
-            money item retailPrice :></b></font></td>
+            money item price :></b></font></td>
         </tr>
         <:iterator end items:> 
         <:if Shipping_cost:>
index 27bc062..fc684e3 100644 (file)
@@ -53,7 +53,7 @@
           <td nowrap align="center"><font face="Verdana, Arial, Helvetica, sans-serif" size="-2"><:item 
             units:></font></td>
           <td align="right"> <font face="Verdana, Arial, Helvetica, sans-serif" size="-2"><b>$<: 
-            money item retailPrice :></b></font></td>
+            money item price :></b></font></td>
         </tr>
         <:iterator end items:> 
         <:if shipping_cost:>
index 9291b07..2555615 100644 (file)
@@ -55,7 +55,7 @@
             <td bgcolor="#666666" align="left"> &nbsp;&nbsp;<font size="2" face="Verdana, Arial, Helvetica, sans-serif" color="#FFFFFF"> 
               <b>Price:</b></font> &nbsp;&nbsp;</td>
             <td bgcolor="#FFFFFF"> &nbsp; <font face="Verdana, Arial, Helvetica, sans-serif" size="2" color="#000000"> 
-              <b>$<:money product retailPrice:> </b>(inc GST)</font> </td>
+              <b>$<:if Dynamic:><:money price dynarticle:> <:ifPrice dynarticle discountpc:><:price dynarticle discountpc:>% off!<:or:><:eif:><:or Dynamic:><:money product retailPrice:><:eif Dynamic:></b>(inc GST)</font> </td>
           </tr>
         </table>
       </td>
index 2822a44..f64cce4 100644 (file)
@@ -296,6 +296,23 @@ Column size_in_bytes;int(11);NO;NULL;
 Column filekey;varchar(80);NO;;
 Index PRIMARY;1;[id]
 Index by_owner_category;0;[owner_type;owner_id;category]
+Table bse_price_tier_prices
+Engine MyISAM
+Column id;int(11);NO;NULL;auto_increment
+Column tier_id;int(11);NO;NULL;
+Column product_id;int(11);NO;NULL;
+Column retailPrice;int(11);NO;NULL;
+Index PRIMARY;1;[id]
+Index tier_product;1;[tier_id;product_id]
+Table bse_price_tiers
+Engine MyISAM
+Column id;int(11);NO;NULL;auto_increment
+Column description;text;NO;NULL;
+Column group_id;int(11);YES;NULL;
+Column from_date;date;YES;NULL;
+Column to_date;date;YES;NULL;
+Column display_order;int(11);YES;NULL;
+Index PRIMARY;1;[id]
 Table bse_product_option_values
 Engine InnoDB
 Column id;int(11);NO;NULL;auto_increment