Skip to content

Commit

Permalink
NEW: Major improvements to discount system: restructured the way coup…
Browse files Browse the repository at this point in the history
…ons are validated and applied to orders. Introduced Gift Vouchers. Created coupon usage report.

fixes: #7 #4 #3 #1
  • Loading branch information
jedateach committed Feb 14, 2013
1 parent 64923a9 commit 5aa6860
Show file tree
Hide file tree
Showing 18 changed files with 830 additions and 153 deletions.
4 changes: 3 additions & 1 deletion _config.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,6 @@
define('ECOMMERCE_COUPON_DIR','shop_discount');
Object::add_extension("CheckoutPage_Controller", "CouponFormCheckoutDecorator");
DataObject::add_extension("Product_OrderItem", "DiscountedOrderItem");
DataObject::add_extension("CheckoutPage_Controller", "Product_OrderItem_Coupon");
DataObject::add_extension("CheckoutPage_Controller", "Product_OrderItem_Coupon");

SS_Report::register('ReportAdmin', 'CouponReport',3);
9 changes: 9 additions & 0 deletions code/CheckoutStep_Discount.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?php

class CheckoutStep_Discount extends CheckoutStep{

function CouponForm(){
return new CouponForm($this->owner,"CouponForm");
}

}
57 changes: 57 additions & 0 deletions code/cms/CouponReport.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
<?php

class CouponReport extends ShopPeriodReport{

protected $title = "Coupon Usage";
protected $dataClass = "OrderCoupon";
protected $periodfield = "Order.Paid";

function columns(){
return array(
"Name" => "Title",
"Code" => "Code",
"DiscountNice" => "Discount",
"Uses" => "Uses",
"Savings" => "Savings"
);
}

/**
* Remove unsortable columns
*/
function sortColumns() {
$cols = parent::sortColumns();
unset($cols['DiscountNice']);
return $cols;
}

function getReportField(){
$field = parent::getReportField();
$field->addSummary("Total",array(
"Uses"=>"sum",
"Savings"=> array("sum","Currency->Nice")
));
return $field;
}

function query($params){
$query = parent::query($params);
$query->select(
"$this->periodfield AS FilterPeriod",
"OrderCoupon.*",
"\"Title\" AS \"Name\"",
"COUNT(OrderCouponModifier.ID) AS Uses",
"SUM(OrderModifier.Amount) AS Savings");
$query->innerJoin("OrderCouponModifier", "OrderCoupon.ID = OrderCouponModifier.CouponID");
$query->innerJoin("OrderAttribute", "OrderCouponModifier.ID = OrderAttribute.ID");
$query->innerJoin("OrderModifier", "OrderCouponModifier.ID = OrderModifier.ID");
$query->innerJoin("Order", "OrderAttribute.OrderID = Order.ID");
$query->groupby("OrderCoupon.ID");
if(!$query->orderby){
$query->orderby("Uses DESC,Title ASC");
}
$query->limit("50");
return $query;
}

}
3 changes: 1 addition & 2 deletions code/cms/CouponsModelAdmin.php
Original file line number Diff line number Diff line change
Expand Up @@ -94,13 +94,12 @@ function ResultsForm($searchCriteria){
}
return $form;
}


}

/**
* @package shop-discount
*/
class CouponsModelAdmin_RecordController extends ModelAdmin_RecordController{

}
18 changes: 18 additions & 0 deletions code/forms/CouponDatetimeField.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?php
class CouponDatetimeField extends DatetimeField{

function __construct($name, $title = null, $value = ""){
parent::__construct($name, $title, $value);

$this->dateField->setConfig("showcalendar", true);

}

function setValue($val){
if($val == array('date' => '', 'time' => '')){
$val = null;
}
parent::setValue($val);
}

}
16 changes: 7 additions & 9 deletions code/CouponForm.php → code/forms/CouponForm.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@
class CouponForm extends OrderModifierForm{

function __construct($controller = null, $name){
$fields = new FieldSet();
$fields->push(new HeaderField('CouponHeading',_t("CouponForm.COUPONHEADING", 'Coupon/Voucher Code'),3));
$fields->push(new TextField('Code',_t("CouponForm.COUPON", 'Enter your coupon code if you have one.')));
$fields = new FieldSet(
new TextField('Code',_t("CouponForm.COUPON", 'Enter your coupon code if you have one.'))
);
$actions = new FieldSet(new FormAction('apply', _t("CouponForm.APPLY", 'Apply')));
$validator = new CouponFormValidator(array('Code'));

Expand All @@ -36,10 +36,8 @@ function apply($data,$form){
if(Director::is_ajax()) {
return $messagetype;
}
else {
$form->sessionMessage($message,$messagetype);
Director::redirect(CheckoutPage::find_link());
}
$form->sessionMessage($message,$messagetype);
$this->Controller()->redirectBack();
return;
}

Expand All @@ -61,8 +59,8 @@ function php($data) {
}
//check the coupon exists, and can be used
if($coupon = OrderCoupon::get_by_code($data['Code'])){
if(!$coupon->valid($cart)){
$this->validationError('Code',$coupon->validationerror,"bad");
if(!$coupon->valid($order)){
$this->validationError('Code',$coupon->getMessage(),"bad");
return false;
}
}else{
Expand Down
163 changes: 163 additions & 0 deletions code/model/GiftVoucherProduct.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
<?php
/**
* Gift voucher products, when purchased will send out a voucher code to the customer via email.
*/
class GiftVoucherProduct extends Product{

static $db = array(
"VariableAmount" => "Boolean",
"MinimumAmount" => "Currency"
);

static $order_item = "GiftVoucher_OrderItem";

public static $singular_name = "Gift Voucher";
function i18n_singular_name() { return _t("GiftVoucherProduct.SINGULAR", $this->stat('singular_name')); }
public static $plural_name = "Gift Vouchers";
function i18n_plural_name() { return _t("GiftVoucherProduct.PLURAL", $this->stat('plural_name')); }

function getCMSFields(){
$fields = parent::getCMSFields();
$fields->addFieldToTab("Root.Content.Pricing",
new OptionsetField("VariableAmount","Price",array(
0 => "Fixed",
1 => "Allow customer to choose"
)),
"BasePrice"
);
$fields->addFieldsToTab("Root.Content.Pricing", array(
$minimumamount = new TextField("MinimumAmount","Minimum Amount") //text field, because of CMS js validation issue
));
$fields->removeByName("CostPrice");
$fields->removeByName("Variations");
$fields->removeByName("Model");
return $fields;
}

function canPurchase($member = null) {
if(!self::$global_allow_purchase) return false;
if(!$this->dbObject('AllowPurchase')->getValue()) return false;
if(!$this->isPublished()) return false;
return true;
}

}

class GiftVoucherProduct_Controller extends Product_Controller{

function Form(){
$form = parent::Form();
if($this->VariableAmount){
$form->setSaveableFields(array(
"UnitPrice"
));
$form->Fields()->push($giftamount = new CurrencyField("UnitPrice","Amount",$this->BasePrice)); //TODO: set minimum amount
$giftamount->setForm($form);
}
$form->setValidator($validator = new GiftVoucherFormValidator(array(
"Quantity", "UnitPrice"
)));
return $form;
}

}

class GiftVoucherFormValidator extends RequiredFields{

function php($data){
$valid = parent::php($data);
if($valid){
$controller = $this->form->Controller();
if($controller->VariableAmount){
$giftvalue = $data['UnitPrice'];
if($controller->MinimumAmount > 0 && $giftvalue < $controller->MinimumAmount){
$this->validationError("UnitPrice", "Gift value must be at least ".$controller->MinimumAmount);
return false;
}
if($giftvalue <= 0){
$this->validationError("UnitPrice", "Gift value must be greater than 0");
return false;
}
}
}
return $valid;
}

}

class GiftVoucher_OrderItem extends Product_OrderItem{

static $db = array(
"GiftedTo" => "Varchar"
);

static $has_many = array(
"Coupons" => "OrderCoupon"
);

static $required_fields = array(
"UnitPrice"
);

/**
* Don't get unit price from product
*/
function UnitPrice() {
if($this->Product()->VariableAmount){
return $this->UnitPrice;
}
return parent::UnitPrice();
}

/**
* Create vouchers on order payment success event
*/
function onPayment(){
parent::onPayment();
if($this->Coupons()->Count() < $this->Quantity){
$remaining = $this->Quantity - $this->Coupons()->Count();
for($i = 0; $i < $remaining; $i++){
if($coupon = $this->createCoupon()){
$this->sendVoucher($coupon);
}
}
}
}

/**
* Create a new coupon, based on this orderitem
* @return OrderCoupon
*/
function createCoupon(){
if(!$this->Product()){
return false;
}
$coupon = new OrderCoupon(array(
"Title" => $this->Product()->Title,
"Type" => "Amount",
"Amount" => $this->UnitPrice,
"UseLimit" => 1,
"MinOrderValue" => $this->UnitPrice //safeguard that means coupons must be used entirely
));
$this->extend("updateCreateCupon",$coupon);
$coupon->write();
$this->Coupons()->add($coupon);
return $coupon;
}

/*
* Send the voucher to the appropriate email
*/
function sendVoucher(OrderCoupon $coupon){
$from = Email::getAdminEmail();
$to = $this->Order()->getLatestEmail();
$subject = _t("Order.GIFTVOUCHERSUBJECT", "Gift voucher");
$email = new Email($from, $to, $subject);
$email->setTemplate("GiftVoucherEmail");
$email->populateTemplate(array(
'Coupon' => $coupon
));
return $email->send();
}

}
Loading

0 comments on commit 5aa6860

Please sign in to comment.