]> git.imager.perl.org - bse.git/blobdiff - site/cgi-bin/modules/BSE/TB/Order.pm
re-work coupons to allow multiple coupon types
[bse.git] / site / cgi-bin / modules / BSE / TB / Order.pm
index 4ad931c10cf52923223550b52a2ddbcbc67da302..d73ac07854813ecb8d71930d533e376f30255dcd 100644 (file)
@@ -5,8 +5,9 @@ use Squirrel::Row;
 use vars qw/@ISA/;
 @ISA = qw/Squirrel::Row/;
 use Carp 'confess';
+use BSE::Shop::PaymentTypes;
 
-our $VERSION = "1.013";
+our $VERSION = "1.029";
 
 sub columns {
   return qw/id
@@ -28,7 +29,9 @@ sub columns {
            ccStatus2 ccTranId complete delivOrganization billOrganization
            delivStreet2 billStreet2 purchase_order shipping_method
            shipping_name shipping_trace
-          paypal_token paypal_tran_id freight_tracking stage ccPAN/;
+          paypal_token paypal_tran_id freight_tracking stage ccPAN
+          paid_manually coupon_id coupon_code_discount_pc delivery_in
+           product_cost_discount coupon_cart_wide coupon_description/;
 }
 
 sub table {
@@ -96,6 +99,8 @@ sub defaults {
      freight_tracking => "",
      stage => "incomplete",
      ccPAN => "",
+     paid_manually => 0,
+     delivery_in => undef,
     );
 }
 
@@ -120,7 +125,7 @@ sub payment_columns {
   return qw/ccNumberHash ccName ccExpiryHash ccType
            paidFor paymentReceipt paymentType
            ccOnline ccSuccess ccReceipt ccStatus ccStatusText
-           ccStatus2 ccTranId/;
+           ccStatus2 ccTranId ccPAN paid_manually/;
 }
 
 =item billing_to_delivery_map
@@ -163,16 +168,16 @@ sub siteuser {
   my ($self) = @_;
 
   if ($self->siteuser_id) {
-    require SiteUsers;
-    my $user = SiteUsers->getByPkey($self->siteuser_id);
+    require BSE::TB::SiteUsers;
+    my $user = BSE::TB::SiteUsers->getByPkey($self->siteuser_id);
     $user and return $user;
   }
 
   $self->{userId} or return;
 
-  require SiteUsers;
+  require BSE::TB::SiteUsers;
 
-  return ( SiteUsers->getBy(userId=>$self->{userId}) )[0];
+  return ( BSE::TB::SiteUsers->getBy(userId=>$self->{userId}) )[0];
 }
 
 sub items {
@@ -202,8 +207,8 @@ sub paid_files {
 sub products {
   my ($self) = @_;
 
-  require Products;
-  Products->getSpecial(orderProducts=>$self->{id});
+  require BSE::TB::Products;
+  BSE::TB::Products->getSpecial(orderProducts=>$self->{id});
 }
 
 sub valid_fields {
@@ -211,41 +216,41 @@ sub valid_fields {
 
   my %fields =
     (
-     delivFirstName => { description=>'Delivery First Name', 
+     delivFirstName => { description=>'Delivery First Name',
                         rules=>'dh_one_line' },
-     delivLastName => { description => 'Delivery Last Name', 
+     delivLastName => { description => 'Delivery Last Name',
                         rules=>'dh_one_line'  },
-     delivOrganization => { description => 'Delivery Organization', 
+     delivOrganization => { description => 'Delivery Organization',
                            rules=>'dh_one_line'  },
-     delivStreet => { description => 'Delivery Street', 
+     delivStreet => { description => 'Delivery Street',
                         rules=>'dh_one_line'  },
-     delivStreet2 => { description => 'Delivery Street 2', 
+     delivStreet2 => { description => 'Delivery Street 2',
                         rules=>'dh_one_line'  },
-     delivState => { description => 'Delivery State', 
+     delivState => { description => 'Delivery State',
                         rules=>'dh_one_line'  },
-     delivSuburb => { description => 'Delivery Suburb', 
+     delivSuburb => { description => 'Delivery Suburb',
                         rules=>'dh_one_line'  },
-     delivPostCode => { description => 'Delivery Post Code', 
+     delivPostCode => { description => 'Delivery Post Code',
                         rules=>'dh_one_line;dh_int_postcode'  },
-     delivCountry => { description => 'Delivery Country', 
+     delivCountry => { description => 'Delivery Country',
                         rules=>'dh_one_line'  },
-     billFirstName => { description => 'Billing First Name', 
+     billFirstName => { description => 'Billing First Name',
                         rules=>'dh_one_line'  },
-     billLastName => { description => 'Billing Last Name', 
+     billLastName => { description => 'Billing Last Name',
                         rules=>'dh_one_line'  },
-     billOrganization => { description => 'Billing Organization', 
+     billOrganization => { description => 'Billing Organization',
                           rules=>'dh_one_line'  },
-     billStreet => { description => 'Billing Street', 
+     billStreet => { description => 'Billing Street',
                         rules=>'dh_one_line'  },
-     billStreet2 => { description => 'Billing Street 2', 
+     billStreet2 => { description => 'Billing Street 2',
                         rules=>'dh_one_line'  },
-     billSuburb => { description => 'Billing Suburb', 
+     billSuburb => { description => 'Billing Suburb',
                         rules=>'dh_one_line'  },
-     billState => { description => 'Billing State', 
+     billState => { description => 'Billing State',
                         rules=>'dh_one_line'  },
-     billPostCode => { description => 'Billing Post Code', 
+     billPostCode => { description => 'Billing Post Code',
                         rules=>'dh_one_line;dh_int_postcode'  },
-     billCountry => { description => 'Billing First Name', 
+     billCountry => { description => 'Billing First Name',
                         rules=>'dh_one_line'  },
      telephone => { description => 'Telephone Number',
                    rules => "phone" },
@@ -254,7 +259,7 @@ sub valid_fields {
      emailAddress => { description => 'Email Address',
                       rules=>'email' },
      instructions => { description => 'Instructions' },
-     billTelephone => { description => 'Billing Telephone Number', 
+     billTelephone => { description => 'Billing Telephone Number',
                        rules=>'phone' },
      billFacsimile => { description => 'Billing Facsimile Number',
                        rules=>'phone' },
@@ -289,20 +294,20 @@ sub valid_payment_fields {
 
   my %fields =
     (
-     cardNumber => 
-     { 
+     cardNumber =>
+     {
       description => "Credit Card Number",
       rules=>"creditcardnumber",
      },
-     cardExpiry => 
+     cardExpiry =>
      {
       description => "Credit Card Expiry Date",
       rules => 'creditcardexpirysingle',
      },
      ccName => { description => "Credit Card Holder" },
      ccType => { description => "Credit Card Type" },
-     cardVerify => 
-     { 
+     cardVerify =>
+     {
       description => 'Card Verification Value',
       rules => 'creditcardcvv',
      },
@@ -325,7 +330,7 @@ sub clear_items {
 
   confess "Attempt to clear items on completed order $self->{id}"
     if $self->{complete};
-  
+
   BSE::DB->run(deleteOrdersItems => $self->{id});
 }
 
@@ -346,7 +351,7 @@ sub add_item {
       $options = delete $opts{options};
     }
   }
-  
+
   require BSE::TB::OrderItems;
   my %item =
     (
@@ -517,9 +522,9 @@ sub _tags {
        $current_item
         or return '* only usable in items *';
 
-       require Products;
+       require BSE::TB::Products;
        my $id = $current_item->productId;
-       $products{$id} ||= Products->getByPkey($id);
+       $products{$id} ||= BSE::TB::Products->getByPkey($id);
 
        my $product = $products{$id}
         or return '';
@@ -541,9 +546,9 @@ sub _tags {
        $current_item
         or return '* only usable in items *';
 
-       require Products;
+       require BSE::TB::Products;
        my $id = $current_item->productId;
-       $products{$id} ||= Products->getByPkey($id);
+       $products{$id} ||= BSE::TB::Products->getByPkey($id);
 
        my $product = $products{$id}
         or return '';
@@ -564,6 +569,34 @@ sub _tags {
     );
 }
 
+sub cfg_must_be_paid {
+  BSE::Cfg->single->entryBool("download", "must_be_paid", 0);
+}
+
+sub cfg_must_be_filled {
+  BSE::Cfg->single->entryBool("download", "must_be_filled", 0);
+}
+
+=item file_available
+
+Given an order file, return true if available for download.
+
+This will return nonsensical results for files not associated with the
+order.
+
+=cut
+
+sub file_available {
+  my ($self, $file) = @_;
+
+  $file->forSale or return 1;
+
+  return 0 if $self->cfg_must_be_paid && !$self->paidFor;
+  return 0 if $self->cfg_must_be_filled && !$self->filled;
+
+  return 1;
+}
+
 =item mail_tags
 
 =cut
@@ -604,9 +637,10 @@ sub send_shipped_email {
      subject => "Your order has shipped",
      template => "email/ordershipped",
      acts => \%acts,
-     log_msg => "Notify customer order has shipped",
+     log_msg => "Notify customer that Order No. " . $self->id . " has shipped",
      log_object => $self,
      log_component => "shopadmin:orders:saveorder",
+     vars => { order => $self },
     );
   if ($self->emailAddress && $self->billEmail
       && lc $self->emailAddress ne $self->billEmail) {
@@ -625,7 +659,7 @@ sub new_stage {
   }
 
   my $old_stage = $self->stage;
-  my $msg = "Set to stage '$stage'";
+  my $msg = "Set Order No. ". $self->id . " stage to '$stage'";
   if (defined $stage_note && $stage_note =~ /\S/) {
     $msg .= ": $stage_note";
   }
@@ -635,13 +669,19 @@ sub new_stage {
      component => "shopadmin:orders:saveorder",
      object => $self,
      msg => $msg,
-     level => "info",
+     level => "notice",
      actor => $who || "U"
     );
 
   if ($stage ne $old_stage) {
     $self->set_stage($stage);
     if ($stage eq "shipped") {
+      if (!$self->filled) {
+       require BSE::Util::SQL;
+
+       $self->set_whoFilled($who ? $who->logon : "-unknown-");
+       $self->set_whenFilled(BSE::Util::SQL::now_datetime());
+      }
       $self->send_shipped_email();
       $self->set_filled(1);
     }
@@ -661,4 +701,115 @@ sub set_ccPANTruncate {
   $self->set_ccPAN($pan);
 }
 
+=item is_manually_paid
+
+Returns true if the order is marked as manually paid, either through
+the older PAYMENT_MANUAL paymentType value or via the newer flag.
+
+=cut
+
+sub is_manually_paid {
+  my ($self) = @_;
+
+  return $self->paidFor &&
+    ($self->paid_manually || $self->paymentType == PAYMENT_MANUAL);
+}
+
+=item coupon_valid
+
+For compatibility with cart objects, returns true if the currently
+stored coupon is valid.
+
+Since only an active coupon is stored, if we have a coupon code, then
+it's valid.
+
+=cut
+
+sub coupon_valid {
+  my ($self) = @_;
+
+  return defined($self->coupon_id);
+}
+
+=item coupon_active
+
+For compatibility with cart objects, returns true if the currently
+stored coupon is active.
+
+Since only an active coupon is stored, if we have a coupon code, then
+it's valid.
+
+=cut
+
+*coupon_active = \&coupon_valid;
+
+=item total_cost
+
+Return the total cost of products without the coupon discount applied.
+
+=cut
+
+sub total_cost {
+  my ($self) = @_;
+
+  my $total = 0;
+  for my $item ($self->items) {
+    $total += $item->extended("price");
+  }
+
+  return $total;
+}
+
+=item discounted_product_cost
+
+Return the total cost of products less the discount from the coupon
+code.
+
+=cut
+
+sub discounted_product_cost {
+  my ($self) = @_;
+
+  my $cost = $self->total_cost;
+
+  if ($self->product_cost_discount) {
+    return $cost - $self->product_cost_discount;
+  }
+
+  $cost -= int($cost * $self->coupon_code_discount_pc / 100);
+
+  return $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);
+}
+
+=item coupon_code
+
+Emulate the cart's coupon-code method.
+
+=cut
+
+sub coupon_code {
+  my ($self) = @_;
+
+  my $coupon = $self->coupon
+    or return;
+
+  return $coupon->code;
+}
+
 1;