<?php
/**
* Order
*
* The WPDeals order class handles order data.
*
* @class wpdeals_order
* @package WPDeals
* @category Class
* @author Tokokoo
*/
class wpdeals_order {
private $_data = array();
public function __get($variable) {
return isset($this->_data[$variable]) ? $this->_data[$variable] : null;
}
public function __set($variable, $value) {
$this->_data[$variable] = $value;
}
/** Get the order if ID is passed, otherwise the order is new and empty */
function wpdeals_order( $id='' ) {
if ($id>0) $this->get_order( $id );
}
/** Gets an order from the database */
function get_order( $id = 0 ) {
if (!$id) return false;
if ($result = get_post( $id )) :
$this->populate( $result );
return true;
endif;
return false;
}
/** Populates an order from the loaded post data */
function populate( $result ) {
global $wpdeals;
// Standard post data
$this->id = $result->ID;
$this->order_date = $result->post_date;
$this->modified_date = $result->post_modified;
$this->customer_note = $result->post_excerpt;
// Custom fields
$this->items = (array) get_post_meta( $this->id, '_order_items', true );
$this->user_id = (int) get_post_meta( $this->id, '_customer_user', true );
$this->completed_date = get_post_meta( $this->id, '_completed_date', true );
$user_info = get_userdata($this->user_id);
if ($user_info->user_email) $this->user_email = $user_info->user_email;
if ($user_info->user_nicename) $this->user_name = $user_info->display_name;
if (!$this->completed_date) $this->completed_date = $this->modified_date;
$order_custom_fields = get_post_custom( $this->id ); //print_r($order_custom_fields);
// Define the data we're going to load: Key => Default value
$load_data = array(
'order_key' => '',
'payment_method' => '',
'payment_method_title' => '',
'order_total' => ''
);
// Load the data from the custom fields
foreach ($load_data as $key => $default) :
if (isset($order_custom_fields[ '_' . $key ][0]) && $order_custom_fields[ '_' . $key ][0]!=='') :
$this->$key = $order_custom_fields[ '_' . $key ][0];
else :
$this->$key = $default;
endif;
endforeach;
// Taxonomy data
$terms = wp_get_object_terms( $this->id, 'deals_sales_status' );
if (!is_wp_error($terms) && $terms) :
$term = current($terms);
$this->status = $term->slug;
else :
$this->status = 'pending';
endif;
}
/** Gets order total */
function get_order_total() {
return $this->order_total;
}
/** Calculate item cost - useful for gateways */
function get_item_cost( $item, $inc_tax = false ) {
if ($inc_tax) :
return number_format( $item['cost'] * (1 + ($item['taxrate']/100)) , 2, '.', '');
else :
return number_format( $item['cost'] , 2, '.', '');
endif;
}
/** Calculate row cost - useful for gateways */
function get_row_cost( $item, $inc_tax = false ) {
if ($inc_tax) :
return number_format( ($item['cost'] * $item['qty']) * (1 + ($item['taxrate']/100)) , 2, '.', '');
else :
return number_format( $item['cost'] * $item['qty'] , 2, '.', '');
endif;
}
/** Gets subtotal */
function get_subtotal_to_display() {
global $wpdeals;
if ($this->display_totals_ex_tax || !$this->prices_include_tax) :
$subtotal = wpdeals_price($this->order_subtotal);
if ($this->order_tax>0 && $this->prices_include_tax) :
$subtotal .= ' <small>'.$wpdeals->countries->ex_tax_or_vat().'</small>';
endif;
else :
// Calculate subtotal inc. tax
$subtotal = 0;
foreach ($this->items as $item) :
if (!isset($item['base_cost'])) $item['base_cost'] = $item['cost'];
$subtotal += round(($item['base_cost']*$item['qty']) * (($item['taxrate']/100) + 1), 2);
endforeach;
$subtotal = wpdeals_price( $subtotal );
if ($this->order_tax>0 && !$this->prices_include_tax) :
$subtotal .= ' <small>'.$wpdeals->countries->inc_tax_or_vat().'</small>';
endif;
endif;
return $subtotal;
}
/** Get a deals (either deals or variation) */
function get_deals_from_item( $item ) {
if (isset($item['variation_id']) && $item['variation_id']>0) :
$_deals = &new wpdeals_deals_variation( $item['variation_id'] );
else :
$_deals = &new wpdeals_deals( $item['id'] );
endif;
return $_deals;
}
/** Output items for display in emails */
function email_order_items_list( $show_download_links = false ) {
$return = '';
foreach($this->items as $item) :
$_deals = $this->get_deals_from_item( $item );
$return .= $item['qty'] . ' x ' . apply_filters('wpdeals_order_deals_title', $item['name'], $_deals);
$return .= ' - ' . strip_tags(wpdeals_price( $item['cost']*$item['qty'], array('ex_tax_label' => 1 )));
$item_meta = &new order_item_meta( $item['item_meta'] );
$return .= PHP_EOL . $item_meta->display( true, true );
if ($show_download_links) :
if ($_deals->exists) :
if ($_deals->is_downloadable()) :
$return .= PHP_EOL . ' - ' . $this->get_downloadable_file_url( $item['id'], $item['variation_id'] ) . '';
endif;
endif;
endif;
$return .= PHP_EOL;
endforeach;
return $return;
}
/** Output items for display in html emails */
function email_order_items_table( $show_item = false ) {
$return = '';
foreach($this->items as $item) :
$_deals = $this->get_deals_from_item( $item );
$file = $variation = '';
// $item_meta = &new order_item_meta( $item['item_meta'] );
// $variation = '<br/><small>' . $item_meta->display( true, true ) . '</small>';
if ($show_item) :
if ($_deals->exists) :
if ($_deals->is_downloadable()) :
$file = '<br/><small><a href="' . $this->get_downloadable_file_url( $item['id'], $item['variation_id'] ) . '">'.__('Download here', 'wpdeals').'</a></small>';
elseif( $_deals->is_vouchers()):
$file = '<br/><small>'.$this->get_voucher( $item['id'] ).'</small>';
endif;
endif;
endif;
$return .= '<tr>
<td style="text-align:left; border: 1px solid #eee;">' . apply_filters('wpdeals_order_deals_title', $item['name'], $_deals) . $file . '</td>
<td style="text-align:left; border: 1px solid #eee;">' . $item['qty'] . '</td>
<td style="text-align:left; border: 1px solid #eee;">';
if (!isset($item['discount_cost'])) $item['discount_cost'] = $item['cost'];
$return .= wpdeals_price( round(($item['discount_cost']*$item['qty']), 2) );
$return .= '
</td>
</tr>';
endforeach;
return $return;
}
/** Returns true if the order contains a downloadable deals */
function has_downloadable_item() {
$has_downloadable_item = false;
foreach($this->items as $item) :
$_deals = $this->get_deals_from_item( $item );
if ($_deals->exists && $_deals->is_downloadable()) :
$has_downloadable_item = true;
endif;
endforeach;
return $has_downloadable_item;
}
/** Generates a URL so that a customer can checkout/pay for their (unpaid - pending) order via a link */
function get_checkout_payment_url() {
$payment_page = get_permalink(get_option('wpdeals_pay_page_id'));
if (get_option('wpdeals_force_ssl_checkout')=='yes' || is_ssl()) $payment_page = str_replace('http:', 'https:', $payment_page);
return add_query_arg('pay_for_order', 'true', add_query_arg('order', $this->order_key, add_query_arg('order_id', $this->id, $payment_page)));
}
/** Generates a URL so that a customer can cancel their (unpaid - pending) order */
function get_cancel_order_url() {
global $wpdeals;
return $wpdeals->nonce_url( 'cancel_order', add_query_arg('cancel_order', 'true', add_query_arg('order', $this->order_key, add_query_arg('order_id', $this->id, trailingslashit( home_url() )))));
}
/** Gets a downloadable deals file url */
function get_downloadable_file_url( $item_id, $variation_id ) {
$download_id = ($variation_id>0) ? $variation_id : $item_id;
if ($this->user_id>0) :
$user_info = get_userdata($this->user_id);
if ($user_info->user_email) :
$user_email = $user_info->user_email;
endif;
endif;
return add_query_arg('download_file', $download_id, add_query_arg('order', $this->order_key, add_query_arg('email', $user_email, trailingslashit( home_url() ))));
}
/** Gets a voucher deals*/
function get_voucher( $item_id ) {
global $wpdb;
$voucher = '';
if ($this->user_id>0) :
$user_info = get_userdata($this->user_id);
if ($user_info->user_email)
$user_email = $user_info->user_email;
$results = $wpdb->get_results( $wpdb->prepare("SELECT * FROM ".$wpdb->prefix."wpdeals_permissions WHERE user_id = $this->user_id AND order_key = '$this->order_key' AND deal_id = $item_id" ));
if ($results) foreach ($results as $result) :
if ($result->order_id>0) :
$order = &new wpdeals_order( $result->order_id );
if ( $order->status!='completed' && $order->status!='processing' ) continue;
$voucher = sprintf( __('Voucher: %s', 'wpdeals'), $result->vouchers);
endif;
endforeach;
endif;
return apply_filters('wpdeals_email_get_voucher', $voucher);
}
/**
* Adds a note (comment) to the order
*
* @param string $note Note to add
* @param int $is_customer_note Is this a note for the customer?
*/
function add_order_note( $note, $is_customer_note = 0 ) {
$comment_post_ID = $this->id;
$comment_author = 'WPDeals';
$comment_author_email = 'wpdeals@' . str_replace('www.', '', str_replace('http://', '', site_url()));
$comment_author_url = '';
$comment_content = $note;
$comment_type = '';
$comment_parent = 0;
$commentdata = compact('comment_post_ID', 'comment_author', 'comment_author_email', 'comment_author_url', 'comment_content', 'comment_type', 'comment_parent', 'user_ID');
$commentdata['comment_author_IP'] = preg_replace( '/[^0-9a-fA-F:., ]/', '', $_SERVER['REMOTE_ADDR'] );
$commentdata['comment_agent'] = substr($_SERVER['HTTP_USER_AGENT'], 0, 254);
$commentdata['comment_date'] = current_time('mysql');
$commentdata['comment_date_gmt'] = current_time('mysql', 1);
$comment_id = wp_insert_comment( $commentdata );
add_comment_meta($comment_id, 'is_customer_note', $is_customer_note);
if ($is_customer_note) :
do_action( 'wpdeals_new_customer_note', $this->id, $note );
endif;
return $comment_id;
}
/**
* Adds a note (comment) to the order
*
* @param string $new_status Status to change the order to
* @param string $note Optional note to add
*/
function update_status( $new_status, $note = '' ) {
if ($note) $note .= ' ';
$new_status = get_term_by( 'slug', sanitize_title( $new_status ), 'deals_sales_status');
if ($new_status) :
wp_set_object_terms($this->id, $new_status->slug, 'deals_sales_status');
if ( $this->status != $new_status->slug ) :
// Status was changed
do_action( 'wpdeals_sales_status_'.$new_status->slug, $this->id );
do_action( 'wpdeals_sales_status_'.$this->status.'_to_'.$new_status->slug, $this->id );
$this->add_order_note( $note . sprintf( __('Order status changed from %s to %s.', 'wpdeals'), $this->status, $new_status->slug ) );
clean_term_cache( '', 'deals_sales_status' );
// Date
if ($new_status->slug=='completed') :
update_post_meta( $this->id, '_completed_date', current_time('mysql') );
endif;
// Sales
if ($this->status == 'on-hold' && ($new_status->slug=='processing' || $new_status->slug=='completed')) :
$this->record_deals_sales();
endif;
endif;
endif;
}
/**
* Cancel the order and restore the cart (before payment)
*
* @param string $note Optional note to add
*/
function cancel_order( $note = '' ) {
unset($_SESSION['order_awaiting_payment']);
$this->update_status('cancelled', $note);
}
/**
* When a payment is complete this function is called
*
* Most of the time this should mark an order as 'processing' so that admin can process/post the items
* If the cart contains only downloadable items then the order is 'complete' since the admin needs to take no action
* Stock levels are reduced at this point
* Sales are also recorded for deals
*/
function payment_complete() {
unset($_SESSION['order_awaiting_payment']);
if ( $this->status=='on-hold' || $this->status=='pending' ) :
$downloadable_order = false;
$voucher_order = false;
if (sizeof($this->items)>0) foreach ($this->items as $item) :
if ($item['id']>0) :
$_deals = $this->get_deals_from_item( $item );
if ( $_deals->exists && $_deals->is_downloadable() ) :
$downloadable_order = true;
continue;
elseif ( $_deals->exists && $_deals->is_vouchers() ) :
$voucher_order = true;
continue;
endif;
endif;
$downloadable_order = false;
break;
endforeach;
if ($downloadable_order || $voucher_order) :
$new_sales_status = 'completed';
else :
$new_sales_status = 'processing';
endif;
$new_sales_status = apply_filters('wpdeals_payment_complete_sales_status', $new_sales_status, $this->id);
$this->update_status($new_sales_status);
// Payment is complete so reduce stock levels
$this->reduce_order_stock();
do_action( 'wpdeals_payment_complete', $this->id );
endif;
}
/**
* Record sales
*/
function record_deals_sales() {
if ( get_post_meta( $this->id, '_recorded_sales', true )=='yes' ) return;
if (sizeof($this->items)>0) foreach ($this->items as $item) :
if ($item['id']>0) :
$sales = (int) get_post_meta( $item['id'], 'total_sales', true );
$sales += (int) $item['qty'];
if ($sales) update_post_meta( $item['id'], 'total_sales', $sales );
endif;
endforeach;
update_post_meta( $this->id, '_recorded_sales', 'yes' );
}
/**
* Reduce stock levels
*/
function reduce_order_stock() {
if (sizeof($this->items)>0) :
// Reduce stock levels and do any other actions with deals in the cart
foreach ($this->items as $item) :
if ($item['id']>0) :
$_deals = $this->get_deals_from_item( $item );
if ( $_deals->exists && $_deals->managing_stock() ) :
$old_stock = $_deals->_stock;
$new_quantity = $_deals->reduce_stock( $item['qty'] );
$this->add_order_note( sprintf( __('Item #%s stock reduced from %s to %s.', 'wpdeals'), $item['id'], $old_stock, $new_quantity) );
if ($new_quantity<0) :
do_action('wpdeals_deals_on_backorder_notification', $item['id'], $item['qty']);
endif;
// stock status notifications
if (get_option('wpdeals_notify_no_stock_amount') && get_option('wpdeals_notify_no_stock_amount')>=$new_quantity) :
do_action('wpdeals_no_stock_notification', $item['id']);
elseif (get_option('wpdeals_notify_low_stock_amount') && get_option('wpdeals_notify_low_stock_amount')>=$new_quantity) :
do_action('wpdeals_low_stock_notification', $item['id']);
endif;
endif;
endif;
endforeach;
$this->add_order_note( __('Order item stock reduced successfully.', 'wpdeals') );
endif;
}
/**
* List order notes (public) for the customer
*/
function get_customer_order_notes() {
$notes = array();
$args = array(
'post_id' => $this->id,
'approve' => 'approve',
'type' => ''
);
remove_filter('comments_clauses', 'wpdeals_exclude_order_comments');
$comments = get_comments( $args );
foreach ($comments as $comment) :
$is_customer_note = get_comment_meta($comment->comment_ID, 'is_customer_note', true);
if ($is_customer_note) $notes[] = $comment;
endforeach;
add_filter('comments_clauses', 'wpdeals_exclude_order_comments');
return (array) $notes;
}
}
/**
* Order Item Meta
*
* A Simple class for managing order item meta so plugins add it in the correct format
*/
class order_item_meta {
var $meta;
/**
* Constructor
*/
function __construct( $item_meta = '' ) {
$this->meta = array();
if ($item_meta) $this->meta = $item_meta;
}
/**
* Load item meta
*/
function new_order_item( $item ) {
if ($item) :
do_action('wpdeals_order_item_meta', $this, $item);
endif;
}
/**
* Add meta
*/
function add( $name, $value ) {
$this->meta[] = array(
'meta_name' => $name,
'meta_value' => $value
);
}
/**
* Display meta in a formatted list
*/
function display( $flat = false, $return = false ) {
global $wpdeals;
if ($this->meta && is_array($this->meta)) :
if (!$flat) $output = '<dl class="variation">'; else $output = '';
$meta_list = array();
foreach ($this->meta as $meta) :
$name = $meta['meta_name'];
$value = $meta['meta_value'];
if (!$value) continue;
// If this is a term slug, get the term's nice name
if (taxonomy_exists(esc_attr(str_replace('attribute_', '', $name)))) :
$term = get_term_by('slug', $value, esc_attr(str_replace('attribute_', '', $name)));
if (!is_wp_error($term) && $term->name) :
$value = $term->name;
endif;
else :
$value = ucfirst($value);
endif;
if ($flat) :
$meta_list[] = $wpdeals->attribute_label(str_replace('attribute_', '', $name)).': '.$value;
else :
$meta_list[] = '<dt>'.$wpdeals->attribute_label(str_replace('attribute_', '', $name)).':</dt><dd>'.$value.'</dd>';
endif;
endforeach;
if ($flat) :
$output .= implode(', ', $meta_list);
else :
$output .= implode('', $meta_list);
endif;
if (!$flat) $output .= '</dl>';
if ($return) return $output; else echo $output;
endif;
}
}